djust 1.2.0

StableSecurityReleased
Install
pip install djust==1.2.0

Before you upgrade, read Backwards incompatible changes in 1.2 below.

Welcome to djust 1.2!

These notes cover the new features, the backwards incompatible changes you should know about when upgrading from djust 1.1, and the security fixes in this release. When you're ready, follow the upgrade checklist.

djust 1.2 has three themes:

  • The Rust template engine now behaves like Django's. It supports every built-in tag and filter, loads your own template tag libraries, and raises the errors Django raises. Templates that relied on djust being more lenient than Django will now fail. Most of the incompatible changes are in this area.
  • Components are state. A component assigned on a view binds to that view, handles its own events, re-renders when a handler writes to it, and can patch just its own part of the page.
  • Security hardening. 1.2.0 fixes eleven published advisories. Several of the fixes change a default, so read Security hardening that changes behaviour before you deploy.

djust 1.1.4 was released the same day with the same security fixes for the 1.1 line; see the 1.1.4 release notes. If you are coming from 0.9.x, read Upgrading to djust 1.0 first.

Python and Django compatibility

djust 1.2 supports Python 3.10, 3.11, 3.12, 3.13 and 3.14, and Django 4.2 (4.2.29 or later), 5.0, 5.1 and 5.2. Django 6.0 is not supported yet. This is the same range as djust 1.1.

The only new runtime dependency is packaging>=21, used by the update notice.

What's new in djust 1.2

Django template compatibility

djust renders LiveView templates, and optionally every template in your project through DjustTemplateBackend, with a Rust engine. In 1.2 that engine closes nearly all of its remaining gaps with Django:

  • Every built-in tag and filter. All 25 of Django's built-in tags and 57 built-in filters are supported, including {% autoescape %}, {% filter %}, {% ifchanged %}, {% cycle %} with {% resetcycle %}, {% querystring %}, {% lorem %}, {% debug %} and {% cache %}.
  • Your own template tags and filters. {% load app_tags %} imports the Django template library and makes its tags and filters available to the Rust engine, in included and extended templates too.
  • Translations. {% translate %}, {% blocktranslate %}, the _("...") literal and the i18n, l10n and tz libraries work. They run Django's own code, so catalogs, plural rules and escaping are Django's.
  • Lookups against the live object. A dotted lookup such as {{ order.customer.get_full_name }} resolves one segment at a time against the Python object, as Django's Variable._resolve_lookup does: attributes, properties, zero-argument methods and callables all work.
  • Values render as Django renders them. {{ flag }} shows True, {{ items }} shows [1, 2], aware datetimes are converted to the current time zone, numbers follow the active locale, and Decimal values keep every digit.

The score to watch is Django's own template_tests suite run against DjustTemplateBackend: 1032 of the 1047 tests that reach a template engine pass (98.57%). When the scoreboard was first measured during the 1.2 cycle the figure was 43.55%. The remaining differences are listed in docs/TEMPLATE_COMPATIBILITY_LIMITS.md, and docs/TEMPLATE_BACKEND.md describes the backend.

Rendering also got cheaper. A render no longer copies the whole view state, and a {% for %} loop no longer copies it once per iteration, so the cost of a render depends on what the template reads rather than on everything the view holds (#2732, #2735, #2737). Large lists and querysets are no longer converted up front (#2717), and tags bridged from Django reuse one converted context per render (#2914).

Components are state

Three changes, each described by an ADR, make components behave like the rest of a view's state. See Components and the components guide.

  • Class-level components bind per view (ADR-031). nav = Tabs(active="overview") on a LiveView class gives each view instance its own bound component, self.nav, whose state is self.nav.state. Event handlers defined on the component class receive the bound component as self. {{ nav }} renders the component's template.
  • Component-scoped patches (ADR-032). When an event only changes one bound component and the page template uses it only as {{ nav }}, djust renders that component alone and patches its subtree instead of re-rendering the page.
  • Plain components hold state (ADR-033). self.rating.value = 5 in a handler re-renders, and the value survives a reconnect. Component.event_attrs() writes typed dj-value-* attributes, so a handler receives 4, not "4". Component(name="row-7") lets one handler serve several instances. Large components can narrow change detection with fingerprint_fields.

The component catalogue

djust.theming ships a browsable catalogue of the built-in components at /theme/components/. Each page shows a live preview with controls for the example's arguments, the view and template code to copy, a parameter table, events, accessibility notes, slots, and the source and stylesheet files that define the component. Moving between pages uses dj-navigate.

The same data is available from djust.theming.gallery.component_registry.describe_component(name), which also reports whether a component needs a script beyond djust's client. A project can wrap the catalogue in its own navigation by overriding the template djust_theming/catalogue/_document.html.

Bug capture and replay

Bug capture, introduced in 1.1, grows three pieces (Bug capture guide):

  • LiveView.time_travel_excluded_fields names state that must never leave the server in a shared capture. The new djust.V014 check warns when a view with time_travel_enabled = True holds a field that looks like personal data (an email address, a phone number) and doesn't exclude it.
  • djust replay <capture> opens a capture in the browser, and --inspect / --diff print it or diff its before and after state in the terminal.
  • LIVEVIEW_CONFIG["bug_capture_store"] sends captures too large for a URL by reference instead.

Getting started

  • djust init adds djust to an existing Django project: it appends a marked settings block, replaces a stock asgi.py with WebSocket routing, installs djust, channels and uvicorn[standard], and runs manage.py check. --dry-run shows the changes. Files with uncommitted changes are left alone unless you pass --force. See Installation.
  • djust new generates a themed starter page built from djust.theming components, with a theme switcher. --bare gives a one-button page instead. The generated Makefile runs from the project's .venv, so cd <name> && make dev works without activating it.
  • Update and security notices. djust new, djust init, the development server and manage.py check (as djust.U001) print one line when a newer release exists or the installed version has a published advisory. See Update and security notices.

Minor features

Templates

  • DjustTemplateBackend raises djust.template.DjustTemplateSyntaxError, a subclass of Django's TemplateSyntaxError, when a template is loaded, with Django's template_debug data for the debug page.
  • Importing DjustTemplateBackend no longer imports the LiveView stack (Channels, presence, the WebSocket consumer). from djust import LiveView is unchanged.
  • The new djust.C016 check warns when a DjangoTemplates entry comes before DjustTemplateBackend in TEMPLATES, which hides every template djust could render, or when the admin is installed without a DjangoTemplates entry.
  • The new djust.T018 check warns when a LiveView template references a variable the view never provides. See Type-safe template validation.

Events and the client

  • @debounce and @throttle are implemented in the client. The settings reach the browser on the mount frame, and a pending send is flushed on dj-submit and at page teardown.
  • dj-mouseenter and dj-mouseleave are event directives, with the platform's non-bubbling semantics. See Events.
  • The HTTP event fallback and djust.call() find the CSRF token when CSRF_COOKIE_NAME, CSRF_COOKIE_HTTPONLY or CSRF_USE_SESSIONS is set. djust injects <meta name="djust-csrf-cookie"> and <meta name="djust-csrf-token">, and window.djust.csrfToken() returns the token.
  • Cmd, Ctrl, Shift and middle clicks on dj-navigate and dj-patch links open a new tab, and the back button returns to the previous view.
  • A page restored from the browser's back/forward cache reconnects immediately.
  • A live_redirect flushes what the new view queued in mount(), such as page_title and flash messages.

Components

  • TableComponent gains filtering (filterable=True, and per column), aria-sort on sortable headers, a caption, a footer summary row and class hooks for each part of the table.
  • ModalComponent accepts a footer.
  • The theming tabs, dropdown and modal components take their state from the view, so {% theme_tabs active=tabs.active %} drives them.
  • theme_nav_group, theme_breadcrumb and theme_nav_item items can set navigate to render their link with dj-navigate.
  • otp_input works; include djust_components/otp-input.js on the page.
  • LiveComponent.trigger_update() re-renders the parent.

Audio

The opt-in AudioMixin and the {% djust_audio %} tag play short sound effects from a validated sound bank, activated by a user gesture. See Declarative audio.

Theming

  • The theming types (ThemePack, DesignSystem, LayoutStyle and the others) are importable from djust.theming.
  • djust.theming provides request-scoped helpers: get_active_pack, set_active_pack, get_active_mode, set_active_mode, reset_to_defaults and get_theme_css_url. ThemeManager gains set_pack() and reset().
  • A theme pack's icon style no longer restyles charts drawn by djust's components.

Testing

  • djust.testing.LiveComponentTestClient tests a LiveComponent without a parent view: LiveComponentTestClient(MyComponent).mount(**props), then send_event(), get_state() and render().
  • LiveViewTestClient.mount() exercises the WebSocket mount, and records which branch it took in client.via_websocket. See Testing LiveViews and the incompatible change below.

Observability and tooling

  • manage.py djust_observability_token prints the token the observability endpoints now require. manage.py djust_mcp sends it automatically.
  • djust.V008 accepts a return annotation such as -> str on a helper function or method defined in the same module as the view.

Backwards incompatible changes in 1.2

Most of the changes below make djust do what Django or the documentation already said it did. They are still breaking for code that depended on the old behaviour.

Security hardening that changes behaviour

Each of these closes a published advisory.

Built-in components escape their content. Every built-in component class, component template tag and Rust component renderer passes interpolated values through conditional_escape. HTML you pass into a content slot (a modal, card, sheet, popover, tab or accordion body, a header or footer, an icon) is shown as text unless it is marked safe.

Who is affected: anyone passing markup strings to built-in components.

What to do: pass mark_safe(...) or format_html(...) output, or another component's rendered output, for slots that should contain HTML. href and src values with a javascript:, vbscript: or non-image data: scheme now render as #; a value marked safe is passed through unchanged. The rich-text editor is the exception: its value is cleaned to an HTML allowlist that keeps formatting and removes scripts and event attributes.

djust admin enforces model permissions. DjustModelAdmin checks has_view_permission, has_add_permission, has_change_permission and has_delete_permission before mounting each page and again on every save, delete and delete_selected action. The default hooks now call user.has_perm() with the model's view_, add_, change_ and delete_ permission, as Django's ModelAdmin does. The index lists only the models the user has a permission on.

Who is affected: staff users who aren't superusers. Before 1.2, any active staff account had full access.

What to do: give those users or their groups the model permissions, or override the hooks on your DjustModelAdmin subclass.

The observability endpoints require a token. Besides DEBUG and a loopback client, a request to /_djust/observability/ must carry none of the proxy headers (X-Forwarded-For, Forwarded, X-Real-IP, X-Forwarded-Host, X-Forwarded-Proto) and must send the X-Djust-Observability-Token header. The token is derived from SECRET_KEY; set the DJUST_OBSERVABILITY_TOKEN environment variable to choose your own.

What to do: nothing if you use manage.py djust_mcp, which sends the header. For other tools, print the token with manage.py djust_observability_token and add the header.

Custom filters registered with is_safe=True keep unsafe input escaped. As in Django, the flag preserves safety; it doesn't grant it. A filter that returns a plain str built from user input is escaped even if it is registered is_safe=True.

What to do: a filter that produces markup must return mark_safe(...) or format_html(...) itself.

Custom tag return values are escaped. A simple_tag (or other custom tag handler) that returns a plain str is escaped, as Django escapes it. Return format_html(...) or mark_safe(...) for markup.

DataTable sorts, filters and groups only on declared columns. on_table_sort, on_table_filter, group-by and expression filters accept only keys listed in table_columns. A column is sortable unless it sets "sortable": False, and filterable only when it sets "filterable": True. table_default_sort is always accepted. Unknown column names are ignored.

What to do: add "filterable": True to every column users filter on, and list every column you sort or group by in table_columns.

Channel groups are joined after authorization. A LiveView joins its view, presence and db_notify groups only after check_permissions and the on_mount hooks pass, and those now run before on_view_mounted. A refused mount leaves every group.

Who is affected: code in on_view_mounted that assumed permission checks hadn't run yet.

Sticky children are re-authorized. A reused sticky {% live_render %} child re-runs its view and object permission checks on every parent render, and live_redirect carry-over re-checks object permissions. A child the user may no longer see is unmounted and the render fails as a fresh mount would.

Back-navigation state snapshots are bound to a digest of the session key. Snapshots issued by djust 1.1 fail verification once after the upgrade, and the view mounts fresh. No action is needed.

Resumable uploads belong to the session that started them. Resuming over the WebSocket and the HTTP upload-status endpoint answer only that session. Uploads started before the upgrade, or whose session key changed (for example at login), restart from the beginning.

Theme cookies are validated. A cookie naming an unregistered theme pack falls back to the session's pack, then the configured default. Layout names must match [A-Za-z0-9_-]{1,64}.

Templates are checked like Django's

These templates rendered on djust 1.1 and now raise, as they do on Django. Most raise TemplateSyntaxError when the template is loaded, even if the offending part is in a branch that never renders.

  • An unknown filter name, or an unknown tag, even inside {% if False %}.
  • A variable or attribute that starts with an underscore: {{ _x }}, {{ obj._private }}, {{ obj.__class__ }}.
  • A filter with the wrong number of arguments ({{ name|upper:"x" }}), or with two arguments.
  • A filter argument that can't be parsed or resolved, such as a misspelt variable name ({{ text|wordwrap:widht }}). This raises at render time.
  • An empty {{ }}, a duplicate {% block %} name, and malformed {% if %}, {% url %}, {% include %} and {{ }} expressions.
  • {% cycle 'a' %} with a single value that isn't a named cycle.

These raise at render time:

  • {% for x in value %} where value isn't iterable (for example an integer). None still renders the {% empty %} block.
  • first, last, random, escapeseq, safeseq, unordered_list and phone2numeric on a value they can't iterate, index or lowercase (for example {{ 5|first }}), and get_digit and divisibleby on a date or datetime.
  • {% url %} with a name or arguments that don't reverse raises NoReverseMatch. Use {% url ... as var %} where a failed reverse is expected; var is then ''.
  • A zero-argument method that raises during a lookup propagates the exception. PermissionDenied and Http404 raised while rendering give a 403 or 404 rather than a 500.

What to do: run your test suite and load every template. The error messages are Django's.

Templates render values the way Django does

Pages may look different after the upgrade:

  • Time zones. With USE_TZ = True, aware datetimes are converted to the current time zone before formatting, as Django does. djust 1.1 printed them in UTC. Naive datetimes are unchanged.
  • Localization. Numbers use the active language's decimal separator, and thousand separators when USE_THOUSAND_SEPARATOR is on.
  • Plain values. {{ value }} renders True, None, [1, 2], {'a': 1} and (1, 2) like Django. Set LIVEVIEW_CONFIG["django_value_repr"] = False to restore the 1.1 rendering while you update templates.
  • Deeper lookups. Templates can reach attributes, properties and methods that used to render empty. A custom tag handler that received a model from the context as a dict now receives an object, so ctx["user"]["username"] must become ctx["user"].username. Names starting with _ stay refused.
  • Callables in the context are called, as in Django, instead of rendering None.
  • |date formats a full datetime instead of echoing it.
  • {% cache %} skips its body on a cache hit, including any side effects in it.

Values sent to the browser

If client JavaScript reads LiveView state, check these:

  • Booleans arrive as true and false, not 1 and 0.
  • Decimal values arrive as strings with every digit, not as floats.
  • Datetimes are formatted like DjangoJSONEncoder: milliseconds, and Z for UTC.

Change detection and rendering

  • In-place mutation re-renders. self.items.append(x) or self.columns[key].append(card) in a handler now produces a patch. Previously it could be missed. Views that relied on the missed render will see more updates.
  • Lists of model instances aren't re-queried on every event. A list[Model] assigned in mount() keeps its instances, so an unsaved edit to a row renders instead of being overwritten from the database. Call set_changed_keys("rows") after mutating an instance in place, and re-query explicitly when you want fresh rows.
  • The tick loop honours _skip_render. A handle_tick() that sets self._skip_render = True no longer renders. A handler that sets both _skip_render and a forced render now renders on every path.
  • URL keyword arguments are decoded. A LiveView mounted over the WebSocket receives "Core UI", not "Core%20UI". Remove any unquote() you added to work around this.

Components

  • Class-level components are bound. self.nav is now a BoundComponent, so isinstance(self.nav, Tabs.State) is False; read self.nav.state. Attribute reads and writes are forwarded to the state.
  • state is a reserved Component keyword argument and raises TypeError.
  • Built-in components emit typed dj-value-* attributes instead of data-value. The client still reads data-value.
  • TableComponent.sort_by() takes column (was column_key), TabsComponent.activate_tab() takes tab=, and table row checkboxes carry dj-value-row-id instead of data-row-id.
  • {% modal %}, {% confirm_dialog %}, ExportDialog and BottomSheet draw their backdrop as a separate .dj-scrim element and no longer use an inline onclick. Update any CSS that targeted the old structure.
  • multi_select checkboxes also send option; a handler with a strict signature must accept it.
  • Sixteen components need a script from djust_components/ on the page (for example countdown.js or scroll-spy.js). describe_component() and the catalogue say which.
  • The component storybook is now the component catalogue at /theme/components/ (was /theme/gallery/storybook/), and the theme gallery is at /theme/themes/ (was /theme/gallery/). The old paths redirect permanently.
  • The older djust.components gallery moved from /theme/components/ to /theme/components-legacy/, without a redirect, because the catalogue now uses that path.
  • Python names were renamed to match: theming.gallery.storybook is theming.gallery.catalogue, Storybook*View is Components*View, {% storybook_preview %} and {% storybook_thumbnail %} are {% component_preview %} and {% component_thumbnail %}, URL names storybook* are components*, and the pages' CSS prefix sb- is dc-.

Testing utilities

These come from real upgrades; expect a handful of test failures that are not app regressions.

  • LiveViewTestClient.mount() takes the WebSocket branch by default. It sets _websocket_session_id and the other attributes a live connection has, so code guarded by hasattr(self, "_websocket_session_id") (claiming a seat, presence tracking, subscriptions) now runs in tests. Each client gets its own synthetic session id. Call client.mount(via_websocket=False) for a test about the HTTP prerender.
  • send_event() raises djust.testing.NoHandlerFoundError for an event with no handler, instead of returning {"success": False, ...}. Pass raise_on_missing=False to get the old return value.
  • Anonymous presence for a session that has no key uses a per-view identity, so two test clients count as two presences instead of one.

System checks

manage.py check can report new messages after the upgrade. They are warnings or information, not errors, unless you run with --fail-level:

  • djust.T018 warns about template variables the view never provides. It skips templates that use {% extends %} and says so in one Info message; run manage.py djust_typecheck for those. Silence a name with {# djust_typecheck: noqa name #}, or the whole check with DJUST_CONFIG = {"suppress_checks": ["T018"]}.
  • djust.C016 warns about the order of TEMPLATES entries.
  • djust.V014 warns about personal-data fields on views with time_travel_enabled = True that aren't in time_travel_excluded_fields.
  • djust.U001 reports a newer release or a published advisory. It caches the advisory list for a day (in ~/.cache/djust/updates.json unless XDG_CACHE_HOME or DJUST_CACHE_DIR is set), so after an advisory is corrected it can still report it until the cache expires. Delete the file to refresh it now. Turn the notice off with DJUST_CONFIG = {"update_check": False} or DJUST_NO_UPDATE_CHECK=1.
  • djust.C013 warns that the collected client.min.js is older than the installed one. It fires after any upgrade until you run collectstatic.
  • djust.V008 now accepts a primitive return annotation on a helper or method defined in the same module. An annotation on a helper imported from another module is still not read, so keep the # noqa: V008 there.

Features removed in 1.2

  • LIVEVIEW_CONFIG["template_resolve_lazy"] is gone. Earlier 1.2 release candidates used it to switch off live-object lookups. Setting it now does nothing and raises no warning.
  • The static files djust/security.js, djust/decorators.js and djust/js/pwa.js are no longer shipped. djust's client never loaded them; remove any <script> tag or {% static %} reference to them. The dj-offline="..." value form they implied is not supported.
  • The client-side StateBus module was deleted. It was never exposed on window.djust. @client_state and @optimistic remain inert decorators.

Deprecated features

djust 1.2 deprecates nothing new. The three symbols deprecated in 0.9.7, @event, LiveViewForm and the _legacy theming module, still work and still warn. See API stability and deprecations.

Security

djust 1.2.0 and 1.1.4 include fixes for the following advisories. Each advisory has the details and affected versions.

AdvisoryArea
GHSA-fccp-5h88-g34jDataTable sorting, filtering and grouping
GHSA-jv2m-fcq9-94xfdjust admin (admin_ext) permissions
GHSA-c44q-w252-mr67Observability endpoints
GHSA-hc2m-gvfj-x6r3Built-in components
GHSA-r372-rrpw-5cgjBuilt-in components
GHSA-j23m-jxwp-m3vqTheming
GHSA-6q7c-hvpc-ff2qPresence and channel groups
GHSA-5ffg-p52h-v2phBack-navigation state snapshots
GHSA-74vj-mpp4-45cgSticky {% live_render %} children
GHSA-7fcf-23mf-rhhmResumable uploads
GHSA-p9vp-rh5f-2cvqCustom template filters (fixed in 1.2.0rc2)

GHSA-3hp5-hxf8-qc48, the HTTP POST fallback skipping authorization, was fixed during the 1.2 cycle in 1.2.0rc8 and on the 1.1 line in 1.1.3.

The template engine work also closed several hardening gaps, listed in the CHANGELOG's Security sections:

  • A key removed from a view's context stops rendering; before, its last value stayed in the Rust state.
  • A mark_safe grant no longer outlives the value it was granted for.
  • Password hashes and other sensitive model fields are withheld from templates and custom tag handlers on every path, including nested containers, the JIT serializer and the Rust queryset serializer.
  • A template name can't escape its template directory ({% include "../secret.txt" %}).
  • Component methods that change state (mount, unmount, update, trigger_update) can't be called from a template.

Bug fixes

1.2 includes several hundred bug fixes. The main areas:

  • Templates: inheritance and {{ block.super }}, {% regroup %} over objects, dictionary views and key types, Decimal, datetime and time-zone handling, and several crashes on unusual input.
  • Client and VDOM: scoped listeners are rebound or removed when their attributes change, dj-keydown.enter bindings fire, and patches arriving during an event no longer force a full re-render.
  • Runtime: async work renders under the render lock, start_async from a db_notify-released event runs, and use_actors views mount their real template.
  • Components: modal, alert and badge dismiss, table sorting, pagination and selection, and buttons inside modals and sheets all reach the server.
  • Admin: admin pages no longer return a 500 under the recommended TEMPLATES setup.
  • Scaffolding: djust new --with-streaming, --from-schema, --with-auth and --with-presence generate working projects.
  • Packaging: collectstatic with ManifestStaticFilesStorage (and WhiteNoise) no longer fails on a missing client.min.js.map.

The full list is in the 1.2.0rc1 to 1.2.0rc11 sections of the CHANGELOG. 1.2.0 itself is identical to 1.2.0rc11 apart from the version number.

Upgrading from 1.1

  1. Upgrade and collect static files.

    pip install "djust==1.2.0"
    python manage.py collectstatic --noinput
    

    djust.C013 stays until collectstatic has run.

  2. Run the checks. python manage.py check reports the new T018, C016, V014 and U001 messages. Fix or silence them; for templates using {% extends %}, also run python manage.py djust_typecheck.

  3. Run your test suite, and expect these test-only changes:

    • add via_websocket=False to LiveViewTestClient.mount() calls that test the HTTP prerender;
    • catch NoHandlerFoundError (or pass raise_on_missing=False) where a test sends an event with no handler.
  4. Load every template. Template errors that djust 1.1 ignored are now raised when a template is loaded or rendered. Rendering each page once, in a test or by hand, finds them.

  5. Check what changed on the page: timestamps (now in local time), number formatting, {{ }} output of booleans, lists and dicts, and any {% url %} whose reverse used to fail silently.

  6. Mark component HTML safe. Wrap markup passed to built-in component slots in mark_safe() or format_html(), and make custom filters and tags that produce markup return safe strings.

  7. Review permissions:

    • grant model permissions to staff who use djust admin;
    • add "filterable": True to DataTable columns that users filter;
    • give observability clients other than djust_mcp the X-Djust-Observability-Token header.
  8. Update client JavaScript and CSS that reads booleans, decimals or datetimes from state, or targets the modal backdrop, data-value or data-row-id.

  9. Update links and imports to the theme gallery and component catalogue if you used the old paths or Storybook* names.

  10. Remove workarounds that 1.2 makes unnecessary: unquote() on URL kwargs, # noqa: V008 on same-module helpers annotated -> str, and any template_resolve_lazy setting.

In-flight resumable uploads restart after the upgrade, and back-navigation snapshots from 1.1 are ignored once. Neither needs any action.

All changes in 1.2.0

The CHANGELOG.md entry for djust 1.2.0. Most of the changes are recorded under its 11 pre-releases: 1.2.0rc1, 1.2.0rc2, 1.2.0rc3, 1.2.0rc4, 1.2.0rc5, 1.2.0rc6, 1.2.0rc7, 1.2.0rc8, 1.2.0rc9, 1.2.0rc10, 1.2.0rc11.

The first stable release of the 1.2 line. The code is identical to 1.2.0rc11; see the 1.2.0rc1 to 1.2.0rc11 sections below for every change since 1.1, including the behaviour changes listed under rc11. 1.1.4 carries the same fixes for the 1.1 line.

Security

All releases · Atom feed