diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..2108e34 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,30 @@ +name: CI + +on: + pull_request: + branches: [main, development] + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.12", "3.13"] + env: + PYTHONPATH: . + + steps: + - uses: actions/checkout@v6 + + - name: Install uv + uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + cache-dependency-glob: "uv.lock" + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: uv sync --locked + + - name: Run tests + run: uv run pytest -rs diff --git a/.gitignore b/.gitignore index ffcda29..4940d00 100644 --- a/.gitignore +++ b/.gitignore @@ -37,7 +37,6 @@ package-lock.json pnpm-lock.yaml # CSS/JS compiled files -unfold_extra/static/*/css/styles.css unfold_extra/static/*/js/bundle.js # Environment diff --git a/.sourcery.yaml b/.sourcery.yaml new file mode 100644 index 0000000..85c47ea --- /dev/null +++ b/.sourcery.yaml @@ -0,0 +1,6 @@ +# Sourcery configuration +# https://docs.sourcery.ai/ + +# Ignore compiled/generated files +ignore: + - unfold_extra/static/unfold_extra/css/styles.css diff --git a/CHANGELOG.rst b/CHANGELOG.rst new file mode 100644 index 0000000..0f05464 --- /dev/null +++ b/CHANGELOG.rst @@ -0,0 +1,304 @@ +========= +Changelog +========= + +All notable changes to django-unfold-extra are documented here. +This project adheres to `Semantic Versioning `_. + + +0.3.0a0 (2026-06-17) +==================== + +Breaking Changes: +----------------- + +* Require Python >=3.12; drop support for 3.9 / 3.10 / 3.11 (required by django-unfold >=0.92) +* Require django-unfold >=0.92 for the reworked ``unfold.mixins`` API; ``UnfoldCMSPluginBase`` now composes ``FormFieldModelAdminMixin``, ``ActionModelAdminMixin``, ``DatasetModelAdminMixin`` and ``NestedInlinesModelAdminMixin`` (previously the removed ``BaseModelAdminMixin``) + +Other: +------ + +* Migrate dependency management from Poetry to uv +* Restore djangocms-versioning to the test dependency group +* Update CI to run on uv with a Python 3.12 / 3.13 matrix + + +0.2.9 (2026-06-17) +================== + +Features: +--------- + +* Update django-cms to 5.0.8 and re-sync the pagetree icon font (ff5b285) + +Bug Fixes: +---------- + +* Handle missing fields in CMS form initialization for locked URLs (d2be3de) +* Fix hint rendering for locked URL fields (cf35b8e) + +Other: +------ + +* Update Tailwind CSS to v4.1.7 (f3fc10e) + + +0.2.8 (2026-05-29) +================== + +Features: +--------- + +* Add full-width CMS modal (cf01eb4) + +Bug Fixes: +---------- + +* Fix CMS modal breadcrumb style (0daa52b) +* Remove border radius on the text editor toolbar (8f21c29) + + +0.2.7 (2026-05-12) +================== + +Bug Fixes: +---------- + +* Add inline-safe variant of ``PageSelectWidget`` to handle cloned rows (f22c19f) + + +0.2.6 (2026-05-11) +================== + +Features: +--------- + +* Add Unfold objecttools buttons for versioning templates (b17f70b) + +Bug Fixes: +---------- + +* Fix styling for django-cms versioning template confirmation actions (3a42bf2) + + +0.2.5 (2026-05-11) +================== + +Bug Fixes: +---------- + +* Update styles for CMS modal breadcrumbs and editor layout (1260d25) + + +0.2.4 (2026-05-10) +================== + +Bug Fixes: +---------- + +* Fix fieldset spacings in page settings (7d18bab) + + +0.2.3 (2026-05-10) +================== + +Bug Fixes: +---------- + +* Prevent CMS toolbar from overlapping changelist filters (832d0b1) +* Ensure stock Site admin registers before custom unregister/re-register (a3fe7d5) + + +0.2.2 (2026-05-08) +================== + +Features: +--------- + +* Add search fields (domain, name) to ``SiteAdmin`` (5db5830) + +Other: +------ + +* Remove unused Tailwind CSS declarations from ``styles.css`` (d324734) + + +0.2.1 (2026-04-07) +================== + +Features: +--------- + +* Patch the back-button template (f5ab0c9) + +Other: +------ + +* Documentation updates for Django CMS configuration and package support + + +0.2.0 (2026-04-03) +================== + +Other: +------ + +* Improve README and clarify package support; milestone release (a06575b) + + +0.1.14 (2026-03-31) +=================== + +Features: +--------- + +* Add django-versatileimagefield contrib integration (9fb2469) + + +0.1.13 (2026-03-16) +=================== + +Features: +--------- + +* Add initial test setup with models, admin, and smoke tests (1e55889) +* Migrate dependency management to Poetry (9360b03) +* Add CI workflow configuration and build badge (1384f56) +* Support CMS user settings and add Playwright frontend verification (071de00) +* Implement language synchronization between Unfold and CMS via a custom ``set_language`` view (d7e24cc) +* Add language switch with ``reload_window`` parameter for CMS sideframe support (885c26e) +* Unify the add-object location (20fb035) +* Add setting for the CMS page add button (511af83) +* Add ``UnfoldCMSPluginBase`` and demo plugin (4533228) +* Add ``PageLinkPlugin`` and support for custom widgets (Fixes #1) (b5532b4) +* Add ``DJANGOCMS_VERSIONING_ON_PUBLISH_REDIRECT`` setting (b6207c4) + +Bug Fixes: +---------- + +* Patch Unfold's ``header_title`` to prevent admin page crashes (a55b5a6) +* Ensure Site is unregistered only if present in the registry (89de475) +* Fix font files and color configuration retrieval (8c1d15f) +* Improve CMS text plugin (tiptap) styles (Fixes #4) (4e7691b) +* Fix CMS ``UserSettings`` Unfold admin navigation and history (9038ea0) + +Other: +------ + +* Refactor cms.pagetree styling (4b6a7b8) + + +0.1.11 (2026-01-31) +=================== + +Other: +------ + +* Update README for Django Unfold version and upgrade Tailwind CSS to v4.1.18 (d207c88) + + +0.1.10a (2025-11-08) +==================== + +Other: +------ + +* Disable source maps in the build configuration (3b634a8) + + +0.1.9 (2025-11-08) +================== + +Bug Fixes: +---------- + +* Prevent sidebar content from being hidden under the django-cms topbar (e6d6f0f) + +Other: +------ + +* Remove source-map comment from CSS (e606b3b) + + +0.1.8 (2025-10-16) +================== + +Bug Fixes: +---------- + +* Support page content admin across use cases (admin / sidepanel / modal) (bec054d) +* Fix modal page content settings (7fbdef1) + + +0.1.7 (2025-09-29) +================== + +Features: +--------- + +* Add theme synchronization between the iframe (Unfold) and parent window (django-cms) (8bbebc7) + +Bug Fixes: +---------- + +* Support switching theme from CMS and Unfold simultaneously (7fb7762) +* Apply theme on page load (ade5d56) +* Conditionally unregister CMS admin models (5c2077e) + + +0.1.5 (2025-09-26) +================== + +Features: +--------- + +* Integrate custom change form for Page and PageContent admins (2c1c9c8) + +Bug Fixes: +---------- + +* Improve action buttons in ``PageContentAdmin`` (a533594) +* Fix CMS pagetree display with the Unfold sideframe (1da821b) +* Disable submit-row overwrite (64736cb) +* Fix djangocms-versioning changelist (c8301f9) +* Remove versioning breadcrumbs template (Unfold no longer uses breadcrumbs) (e87cf39) +* Fix CMS styling (sideframe, pagetree) (c710c17) + + +0.1.4 (2025-09-24) +================== + +Other: +------ + +* Maintenance release (44af9d0) + + +0.1.3 (2025-09-24) +================== + +Features: +--------- + +* Add djangocms-text support (c3da302) + +Other: +------ + +* Update django-cms dependency (94b2933) + + +0.1.2 (2025-06-12) +================== + +Features: +--------- + +* Initial project setup for django-unfold-extra (a62effe) +* Add View restrictions and Page permissions support (d8a3e1e) +* Add auth group defaults (ddeca8e) + +Bug Fixes: +---------- + +* Add custom ``change_form.html`` for plugins to fix saving plugin content via modal (217a49e) diff --git a/README.md b/README.md index 4803e5a..a79fedd 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,21 @@ # Django Unfold Extra +[![PyPI - Version](https://img.shields.io/pypi/v/django-unfold-extra.svg?style=for-the-badge)](https://pypi.org/project/django-unfold-extra/) [![Build](https://img.shields.io/github/actions/workflow/status/metaforx/django-unfold-extra/ci.yml?style=for-the-badge&event=pull_request)](https://github.com/metaforx/django-unfold-extra/actions/workflows/ci.yml) -Unofficial extension for Django Unfold Admin. Adds support for django-parler and django-cms to the modern and +Unofficial extension for Django `Unfold Admin`. Adds support for `Django CMS` and other common django packages to support the modern and clean [Django Unfold](https://github.com/unfoldadmin/django-unfold) admin interface. +This package can be combined with the additional non-mandatory `Unfold Modal` package to provide a unified admin experience. +See the [django-unfold-modal](https://github.com/metaforx/django-unfold-modal) package for more details. + ## Overview -Django Unfold Extra enhances the beautiful Django Unfold admin interface with additional functionality for: +Django Unfold Extra enhances the Django Unfold admin interface with additional functionality for: +- **django-cms**: Integration with Django CMS 5.0, including theme, page tree, plugins, and versioning support - **django-parler**: Multilingual support for your Django models -- **django-cms**: Integration with Django CMS 5.0 +- **versatile-image**: Improved integration with django-versatileimagefield, including preview and ppoi +- **Unfold auto-update**: Styles can be updated from the official Unfold package via npm +- **Theme-Sync**: Use either Unfold or Django CMS switcher to control themes. You can run both at the same time, with or without both controls enabled. ![img.png](docs/img/cms-pagetree.png) ![img.png](docs/img/parler-tabs.png) @@ -16,10 +23,10 @@ Django Unfold Extra enhances the beautiful Django Unfold admin interface with ad This package maintains the clean, modern aesthetic of Django Unfold while adding specialized interfaces for these popular Django packages. -It uses CSS overrides where possible. As Django CMS uses many '!important' flags, I had to rebuild pagetree.css to -remove some conflicting style definitions. +It uses unobtrusive template and CSS-styling overrides where possible. As Django CMS uses many '!important' flags, +pagetree.css had to be rebuilt from sources to remove some conflicting style definitions. -> **Note:** Work in progress. Django CMS support is working but not yet fully integrated. Use at your own risk. +> **Note:** This package is already used in production but expect additional implementation work. I suggest using it if most of your cms plugins are custom-built. ## Installation @@ -32,32 +39,46 @@ remove some conflicting style definitions. ```python INSTALLED_APPS = [ - # Unfold theme - 'unfold', - 'unfold_extra', - 'unfold_extra.contrib.cms', # if extra packages - 'unfold_extra.contrib.parler', - 'unfold_extra.contrib.auth' # you likely want to use your own implementation - 'unfold_extra.contrib.sites' - - # Your apps - # ... + # Unfold theme + "unfold", + "unfold_extra", + # Optional integrations + "unfold_extra.contrib.cms", + "unfold_extra.contrib.parler", + "unfold_extra.contrib.auth", # you will likely want a custom auth admin + "unfold_extra.contrib.sites", ] ``` -Make sure you've set up Django Unfold according to its documentation. -https://github.com/unfoldadmin/django-unfold +Make sure you have already configured Django Unfold and any optional upstream packages you use +such as django CMS and django-parler. + +### Basic configuration + +Add the shared styles integration to your `settings.py`: + +```python +from django.templatetags.static import static + +UNFOLD = { + "STYLES": [ + lambda request: static("unfold_extra/css/styles.css"), # additional styles for supported integrations + ], +} +``` ## Usage -### django-parler Support +### Integrations + +#### django-parler Support - UnfoldTranslatableAdminMixin - UnfoldTranslatableStackedAdminMixin - UnfoldTranslatableTabularAdminMixin - TranslatableStackedInline, TranslatableTabularInline -#### Example use: +##### Example use: ```python class TranslatableAdmin(UnfoldTranslatableAdminMixin, BaseTranslatableAdmin): @@ -72,25 +93,186 @@ class MyInlineAdmin(TranslatableStackedInline): extra = 0 # django inline settings ``` -### django-cms Support +#### Versatile Image Support + +- Improved unfold integration via CSS only. + +#### Django Auth, Sites + +- Adds Unfold-based admin registrations for `django.contrib.auth` and `django.contrib.sites`. -- Theme integration in django admin (partial support in frontend) -- Pagetree +## CMS Integration + +### Features + +Unfold support for all common Django CMS admin pages and plugins including: +- Pagetree (with add button and language switcher) - PageUser, PageUserGroup, GlobalPagePermission -- Versioning (partial support) +- CMS Versioning admin and page inline +- CMS User Settings - Modal support -- Not supported: Filer -Support is automatically applied. Currently, it does not support customization besides compiling your own unfold_extra -styles. +Untested and likely not supported: +- Django Filer + +Customization is possible by compiling your own unfold_extra styles. -#### Frontend django CMS support +### Configuration -Add `unfold_extra_tags` to your base HTML template to after loading all CSS styles. This adds additional styles to hide -unfold admin like header in the django cms, for example, for page settings etc. +Add the django CMS-specific settings to your `settings.py`: + +```python + # ... + "unfold_extra.contrib.cms", # required to patch template loading order + "unfold", + # ... +``` + +```python +from django.templatetags.static import static + +UNFOLD = { + "STYLES": [ + lambda request: static("unfold_extra/css/styles.css"), # additional styles for supported integrations + ], + "SCRIPTS": [ + lambda request: static("unfold_extra/js/theme-sync.js"), # keep django CMS theme in sync with Unfold + ], +} +``` + +Optional: let Unfold be the single theme switch and hide theme toggle of Django CMS in toolbar. + +```python +CMS_COLOR_SCHEME_TOGGLE = False #default option +``` + +Optional: move the CMS "New Page" button into Unfold's header. Set this to +`False` to keep the button in the CMS pagetree body. Default of Django CMS. + +```python +UNFOLD_CMS_HEADER_ADD_BUTTON = True #default option +``` + +### Base Template Integration + +Add `{% unfold_extra_styles %}` and `{% unfold_extra_theme_sync %}` from `unfold_extra_tags` +to your base HTML template. + +- Enables Unfold admin colors in django CMS +- Syncs the Unfold theme with django CMS (light/dark/auto) +- Adds Unfold-styled django CMS plugin admin support ```html -{% load cms_tags sekizai_tags unfold_extra_tags%} +{% load static cms_tags sekizai_tags unfold_extra_tags %} + + + + {% block title %}{% endblock title %} + + {% render_block "css" %} + {% unfold_extra_styles %} + {% unfold_extra_theme_sync %} + ... + +... + +``` + +### Language Sync (Unfold ↔ CMS) + +To keep the Unfold language switcher and the CMS toolbar/admin in sync, register +`cms_set_language` from `unfold_extra.views` as the `set_language` URL +**before** Django's i18n URLs: + +```python +from unfold_extra.views import cms_set_language + +urlpatterns = [ + path("i18n/setlang/", cms_set_language, name="set_language"), + path("i18n/", include("django.conf.urls.i18n")), + # ... +] +``` + +When a user switches language via Unfold's sidebar, `cms_set_language` updates +the CMS `UserSettings.language` before the redirect so the CMS toolbar renders +in the same language on the next request. + +### CMS Plugins With Unfold Styling + +For the general django CMS plugin model, see the official guide: +https://docs.django-cms.org/en/stable/how_to/09-custom_plugins.html + +This package only changes the admin side: +- use `UnfoldCMSPluginBase` instead of `CMSPluginBase` +- use `UnfoldStackedInline` or `UnfoldTabularInline` for plugin inlines + + +```python +# cms_plugins.py +from unfold_extra.contrib.cms.plugins import UnfoldCMSPluginBase +from .models import HeroPluginModel + +@plugin_pool.register_plugin +class HeroPlugin(UnfoldCMSPluginBase): + model = HeroPluginModel + name = _("Hero") + render_template = "plugins/hero.html" +``` + +Most Unfold/Django admin edit options also work on plugins, including +`compressed_fields`, `fieldsets`, `readonly_fields`, `autocomplete_fields`, +`raw_id_fields` lookup popups, `radio_fields`, and `formfield_overrides`. + +Use `cms_widget_overrides` when you need to replace plugin form widgets that +should use Unfold-compatible widgets: + +```python +from unfold_extra.contrib.cms.plugins import UnfoldCMSPluginBase + + +class MyPlugin(UnfoldCMSPluginBase): + cms_widget_overrides = { + **UnfoldCMSPluginBase.cms_widget_overrides, + SomeField: MyCustomWidget, + } +``` + +See Unfold docs: +- https://unfoldadmin.com/docs/configuration/modeladmin/ +- https://unfoldadmin.com/docs/tabs/fieldsets/ + +### Page Select Widget + +Unfold-styled replacements for django CMS's `PageSelectWidget`: + +- `UnfoldPageSelectWidget` — use in regular admin forms and CMS plugin forms. +- `UnfoldPageSelectInlineWidget` — use in Django admin inlines. Django admin's + inline cloning leaves `__prefix__` inside the widget's JSON config; this + variant ships a small JS patch so the site/page change handler binds to + dynamically added rows. + +```python +from cms.forms.fields import PageSelectFormField +from unfold_extra.contrib.cms.widgets import ( + UnfoldPageSelectInlineWidget, + UnfoldPageSelectWidget, +) + + +class MyInlineForm(forms.ModelForm): + page = PageSelectFormField(widget=UnfoldPageSelectInlineWidget()) +``` + +### Frontend django CMS Support + +Add `unfold_extra_tags` to your base HTML template after loading all CSS styles. +This adds additional styles to integrate django CMS with Unfold Admin and exposes `"COLORS"` from Unfold settings on +the public website for authenticated django-cms admin users. + +```html +{% load cms_tags sekizai_tags unfold_extra_tags %} ... {% render_block "css" %} @@ -99,82 +281,81 @@ unfold admin like header in the django cms, for example, for page settings etc. ``` -#### Custom compilation via npm/pnpm (see src/package.json) - -```json -{ - "name": "django-unfold-extra", - "description": "Enhancing Django Unfold to support additional packages", - "scripts": { - "update:unfold-deps": "curl -s https://raw.githubusercontent.com/unfoldadmin/django-unfold/main/package.json | jq -r '[\"tailwindcss@\" + .dependencies.tailwindcss, \"@tailwindcss/typography@\" + .devDependencies[\"@tailwindcss/typography\"]] | join(\" \")' | xargs npm install --save-dev", - "update:unfold-css": "curl -o css/styles.css https://raw.githubusercontent.com/unfoldadmin/django-unfold/main/src/unfold/styles.css", - "update:unfold": "npm run update:unfold-deps && npm run update:unfold-config", - "tailwind:build": "npx @tailwindcss/cli -i css/unfold_extra.css -o ../static/unfold_extra/css/styles.css --minify", - "tailwind:watch": "npx @tailwindcss/cli -i css/unfold_extra.css -o ../static/unfold_extra/css/styles.css --watch --minify" - }, - "devDependencies": { - "@tailwindcss/cli": "^4.1.7", - "@tailwindcss/typography": "^0.5.16", - "tailwindcss": "^4.1.7" - } -} -``` +### Custom Compilation via npm/pnpm -#### Change colors for Django CMS - -Currently, you have to manually update src/css/unfold_extra.css and compile a new styles.css extending unfold styles. - -1. Fetch the latest Unfold version using `update:unfold-deps` and `update:unfold-css` -2. Update config `update:unfold` -3. Add changes `tailwind:watch` and `tailwind:build` - -```css -html:root { - --dca-light-mode: 1; - --dca-dark-mode: 0; - --dca-white: theme('colors.white'); - --dca-black: theme('colors.black'); - --dca-shadow: theme('colors.base.950'); - --dca-primary: theme('colors.primary.600'); - --dca-gray: theme('colors.base.500'); - --dca-gray-lightest: theme('colors.base.100'); - --dca-gray-lighter: theme('colors.base.200'); - --dca-gray-light: theme('colors.base.400'); - --dca-gray-darker: theme('colors.base.700'); - --dca-gray-darkest: theme('colors.base.800'); - --dca-gray-super-lightest: theme('colors.base.50'); - - --active-brightness: 0.9; - --focus-brightness: 0.95; -} +The current frontend scripts live in `unfold_extra/src/package.json`. Run them from +`unfold_extra/src`, for example: +```bash +npm run update:unfold +npm run tailwind:build +npm run tailwind:watch +npm run build:js +``` -html.dark { - --dca-light-mode: 0; - --dca-dark-mode: 1; - --dca-white: theme('colors.base.900'); - --dca-black: theme('colors.white'); - --dca-primary: theme('colors.primary.500'); - --dca-gray: theme('colors.base.300') !important; - --dca-gray-lightest: theme('colors.base.700'); - --dca-gray-lighter: theme('colors.base.600'); - --dca-gray-light: theme('colors.base.400'); - --dca-gray-darker: theme('colors.base.200'); - --dca-gray-darkest: theme('colors.base.100'); - --dca-gray-super-lightest: theme('colors.base.800'); - - --active-brightness: 2; - --focus-brightness: 1.5; -} +### Sync CMS Pagetree CSS After Upgrading django-cms +The CMS pagetree CSS is vendored with Unfold compatibility patches (e.g. removing the bare `.hidden` selector +that conflicts with Tailwind/Unfold sidebar). After upgrading django-cms, re-sync the patched CSS: + +```bash +poetry run python scripts/sync_cms_pagetree.py ``` -### Versatile Image Support +The script will warn if any patch targets have changed upstream and need manual review. -- Improved unfold integration via CSS only. +### Change Colors for Django CMS + +Configure colors through Unfold in `settings.py` using `UNFOLD["COLORS"]`. +This is the minimal and recommended way to align the admin theme, including the +shared base, primary, and font colors used by this package. + +```python +UNFOLD = { + "COLORS": { + "base": { + "50": "oklch(98.5% 0.002 247.839)", + "100": "oklch(96.7% 0.003 264.542)", + "200": "oklch(92.8% 0.006 264.531)", + "300": "oklch(87.2% 0.009 258.338)", + "400": "oklch(71.4% 0.019 261.325)", + "500": "oklch(55.1% 0.023 264.364)", + "600": "oklch(44.6% 0.026 256.802)", + "700": "oklch(37.3% 0.031 259.733)", + "800": "oklch(27.8% 0.030 256.848)", + "900": "oklch(21.0% 0.032 264.665)", + "950": "oklch(13.0% 0.027 261.692)", + }, + "primary": { + "50": "oklch(97.7% 0.014 308.299)", + "100": "oklch(94.6% 0.033 307.174)", + "200": "oklch(90.2% 0.060 306.703)", + "300": "oklch(82.7% 0.108 306.383)", + "400": "oklch(72.2% 0.177 305.504)", + "500": "oklch(62.7% 0.233 303.900)", + "600": "oklch(55.8% 0.252 302.321)", + "700": "oklch(49.6% 0.237 301.924)", + "800": "oklch(43.8% 0.198 303.724)", + "900": "oklch(38.1% 0.166 304.987)", + "950": "oklch(29.1% 0.143 302.717)", + }, + "font": { + "subtle-light": "var(--color-base-500)", + "subtle-dark": "var(--color-base-400)", + "default-light": "var(--color-base-600)", + "default-dark": "var(--color-base-300)", + "important-light": "var(--color-base-900)", + "important-dark": "var(--color-base-100)", + }, + }, +} +``` -### Django Auth, Sites +For CMS-specific theme adjustments beyond the shared Unfold palette, update the +frontend assets in `unfold_extra/src`. -- Add Unfolds standard settings to `django.contrib.auth`,`django.contrib.sites`. +See the official Unfold docs: +- Settings options: https://unfoldadmin.com/docs/configuration/settings/ +- Customizing Tailwind stylesheet: https://unfoldadmin.com/docs/styles-scripts/customizing-tailwind/ -This is for personal use. You likely want to customize this. \ No newline at end of file +This is for personal use. You likely want to customize this. diff --git a/pyproject.toml b/pyproject.toml index b1f9805..28559db 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,28 +1,63 @@ -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - [project] -name = "unfold_extra" -version = "0.1.0" +name = "django-unfold-extra" description = "Unofficial extra for Django Unfold Admin. Supports django-parler and django-cms." +authors = [{ name = "Marc Widmer", email = "marc@koga.cx" }] +requires-python = ">=3.12" readme = "README.md" -requires-python = ">=3.6" -authors = [ - { name = "Marc Widmer", email = "marc@koga.cx" } +license = "MIT" +keywords = [ + "django", + "django cms", + "unfold", + "admin", + "tailwind", + "theme", ] -urls = { "Homepage" = "https://github.com/metaforx/django-unfold-extra" } classifiers = [ - "Programming Language :: Python :: 3", - "Operating System :: OS Independent" + "Programming Language :: Python", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Operating System :: OS Independent", + "Environment :: Web Environment", + "Intended Audience :: Developers", + "Framework :: Django", ] dependencies = [ - "django-cms==5.0.0a1", + "django-cms>=5.0,<5.1", "django-parler~=2.3", - "django-unfold" + "django-unfold>=0.92", ] +dynamic = ["version"] + +[project.optional-dependencies] +test = [ + "pytest>=8.0", + "pytest-django>=4.5", + "pytest-env>=1.0", +] + +[project.urls] +Homepage = "https://github.com/metaforx/django-unfold-extra" [project.scripts] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[dependency-groups] +test = [ + "pytest>=8.0", + "pytest-django>=4.5", + "pytest-env>=1.0", + "pytest-playwright>=0.7.2,<0.8", + "djangocms-versioning>=2.5.1", +] +dev = ["ruff>=0.4"] + +[tool.uv] +package = false +default-groups = "all" # Uncomment and adjust the script below as needed: # unfold-cms = "unfold_extra.cli:main" @@ -31,21 +66,42 @@ path = "build_hook.py" [tool.hatch.build.targets.sdist] type = "sdist" +packages = ["unfold_extra"] [tool.hatch.build.targets.wheel] type = "wheel" +packages = ["unfold_extra"] + +[tool.hatch.version] +path = "unfold_extra/__init__.py" [tool.hatch.build] exclude = [ - "unfold_extra/src/node_modules", - "unfold_extra/src/node_modules/**/*", - "unfold_extra/src/*.lock", - "unfold_extra/src/package-lock.json", - "**/*.map", - "**/.DS_Store", - "**/__pycache__", - "docs/**/*" + "unfold_extra/src/node_modules", + "unfold_extra/src/node_modules/**/*", + "unfold_extra/src/*.lock", + "unfold_extra/src/package-lock.json", + "**/*.map", + "**/.DS_Store", + "**/__pycache__", + "docs/**/*" +, ] - +[tool.pytest.ini_options] +addopts = """\ + --strict-config + --strict-markers + --ds=testapp.settings + -m 'not ui' + """ +testpaths = ["tests"] +pythonpath = ["tests/server"] +django_find_project = false +env = [ + "DJANGO_ALLOW_ASYNC_UNSAFE=true", +] +markers = [ + "ui: Playwright browser tests (deselected by default, run with: pytest -m ui)", +] diff --git a/scripts/sync_cms_pagetree.py b/scripts/sync_cms_pagetree.py new file mode 100644 index 0000000..40d9193 --- /dev/null +++ b/scripts/sync_cms_pagetree.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 +""" +Sync cms.pagetree.css from the installed django-cms package and apply +Unfold compatibility patches. + +Usage: + poetry run python scripts/sync_cms_pagetree.py + +What it does: + 1. Locates the installed django-cms static CSS + 2. Copies cms.pagetree.css to unfold_extra/static/unfold_extra/cms/css/ + 3. Applies patches for Unfold compatibility: + - Removes bare `.hidden` from `.cms-hidden, .hidden` rule + (conflicts with Tailwind's `.hidden` utility used by Unfold sidebar) + 4. Prints the CMS version and a summary of changes + +After running, review the diff and commit the updated file. +""" + +import importlib +import re +import sys +from pathlib import Path + +DEST = Path(__file__).resolve().parent.parent / "unfold_extra" / "static" / "unfold_extra" / "cms" / "css" / "cms.pagetree.css" + +PATCHES = [ + # (description, search, replace) + ( + "Remove bare .hidden selector (conflicts with Tailwind/Unfold sidebar)", + ".cms-hidden,.hidden{display:none!important}", + ".cms-hidden{display:none!important}", + ), +] + +# CMS CSS lives at cms/css//cms.pagetree.css and uses relative paths +# like ../../fonts// and ../../img/pagetree/. Our vendored copy lives +# at unfold_extra/cms/css/cms.pagetree.css, so we rewrite those paths to +# absolute /static/cms/ references. +FONT_PATH_RE = re.compile(r'url\((\.\./\.\./)(?=fonts/|img/)') +FONT_PATH_REPLACE = "url(/static/cms/" + + +def find_cms_pagetree_css() -> Path: + """Locate cms.pagetree.css from the installed django-cms package.""" + try: + cms = importlib.import_module("cms") + except ImportError: + print("ERROR: django-cms is not installed. Install it first:", file=sys.stderr) + print(" poetry install", file=sys.stderr) + sys.exit(1) + + cms_static = Path(cms.__file__).parent / "static" / "cms" / "css" + # CMS versions its static files in subdirs (e.g. 5.0.3/) + candidates = list(cms_static.glob("*/cms.pagetree.css")) + if not candidates: + # fallback: maybe it's directly in css/ + direct = cms_static / "cms.pagetree.css" + if direct.exists(): + return direct + print(f"ERROR: cms.pagetree.css not found under {cms_static}", file=sys.stderr) + sys.exit(1) + + # Use the latest version by semantic version, not lexicographic order + def _version_key(p: Path): + parts = p.parent.name.split(".") + try: + return tuple(int(x) for x in parts) + except ValueError: + return (0,) + + source = max(candidates, key=_version_key) + return source + + +def main(): + source = find_cms_pagetree_css() + version_dir = source.parent.name # e.g. "5.0.3" + print(f"Source: {source}") + print(f"CMS static version: {version_dir}") + + css = source.read_text() + original = css + + for desc, search, replace in PATCHES: + if search in css: + css = css.replace(search, replace) + print(f" PATCHED: {desc}") + else: + # Try with spaces (in case of unminified CSS) + search_spaced = search.replace(",", ", ").replace("{", " { ").replace("}", " }").replace("!important", " !important") + if search_spaced in css: + css = css.replace(search_spaced, replace.replace(",", ", ").replace("{", " { ").replace("}", " }").replace("!important", " !important")) + print(f" PATCHED: {desc}") + else: + print(f" WARNING: patch target not found — {desc}") + print(f" Expected: {search}") + print(f" CSS may have changed in this CMS version. Manual review needed.") + + # Rewrite relative asset paths to absolute /static/cms/ paths + count = len(FONT_PATH_RE.findall(css)) + if count: + css = FONT_PATH_RE.sub(FONT_PATH_REPLACE, css) + print(f" PATCHED: Rewrote {count} relative asset paths to /static/cms/") + else: + print(" WARNING: no relative asset paths found — font/image URLs may need manual review") + + if css == original: + print("\nNo patches were applied. CSS may have changed upstream.") + + DEST.parent.mkdir(parents=True, exist_ok=True) + DEST.write_text(css) + print(f"\nWritten to: {DEST}") + print(f"File size: {len(css):,} bytes") + print("\nDone. Review with: git diff unfold_extra/static/unfold_extra/cms/css/cms.pagetree.css") + + +if __name__ == "__main__": + main() diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..a13f23f --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,39 @@ +import pytest +from django.contrib.auth import get_user_model + + +@pytest.fixture +def admin_client(client, django_user_model): + """A Django test client logged in as an admin user.""" + user = django_user_model.objects.create_superuser( + username="admin", + email="admin@example.com", + password="password", + ) + client.force_login(user) + return client + + +@pytest.fixture(scope="function") +def admin_user(db): + """Create an admin user.""" + User = get_user_model() + return User.objects.create_superuser( + username="testadmin", + email="testadmin@example.com", + password="testpass", + ) + + +@pytest.fixture(scope="function") +def authenticated_page(browser, live_server, admin_user): + """Playwright page authenticated as admin via the Django login form.""" + context = browser.new_context() + page = context.new_page() + page.goto(f"{live_server.url}/admin/login/?next=/admin/") + page.fill("#id_username", "testadmin") + page.fill("#id_password", "testpass") + page.click("[type=submit]") + page.wait_for_load_state("networkidle") + yield page + context.close() diff --git a/tests/server/manage.py b/tests/server/manage.py new file mode 100755 index 0000000..218e066 --- /dev/null +++ b/tests/server/manage.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +import os +import sys + + +def main(): + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "testapp.settings") + + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == "__main__": + main() diff --git a/tests/server/testapp/__init__.py b/tests/server/testapp/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/server/testapp/admin.py b/tests/server/testapp/admin.py new file mode 100644 index 0000000..7c5e03f --- /dev/null +++ b/tests/server/testapp/admin.py @@ -0,0 +1,28 @@ +from django.contrib import admin +from unfold.admin import ModelAdmin + +from unfold_extra.contrib.parler.admin import ( + UnfoldTranslatableAdminMixin, +) +from parler.admin import TranslatableAdmin + +from .models import Article, Category, SimpleModel + + +@admin.register(Category) +class CategoryAdmin(UnfoldTranslatableAdminMixin, TranslatableAdmin, ModelAdmin): + list_display = ["name"] + search_fields = ["translations__name"] + + +@admin.register(Article) +class ArticleAdmin(UnfoldTranslatableAdminMixin, TranslatableAdmin, ModelAdmin): + list_display = ["title", "category", "created"] + list_filter = ["category"] + search_fields = ["translations__title"] + + +@admin.register(SimpleModel) +class SimpleModelAdmin(ModelAdmin): + list_display = ["name", "is_active"] + search_fields = ["name"] diff --git a/tests/server/testapp/cms_plugins.py b/tests/server/testapp/cms_plugins.py new file mode 100644 index 0000000..b1ea936 --- /dev/null +++ b/tests/server/testapp/cms_plugins.py @@ -0,0 +1,49 @@ +from django.contrib import admin +from django.utils.translation import gettext_lazy as _ +from cms.plugin_pool import plugin_pool + +from unfold_extra.contrib.cms.plugins import UnfoldCMSPluginBase, UnfoldStackedInline + +from .models import HeroPluginModel, HeroButton, PageLinkPluginModel + + +class HeroButtonInline(UnfoldStackedInline): + model = HeroButton + extra = 0 + + +@plugin_pool.register_plugin +class HeroPlugin(UnfoldCMSPluginBase): + model = HeroPluginModel + name = _("Hero") + render_template = "testapp/plugins/hero.html" + + fieldsets = ( + ( + None, + { + "fields": ( + "title", + "subtitle", + "image", + "cta_label", + "cta_url", + "is_highlighted", + "layout", + "categories", + ) + }, + ), + ) + compressed_fields = True + autocomplete_fields = ("categories",) + #radio_fields = {"layout": admin.HORIZONTAL} + readonly_fields = ("created_at",) + inlines = (HeroButtonInline,) + + +@plugin_pool.register_plugin +class PageLinkPlugin(UnfoldCMSPluginBase): + model = PageLinkPluginModel + name = _("Page Link") + render_template = "testapp/plugins/page_link.html" diff --git a/tests/server/testapp/migrations/0001_initial.py b/tests/server/testapp/migrations/0001_initial.py new file mode 100644 index 0000000..b7e5915 --- /dev/null +++ b/tests/server/testapp/migrations/0001_initial.py @@ -0,0 +1,159 @@ +# Generated by Django 5.2.12 on 2026-03-13 17:54 + +import django.db.models.deletion +import parler.fields +import parler.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="Category", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ], + options={ + "verbose_name_plural": "categories", + }, + bases=(parler.models.TranslatableModelMixin, models.Model), + ), + migrations.CreateModel( + name="SimpleModel", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=100)), + ("is_active", models.BooleanField(default=True)), + ], + ), + migrations.CreateModel( + name="Article", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created", models.DateTimeField(auto_now_add=True)), + ( + "category", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="testapp.category", + ), + ), + ], + options={ + "abstract": False, + }, + bases=(parler.models.TranslatableModelMixin, models.Model), + ), + migrations.CreateModel( + name="ArticleTranslation", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "language_code", + models.CharField( + db_index=True, max_length=15, verbose_name="Language" + ), + ), + ("title", models.CharField(max_length=200)), + ("body", models.TextField(blank=True)), + ( + "master", + parler.fields.TranslationsForeignKey( + editable=False, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="translations", + to="testapp.article", + ), + ), + ], + options={ + "verbose_name": "article Translation", + "db_table": "testapp_article_translation", + "db_tablespace": "", + "managed": True, + "default_permissions": (), + "unique_together": {("language_code", "master")}, + }, + bases=(parler.models.TranslatedFieldsModelMixin, models.Model), + ), + migrations.CreateModel( + name="CategoryTranslation", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "language_code", + models.CharField( + db_index=True, max_length=15, verbose_name="Language" + ), + ), + ("name", models.CharField(max_length=100)), + ("description", models.TextField(blank=True)), + ( + "master", + parler.fields.TranslationsForeignKey( + editable=False, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="translations", + to="testapp.category", + ), + ), + ], + options={ + "verbose_name": "category Translation", + "db_table": "testapp_category_translation", + "db_tablespace": "", + "managed": True, + "default_permissions": (), + "unique_together": {("language_code", "master")}, + }, + bases=(parler.models.TranslatedFieldsModelMixin, models.Model), + ), + ] diff --git a/tests/server/testapp/migrations/0002_heropluginmodel_herobutton.py b/tests/server/testapp/migrations/0002_heropluginmodel_herobutton.py new file mode 100644 index 0000000..e1409ad --- /dev/null +++ b/tests/server/testapp/migrations/0002_heropluginmodel_herobutton.py @@ -0,0 +1,89 @@ +# Generated by Django 5.2.12 on 2026-03-14 21:45 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("cms", "0041_alter_pageurl_unique_together_pageurl_site_and_more"), + ("testapp", "0001_initial"), + ] + + operations = [ + migrations.CreateModel( + name="HeroPluginModel", + fields=[ + ( + "cmsplugin_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + related_name="%(app_label)s_%(class)s", + serialize=False, + to="cms.cmsplugin", + ), + ), + ("title", models.CharField(max_length=200)), + ("subtitle", models.TextField(blank=True)), + ("image", models.FileField(blank=True, upload_to="hero/")), + ( + "cta_label", + models.CharField( + blank=True, max_length=80, verbose_name="CTA label" + ), + ), + ("cta_url", models.URLField(blank=True, verbose_name="CTA URL")), + ("is_highlighted", models.BooleanField(default=False)), + ( + "layout", + models.CharField( + choices=[ + ("centered", "Centered"), + ("left", "Left-aligned"), + ("right", "Right-aligned"), + ], + default="centered", + max_length=20, + ), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ( + "categories", + models.ManyToManyField(blank=True, to="testapp.category"), + ), + ], + bases=("cms.cmsplugin",), + ), + migrations.CreateModel( + name="HeroButton", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("label", models.CharField(max_length=80)), + ("url", models.URLField()), + ("is_primary", models.BooleanField(default=False)), + ("sort_order", models.PositiveSmallIntegerField(default=0)), + ( + "hero", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="buttons", + to="testapp.heropluginmodel", + ), + ), + ], + options={ + "ordering": ["sort_order"], + }, + ), + ] diff --git a/tests/server/testapp/migrations/0003_pagelinkpluginmodel.py b/tests/server/testapp/migrations/0003_pagelinkpluginmodel.py new file mode 100644 index 0000000..3a5aec5 --- /dev/null +++ b/tests/server/testapp/migrations/0003_pagelinkpluginmodel.py @@ -0,0 +1,43 @@ +# Generated by Django 5.2.12 on 2026-03-15 17:37 + +import cms.models.fields +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("cms", "0041_alter_pageurl_unique_together_pageurl_site_and_more"), + ("testapp", "0002_heropluginmodel_herobutton"), + ] + + operations = [ + migrations.CreateModel( + name="PageLinkPluginModel", + fields=[ + ( + "cmsplugin_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + related_name="%(app_label)s_%(class)s", + serialize=False, + to="cms.cmsplugin", + ), + ), + ("label", models.CharField(blank=True, max_length=80)), + ( + "page_link", + cms.models.fields.PageField( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="cms.page", + ), + ), + ], + bases=("cms.cmsplugin",), + ), + ] diff --git a/tests/server/testapp/migrations/__init__.py b/tests/server/testapp/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/server/testapp/models.py b/tests/server/testapp/models.py new file mode 100644 index 0000000..a665aed --- /dev/null +++ b/tests/server/testapp/models.py @@ -0,0 +1,101 @@ +from cms.models.fields import PageField +from cms.models.pluginmodel import CMSPlugin +from django.db import models +from parler.models import TranslatableModel, TranslatedFields + + +class Category(TranslatableModel): + """Translatable model for testing parler + unfold integration.""" + + translations = TranslatedFields( + name=models.CharField(max_length=100), + description=models.TextField(blank=True), + ) + + class Meta: + verbose_name_plural = "categories" + + def __str__(self): + return self.safe_translation_getter("name", any_language=True) or "" + + +class Article(TranslatableModel): + """Translatable model with FK for testing parler admin + unfold.""" + + category = models.ForeignKey( + Category, on_delete=models.SET_NULL, null=True, blank=True + ) + created = models.DateTimeField(auto_now_add=True) + + translations = TranslatedFields( + title=models.CharField(max_length=200), + body=models.TextField(blank=True), + ) + + def __str__(self): + return self.safe_translation_getter("title", any_language=True) or "" + + +class SimpleModel(models.Model): + """Non-translatable model for basic unfold admin testing.""" + + name = models.CharField(max_length=100) + is_active = models.BooleanField(default=True) + + def __str__(self): + return self.name + + +# --------------------------------------------------------------------------- +# CMS Plugin models for testing UnfoldCMSPluginBase +# --------------------------------------------------------------------------- + +class HeroPluginModel(CMSPlugin): + """Hero section plugin exercising diverse field types.""" + + LAYOUT_CHOICES = [ + ("centered", "Centered"), + ("left", "Left-aligned"), + ("right", "Right-aligned"), + ] + + title = models.CharField(max_length=200) + subtitle = models.TextField(blank=True) + image = models.FileField(upload_to="hero/", blank=True) + cta_label = models.CharField("CTA label", max_length=80, blank=True) + cta_url = models.URLField("CTA URL", blank=True) + is_highlighted = models.BooleanField(default=False) + layout = models.CharField(max_length=20, choices=LAYOUT_CHOICES, default="centered") + categories = models.ManyToManyField(Category, blank=True) + created_at = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return self.title + + +class PageLinkPluginModel(CMSPlugin): + """Simple plugin using PageField to test UnfoldPageSelectWidget.""" + + label = models.CharField(max_length=80, blank=True) + page_link = PageField(blank=True, null=True) + + def __str__(self): + return self.label or "Page Link" + + +class HeroButton(models.Model): + """Inline child for HeroPluginModel — tests UnfoldStackedInline.""" + + hero = models.ForeignKey( + HeroPluginModel, on_delete=models.CASCADE, related_name="buttons" + ) + label = models.CharField(max_length=80) + url = models.URLField() + is_primary = models.BooleanField(default=False) + sort_order = models.PositiveSmallIntegerField(default=0) + + class Meta: + ordering = ["sort_order"] + + def __str__(self): + return self.label diff --git a/tests/server/testapp/settings.py b/tests/server/testapp/settings.py new file mode 100644 index 0000000..f58bd55 --- /dev/null +++ b/tests/server/testapp/settings.py @@ -0,0 +1,168 @@ +from os import environ +from pathlib import Path + +from django.core.management.utils import get_random_secret_key +from django.templatetags.static import static +from django.utils.translation import gettext_lazy as _ + +BASE_DIR = Path(__file__).resolve().parent.parent + +SECRET_KEY = environ.get("SECRET_KEY", get_random_secret_key()) + +DEBUG = True + +ALLOWED_HOSTS = ["localhost", "127.0.0.1"] + +USE_TZ = True +USE_I18N = True + +INSTALLED_APPS = [ + # Unfold must come before django.contrib.admin + "unfold", + "unfold.contrib.filters", + "unfold_extra", + "unfold_extra.contrib.cms", + "unfold_extra.contrib.parler", + "unfold_extra.contrib.auth", + "unfold_extra.contrib.sites", + # Django core + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "django.contrib.sites", + # Django CMS core + "cms", + "menus", + "treebeard", + "sekizai", + # Django CMS versioning + "djangocms_versioning", + # Parler + "parler", + # Test app + "testapp", +] + +MIDDLEWARE = [ + "cms.middleware.utils.ApphookReloadMiddleware", + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.locale.LocaleMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + "cms.middleware.user.CurrentUserMiddleware", + "cms.middleware.page.CurrentPageMiddleware", + "cms.middleware.toolbar.ToolbarMiddleware", + "cms.middleware.language.LanguageCookieMiddleware", +] + +ROOT_URLCONF = "testapp.urls" + +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [BASE_DIR / "testapp" / "templates"], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + "django.template.context_processors.i18n", + "sekizai.context_processors.sekizai", + "cms.context_processors.cms_settings", + ], + }, + }, +] + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": BASE_DIR / "db.sqlite3", + } +} + +STATIC_URL = "static/" + +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" + +# Django sites framework +SITE_ID = 1 + +# --- Language / i18n --- +LANGUAGE_CODE = "en" + +LANGUAGES = [ + ("en", _("English")), + ("de", _("German")), +] + +# CMS language configuration (keyed by SITE_ID) +# https://docs.django-cms.org/en/latest/topics/i18n.html +CMS_LANGUAGES = { + SITE_ID: [ + { + "code": "en", + "name": _("English"), + "public": True, + }, + { + "code": "de", + "name": _("Deutsch"), + "public": True, + "hide_untranslated": True, + }, + ], + "default": { + "fallbacks": ["en"], + "redirect_on_fallback": True, + "public": True, + "hide_untranslated": False, + }, +} + +# Parler settings +PARLER_LANGUAGES = { + SITE_ID: ( + {"code": "en"}, + {"code": "de"}, + ), + "default": { + "fallbacks": ["en"], + "hide_untranslated": False, + }, +} + +# --- Django CMS --- +CMS_TEMPLATES = [ + ("base.html", "Base"), +] + +CMS_CONFIRM_VERSION4 = True +CMS_PERMISSION = False +CMS_COLOR_SCHEME_TOGGLE = False + +# djangocms-versioning: default user for programmatic version creation (tests/fixtures) +DJANGOCMS_VERSIONING_DEFAULT_USER = 1 + +# --- Unfold --- +UNFOLD = { + "SHOW_LANGUAGES": True, + "STYLES": [ + lambda request: static("unfold_extra/css/styles.css"), + ], + "SCRIPTS": [ + lambda request: static("unfold_extra/js/theme-sync.js"), + ], +} + +X_FRAME_OPTIONS = "SAMEORIGIN" +DJANGOCMS_VERISONING_ON_PUBLISH_REDIRECT = "versions" \ No newline at end of file diff --git a/tests/server/testapp/templates/base.html b/tests/server/testapp/templates/base.html new file mode 100644 index 0000000..f895189 --- /dev/null +++ b/tests/server/testapp/templates/base.html @@ -0,0 +1,15 @@ +{% load cms_tags sekizai_tags unfold_extra_tags %} + + + + {% page_attribute "page_title" %} + {% render_block "css" %} + {% unfold_extra_styles %} + {% unfold_extra_theme_sync %} + + + {% cms_toolbar %} + {% placeholder "content" %} + {% render_block "js" %} + + diff --git a/tests/server/testapp/templates/testapp/plugins/hero.html b/tests/server/testapp/templates/testapp/plugins/hero.html new file mode 100644 index 0000000..471b71c --- /dev/null +++ b/tests/server/testapp/templates/testapp/plugins/hero.html @@ -0,0 +1,12 @@ +{% load cms_tags %} + +
+

{{ instance.title }}

+ {% if instance.subtitle %}

{{ instance.subtitle }}

{% endif %} + {% if instance.cta_url %} + {{ instance.cta_label|default:"Learn more" }} + {% endif %} + {% for btn in instance.buttons.all %} + {{ btn.label }} + {% endfor %} +
diff --git a/tests/server/testapp/templates/testapp/plugins/page_link.html b/tests/server/testapp/templates/testapp/plugins/page_link.html new file mode 100644 index 0000000..a3063f5 --- /dev/null +++ b/tests/server/testapp/templates/testapp/plugins/page_link.html @@ -0,0 +1,3 @@ +{% if instance.page_link %} + {{ instance.label|default:instance.page_link.get_title }} +{% endif %} diff --git a/tests/server/testapp/urls.py b/tests/server/testapp/urls.py new file mode 100644 index 0000000..d06d306 --- /dev/null +++ b/tests/server/testapp/urls.py @@ -0,0 +1,16 @@ +from django.conf.urls.i18n import i18n_patterns +from django.contrib import admin +from django.urls import include, path + +from unfold_extra.views import cms_set_language + +urlpatterns = [ + path("i18n/setlang/", cms_set_language, name="set_language"), + path("i18n/", include("django.conf.urls.i18n")), +] + +urlpatterns += i18n_patterns( + path("admin/", admin.site.urls), + path("", include("cms.urls")), + prefix_default_language=False, +) diff --git a/tests/test_language_sync.py b/tests/test_language_sync.py new file mode 100644 index 0000000..f875e36 --- /dev/null +++ b/tests/test_language_sync.py @@ -0,0 +1,119 @@ +import pytest +from django.test import override_settings +from django.urls import reverse + + +@pytest.mark.django_db +class TestCmsSetLanguageView: + """Verify that cms_set_language view syncs language into CMS UserSettings + and redirects correctly in both directions (en→de, de→en).""" + + def test_set_language_updates_cms_user_settings(self, admin_client, django_user_model): + from cms.models import UserSettings + + user = django_user_model.objects.get(username="admin") + UserSettings.objects.get_or_create(user=user, defaults={"language": "en"}) + + admin_client.post(reverse("set_language"), {"language": "de", "next": "/admin/"}) + + assert UserSettings.objects.get(user=user).language == "de" + + def test_creates_user_settings_if_missing(self, admin_client, django_user_model): + """update_or_create should create UserSettings when no row exists.""" + from cms.models import UserSettings + + user = django_user_model.objects.get(username="admin") + assert not UserSettings.objects.filter(user=user).exists() + + admin_client.post(reverse("set_language"), {"language": "de", "next": "/admin/"}) + + assert UserSettings.objects.get(user=user).language == "de" + + def test_skips_cms_sync_when_cms_is_not_installed(self, admin_client, monkeypatch): + """The view should still switch language when the CMS app is unavailable.""" + from unfold_extra import views + + is_installed = views.apps.is_installed + monkeypatch.setattr( + views.apps, + "is_installed", + lambda app_label: False if app_label == "cms" else is_installed(app_label), + ) + + response = admin_client.post( + reverse("set_language"), {"language": "de", "next": "/admin/"} + ) + + assert response.status_code == 302 + assert response.url == "/de/admin/" + + def test_redirect_en_to_de(self, admin_client): + """en→de: /admin/ should redirect to /de/admin/.""" + response = admin_client.post( + reverse("set_language"), {"language": "de", "next": "/admin/"} + ) + assert response.status_code == 302 + assert response.url == "/de/admin/" + + def test_redirect_de_to_en_with_referer(self, admin_client): + """de→en via Unfold's empty-next form: should redirect to /admin/. + + This is the critical test. Unfold's language form sends next="" and + set_language falls back to HTTP_REFERER. With prefix_default_language=False, + LocaleMiddleware forces LANGUAGE_CODE for non-prefixed URLs, so + cms_set_language must re-activate the cookie language for translate_url + to resolve the German-prefixed referer correctly. + """ + # First switch to German so the cookie is "de" + admin_client.post(reverse("set_language"), {"language": "de", "next": "/admin/"}) + + # Switch back, simulating Unfold's empty next + referer + response = admin_client.post( + reverse("set_language"), + {"language": "en", "next": ""}, + HTTP_REFERER="http://testserver/de/admin/", + ) + assert response.status_code == 302 + assert response.url == "http://testserver/admin/" + + def test_cookie_roundtrip(self, admin_client): + """Cookie is set correctly in both directions.""" + r1 = admin_client.post( + reverse("set_language"), {"language": "de", "next": "/admin/"} + ) + assert r1.cookies["django_language"].value == "de" + + r2 = admin_client.post( + reverse("set_language"), {"language": "en", "next": "/de/admin/"} + ) + assert r2.cookies["django_language"].value == "en" + + def test_get_does_not_update_cms_user_settings(self, admin_client, django_user_model): + from cms.models import UserSettings + + user = django_user_model.objects.get(username="admin") + UserSettings.objects.get_or_create(user=user, defaults={"language": "en"}) + + response = admin_client.get( + reverse("set_language"), {"language": "de", "next": "/admin/"} + ) + + assert response.status_code == 302 + assert response.url == "/admin/" + assert UserSettings.objects.get(user=user).language == "en" + + @override_settings(LANGUAGE_QUERY_PARAMETER="lang") + def test_uses_configured_language_query_parameter( + self, admin_client, django_user_model + ): + from cms.models import UserSettings + + user = django_user_model.objects.get(username="admin") + UserSettings.objects.get_or_create(user=user, defaults={"language": "en"}) + + response = admin_client.post(reverse("set_language"), {"lang": "de", "next": "/admin/"}) + + assert response.status_code == 302 + assert response.url == "/de/admin/" + assert response.cookies["django_language"].value == "de" + assert UserSettings.objects.get(user=user).language == "de" diff --git a/tests/test_package.py b/tests/test_package.py new file mode 100644 index 0000000..ca80490 --- /dev/null +++ b/tests/test_package.py @@ -0,0 +1,38 @@ +"""Tests for unfold_extra package imports and configuration.""" + + +class TestPackageImport: + """Verify package can be imported and has expected attributes.""" + + def test_import_package(self): + import unfold_extra + + assert hasattr(unfold_extra, "__version__") + + def test_import_app_config(self): + from unfold_extra.apps import UnfoldCMSConfig + + assert UnfoldCMSConfig.name == "unfold_extra" + + def test_import_language_view(self): + from unfold_extra.views import cms_set_language + + assert cms_set_language is not None + + def test_import_template_tags(self): + from unfold_extra.templatetags import unfold_extra_tags + + assert hasattr(unfold_extra_tags, "unfold_theme_colors") + assert hasattr(unfold_extra_tags, "unfold_extra_styles") + assert hasattr(unfold_extra_tags, "unfold_extra_theme_sync") + + def test_import_parler_admin(self): + from unfold_extra.contrib.parler.admin import UnfoldTranslatableAdminMixin + + assert UnfoldTranslatableAdminMixin is not None + + def test_import_cms_admin(self): + from unfold_extra.contrib.cms.admin import PageContentAdmin, PageAdmin + + assert PageContentAdmin is not None + assert PageAdmin is not None diff --git a/tests/test_smoke.py b/tests/test_smoke.py new file mode 100644 index 0000000..12ea217 --- /dev/null +++ b/tests/test_smoke.py @@ -0,0 +1,89 @@ +"""Smoke tests to verify admin registration and page access work with Unfold styling.""" + +import pytest +from django.contrib.admin.sites import site as admin_site +from django.test import override_settings +from django.urls import reverse + + +@pytest.mark.django_db +class TestAdminRegistered: + """Verify all models are registered with the admin site.""" + + def test_testapp_models_registered(self): + registered_models = [model.__name__ for model in admin_site._registry.keys()] + for model_name in ["Category", "Article", "SimpleModel"]: + assert model_name in registered_models, f"{model_name} not registered" + + def test_cms_page_registered(self): + """Verify CMS Page model is registered (re-registered by unfold_extra.contrib.cms).""" + registered_models = [model.__name__ for model in admin_site._registry.keys()] + assert "Page" in registered_models + assert "PageContent" in registered_models + + def test_auth_models_registered(self): + """Verify auth models are registered (re-registered by unfold_extra.contrib.auth).""" + registered_models = [model.__name__ for model in admin_site._registry.keys()] + assert "User" in registered_models + assert "Group" in registered_models + + def test_site_model_registered(self): + """Verify Site model is registered (re-registered by unfold_extra.contrib.sites).""" + registered_models = [model.__name__ for model in admin_site._registry.keys()] + assert "Site" in registered_models + + +@pytest.mark.django_db +class TestAdminAccess: + """Verify admin pages are accessible with Unfold styling.""" + + def test_admin_index(self, admin_client): + response = admin_client.get("/admin/") + assert response.status_code == 200 + + def test_simple_model_changelist(self, admin_client): + response = admin_client.get("/admin/testapp/simplemodel/") + assert response.status_code == 200 + + def test_simple_model_add(self, admin_client): + response = admin_client.get("/admin/testapp/simplemodel/add/") + assert response.status_code == 200 + + def test_category_changelist(self, admin_client): + response = admin_client.get("/admin/testapp/category/") + assert response.status_code == 200 + + def test_category_add(self, admin_client): + response = admin_client.get("/admin/testapp/category/add/") + assert response.status_code == 200 + + def test_article_changelist(self, admin_client): + response = admin_client.get("/admin/testapp/article/") + assert response.status_code == 200 + + def test_article_add(self, admin_client): + response = admin_client.get("/admin/testapp/article/add/") + assert response.status_code == 200 + + def test_cms_usersettings_back_link_targets_cms_app_list(self, admin_client): + response = admin_client.get("/de/admin/cms/usersettings/") + assert response.status_code == 200 + assert reverse("admin:app_list", kwargs={"app_label": "cms"}) in response.content.decode() + + +@pytest.mark.django_db +class TestPagetreeHeaderAddButton: + """Verify UNFOLD_CMS_HEADER_ADD_BUTTON controls add button placement.""" + + def test_enabled_by_default(self, admin_client): + response = admin_client.get("/admin/cms/pagecontent/") + assert response.status_code == 200 + body = response.content.decode() + assert "unfold-header-add" in body + + @override_settings(UNFOLD_CMS_HEADER_ADD_BUTTON=False) + def test_disabled(self, admin_client): + response = admin_client.get("/admin/cms/pagecontent/") + assert response.status_code == 200 + body = response.content.decode() + assert "unfold-header-add" not in body \ No newline at end of file diff --git a/tests/test_ui_submit_row.py b/tests/test_ui_submit_row.py new file mode 100644 index 0000000..9f53d70 --- /dev/null +++ b/tests/test_ui_submit_row.py @@ -0,0 +1,35 @@ +import pytest +from playwright.sync_api import expect + + +@pytest.mark.ui +@pytest.mark.django_db(transaction=True) +class TestUserSettingsSubmitRow: + """Verify that the CMS UserSettings admin uses Unfold's styled submit row.""" + + def test_submit_row_has_unfold_styling(self, authenticated_page, live_server): + page = authenticated_page + page.goto(f"{live_server.url}/admin/cms/usersettings/") + page.wait_for_load_state("networkidle") + + # Unfold's submit row uses id="submit-row" (not class="submit-row") + submit_row = page.locator("#submit-row") + expect(submit_row).to_be_visible() + + def test_submit_row_has_styled_save_button(self, authenticated_page, live_server): + page = authenticated_page + page.goto(f"{live_server.url}/admin/cms/usersettings/") + page.wait_for_load_state("networkidle") + + # Unfold renders + + + +{% endblock %} \ No newline at end of file diff --git a/unfold_extra/contrib/cms/templates/unfold/helpers/header_back_button.html b/unfold_extra/contrib/cms/templates/unfold/helpers/header_back_button.html new file mode 100644 index 0000000..b271cc0 --- /dev/null +++ b/unfold_extra/contrib/cms/templates/unfold/helpers/header_back_button.html @@ -0,0 +1,26 @@ +{% load admin_urls i18n unfold %} + +{% if show_back_button %} + {% if object %} + {% admin_object_app_url object "changelist" as changelist_url %} + {% url changelist_url as resolved_changelist_url %} + + {% if resolved_changelist_url %} + + + arrow_back + + + {% endif %} + {% elif opts and adminform %} + {% url opts|admin_urlname:'changelist' as changelist_url %} + + {% if changelist_url %} + + + arrow_back + + + {% endif %} + {% endif %} +{% endif %} diff --git a/unfold_extra/contrib/cms/templates/unfold_extra/cms/page/change_form.html b/unfold_extra/contrib/cms/templates/unfold_extra/cms/page/change_form.html index f42ba4b..c35cb54 100644 --- a/unfold_extra/contrib/cms/templates/unfold_extra/cms/page/change_form.html +++ b/unfold_extra/contrib/cms/templates/unfold_extra/cms/page/change_form.html @@ -1 +1,6 @@ -{% extends "admin/change_form.html" %} +{% extends "admin/change_form.html" %} {# Unfold’s base via Unfold #} +{% load admin_modify %} + +{# ensure submit buttons exist #} +{% block submit_buttons_top %}{% submit_row %}{% endblock %} +{% block submit_buttons_bottom %}{% submit_row %}{% endblock %} \ No newline at end of file diff --git a/unfold_extra/contrib/cms/templates/unfold_extra/cms/page/tree/base.html b/unfold_extra/contrib/cms/templates/unfold_extra/cms/page/tree/base.html index 8771020..f5f8b8c 100644 --- a/unfold_extra/contrib/cms/templates/unfold_extra/cms/page/tree/base.html +++ b/unfold_extra/contrib/cms/templates/unfold_extra/cms/page/tree/base.html @@ -1,6 +1,8 @@ {% extends "admin/cms/page/tree/base.html" %} -{% load i18n static admin_urls cms_static %} +{% load i18n static admin_urls cms_static unfold_extra_tags %} + +{% block bodyclass %}{{ block.super }}{% unfold_cms_header_add_button as header_add %}{% if header_add %} unfold-header-add{% endif %}{% endblock %} {% block extrahead %} {# {{ block.super }} #NOTE: avoid super call to prevent loading of original css and js#} @@ -10,6 +12,19 @@ {% endblock extrahead %} +{% block object-tools-items %} + {% unfold_cms_header_add_button as header_add %} + {% if header_add and has_add_permission %} + + {% endif %} +{% endblock %} + {% block breadcrumbs %} {% if not is_popup %}
@@ -28,4 +43,4 @@
{% endif %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/unfold_extra/contrib/cms/templates/unfold_extra/cms/usersettings/header.html b/unfold_extra/contrib/cms/templates/unfold_extra/cms/usersettings/header.html new file mode 100644 index 0000000..c520a59 --- /dev/null +++ b/unfold_extra/contrib/cms/templates/unfold_extra/cms/usersettings/header.html @@ -0,0 +1,43 @@ +{% extends "unfold/helpers/header.html" %} + +{% load i18n unfold %} + +{% block welcome-msg %} +
+ {% if is_nav_sidebar_enabled and not is_popup %} + + + {% component "unfold/components/icon.html" with class="hidden! xl:block!" %} + dock_to_right + {% endcomponent %} + + + {% component "unfold/components/icon.html" with class="block! xl:hidden!" %} + dock_to_right + {% endcomponent %} + + + + + {% endif %} + + {% url "admin:app_list" app_label=opts.app_label as app_list_url %} + {% component "unfold/components/link.html" with href=app_list_url class="block h-[18px] mr-3" %} + {% component "unfold/components/icon.html" %} + arrow_back + {% endcomponent %} + {% endcomponent %} + +

+ + {{ opts.app_config.verbose_name|capfirst }} + + {% component "unfold/components/icon.html" with class="inline-block align-middle leading-5 mx-2 text-base-400 dark:text-base-500" %} + chevron_right + {% endcomponent %} + + {{ title }} + +

+
+{% endblock %} diff --git a/unfold_extra/contrib/cms/templates/unfold_extra/cms/widgets/pageselectwidget.html b/unfold_extra/contrib/cms/templates/unfold_extra/cms/widgets/pageselectwidget.html new file mode 100644 index 0000000..2b9bb24 --- /dev/null +++ b/unfold_extra/contrib/cms/templates/unfold_extra/cms/widgets/pageselectwidget.html @@ -0,0 +1,12 @@ +{% spaceless %} +
+ {% for widget in widget.subwidgets %} + {% if forloop.last %}{% endif %} + {% endfor %} +
+{% endspaceless %} + diff --git a/unfold_extra/contrib/cms/unfold_patches.py b/unfold_extra/contrib/cms/unfold_patches.py new file mode 100644 index 0000000..8391bd7 --- /dev/null +++ b/unfold_extra/contrib/cms/unfold_patches.py @@ -0,0 +1,34 @@ +from django.template.loader import render_to_string +from django.urls import NoReverseMatch + + +def patch_unfold_header_title() -> None: + """ + Patch unfold's header_title tag so missing admin app_list URLs for + plugin-only app labels do not crash admin pages. + """ + from unfold.templatetags import unfold as unfold_tags + + if getattr(unfold_tags, "_unfold_extra_header_title_patched", False): + return + + original_header_title = unfold_tags.header_title + + def safe_header_title(context): + try: + return original_header_title(context) + except NoReverseMatch: + title = context.get("title") + if not title: + return "" + return render_to_string( + "unfold/helpers/header_title.html", + request=context.request, + context={"parts": [{"title": title}]}, + ) + + unfold_tags.header_title = safe_header_title + unfold_tags.register.simple_tag(name="header_title", takes_context=True)( + safe_header_title + ) + unfold_tags._unfold_extra_header_title_patched = True diff --git a/unfold_extra/contrib/cms/utils.py b/unfold_extra/contrib/cms/utils.py new file mode 100644 index 0000000..79d98ed --- /dev/null +++ b/unfold_extra/contrib/cms/utils.py @@ -0,0 +1,92 @@ +from cms.admin.pageadmin import get_site +from cms.constants import MODAL_HTML_REDIRECT +from cms.utils.conf import get_cms_setting +from cms.utils.i18n import get_site_language_from_request +from django.http import HttpResponse +from django.urls import reverse +from urllib.parse import urlencode +from json import dumps + +from django.utils.text import capfirst +from django.utils.translation import gettext + +SIDEPANEL_HTML_REDIRECT = """ + + +Redirecting… +""" + +def _request_is_iframe(request) -> bool: + """ + Determines if the request is intended for an iframe. + + This function inspects the request headers to check if the "Sec-Fetch-Dest" + header is present and its value is set to "iframe". + """ + return request.headers.get("Sec-Fetch-Dest") == "iframe" + +def _request_is_toolbar_modal(request) -> bool: + """ + Determines if the current request is related to a CMS toolbar modal. + """ + if request.POST.get("_cms_modal") == "1": + return True + if "cms_path" in request.GET: + return True + ref = (request.META.get("HTTP_REFERER") or "").lower() + return "cms_path=" in ref + + +def _language_from_request(request) -> str: + """ + Determines the language to be used based on the request object. + """ + site = get_site(request) + lang = request.GET.get("language") or get_site_language_from_request(request, site_id=site.pk) + return lang or get_cms_setting("LANGUAGE_CODE") + +def _sidepanel_return_url(request, url) -> str: + """ + Build the admin PageContent changelist URL and preserve existing query parameters + from the current request, excluding internal admin flags. + """ + params = request.GET.copy() + for excluded_param in ["_popup", "_to_field", "_changelist_filters"]: + params.pop(excluded_param, None) + return f"{url}?{urlencode(params, doseq=True)}" if params else url + +def _html_modal_redirect(url) -> HttpResponse: + """ + Redirects to a given URL using an HTML modal. + """ + return HttpResponse(MODAL_HTML_REDIRECT.format(url=url, url_json=dumps(url))) + +def _html_sidepanel_redirect(url) -> HttpResponse: + """ + Redirects to a URL via an HTML sidepanel. + """ + return HttpResponse(SIDEPANEL_HTML_REDIRECT.format(url=url, url_json=dumps(url))) + + +def _admin_add_success_message(obj) -> str: + """ + Build a translatable success message for an added object. + """ + return gettext('%(name)s "%(obj)s" was added successfully.') % { + "name": capfirst(obj._meta.verbose_name), + "obj": obj, + } + +def _admin_change_success_message(obj) -> str: + """ + Generate a success message for an admin object when it has been successfully changed. + """ + return gettext('%(name)s "%(obj)s" was changed successfully.') % { + "name": capfirst(obj._meta.verbose_name), + "obj": obj, + } + +def _admin_delete_success_message(obj) -> str: + return gettext('"%(obj)s" was deleted successfully.') % { + "obj": obj, + } \ No newline at end of file diff --git a/unfold_extra/contrib/cms/widgets.py b/unfold_extra/contrib/cms/widgets.py index 71536b9..374ffab 100644 --- a/unfold_extra/contrib/cms/widgets.py +++ b/unfold_extra/contrib/cms/widgets.py @@ -4,6 +4,10 @@ class UnfoldPageSelectWidget(PageSelectWidget): + """PageSelectWidget with Unfold-styled selects in a stacked layout.""" + + template_name = "unfold_extra/cms/widgets/pageselectwidget.html" + def _build_widgets(self): site_choices = get_site_choices() page_choices = get_page_choices() @@ -12,7 +16,12 @@ def _build_widgets(self): self.widgets = ( UnfoldAdminSelectWidget(choices=site_choices), UnfoldAdminSelectWidget(choices=[("", "----")]), - UnfoldAdminSelectWidget( - choices=self.choices, attrs={"style": "display:none;"} - ), + UnfoldAdminSelectWidget(choices=self.choices), ) + + +class UnfoldPageSelectInlineWidget(UnfoldPageSelectWidget): + """Inline-safe variant: patches the ``__prefix__`` bug in cloned rows.""" + + class Media: + js = ("unfold_extra/cms/js/pageselect_inline.js",) diff --git a/unfold_extra/contrib/sites/admin.py b/unfold_extra/contrib/sites/admin.py index 58f3da9..68d0bd3 100644 --- a/unfold_extra/contrib/sites/admin.py +++ b/unfold_extra/contrib/sites/admin.py @@ -1,11 +1,14 @@ +import django.contrib.sites.admin # noqa: F401 -- ensure stock Site admin registers before our unregister/re-register + from django.contrib import admin from django.contrib.sites.models import Site from unfold.admin import ModelAdmin -admin.site.unregister(Site) +if Site in admin.site._registry: + admin.site.unregister(Site) @admin.register(Site) class SiteAdmin(ModelAdmin): - pass + search_fields = ["domain", "name"] diff --git a/unfold_extra/contrib/versatileimagefield/__init__.py b/unfold_extra/contrib/versatileimagefield/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/unfold_extra/contrib/versatileimagefield/admin.py b/unfold_extra/contrib/versatileimagefield/admin.py new file mode 100644 index 0000000..e169f28 --- /dev/null +++ b/unfold_extra/contrib/versatileimagefield/admin.py @@ -0,0 +1,50 @@ +from django.contrib import admin +from versatileimagefield.fields import VersatileImageField +from versatileimagefield.widgets import VersatileImagePPOIClickWidget + + +class VersatileImageFieldMixin: + def _apply_versatile_image_widget(self, form_or_formset): + image_fields = [ + field.name + for field in self.model._meta.fields # noqa: SLF001 + if isinstance(field, VersatileImageField) + ] + + base_fields = ( + form_or_formset.base_fields + if hasattr(form_or_formset, "base_fields") + else form_or_formset.form.base_fields + ) + + for field_name in image_fields: + if field_name in base_fields: + base_fields[ + field_name + ].widget = VersatileImagePPOIClickWidget() + + return form_or_formset + + +class UnfoldVersatileImageAdmin(VersatileImageFieldMixin, admin.ModelAdmin): + def get_form(self, request, obj=None, **kwargs): + form = super().get_form(request, obj, **kwargs) + return self._apply_versatile_image_widget(form) + + +class UnfoldVersatileImageTabularInline( + VersatileImageFieldMixin, + admin.TabularInline, +): + def get_formset(self, request, obj=None, **kwargs): + formset = super().get_formset(request, obj, **kwargs) + return self._apply_versatile_image_widget(formset) + + +class UnfoldVersatileImageStackedInline( + VersatileImageFieldMixin, + admin.StackedInline, +): + def get_formset(self, request, obj=None, **kwargs): + formset = super().get_formset(request, obj, **kwargs) + return self._apply_versatile_image_widget(formset) diff --git a/unfold_extra/contrib/versatileimagefield/apps.py b/unfold_extra/contrib/versatileimagefield/apps.py new file mode 100644 index 0000000..e9374c8 --- /dev/null +++ b/unfold_extra/contrib/versatileimagefield/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class VersatileimagefieldConfig(AppConfig): + name = "unfold_extra.contrib.versatileimagefield" + label = "unfold_extra_versatileimagefield" diff --git a/unfold_extra/contrib/versatileimagefield/templates/versatileimagefield/forms/widgets/versatile_image.html b/unfold_extra/contrib/versatileimagefield/templates/versatileimagefield/forms/widgets/versatile_image.html new file mode 100644 index 0000000..d9de249 --- /dev/null +++ b/unfold_extra/contrib/versatileimagefield/templates/versatileimagefield/forms/widgets/versatile_image.html @@ -0,0 +1,70 @@ +{% load i18n %} + +{% if widget.sized_url %} +
+ +
+
+
+
+ {% if widget.value and widget.sized_url %} +
+ +
+ {% endif %} +
+
+{% endif %} +
+
+ {% if widget.is_initial and not widget.required %} +
+ +
+ {% endif %} + + +
+ + + {% if widget.is_initial %} + + download + + {% endif %} + + +
+
+ +
\ No newline at end of file diff --git a/unfold_extra/src/.nvmrc b/unfold_extra/src/.nvmrc new file mode 100644 index 0000000..5f53e87 --- /dev/null +++ b/unfold_extra/src/.nvmrc @@ -0,0 +1 @@ +v20.19.0 diff --git a/unfold_extra/src/css/styles.css b/unfold_extra/src/css/styles.css index e658a5f..0a52c72 100644 --- a/unfold_extra/src/css/styles.css +++ b/unfold_extra/src/css/styles.css @@ -1,19 +1,28 @@ @import 'tailwindcss'; @plugin '@tailwindcss/typography'; -@custom-variant dark (&:where(.dark, .dark *)); -@source inline("h-3 w-3 h-[64px] w-[65px]! left-[65px] dark:hover:bg-white/[.06] translate-x-1/4 -translate-y-1/4"); -@source inline("{-,}{top,bottom,left,right}-{0..6}"); -@source inline("rounded rounded-{t,r,b,l} rounded-{t,r,b,l}-default"); -@source inline("{md:,lg:,2xl:,}relative {md:,lg:,2xl:,}flex {md:,lg:,2xl:,}absolute {md:,lg:,2xl:,}sticky"); -@source inline("{-,}m{t,r,b,l,x,y,}-{0..6} {-,}p{t,r,b,l,x,y,}-{0..6}"); + +@custom-variant dark { + @media not print { + &:where(.dark, .dark *) { + @slot + } + } +} + +@source inline("aspect-square group-hover:block self-start font-bold bg-white/[.06] border-white/40 h-[64px] w-[65px]! left-[65px] {dark:,}text-white/{0..100..10} {dark:,}bg-white/{0..100..10} {dark:,}text-{base,primary}-{50,{100..900},950} dark:hover:bg-white/[.06] {-,}translate-{x,y}-{1/2}"); +@source inline("{-,}{top,bottom,left,right}-{1/2,0..6} {!,}leading-[38px] leading-{6..16} text-{default,base,link,icon,subtle,important,xl,2xl,3xl,4xl}"); +@source inline("bg-linear-to-{{t,r,b,l,tl,tr,bl,br}} {dark:,}{from,to}-{{{base,primary}-{50,{100..900..100},950}},white}"); +@source inline("rounded rounded-{t,r,b,l} rounded-{t,r,b,l}-default rounded-{xs,sm,md,lg,xl,2xl,3xl,full,none}"); +@source inline("{md:,lg:,xl:,2xl:,}{absolute,flex,relative,block,sticky}"); +@source inline("{-,}{lg:,}m{t,r,b,l,x,y,}-{0..24} {-,}{lg:,}p{t,r,b,l,x,y,}-{0..24}"); @source inline("{md:,lg:,}border-{0,2} {md:,lg:,}border-{t,r,b,l}-{0,2}"); -@source inline("{hover:,dark:,}bg-primary-{50,{100..900..100},950}"); +@source inline("{!,}{hover:,dark:,}bg-primary-{50,{100..900..100},950}"); @source inline("{hover:,dark:,}border-base-{50,{100..900..100},950} {hover:,dark:,}border-transparent"); @source inline("{hover:,dark:,}bg-base-{50,{100..900..100},950}"); -@source inline("{md:,lg:,}w-{1/2,1/3,2/3,1/4,2/4,3/4,1/5,2/5,3/5,4/5,1/6,2/6,3/6,4/6,5/6,1/7,2/7,3/7,4/7,5/7,6/7}"); -@source inline("{md:,lg:,}gap-{0.5,{1..12}}"); -@source inline("{md:,lg:,}grid-cols-{1..12}"); -@source inline("{md:,lg:,}col-span-{1..12}"); +@source inline("{md:,lg:,}{min-h,h,min-w,w}-{1..10} {md:,lg:,}h-{1..10} {md:,lg:,}w-{1/2,1/3,2/3,1/4,2/4,3/4,1/5,2/5,3/5,4/5,1/6,2/6,3/6,4/6,5/6,1/7,2/7,3/7,4/7,5/7,6/7,1..10,96}"); +@source inline("{md:,lg:,xl:,}gap-{0.5,{1..16}}"); +@source inline("{md:,lg:,xl:,}grid-cols-{1..16}"); +@source inline("{md:,lg:,xl:,}col-span-{1..16}"); @layer base { *, @@ -21,47 +30,70 @@ ::before, ::backdrop, ::file-selector-button { - border-color: var(--color-gray-200, currentColor); + border-color: var(--color-base-200, currentColor); } } @theme { --font-sans: 'Inter', sans-serif; + --shadow-raised: 0 2px 12px var(--color-base-300); + --shadow-raised-dark: 0 2px 12px var(--color-base-700); } @theme inline{ --radius-default: var(--border-radius, 6px); - --color-primary-50: rgb(var(--color-primary-50)); - --color-primary-100: rgb(var(--color-primary-100)); - --color-primary-200: rgb(var(--color-primary-200)); - --color-primary-300: rgb(var(--color-primary-300)); - --color-primary-400: rgb(var(--color-primary-400)); - --color-primary-500: rgb(var(--color-primary-500)); - --color-primary-600: rgb(var(--color-primary-600)); - --color-primary-700: rgb(var(--color-primary-700)); - --color-primary-800: rgb(var(--color-primary-800)); - --color-primary-900: rgb(var(--color-primary-900)); - --color-primary-950: rgb(var(--color-primary-950)); - - --color-base-50: rgb(var(--color-base-50)); - --color-base-100: rgb(var(--color-base-100)); - --color-base-200: rgb(var(--color-base-200)); - --color-base-300: rgb(var(--color-base-300)); - --color-base-400: rgb(var(--color-base-400)); - --color-base-500: rgb(var(--color-base-500)); - --color-base-600: rgb(var(--color-base-600)); - --color-base-700: rgb(var(--color-base-700)); - --color-base-800: rgb(var(--color-base-800)); - --color-base-900: rgb(var(--color-base-900)); - --color-base-950: rgb(var(--color-base-950)); - - --color-font-subtle-light: rgb(var(--color-font-subtle-light)); - --color-font-subtle-dark: rgb(var(--color-font-subtle-dark)); - --color-font-default-light: rgb(var(--color-font-default-light)); - --color-font-default-dark: rgb(var(--color-font-default-dark)); - --color-font-important-light: rgb(var(--color-font-important-light)); - --color-font-important-dark: rgb(var(--color-font-important-dark)); + --color-primary-50: var(--color-primary-50); + --color-primary-100: var(--color-primary-100); + --color-primary-200: var(--color-primary-200); + --color-primary-300: var(--color-primary-300); + --color-primary-400: var(--color-primary-400); + --color-primary-500: var(--color-primary-500); + --color-primary-600: var(--color-primary-600); + --color-primary-700: var(--color-primary-700); + --color-primary-800: var(--color-primary-800); + --color-primary-900: var(--color-primary-900); + --color-primary-950: var(--color-primary-950); + + --color-base-50: var(--color-base-50); + --color-base-100: var(--color-base-100); + --color-base-200: var(--color-base-200); + --color-base-300: var(--color-base-300); + --color-base-400: var(--color-base-400); + --color-base-500: var(--color-base-500); + --color-base-600: var(--color-base-600); + --color-base-700: var(--color-base-700); + --color-base-800: var(--color-base-800); + --color-base-900: var(--color-base-900); + --color-base-950: var(--color-base-950); + + --color-font-subtle-light: var(--color-font-subtle-light); + --color-font-subtle-dark: var(--color-font-subtle-dark); + --color-font-default-light: var(--color-font-default-light); + --color-font-default-dark: var(--color-font-default-dark); + --color-font-important-light: var(--color-font-important-light); + --color-font-important-dark: var(--color-font-important-dark); +} + + +@utility text-important { + @apply text-font-important-light dark:text-font-important-dark; +} + +@utility text-default { + @apply text-font-default-light dark:text-font-default-dark; +} + +@utility text-subtle { + @apply text-font-subtle-light dark:text-font-subtle-dark; +} + +@utility text-icon { + @apply text-base-400 dark:text-base-500; +} + +@utility text-link { + @apply text-primary-600 dark:text-primary-500; } @utility field-sizing-content { @@ -89,7 +121,7 @@ } @utility scrollable-top { - @apply after:absolute after:content-[''] after:bg-linear-to-t after:from-gray-100 after:h-4 after:left-0 after:right-0 after:-top-px after:-translate-y-full dark:after:bg-none; + @apply after:absolute after:content-[''] after:bg-linear-to-t after:from-base-100 after:h-4 after:left-0 after:right-0 after:-top-px after:-translate-y-full dark:after:bg-none; } @utility md-16 { @@ -145,19 +177,6 @@ select:after { /******************************************************* Table *******************************************************/ -table select { - @apply appearance-none bg-no-repeat border border-base-200 block cursor-pointer font-medium px-3 py-2 pr-5 rounded-default shadow-xs text-base-500; - - background-image: url('data:image/svg+xml;utf8,'); - background-size: 1.125rem 1.125rem; - background-position: right 0.5rem center; -} - - -table select:focus { - @apply border-primary-600; -} - table tr.selected td, table tr.selected th { @apply bg-yellow-200/[.10] dark:bg-white/[.03]; @@ -201,7 +220,7 @@ table tr.selected th { } .timezonewarning { - @apply absolute block items-center right-20 top-2.5 text-base-500 truncate; + @apply absolute block items-center right-11 top-2 text-base-500 truncate; font-size: 0; } @@ -222,7 +241,7 @@ table tr.selected th { Selector *******************************************************/ .selector { - @apply flex flex-col grow items-center md:flex-row; + @apply flex flex-col grow items-center max-w-5xl md:flex-row; } .selector .selector-available-title label, @@ -266,7 +285,7 @@ table tr.selected th { .selector-chooseall, .selector-clearall { - @apply block border-t border-base-200 py-2 text-center text-sm text-primary-500 dark:border-base-700; + @apply block border-t border-base-200 cursor-pointer py-2 text-center text-sm text-default dark:border-base-700; } .selector-clearall { @@ -323,7 +342,7 @@ table tr.selected th { } [data-inline-type="stacked"] .add-row { - @apply border-t border-base-200 overflow-hidden dark:border-base-800; + @apply overflow-hidden; } .add-row td { @@ -342,10 +361,6 @@ td .inline-deletelink { @apply block lg:mt-2.5; } -h3 span:nth-child(3) { - @apply ml-auto; -} - /******************************************************* Autocomplete *******************************************************/ @@ -384,7 +399,7 @@ h3 span:nth-child(3) { .select2-container.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__clear:after, .select2-container.select2-container--admin-autocomplete .select2-selection--single .select2-selection__clear:after { - @apply material-symbols-outlined transition-all content-['close'] text-[18px] ; + @apply material-symbols-outlined transition-all content-['close\_small'] text-[18px] ; } .select2-container.select2-container--admin-autocomplete .select2-selection--single .select2-selection__arrow { @@ -412,6 +427,10 @@ h3 span:nth-child(3) { @apply py-1; } +.select2-container .select2-results__option--highlighted[aria-selected] { + @apply !bg-base-100 dark:!bg-base-800; +} + .select2-container.select2-container--open .select2-dropdown { @apply bg-white border border-base-200 rounded-default shadow-xs dark:border-base-700 dark:bg-base-900; } @@ -452,9 +471,8 @@ h3 span:nth-child(3) { } .select2-container--admin-autocomplete .select2-selection--multiple li.select2-selection__choice .select2-selection__choice__remove:after { - @apply h-7 leading-7! material-symbols-outlined; + @apply content-['close\_small'] h-7 leading-7! material-symbols-outlined; - content: "close"; font-size: 14px; } @@ -487,7 +505,7 @@ fieldset details > summary { } fieldset details > summary:after { - @apply material-symbols-outlined absolute right-3 top-3.5; + @apply material-symbols-outlined absolute cursor-pointer right-3 top-3.5; content: "expand_more"; } @@ -498,13 +516,17 @@ fieldset details[open] > summary:after { /******************************************************* Calendar *******************************************************/ +.calendarbox .calendar { + @apply min-h-[320px]; +} + .calendarbox, .clockbox { - @apply bg-white border border-base-200 rounded-default shadow-xs text-base-500 text-sm w-80 z-50 dark:bg-base-800 dark:border-base-700 fixed! left-1/2! top-1/2! -translate-x-1/2 -translate-y-1/2; + @apply bg-white border border-base-200 rounded-default shadow-xs w-80 z-50 dark:bg-base-800 dark:border-base-700 fixed! left-1/2! top-1/2! -translate-x-1/2 -translate-y-1/2; } .calendar caption { - @apply font-medium mb-3 py-3 text-base-700 dark:text-base-200; + @apply font-semibold mb-3 py-3 text-important; } .calendar table { @@ -512,7 +534,7 @@ fieldset details[open] > summary:after { } .calendar table th { - @apply font-medium text-center text-base-700 text-xs dark:text-base-200; + @apply font-semibold text-center text-important text-xs; } .calendar table td { @@ -520,7 +542,7 @@ fieldset details[open] > summary:after { } .calendar table td a { - @apply block flex h-8 items-center justify-center rounded-full transition-all w-8 dark:text-base-300; + @apply block flex h-8 items-center justify-center rounded-full w-8 dark:text-base-300; } .calendar table td a:hover { @@ -536,7 +558,7 @@ fieldset details[open] > summary:after { } .calendar-shortcuts a { - @apply border border-base-200 font-medium leading-none mx-1 px-2 py-2 rounded-default shadow-xs text-center text-base-500 text-xs transition-all w-1/3 dark:border-base-700 dark:hover:border-base-600 dark:text-base-300 dark:hover:text-base-200; + @apply border border-base-200 font-medium leading-none mx-1 px-2 py-2 rounded-default shadow-xs text-center text-base-500 text-xs w-1/3 dark:border-base-700 dark:text-base-300 ; } .calendar-cancel { @@ -547,28 +569,25 @@ fieldset details[open] > summary:after { @apply absolute block ml-2 mt-2 left-0 text-[0px] top-0; } -.calendarnav-next:after, -.calendarnav-previous:after { - @apply border border-base-200 flex h-7 items-center justify-center material-symbols-outlined rounded-full text-base-400 transition-all w-7 hover:border-primary-600 hover:text-primary-500 dark:bg-base-800 dark:border-base-700 dark:hover:border-base-800; +.calendarnav-next, +.calendarnav-previous { + @apply after:border after:border-base-200 after:flex after:h-7 after:items-center after:justify-center after:material-symbols-outlined after:rounded-full after:transition-all after:w-7 dark:after:bg-base-800 dark:after:border-base-700; +} +.calendarnav-previous:after { content: "navigate_before"; display: flex; } -.calendarnav-next:hover:after, -.calendarnav-previous:hover:after { - @apply dark:border-base-600 dark:text-base-200; +.calendarnav-next:after { + content: "navigate_next"; + display: flex; } .calendarnav-next { @apply absolute block mr-2 mt-2 right-0 text-[0px] top-0; } -.calendarnav-next:after { - content: "navigate_next"; - display: flex; -} - /******************************************************* Timepicker *******************************************************/ @@ -630,10 +649,6 @@ fieldset details[open] > summary:after { @apply bg-primary-600 border-0 h-1; } -#changelist-filter .admin-numeric-filter-slider-tooltips { - @apply flex flex-row font-medium mb-5 space-x-4 text-base-500 text-sm; -} - /******************************************************* Trix *******************************************************/ @@ -678,3 +693,385 @@ trix-toolbar[id^="trix-toolbar-"] { .simplebar-horizontal-scrollbar-top .simplebar-track.simplebar-horizontal { @apply top-9 } + +/******************************************************* + DjangoQL + *******************************************************/ +textarea#searchbar { + @apply font-medium placeholder-base-400 leading-[35px] max-h-[35px] min-h-[35px] whitespace-nowrap !w-full focus:outline-none; +} + +div.djangoql-completion { + @apply border border-base-200 rounded-default shadow-xs bg-white dark:border-base-700 dark:bg-base-800 -ml-[39px] mt-[6px] w-[384px] z-40; +} + +div.djangoql-completion ul { + @apply py-1; +} + +div.djangoql-completion li { + @apply flex items-center leading-none mx-1 px-3 h-[30px] rounded-default text-sm hover:bg-base-100 hover:text-important hover:text-base-700 dark:hover:bg-base-700 dark:hover:text-base-200; +} + +div.djangoql-completion li.active { + @apply bg-base-100 text-important dark:bg-base-700; +} + +div.djangoql-completion li i { + @apply ml-auto text-sm text-default; +} + +div.djangoql-completion .syntax-help { + @apply hidden; +} + +.djangoql-toggle { + @apply appearance-none bg-white block border border-base-300 cursor-pointer h-4 min-w-4 relative rounded-[4px] shadow-xs w-4 hover:border-base-400 dark:bg-base-700 dark:border-base-500 dark:checked:after:text-white focus:outline focus:outline-2 focus:outline-offset-2 focus:outline-primary-500 after:absolute after:content-['check\_small'] after:flex! after:h-4 after:items-center after:justify-center after:leading-none after:material-symbols-outlined after:-ml-px after:-mt-px after:text-white after:transition-all after:w-4 dark:after:text-base-700 checked:bg-primary-600 dark:checked:bg-primary-600 checked:border-primary-600 dark:checked:border-primary-600 checked:transition-all checked:hover:border-primary-600; + @apply relative -ml-4.5; +} + +/******************************************************* + Django JSON widget + *******************************************************/ +.jsoneditor { + @apply border !border-base-200 overflow-hidden rounded-default shadow-xs dark:!border-base-700; +} + +div.jsoneditor-tree, +.ace-jsoneditor .ace_scroller { + @apply text-default dark:bg-base-900; +} + +.jsoneditor div.jsoneditor-tree { + @apply bg-white dark:!bg-transparent; +} + +.jsoneditor textarea.jsoneditor-text { + @apply !bg-transparent leading-relaxed p-3; +} + +.jsoneditor .jsoneditor-text, +.jsoneditor .jsoneditor-tree { + @apply !bg-transparent !text-default dark:!bg-transparent; +} + +.jsoneditor .jsoneditor-readonly { + @apply !text-subtle; +} + +.jsoneditor .jsoneditor-field { + @apply !border-none !text-default hover:!bg-base-100 dark:bg-transparent dark:hover:!bg-base-800; +} + +.jsoneditor .jsoneditor-value { + @apply !border-none text-important hover:!bg-base-100 dark:hover:!bg-base-800; +} + +.jsoneditor .jsoneditor-value.jsoneditor-string, +.jsoneditor .ace_string { + @apply !text-green-600 dark:text-green-500; +} + +.jsoneditor .jsoneditor-value.jsoneditor-number, +.jsoneditor .ace_numeric { + @apply text-blue-600 dark:text-blue-500; +} + +.jsoneditor .jsoneditor-value.jsoneditor-boolean, +.jsoneditor .ace_boolean { + @apply text-red-600 dark:text-red-500; +} + +.ace-jsoneditor .ace_gutter { + @apply border-r border-base-200 bg-base-100 text-subtle dark:bg-base-800 dark:border-base-700; +} + +.ace-jsoneditor .ace_gutter-active-line { + @apply bg-yellow-500/20 text-important dark:bg-yellow-500/20; +} + +.ace-jsoneditor .ace_active-line { + @apply !bg-yellow-500/20 dark:!bg-yellow-500/20; +} + +.ace-jsoneditor .ace_variable { + @apply text-important; +} + +.ace-jsoneditor .ace_indent-guide { + @apply bg-none border-r border-base-200 dark:border-base-700; +} + +.ace-jsoneditor .ace_marker-layer .ace_selection { + @apply bg-base-900/10 dark:bg-white/10; +} + +.jsoneditor .jsoneditor-statusbar { + @apply bg-white border-base-200 leading-[26px] px-2 text-subtle text-xs dark:bg-base-900 dark:border-base-700; +} + +.jsoneditor-menu a.jsoneditor-poweredBy { + @apply text-subtle text-xs opacity-100 font-sans; +} + +/* Search */ +.jsoneditor .jsoneditor-search .jsoneditor-results { + @apply font-sans text-xs; +} + +.jsoneditor .jsoneditor-search .jsoneditor-frame { + @apply border border-base-200 rounded-default dark:border-base-700 dark:bg-base-900 dark:border-base-700; +} + +.jsoneditor .jsoneditor-search .jsoneditor-frame input[type="text"] { + @apply !bg-transparent !border-none font-sans text-xs h-[22px] leading-[22px] !text-default; +} + +.jsoneditor .jsoneditor-search .jsoneditor-frame .jsoneditor-refresh { + @apply bg-none ml-1; +} + +.jsoneditor .jsoneditor-search .jsoneditor-frame .jsoneditor-refresh:after { + @apply material-symbols-outlined text-[16px] !leading-[24px] content-['search'] !text-icon; +} + +.jsoneditor .jsoneditor-search .jsoneditor-frame .jsoneditor-next, +.jsoneditor .jsoneditor-search .jsoneditor-frame .jsoneditor-previous { + @apply bg-none text-icon; +} + +.jsoneditor .jsoneditor-search .jsoneditor-frame .jsoneditor-next:after, +.jsoneditor .jsoneditor-search .jsoneditor-frame .jsoneditor-previous:after { + @apply material-symbols-outlined text-[16px] !leading-[24px] +} + +.jsoneditor .jsoneditor-search .jsoneditor-frame .jsoneditor-next:after { + @apply content-['keyboard\_double\_arrow\_down']; +} + +.jsoneditor .jsoneditor-search .jsoneditor-frame .jsoneditor-previous:after { + @apply content-['keyboard\_double\_arrow\_up']; +} + +/* Navigation bar */ +.jsoneditor .jsoneditor-navigation-bar { + @apply !bg-base-50 !border-b border-base-200 leading-[26px] !px-2 text-xs !text-default dark:!bg-base-800 dark:!border-base-700; +} + +.jsoneditor .jsoneditor-treepath .jsoneditor-treepath-seperator { + @apply text-[0px]; +} + +.jsoneditor .jsoneditor-treepath .jsoneditor-treepath-seperator:after { + @apply material-symbols-outlined content-['chevron\_right'] !align-[-4px] text-subtle; +} + +/* Menu */ +.jsoneditor-menu { + @apply !bg-base-50 !border-base-200 dark:!border-base-700 dark:!bg-base-800; +} + +.jsoneditor .jsoneditor-menu > button { + @apply !bg-none !border !border-base-200 !bg-white !shadow-xs cursor-pointer rounded-default text-important dark:!border-base-700 dark:!bg-base-900; +} + +.jsoneditor .jsoneditor-menu > button:after { + @apply material-symbols-outlined text-[16px] !leading-[24px]; +} + +.jsoneditor-menu > button.jsoneditor-format { + @apply after:content-['format\_align\_right'] +} + +.jsoneditor-menu > button.jsoneditor-compact { + @apply after:content-['format\_align\_left'] +} + +.jsoneditor-menu > button.jsoneditor-sort { + @apply after:content-['sort'] +} + +.jsoneditor-menu > button.jsoneditor-transform { + @apply after:content-['transform'] +} + +.jsoneditor-menu > button.jsoneditor-repair { + @apply after:content-['handyman'] +} + +.jsoneditor-menu > button.jsoneditor-undo { + @apply after:content-['undo'] +} + +.jsoneditor-menu > button.jsoneditor-redo { + @apply after:content-['redo'] +} + +.jsoneditor-menu > button.jsoneditor-expand-all { + @apply after:content-['expand'] +} + +.jsoneditor-menu > button.jsoneditor-collapse-all { + @apply after:content-['compress'] +} + +.jsoneditor-menu > div.jsoneditor-modes > button { + @apply !bg-transparent !border-none cursor-pointer !font-sans text-default hover:!bg-none hover:text-important; +} + +/* Context menu */ +.jsoneditor-contextmenu .jsoneditor-menu { + @apply !bg-white border border-base-200 overflow-clip rounded-default !py-1 !shadow-xs dark:!bg-base-800 dark:border-base-800; +} + +.jsoneditor-contextmenu .jsoneditor-menu li { + @apply flex flex-col relative; +} + +.jsoneditor-contextmenu .jsoneditor-menu button { + @apply !bg-transparent !flex-grow !mx-1 !px-3 !h-[30px] !rounded-default !w-auto hover:!bg-base-100 hover:!text-base-700 dark:hover:!bg-base-700 dark:hover:!text-base-200; +} + +.jsoneditor-contextmenu button .jsoneditor-icon { + @apply !w-auto; +} + +.jsoneditor-contextmenu button .jsoneditor-text { + @apply !bg-transparent !font-sans p-0 text-default; +} + +.jsoneditor-contextmenu .jsoneditor-selected { + @apply !bg-transparent; +} + +.jsoneditor-contextmenu .jsoneditor-selected > button > .jsoneditor-text { + @apply !text-primary-600 dark:!text-primary-500; +} + +.jsoneditor-contextmenu .jsoneditor-menu li button .jsoneditor-expand { + @apply !flex !items-center justify-center !m-0 !p-0 !top-0.5 !right-0.5; +} + +.jsoneditor .jsoneditor-selected, +.jsoneditor .jsoneditor-highlight { + @apply !bg-base-50 dark:!bg-base-800; +} + +.jsoneditor div.jsoneditor-expand, +.jsoneditor .jsoneditor-button.jsoneditor-collapsed, +.jsoneditor .jsoneditor-button.jsoneditor-expanded, +.jsoneditor .jsoneditor-dragarea, +.jsoneditor .jsoneditor-contextmenu-button { + @apply !bg-none !bg-transparent !outline-none; +} + +.jsoneditor div.jsoneditor-expand:after, +.jsoneditor .jsoneditor-dragarea:after, +.jsoneditor .jsoneditor-contextmenu-button:after, +.jsoneditor .jsoneditor-button.jsoneditor-collapsed:after, +.jsoneditor .jsoneditor-button.jsoneditor-expanded:after { + @apply material-symbols-outlined text-[16px] !leading-[24px] text-icon; +} + +.jsoneditor div.jsoneditor-expand:after { + @apply content-['arrow\_drop\_down']; +} + +.jsoneditor .jsoneditor-contextmenu-button:after { + @apply content-['toolbar']; +} + +.jsoneditor .jsoneditor-dragarea:after { + @apply content-['drag\_indicator']; +} + +.jsoneditor .jsoneditor-button.jsoneditor-collapsed:after { + @apply content-['keyboard\_arrow\_up']; +} + +.jsoneditor .jsoneditor-button.jsoneditor-expanded:after { + @apply content-['keyboard\_arrow\_down']; +} + + +.jsoneditor-contextmenu .jsoneditor-menu button.jsoneditor-expand { + @apply absolute !border-none right-0 top-0 hover:!bg-transparent; +} + +.jsoneditor .jsoneditor-separator { + @apply !border-base-200 dark:!border-base-700; +} + +/* Modal */ +.jsoneditor-modal.pico-content { + @apply !border-none !rounded-default !font-sans !text-default !shadow-xs dark:border-base-700 dark:!bg-base-800; +} + +.jsoneditor-modal p { + @apply leading-relaxed; +} + +.jsoneditor-modal p a { + @apply !text-link; +} + +.jsoneditor-modal .pico-modal-header { + @apply !bg-transparent border-b border-base-200 !h-auto text-base !font-semibold !text-important !px-3 !py-1.5 !tracking-tight dark:border-base-700 ; +} + +.jsoneditor-modal .pico-modal-contents { + @apply block !mt-3; +} + +.jsoneditor-modal .jsoneditor-jmespath-label { + @apply !text-sm !text-important; +} + +.jsoneditor-modal.pico-content .pico-close { + @apply !text-[0px] !text-icon -mt-[13px] mr-2 hover:!text-important; +} + +.jsoneditor-modal.pico-content .pico-close:after { + @apply material-symbols-outlined text-[16px] !leading-[24px] content-['close']; +} + +.jsoneditor-modal.pico-content select, +.jsoneditor-modal.pico-content #query, +.jsoneditor-modal.pico-content textarea { + @apply bg-white border border border-base-200 min-h-[38px] min-w-sm shadow-xs px-3 rounded-default text-default dark:bg-base-900 dark:border-base-700 outline-none; +} + +.jsoneditor-modal.pico-content #query, +.jsoneditor-modal.pico-content textarea { + @apply py-2; +} + +.jsoneditor-modal.pico-content input[type="submit"] { + @apply border border-base-200 bg-primary-600 border-transparent text-white cursor-pointer font-medium flex items-center gap-2 px-3 py-2 relative rounded-default justify-center whitespace-nowrap; +} + +.jsoneditor-modal.pico-content .jsoneditor-modal-actions { + @apply m-0; +} + +.jsoneditor-modal.pico-content form { + @apply !font-sans !text-default w-[570px]; +} + +.jsoneditor-modal.pico-content .jsoneditor-button-group { + @apply flex flex-row gap-2 justify-end; +} + +.jsoneditor-modal-input input[type="button"].jsoneditor-button-asc, +.jsoneditor-modal-input input[type="button"].jsoneditor-button-desc { + @apply !bg-base-50 !border !border-base-200 cursor-pointer !font-sans !h-[38px] !text-default !rounded-default dark:!bg-base-800 dark:!border-base-700; +} + +.jsoneditor-modal .jsoneditor-button-group.jsoneditor-button-group-value-asc input.jsoneditor-button-asc, +.jsoneditor-modal .jsoneditor-button-group.jsoneditor-button-group-value-desc input.jsoneditor-button-desc { + @apply !border-primary-600 !bg-primary-600 !text-white; +} + +.jsoneditor-modal table td, .jsoneditor-modal table th { + @apply !font-sans !text-default; +} diff --git a/unfold_extra/src/css/unfold_extra.css b/unfold_extra/src/css/unfold_extra.css index 3db9669..c59523a 100644 --- a/unfold_extra/src/css/unfold_extra.css +++ b/unfold_extra/src/css/unfold_extra.css @@ -20,7 +20,7 @@ div.parler-language-tabs { border-0 border-b-0 dark:bg-white/[.04] - after:hidden + after:hidden; } div.parler-language-tabs span { @@ -68,7 +68,7 @@ div.parler-language-tabs span.current { versatile image *******************************************************/ .sizedimage-mod { - @apply flex flex-col + @apply flex flex-col; } .sizedimage-mod img { @@ -83,25 +83,41 @@ div.parler-language-tabs span.current { @apply border-0; } - - - /******************************************************* django-cms *******************************************************/ /* Prevent content from being hidden under django cms Topbar */ -div.cms-sideframe { +body.cms-admin-sideframe #page { + @apply pt-[46px]; +} + +/* Prevent content from being hidden under django cms Topbar */ +body.cms-admin-sideframe #nav-sidebar-inner { + @apply pt-[46px]; +} + +/* Prevent content from being hidden under django cms Topbar */ +#nav-sidebar .simplebar-content-wrapper { @apply mt-[46px]; } -body.cms-admin-sideframe:not(.djangocms-admin-style) { - margin-block-start: 0; +/* Reposition Django CMS admin navigation controls */ +div.cms-sideframe { + @apply !w-[calc(95%-64px)]; +} + +div.cms-sideframe .cms-sideframe-controls .cms-sideframe-history { + @apply hidden; } /* Reposition Django CMS admin navigation controls */ -div.cms.cms-toolbar-debug .cms-sideframe-controls { - @apply top-[58px] right-[64px]; +div.cms-sideframe .cms-sideframe-controls { + @apply top-[58px] right-[calc(-5%-64px)]; +} + +body.cms-admin-sideframe:not(.djangocms-admin-style) { + margin-block-start: 0; } /* Fix Modal displaying django parent admin navigation */ @@ -109,31 +125,97 @@ body.cms-admin-modal #page #main { @apply pt-8; } +/* Hide django submit row in modal */ body.cms-admin-modal #page #main #submit-row { @apply hidden; } +/* Hide main app side navigation in modal */ body.cms-admin-modal #page > div.relative:first-child { @apply hidden; } -/*TODO: breaks plugin display */ +/* Hide topbar in modal window !*TODO: breaks plugin display *!*/ body.cms-admin-modal #page #main > div:first-child { @apply hidden; } -body.cms-admin-modal #page #main { - @apply -mt-10; +/* Fix Modal width */ +body.cms-admin-modal #page #main #content { + @apply max-w-full; +} + +/* Hide meta navigation (view on site / version) in modal */ +body.cms-admin-modal #page #main #content > div:not(#content-main) ul li a { + @apply hidden; } -body.cms-admin-modal #page #main #content > div:not(#content-main) { +/* Hide language tabs in django cms page content edit */ +body #page #main #page_form_lang_tabs { @apply hidden; } -.cms-admin-modal #nav-sidebar{ +/* Hide submit row in django cms page content edit - use unfold instead */ +body #content-main .submit-row { @apply hidden; } +/******************************************************* + django-cms - change form / page settings (tabbed fieldsets) + *******************************************************/ + +/* Modal: any CMS-modal change form (forms in modal don't get the parent's flex gap). + Sideframe: only PageContent change form (page settings) — other sideframe pages + use Unfold's normal flex gap on the parent. */ +body.cms-admin-modal fieldset.module:not(.collapse), +body.cms-admin-modal fieldset.module > details[open], +body.cms-admin-sideframe.app-cms.model-pagecontent.change-form fieldset.module:not(.collapse), +body.cms-admin-sideframe.app-cms.model-pagecontent.change-form fieldset.module > details[open] { + @apply mb-4; +} + +/******************************************************* + django-cms - pageContentAdmin - Action List + //This is a workaround until a solution is found to render {% block object-tools-items %} + *******************************************************/ +body #page #main #content > div > ul { + @apply flex flex-col font-medium mb-4 ml-auto mt-2 md:flex-row md:mb-0 md:mt-0 max-md:w-full; +} + +body #page #main #content > div > ul li { + @apply md:first:rounded-l-default md:-ml-px max-md:last:rounded-b-default max-md:-mt-px border border-base-200 md:last:rounded-r-default dark:border-base-700 hover:text-primary-600 max-md:first:rounded-t-default dark:hover:text-primary-500; +} + +body #page #main #content > div > ul li a { + @apply cursor-pointer flex grow items-center gap-2 px-3 py-2 text-left whitespace-nowrap; +} + +/******************************************************* + django-cms - buttons + *******************************************************/ + +div.cms .cms-btn-action { + @apply text-white; +} + +div.cms .cms-btn-caution { + @apply dark:text-red-500 dark:bg-red-500/20 dark:border-red-500/20; +} + +/******************************************************* + django-cms - toolbars + *******************************************************/ + +/* force button text color to white on hover - dark mode */ +html[data-theme=dark] div.cms .cms-toolbar-item-navigation .cms-toolbar-item-navigation-hover > a { + @apply !text-white; +} + +/* prevent CMS topbar from overlapping changelist filter (46px topbar) */ +body.cms-admin-sideframe #changelist-filter { + @apply pt-[46px]; +} + /******************************************************* django-cms - pagetree *******************************************************/ @@ -161,16 +243,30 @@ div.cms-pagetree-root { @apply !shadow-none; } -div.cms-pagetree-header-filter input { +.cms-pagetree-header-filter input { @apply bg-white border border-base-200 dark:border-base-600 rounded font-medium grow min-w-0 h-9 px-3 text-font-default-light text-sm focus:outline-none dark:bg-base-900 dark:text-font-default-dark placeholder-shown:truncate placeholder-base-400; } -button.cms-pagetree-header-search-btn { - @apply !rounded-full; +.cms-pagetree-header-search button.cms-pagetree-header-search-btn { + @apply cursor-pointer !rounded-full !bg-transparent !text-[var(--color-font-default-light)]; +} + +.cms-pagetree-header-search button.cms-pagetree-header-search-btn:hover { + @apply !rounded-full !bg-base-100 text-primary-500; } div.cms-pagetree-header a.cms-btn-action { - @apply !rounded; + @apply !rounded !text-white bg-primary-500; +} + +/* When the add button is moved to the Unfold header, hide the CMS inline + button and remove trailing spacing from the last remaining element. */ +.unfold-header-add .cms-pagetree-header-create { + @apply !hidden; +} + +.unfold-header-add .cms-pagetree-sites-list.cms-pagetree-dropdown { + @apply mr-0 pr-0; } input#nav-filter { @@ -187,6 +283,111 @@ input#nav-filter { } +/******************************************************* + djangocms-text - plugin + *******************************************************/ +.app-djangocms_text.model-text.change-form fieldset.module > div { + @apply !border-0 !border-transparent shadow-none; +} + +.app-djangocms_text.model-text.change-form #content { + @apply max-w-full; +} + +.cms-editor-inline-wrapper > div { + @apply border border-base-200 rounded-2xl shadow-xs min-h-auto +} + +.cms-editor-inline-wrapper > div + div { + @apply mt-4; +} + +.cms-editor-inline-wrapper { + @apply mt-0; +} + +.cms-editor-inline-wrapper > .tiptap { + @apply !p-4 !overflow-y-visible; +} + +textarea.CMS_Editor + div#id_body_editor.fixed .cms-toolbar[role=menubar] { + @apply bg-base-50 dark:bg-base-800 p-4 border-0 mb-16 mt-16; +} + +textarea.CMS_Editor + div#id_body_editor.fixed .cms-toolbar[role=menubar] > * { + @apply bg-transparent space-x-4 p-2 mx-1 my-2 rounded-lg; +} + +textarea.CMS_Editor + div#id_body_editor.fixed .tiptap.ProseMirror a[href] { + @apply underline; +} + +textarea.CMS_Editor + div#id_body_editor.fixed .tiptap-separator:empty { + @apply p-0; +} + +textarea.CMS_Editor + div#id_body_editor.fixed .cms-toolbar .dropdown-content { + @apply border-base-200 dark:border-base-700 rounded-2xl shadow-2xl; +} + +textarea.CMS_Editor + div#id_body_editor.fixed .cms-linkfield-wrapper input { + @apply border border-base-200 bg-white font-medium min-w-20 placeholder-base-400 rounded-default shadow-xs text-font-default-light text-sm focus:outline-2 focus:-outline-offset-2 focus:outline-primary-600 group-[.errors]:border-red-600 focus:group-[.errors]:outline-red-600 dark:bg-base-900 dark:border-base-700 dark:text-font-default-dark dark:group-[.errors]:border-red-500 dark:focus:group-[.errors]:outline-red-500 dark:scheme-dark group-[.primary]:border-transparent px-3 py-2 w-full max-w-2xl; +} + +textarea.CMS_Editor + div#id_body_editor.fixed .cms-linkfield-wrapper select { + @apply border border-base-200 bg-white font-medium min-w-20 placeholder-base-400 rounded-default shadow-xs text-font-default-light text-sm focus:outline-2 focus:-outline-offset-2 focus:outline-primary-600 group-[.errors]:border-red-600 focus:group-[.errors]:outline-red-600 dark:bg-base-900 dark:border-base-700 dark:text-font-default-dark dark:group-[.errors]:border-red-500 dark:focus:group-[.errors]:outline-red-500 dark:scheme-dark group-[.primary]:border-transparent px-3 py-2 w-full pr-8 max-w-2xl appearance-none; +} + +/******************************************************* + django-cms - djangocms-versioning + *******************************************************/ + +button.djangocms-versioning.submit { + @apply mx-10 m-5 border border-base-200 cursor-pointer font-medium px-3 py-2 rounded-default transition-all w-full hover:bg-base-50 lg:block lg:w-auto dark:border-base-700 dark:hover:text-base-200 dark:hover:bg-base-900; +} + +/* Confirmation pages (revert/archive/discard/unpublish): opts.app_label is empty + on these views, so we scope via `:has(form .confirm-link)` instead of `app-...`. */ + +body.delete-confirmation:has(form .confirm-link) #content > h1 { + @apply !font-semibold !mb-4 !mx-0 !text-font-important-light !text-xl dark:!text-font-important-dark; +} + +body.delete-confirmation:has(form .confirm-link) #content > p { + @apply leading-relaxed mb-6 text-sm; +} + +body.delete-confirmation:has(form .confirm-link) #content > h3 { + @apply bg-base-50 border border-base-200 !font-semibold !mb-0 !mt-0 !mx-0 px-4 pt-4 pb-2 rounded-t-default !text-base !text-font-important-light dark:bg-white/[.02] dark:border-base-700 dark:!text-font-important-dark; +} + +body.delete-confirmation:has(form .confirm-link) #content > h4 { + @apply bg-base-50 border border-base-200 border-t-0 !font-normal !mb-6 !mt-0 !mx-0 pt-2 pb-4 px-4 rounded-b-default !text-base-500 !text-sm dark:bg-white/[.02] dark:border-base-700 dark:!text-base-400; +} + +body.delete-confirmation:has(form .confirm-link) #content form > div { + @apply !flex flex-col gap-3 items-stretch mt-6 lg:flex-row lg:items-center !text-left; +} + +/* Archive/Unpublish wrap cancel in — collapse the anchor + so the inner participates directly in the flex row. */ +body.delete-confirmation:has(form .confirm-link) #content form > div > a:has(> input[type="button"]) { + @apply contents; +} + +body.delete-confirmation:has(form .confirm-link) #content form a.cancel-link, +body.delete-confirmation:has(form .confirm-link) #content form input[type="button"] { + @apply !bg-white !border !border-base-200 cursor-pointer flex font-medium h-[38px] items-center justify-center px-4 !rounded-default !text-font-default-light no-underline hover:!bg-base-50 lg:mr-auto lg:w-auto dark:!bg-base-900 dark:!border-base-700 dark:!text-font-default-dark dark:hover:!bg-base-800; +} + +body.delete-confirmation:has(form .confirm-link) #content form input[type="submit"].confirm-link { + @apply !bg-primary-600 !border-0 cursor-pointer font-medium h-[38px] px-4 !rounded-default !text-white hover:!bg-primary-700 lg:w-auto; +} + +body.delete-confirmation:has(form .confirm-link) #content form input[type="submit"][name="discard"] { + @apply !bg-red-600 hover:!bg-red-700 dark:!bg-red-500/20 dark:!text-red-500; +} + /******************************************************* django-cms - theme *******************************************************/ @@ -198,7 +399,7 @@ html:root { --dca-white: theme('colors.white'); --dca-black: theme('colors.black'); --dca-shadow: theme('colors.base.950'); - --dca-primary: theme('colors.primary.600'); + --dca-primary: theme('colors.primary.600') !important; --dca-gray: theme('colors.base.500'); --dca-gray-lightest: theme('colors.base.100'); --dca-gray-lighter: theme('colors.base.200'); @@ -206,18 +407,18 @@ html:root { --dca-gray-darker: theme('colors.base.700'); --dca-gray-darkest: theme('colors.base.800'); --dca-gray-super-lightest: theme('colors.base.50'); - --active-brightness: 0.9; --focus-brightness: 0.95; } - -html.dark { +html.dark, +html:root[data-theme=dark] { --dca-light-mode: 0; --dca-dark-mode: 1; --dca-white: theme('colors.base.900'); --dca-black: theme('colors.white'); - --dca-primary: theme('colors.primary.500'); + --dca-shadow: theme('colors.white'); + --dca-primary: theme('colors.primary.600'); --dca-gray: theme('colors.base.300') !important; --dca-gray-lightest: theme('colors.base.700'); --dca-gray-lighter: theme('colors.base.600'); @@ -225,42 +426,19 @@ html.dark { --dca-gray-darker: theme('colors.base.200'); --dca-gray-darkest: theme('colors.base.100'); --dca-gray-super-lightest: theme('colors.base.800'); - --active-brightness: 2; --focus-brightness: 1.5; } -/*User prefers dark mode but has set django admin to light mode*/ @media (prefers-color-scheme: dark) { - html:root:not([data-theme]):not(.dark), - html:root[data-theme="auto"]:not(.dark) { - --dca-light-mode: 1; - --dca-dark-mode: 0; - --dca-white: theme('colors.white'); - --dca-black: theme('colors.black'); - --dca-shadow: theme('colors.base.950'); - --dca-primary: theme('colors.primary.600'); - --dca-gray: theme('colors.base.500'); - --dca-gray-lightest: theme('colors.base.100'); - --dca-gray-lighter: theme('colors.base.200'); - --dca-gray-light: theme('colors.base.400'); - --dca-gray-darker: theme('colors.base.700'); - --dca-gray-darkest: theme('colors.base.800'); - --dca-gray-super-lightest: theme('colors.base.50'); - - --active-brightness: 0.9; - --focus-brightness: 0.95; - } -} - -@media (prefers-color-scheme: dark) { - html.dark:root:not([data-theme]), - html.dark:root[data-theme="auto"] { + html:root:not([data-theme]), + html:root[data-theme=auto] { --dca-light-mode: 0; --dca-dark-mode: 1; --dca-white: theme('colors.base.900'); --dca-black: theme('colors.white'); - --dca-primary: theme('colors.primary.500') !important; + --dca-shadow: theme('colors.white'); + --dca-primary: theme('colors.primary.600') !important; --dca-gray: theme('colors.base.300'); --dca-gray-lightest: theme('colors.base.700'); --dca-gray-lighter: theme('colors.base.600'); @@ -268,8 +446,7 @@ html.dark { --dca-gray-darker: theme('colors.base.200'); --dca-gray-darkest: theme('colors.base.100'); --dca-gray-super-lightest: theme('colors.base.800'); - --active-brightness: 2; --focus-brightness: 1.5; } -} \ No newline at end of file +} diff --git a/unfold_extra/src/js/theme-sync.js b/unfold_extra/src/js/theme-sync.js new file mode 100644 index 0000000..b51e602 --- /dev/null +++ b/unfold_extra/src/js/theme-sync.js @@ -0,0 +1,46 @@ +import {KEY_CMS, KEY_UNFOLD, applyTheme} from './utils/theme-utils.js'; + +let mirroring = false; + +applyTheme(localStorage.getItem(KEY_UNFOLD)) + +const normalize = (val) => { + if (val == null) return null; + if (val[0] === '"') { + try { + return JSON.parse(val); + } catch { + } + } + return val; +}; + +const valid = (t) => t === 'light' || t === 'dark' || t === 'auto'; + +window.addEventListener('storage', (e) => { + if (e.key !== KEY_UNFOLD && e.key !== KEY_CMS) return; + if (mirroring) return; // ignore events caused by our own mirror write + + const raw = typeof e.newValue === 'string' ? e.newValue : null; + const theme = normalize(raw); + if (!valid(theme)) return; + + try { + mirroring = true; + + if (e.key === KEY_UNFOLD) { + // mirror to CMS as plain string if different + const current = localStorage.getItem(KEY_CMS); + if (current !== theme) localStorage.setItem(KEY_CMS, theme); + } else { + // mirror to UNFOLD as JSON string if different + const current = localStorage.getItem(KEY_UNFOLD); + const target = JSON.stringify(theme); + if (current !== target) localStorage.setItem(KEY_UNFOLD, target); + } + } finally { + mirroring = false; + } + + applyTheme(theme); +}); \ No newline at end of file diff --git a/unfold_extra/src/js/utils/theme-utils.js b/unfold_extra/src/js/utils/theme-utils.js new file mode 100644 index 0000000..ba787d3 --- /dev/null +++ b/unfold_extra/src/js/utils/theme-utils.js @@ -0,0 +1,38 @@ +export const KEY_UNFOLD = 'adminTheme'; // JSON-encoded: "light" | "dark" | "auto" +export const KEY_CMS = 'theme'; + +export function applyTheme(theme) { + if (!theme) return; + console.log('applyTheme', theme); + let normalizedTheme = theme; + if (normalizedTheme && normalizedTheme[0] === '"') { + try { + normalizedTheme = JSON.parse(normalizedTheme); + } catch { + } + } + if (!['light', 'dark', 'auto'].includes(normalizedTheme)) return; + + const html = document.documentElement; + html.setAttribute('data-theme', normalizedTheme); + html.classList.remove('dark'); + + const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; + const isDark = (normalizedTheme === 'dark') || (normalizedTheme === 'auto' && prefersDark); + if (isDark) html.classList.add('dark'); +} + +export function readTheme() { + try { + const theme_unfold = localStorage.getItem(KEY_UNFOLD); + if (theme_unfold != null) return JSON.parse(theme_unfold); + } catch { + } + // } + // try { + // const theme_cms = localStorage.getItem(KEY_CMS); + // if (theme_cms != null) return JSON.parse(theme_cms); + // } catch { + // } + return null; +} \ No newline at end of file diff --git a/unfold_extra/src/package.json b/unfold_extra/src/package.json index 8c0a046..a59e82b 100644 --- a/unfold_extra/src/package.json +++ b/unfold_extra/src/package.json @@ -1,16 +1,22 @@ { - "name": "django-unfold-extra", - "description": "Enhancing Django Unfold to support additional packages", - "scripts": { - "update:unfold-deps": "curl -s https://raw.githubusercontent.com/unfoldadmin/django-unfold/main/package.json | jq -r '[\"tailwindcss@\" + .dependencies.tailwindcss, \"@tailwindcss/typography@\" + .devDependencies[\"@tailwindcss/typography\"]] | join(\" \")' | xargs npm install --save-dev", - "update:unfold-css": "curl -o css/styles.css https://raw.githubusercontent.com/unfoldadmin/django-unfold/main/src/unfold/styles.css", - "update:unfold": "npm run update:unfold-deps && npm run update:unfold-config", - "tailwind:build": "npx @tailwindcss/cli -i css/unfold_extra.css -o ../static/unfold_extra/css/styles.css --minify", - "tailwind:watch": "npx @tailwindcss/cli -i css/unfold_extra.css -o ../static/unfold_extra/css/styles.css --watch --minify" - }, - "devDependencies": { - "@tailwindcss/cli": "^4.1.7", - "@tailwindcss/typography": "^0.5.16", - "tailwindcss": "^4.1.7" - } + "name": "django-unfold-extra", + "description": "Enhancing Django Unfold to support additional packages", + "scripts": { + "update:unfold-deps": "curl -s https://raw.githubusercontent.com/unfoldadmin/django-unfold/main/package.json | jq -r '[\"tailwindcss@\" + .dependencies.tailwindcss, \"@tailwindcss/typography@\" + .devDependencies[\"@tailwindcss/typography\"]] | join(\" \")' | xargs npm install --save-dev", + "update:unfold-css": "curl -o css/styles.css https://raw.githubusercontent.com/unfoldadmin/django-unfold/main/src/unfold/styles.css", + "update:unfold": "npm run update:unfold-deps && npm run update:unfold-css", + "tailwind:build": "npx @tailwindcss/cli -i css/unfold_extra.css -o ../static/unfold_extra/css/styles.css --minify", + "tailwind:watch": "npx @tailwindcss/cli -i css/unfold_extra.css -o ../static/unfold_extra/css/styles.css --watch --minify", + "dev": "vite", + "build:cms": "vite build", + "build:js": "vite build --config vite.config.ts", + "preview": "vite preview" + }, + "devDependencies": { + "@tailwindcss/cli": "^4.1.7", + "@tailwindcss/typography": "^0.5.19", + "@types/node": "^24.5.2", + "tailwindcss": "^4.1.18", + "vite": "^7.1.7" + } } diff --git a/unfold_extra/src/vite.config.ts b/unfold_extra/src/vite.config.ts new file mode 100644 index 0000000..e5173a5 --- /dev/null +++ b/unfold_extra/src/vite.config.ts @@ -0,0 +1,24 @@ +// TypeScript +import {defineConfig} from 'vite'; +import {resolve} from 'node:path'; + +export default defineConfig({ + root: resolve(__dirname, 'src'), + build: { + outDir: resolve(__dirname, '../static/unfold_extra/js'), + emptyOutDir: false, + sourcemap: false, + target: 'es2018', + rollupOptions: { + input: resolve(__dirname, 'js/theme-sync.js'), + output: { + entryFileNames: 'theme-sync.js', + inlineDynamicImports: false, + format: 'iife', + manualChunks: () => { + return 'Any string' + }, + } + } + } +}); \ No newline at end of file diff --git a/unfold_extra/static/unfold_extra/cms/css/cms.pagetree.css b/unfold_extra/static/unfold_extra/cms/css/cms.pagetree.css index 2f14de8..3715344 100644 --- a/unfold_extra/static/unfold_extra/cms/css/cms.pagetree.css +++ b/unfold_extra/static/unfold_extra/cms/css/cms.pagetree.css @@ -1,1803 +1,3 @@ /*! * @copyright: https://github.com/divio/django-cms - */ -:root, :root[data-theme=light], :root[data-theme=auto] { - --dca-light-mode: 1; - --dca-dark-mode: 0; - --dca-white: #FFFFFF; - --dca-black: #000000; - --dca-shadow: 0, 0, 0; - --dca-primary: #00bbff; - --dca-gray: #666; - --dca-gray-lightest: #f2f2f2; - --dca-gray-lighter: #ddd; - --dca-gray-light: #999; - --dca-gray-darker: #454545; - --dca-gray-darkest: #333; - --dca-gray-super-lightest: #f7f7f7; - --active-brightness: 0.9; - --focus-brightness: 0.95; -} - -:root, :root[data-theme=auto] { - color-scheme: dark light; -} - -:root[data-theme=light] { - color-scheme: light; -} - -:root[data-theme=dark] { - color-scheme: dark; - --dca-light-mode: 0; - --dca-dark-mode: 1; - --dca-white: #2A2C2E; - --dca-black: #FFF; - --dca-primary: #58D1FC; - --dca-gray: #ccc; - --dca-gray-lightest: #444; - --dca-gray-lighter: #666; - --dca-gray-light: #888; - --dca-gray-darker: #ddd; - --dca-gray-darkest: #eee; - --dca-gray-super-lightest: #333; - --active-brightness: 2; - --focus-brightness: 1.5; -} - -@media (prefers-color-scheme: dark) { - :root:not([data-theme]), :root[data-theme=auto] { - --dca-light-mode: 0; - --dca-dark-mode: 1; - --dca-white: #2A2C2E; - --dca-black: #FFF; - --dca-primary: #58D1FC; - --dca-gray: #ccc; - --dca-gray-lightest: #444; - --dca-gray-lighter: #666; - --dca-gray-light: #888; - --dca-gray-darker: #ddd; - --dca-gray-darkest: #eee; - --dca-gray-super-lightest: #333; - --active-brightness: 2; - --focus-brightness: 1.5; - } -} -@font-face { - font-family: "django-cms-iconfont"; - src: url("../fonts/django-cms-iconfont.eot"); - src: url("../fonts/django-cms-iconfont.eot#iefix") format("eot"), url("../fonts/django-cms-iconfont.woff2") format("woff2"), url("../fonts/django-cms-iconfont.woff") format("woff"), url("../fonts/django-cms-iconfont.ttf") format("truetype"), url("../fonts/django-cms-iconfont.svg#django-cms-iconfont") format("svg"); - font-weight: normal; - font-style: normal; -} -.cms-tooltip:before, -.cms-tooltip-touch:before, .cms-tree-node-success > .jstree-anchor .jstree-themeicon, .jstree-django-cms .jstree-ocl, .cms-tree-item-preview a, .cms-icon { - display: inline-block; - font: normal normal normal 16px/1 django-cms-iconfont; - text-rendering: auto; - transform: translate(0, 0); - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -.cms-icon-none { - width: 1em; -} - -.cms-icon-advanced-settings:before { - content: "\E001"; -} - -.cms-icon-alias:before { - content: "\E002"; -} - -.cms-icon-apphook:before { - content: "\E003"; -} - -.cms-icon-archive:before { - content: "\E004"; -} - -.cms-icon-arrow-right:before { - content: "\E005"; -} - -.cms-icon-arrow-wide:before { - content: "\E006"; -} - -.cms-icon-arrow:before { - content: "\E007"; -} - -.cms-icon-bin:before { - content: "\E008"; -} - -.cms-icon-broadcast-off:before { - content: "\E009"; -} - -.cms-icon-broadcast-on:before { - content: "\E00A"; -} - -.cms-icon-check-circle:before { - content: "\E00B"; -} - -.cms-icon-check-o:before { - content: "\E00C"; -} - -.cms-icon-check-square:before { - content: "\E00D"; -} - -.cms-icon-check:before { - content: "\E00E"; -} - -.cms-icon-close:before { - content: "\E00F"; -} - -.cms-icon-cogs:before { - content: "\E010"; -} - -.cms-icon-comment:before { - content: "\E011"; -} - -.cms-icon-compare:before { - content: "\E012"; -} - -.cms-icon-copy:before { - content: "\E013"; -} - -.cms-icon-cut:before { - content: "\E014"; -} - -.cms-icon-edit-new:before { - content: "\E015"; -} - -.cms-icon-edit:before { - content: "\E016"; -} - -.cms-icon-eye:before { - content: "\E017"; -} - -.cms-icon-forbidden:before { - content: "\E018"; -} - -.cms-icon-handler:before { - content: "\E019"; -} - -.cms-icon-highlight:before { - content: "\E01A"; -} - -.cms-icon-home:before { - content: "\E01B"; -} - -.cms-icon-info:before { - content: "\E01C"; -} - -.cms-icon-layers:before { - content: "\E01D"; -} - -.cms-icon-list-ol:before { - content: "\E01E"; -} - -.cms-icon-loader:before { - content: "\E01F"; -} - -.cms-icon-lock:before { - content: "\E020"; -} - -.cms-icon-logo:before { - content: "\E021"; -} - -.cms-icon-manage-versions:before { - content: "\E022"; -} - -.cms-icon-menu:before { - content: "\E023"; -} - -.cms-icon-minimize:before { - content: "\E024"; -} - -.cms-icon-minus-circle:before { - content: "\E025"; -} - -.cms-icon-minus-square-o:before { - content: "\E026"; -} - -.cms-icon-minus-square:before { - content: "\E027"; -} - -.cms-icon-minus:before { - content: "\E028"; -} - -.cms-icon-moderate:before { - content: "\E029"; -} - -.cms-icon-paste:before { - content: "\E02A"; -} - -.cms-icon-pencil:before { - content: "\E02B"; -} - -.cms-icon-pin:before { - content: "\E02C"; -} - -.cms-icon-plugins:before { - content: "\E02D"; -} - -.cms-icon-plus-circle:before { - content: "\E02E"; -} - -.cms-icon-plus-square-o:before { - content: "\E02F"; -} - -.cms-icon-plus:before { - content: "\E030"; -} - -.cms-icon-publish:before { - content: "\E031"; -} - -.cms-icon-puzzle:before { - content: "\E032"; -} - -.cms-icon-redo:before { - content: "\E033"; -} - -.cms-icon-rename:before { - content: "\E034"; -} - -.cms-icon-scissors:before { - content: "\E035"; -} - -.cms-icon-search:before { - content: "\E036"; -} - -.cms-icon-settings:before { - content: "\E037"; -} - -.cms-icon-sitemap:before { - content: "\E038"; -} - -.cms-icon-squares:before { - content: "\E039"; -} - -.cms-icon-theme-auto:before { - content: "\E03A"; -} - -.cms-icon-theme-dark:before { - content: "\E03B"; -} - -.cms-icon-theme-light:before { - content: "\E03C"; -} - -.cms-icon-undo:before { - content: "\E03D"; -} - -.cms-icon-unlock:before { - content: "\E03E"; -} - -.cms-icon-unpublish:before { - content: "\E03F"; -} - -.cms-icon-view:before { - content: "\E040"; -} - -.cms-icon-window:before { - content: "\E041"; -} - -.cms-pagetree-wrapper .cms-btn { - background-image: none !important; - margin-bottom: 0; - border-radius: 3px !important; - color: var(--dca-gray) !important; - background-color: var(--dca-white) !important; - border: 1px solid var(--dca-gray-lighter) !important; - background-clip: padding-box; - -webkit-appearance: none; -} -.cms-pagetree-wrapper .cms-btn:focus, .cms-pagetree-wrapper .cms-btn.focus, .cms-pagetree-wrapper .cms-btn:hover { - color: var(--dca-gray) !important; - background-color: var(--dca-gray-lightest) !important; - border-color: var(--dca-gray-lighter) !important; -} -.cms-pagetree-wrapper .cms-btn:active, .cms-pagetree-wrapper .cms-btn.cms-btn-active, .cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn, .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn { - color: var(--dca-gray) !important; - background-color: var(--dca-white) !important; - border-color: var(--dca-gray-lighter) !important; - filter: brightness(var(--active-brightness)) opacity(1) !important; - box-shadow: inset 0 3px 5px rgba(var(--dca-shadow), 0.125) !important; -} -.cms-pagetree-wrapper .cms-btn:active:hover, .cms-pagetree-wrapper .cms-btn:active:focus, .cms-pagetree-wrapper .cms-btn:active.focus, .cms-pagetree-wrapper .cms-btn.cms-btn-active:hover, .cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn:hover, .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn:hover, .cms-pagetree-wrapper .cms-btn.cms-btn-active:focus, .cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn:focus, .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn:focus, .cms-pagetree-wrapper .cms-btn.cms-btn-active.focus, .cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn.focus, .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn.focus { - color: var(--dca-gray) !important; - background-color: var(--dca-white) !important; - border-color: var(--dca-gray-lighter) !important; - filter: brightness(calc(var(--focus-brightness) * var(--active-brightness))) opacity(1) !important; -} -.cms-pagetree-wrapper .cms-btn:active, .cms-pagetree-wrapper .cms-btn.cms-btn-active, .cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn, .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn { - background-image: none !important; -} -.cms-pagetree-wrapper .cms-btn.cms-btn-disabled, .cms-pagetree-wrapper .cms-btn.cms-btn-disabled:hover, .cms-pagetree-wrapper .cms-btn.cms-btn-disabled:focus, .cms-pagetree-wrapper .cms-btn.cms-btn-disabled.focus, .cms-pagetree-wrapper .cms-btn.cms-btn-disabled:active, .cms-pagetree-wrapper .cms-btn.cms-btn-disabled.cms-btn-active, .cms-pagetree-wrapper .cms-btn[disabled], .cms-pagetree-wrapper .cms-btn[disabled]:hover, .cms-pagetree-wrapper .cms-btn[disabled]:focus, .cms-pagetree-wrapper .cms-btn[disabled].focus, .cms-pagetree-wrapper .cms-btn[disabled]:active, .cms-pagetree-wrapper .cms-btn[disabled].cms-btn-active { - background-color: var(--dca-white) !important; - border-color: var(--dca-gray-lighter) !important; - color: var(--dca-gray-lighter); - cursor: not-allowed; - box-shadow: none !important; -} -.cms-pagetree-wrapper .cms-btn.cms-btn-disabled:before, .cms-pagetree-wrapper .cms-btn.cms-btn-disabled:hover:before, .cms-pagetree-wrapper .cms-btn.cms-btn-disabled:focus:before, .cms-pagetree-wrapper .cms-btn.cms-btn-disabled.focus:before, .cms-pagetree-wrapper .cms-btn.cms-btn-disabled:active:before, .cms-pagetree-wrapper .cms-btn.cms-btn-disabled.cms-btn-active:before, .cms-pagetree-wrapper .cms-btn[disabled]:before, .cms-pagetree-wrapper .cms-btn[disabled]:hover:before, .cms-pagetree-wrapper .cms-btn[disabled]:focus:before, .cms-pagetree-wrapper .cms-btn[disabled].focus:before, .cms-pagetree-wrapper .cms-btn[disabled]:active:before, .cms-pagetree-wrapper .cms-btn[disabled].cms-btn-active:before { - color: var(--dca-gray-lighter); -} -.cms-pagetree-wrapper .cms-btn-action { - background-image: none !important; - margin-bottom: 0; - border-radius: 3px !important; - color: var(--dca-white) !important; - background-color: var(--dca-primary) !important; - border: 1px solid var(--dca-primary) !important; - background-clip: padding-box; - -webkit-appearance: none; -} -.cms-pagetree-wrapper .cms-btn-action:focus, .cms-pagetree-wrapper .cms-btn-action.focus, .cms-pagetree-wrapper .cms-btn-action:hover { - color: var(--dca-white) !important; - background-color: var(--dca-primary) !important; - border-color: var(--dca-primary) !important; - filter: brightness(var(--focus-brightness)) opacity(1) !important; -} -.cms-pagetree-wrapper .cms-btn-action:active, .cms-pagetree-wrapper .cms-btn-action.cms-btn-active, .cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn-action.cms-btn, .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn-action.cms-btn { - color: var(--dca-white) !important; - background-color: var(--dca-primary) !important; - border-color: var(--dca-primary) !important; - filter: brightness(var(--active-brightness)) opacity(1) !important; - box-shadow: inset 0 3px 5px rgba(var(--dca-shadow), 0.125) !important; -} -.cms-pagetree-wrapper .cms-btn-action:active:hover, .cms-pagetree-wrapper .cms-btn-action:active:focus, .cms-pagetree-wrapper .cms-btn-action:active.focus, .cms-pagetree-wrapper .cms-btn-action.cms-btn-active:hover, .cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn-action.cms-btn:hover, .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn-action.cms-btn:hover, .cms-pagetree-wrapper .cms-btn-action.cms-btn-active:focus, .cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn-action.cms-btn:focus, .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn-action.cms-btn:focus, .cms-pagetree-wrapper .cms-btn-action.cms-btn-active.focus, .cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn-action.focus.cms-btn, .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn-action.focus.cms-btn { - color: var(--dca-white) !important; - background-color: var(--dca-primary) !important; - border-color: var(--dca-primary) !important; - filter: brightness(calc(var(--focus-brightness) * var(--active-brightness))) opacity(1) !important; -} -.cms-pagetree-wrapper .cms-btn-action:active, .cms-pagetree-wrapper .cms-btn-action.cms-btn-active, .cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn-action.cms-btn, .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn-action.cms-btn { - background-image: none !important; -} -.cms-pagetree-wrapper .cms-btn-action.cms-btn-disabled, .cms-pagetree-wrapper .cms-btn-action.cms-btn-disabled:hover, .cms-pagetree-wrapper .cms-btn-action.cms-btn-disabled:focus, .cms-pagetree-wrapper .cms-btn-action.cms-btn-disabled.focus, .cms-pagetree-wrapper .cms-btn-action.cms-btn-disabled:active, .cms-pagetree-wrapper .cms-btn-action.cms-btn-disabled.cms-btn-active, .cms-pagetree-wrapper .cms-btn-action[disabled], .cms-pagetree-wrapper .cms-btn-action[disabled]:hover, .cms-pagetree-wrapper .cms-btn-action[disabled]:focus, .cms-pagetree-wrapper .cms-btn-action[disabled].focus, .cms-pagetree-wrapper .cms-btn-action[disabled]:active, .cms-pagetree-wrapper .cms-btn-action[disabled].cms-btn-active { - background-color: var(--dca-primary) !important; - border-color: var(--dca-primary) !important; - color: var(--dca-white); - filter: brightness(0.6) opacity(1); - cursor: not-allowed; - box-shadow: none !important; -} -.cms-pagetree-wrapper .cms-btn-action.cms-btn-disabled:before, .cms-pagetree-wrapper .cms-btn-action.cms-btn-disabled:hover:before, .cms-pagetree-wrapper .cms-btn-action.cms-btn-disabled:focus:before, .cms-pagetree-wrapper .cms-btn-action.cms-btn-disabled.focus:before, .cms-pagetree-wrapper .cms-btn-action.cms-btn-disabled:active:before, .cms-pagetree-wrapper .cms-btn-action.cms-btn-disabled.cms-btn-active:before, .cms-pagetree-wrapper .cms-btn-action[disabled]:before, .cms-pagetree-wrapper .cms-btn-action[disabled]:hover:before, .cms-pagetree-wrapper .cms-btn-action[disabled]:focus:before, .cms-pagetree-wrapper .cms-btn-action[disabled].focus:before, .cms-pagetree-wrapper .cms-btn-action[disabled]:active:before, .cms-pagetree-wrapper .cms-btn-action[disabled].cms-btn-active:before { - color: var(--dca-white); - filter: brightness(0.6) opacity(1); -} -.cms-pagetree-wrapper .cms-btn-caution { - background-image: none !important; - margin-bottom: 0; - border-radius: 3px !important; - color: var(--dca-white) !important; - background-color: rgb(255, 63.75, 0) !important; - border: 1px solid rgb(255, 63.75, 0) !important; - background-clip: padding-box; - -webkit-appearance: none; -} -.cms-pagetree-wrapper .cms-btn-caution:focus, .cms-pagetree-wrapper .cms-btn-caution.focus, .cms-pagetree-wrapper .cms-btn-caution:hover { - color: var(--dca-white) !important; - background-color: rgb(255, 63.75, 0) !important; - border-color: rgb(255, 63.75, 0) !important; - filter: brightness(var(--focus-brightness)) opacity(1) !important; -} -.cms-pagetree-wrapper .cms-btn-caution:active, .cms-pagetree-wrapper .cms-btn-caution.cms-btn-active, .cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn-caution.cms-btn, .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn-caution.cms-btn { - color: var(--dca-white) !important; - background-color: rgb(255, 63.75, 0) !important; - border-color: rgb(255, 63.75, 0) !important; - filter: brightness(var(--active-brightness)) opacity(1) !important; - box-shadow: inset 0 3px 5px rgba(var(--dca-shadow), 0.125) !important; -} -.cms-pagetree-wrapper .cms-btn-caution:active:hover, .cms-pagetree-wrapper .cms-btn-caution:active:focus, .cms-pagetree-wrapper .cms-btn-caution:active.focus, .cms-pagetree-wrapper .cms-btn-caution.cms-btn-active:hover, .cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn-caution.cms-btn:hover, .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn-caution.cms-btn:hover, .cms-pagetree-wrapper .cms-btn-caution.cms-btn-active:focus, .cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn-caution.cms-btn:focus, .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn-caution.cms-btn:focus, .cms-pagetree-wrapper .cms-btn-caution.cms-btn-active.focus, .cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn-caution.focus.cms-btn, .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn-caution.focus.cms-btn { - color: var(--dca-white) !important; - background-color: rgb(255, 63.75, 0) !important; - border-color: rgb(255, 63.75, 0) !important; - filter: brightness(calc(var(--focus-brightness) * var(--active-brightness))) opacity(1) !important; -} -.cms-pagetree-wrapper .cms-btn-caution:active, .cms-pagetree-wrapper .cms-btn-caution.cms-btn-active, .cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn-caution.cms-btn, .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn-caution.cms-btn { - background-image: none !important; -} -.cms-pagetree-wrapper .cms-btn-caution.cms-btn-disabled, .cms-pagetree-wrapper .cms-btn-caution.cms-btn-disabled:hover, .cms-pagetree-wrapper .cms-btn-caution.cms-btn-disabled:focus, .cms-pagetree-wrapper .cms-btn-caution.cms-btn-disabled.focus, .cms-pagetree-wrapper .cms-btn-caution.cms-btn-disabled:active, .cms-pagetree-wrapper .cms-btn-caution.cms-btn-disabled.cms-btn-active, .cms-pagetree-wrapper .cms-btn-caution[disabled], .cms-pagetree-wrapper .cms-btn-caution[disabled]:hover, .cms-pagetree-wrapper .cms-btn-caution[disabled]:focus, .cms-pagetree-wrapper .cms-btn-caution[disabled].focus, .cms-pagetree-wrapper .cms-btn-caution[disabled]:active, .cms-pagetree-wrapper .cms-btn-caution[disabled].cms-btn-active { - background-color: rgb(255, 63.75, 0) !important; - border-color: rgb(255, 63.75, 0) !important; - color: var(--dca-white); - filter: brightness(0.6) opacity(1); - cursor: not-allowed; - box-shadow: none !important; -} -.cms-pagetree-wrapper .cms-btn-caution.cms-btn-disabled:before, .cms-pagetree-wrapper .cms-btn-caution.cms-btn-disabled:hover:before, .cms-pagetree-wrapper .cms-btn-caution.cms-btn-disabled:focus:before, .cms-pagetree-wrapper .cms-btn-caution.cms-btn-disabled.focus:before, .cms-pagetree-wrapper .cms-btn-caution.cms-btn-disabled:active:before, .cms-pagetree-wrapper .cms-btn-caution.cms-btn-disabled.cms-btn-active:before, .cms-pagetree-wrapper .cms-btn-caution[disabled]:before, .cms-pagetree-wrapper .cms-btn-caution[disabled]:hover:before, .cms-pagetree-wrapper .cms-btn-caution[disabled]:focus:before, .cms-pagetree-wrapper .cms-btn-caution[disabled].focus:before, .cms-pagetree-wrapper .cms-btn-caution[disabled]:active:before, .cms-pagetree-wrapper .cms-btn-caution[disabled].cms-btn-active:before { - color: var(--dca-white); - filter: brightness(0.6) opacity(1); -} -.cms-pagetree-wrapper .cms-btn-disabled img { - opacity: 0.2 !important; -} - -.cms-admin-sideframe:not(.djangocms-admin-style) { - margin-block-start: 46px; -} - -.cms-btn-toolbar { - display: inline-block; - line-height: 32px; - height: 32px; - font-size: 12px; - padding-block: 0; - padding-inline: 22px; -} - -.cms-hidden { - display: none !important; -} - -.cms-clear { - clear: both; - overflow: hidden; -} - -.cms-clear-mobile { - clear: both; - overflow: hidden; -} -@media (min-width: 520px) { - .cms-clear-mobile { - display: none; - } -} - -.jstree li { - -ms-touch-action: none; - touch-action: none; -} - -.cms-pagetree-root#changelist { - display: block; - align-items: flex-start; - justify-content: space-between; -} - -.cms-pagetree-root, -.cms-pagetree-root.filtered { - min-height: 0; - padding: 0 !important; - box-shadow: 0 0 5px 0 rgba(var(--dca-shadow), 0.2) !important; -} - -.cms-pagetree { - border: 1px solid var(--dca-gray-lighter); -} -.cms-pagetree .cms-hover-tooltip { - z-index: auto; -} -.cms-pagetree .sr-only { - position: absolute; - width: 1px; - height: 1px; - margin: -1px; - padding: 0; - overflow: hidden; - clip: rect(0, 0, 0, 0); - border: 0; -} -.cms-pagetree .cms-btn.cms-icon { - display: inline-block; - box-sizing: border-box; - width: 39px; - height: 37px; - position: relative; -} -.cms-pagetree .cms-btn.cms-icon:before { - position: absolute; - inset-inline-start: 50%; - inset-block-start: 50%; - margin-block-start: -8px; - margin-inline-start: -8px; -} -.cms-pagetree .cms-btn.cms-btn-no-border { - border: none !important; -} -.cms-pagetree-header { - position: relative; - display: flex; - width: 100%; - box-sizing: border-box; - z-index: auto; - min-height: 36px; - padding-block: 15px; - padding-inline: 20px; - box-shadow: none; - border-block-end: none; - background: var(--dca-gray-super-lightest); -} -.cms-pagetree-header:before { - content: ""; - position: absolute; - inset-block-end: -6px; - inset-inline-start: 0; - inset-inline-end: 0; - height: 5px; - background-color: var(--dca-white); -} -.cms-pagetree-header + .module { - padding-block-start: 0 !important; -} -.djangocms-admin-style .cms-pagetree-header { - margin-block: -40px 0; - margin-inline: 0; -} - -.djangocms-admin-style.cms-admin-sideframe .cms-pagetree-header { - margin-block-start: -25px; -} - -.cms-pagetree-header-title { - display: table-cell; - width: 100%; - line-height: 36px; - height: 36px; -} -.cms-pagetree-header-title h1 { - line-height: 36px; - margin: 0 !important; -} -.cms-pagetree-header-title h1 span { - font-weight: normal; -} - -.cms-pagetree-header-row { - display: table; - width: 100%; - table-layout: fixed; -} -@media (max-width: 520px) { - .cms-pagetree-header-row { - display: block; - } -} - -.cms-pagetree-header-cell { - display: table-cell; - white-space: nowrap; - vertical-align: middle; -} -@media (max-width: 520px) { - .cms-pagetree-header-cell { - display: block; - } -} - -.cms-pagetree-header-cell-fluid { - width: 100%; -} - -.cms-pagetree-header-cell-search { - width: 220px !important; - position: relative; -} -.cms-pagetree-header-filter-active .cms-pagetree-header-cell-search { - position: static; -} -@media (max-width: 520px) { - .cms-pagetree-header-cell-search { - width: 100% !important; - } -} - -@media (max-width: 520px) { - .cms-pagetree-header-body { - text-align: end; - padding-block-start: 10px; - } -} - -.cms-pagetree-header-filter-active .cms-pagetree-search-container { - position: relative; -} - -.cms-pagetree-header-search { - position: absolute; - inset-block-start: 0; - display: table; - width: 100%; - inset-inline-start: 0; - inset-inline-end: 0; - padding: 0; - box-shadow: none; - background: none; -} -@media (max-width: 520px) { - .cms-pagetree-header-search { - position: relative; - } -} -.cms-pagetree-header-search .cms-pagetree-header-search-btn { - display: table-cell; - vertical-align: middle; - position: relative; - outline: none; - color: var(--dca-white) !important; - padding-block: 8px !important; - padding-inline: 10px !important; - border: none !important; - background: var(--dca-primary) !important; - height: 36px; - border-radius: 3px; -} -.cms-pagetree-header-search .cms-pagetree-header-search-btn:hover { - background: rgb(0, 168.3, 229.5) !important; -} -.cms-pagetree-header-search .cms-pagetree-header-search-btn:active, .cms-pagetree-header-search .cms-pagetree-header-search-btn:focus { - color: var(--dca-white) !important; - background: rgb(0, 130.9, 178.5) !important; -} -.cms-pagetree-header-search .cms-pagetree-header-filter { - position: relative; - display: table-cell; - width: 100%; - padding-inline-end: 5px; - vertical-align: middle; -} -.cms-pagetree-header-search .cms-pagetree-header-filter input { - width: 100%; - box-sizing: border-box; - width: 100%; - display: inline-block; - height: 36px; - padding: 6px; - line-height: 24px; - padding-inline-end: 35px !important; - transition: width 200ms; -} -.cms-pagetree-header-filter-active .cms-pagetree-header-search { - inset-inline-start: 0; - inset-inline-end: 0; - inset-block-start: 0; -} -.djangocms-admin-style .cms-pagetree-header-search { - margin-block: 5px 10px; - margin-inline: 0; -} - -.cms-pagetree-header-filter-trigger { - position: absolute; - inset-inline-end: 5px; - inset-block-start: 0; - cursor: pointer; - padding: 10px; - transform: rotate(270deg); -} -.cms-pagetree-header-filter-trigger:hover .cms-icon, .cms-pagetree-header-filter-trigger:active .cms-icon, .cms-pagetree-header-filter-trigger:focus .cms-icon { - color: var(--dca-primary); -} -.cms-pagetree-header-filter-trigger .cms-icon { - font-size: 11px; - color: var(--dca-gray); -} - -.cms-pagetree-header-filter-container { - display: none; - overflow: auto; - position: absolute; - inset-inline-start: 0; - inset-block-start: 38px; - width: 100%; - padding-block: 5px 15px; - padding-inline: 20px; - max-height: 400px; - box-shadow: 0 0 5px 0 rgba(var(--dca-shadow), 0.2); - box-sizing: border-box; - border-radius: 3px; - background: var(--dca-white); - z-index: 1000; -} -.cms-pagetree-header-filter-container h3 { - color: var(--dca-gray); - font-size: 14px; - font-weight: normal; - text-transform: uppercase; - padding-block: 10px 0; - padding-inline: 0; -} -.cms-pagetree-header-filter-container ul, -.cms-pagetree-header-filter-container li { - list-style-type: none; - padding-block: 2px; - padding-inline: 0; - margin: 0; -} -.cms-pagetree-header-filter-container li a { - color: var(--dca-gray-light); -} -.cms-pagetree-header-filter-container .selected a { - color: var(--dca-primary); - font-weight: bold; -} - -.cms-pagetree-header-search-close { - position: absolute; - inset-inline-end: 0; - inset-block-start: 0; - padding: 10px; -} -.cms-pagetree-header-search-close:hover .cms-icon, .cms-pagetree-header-search-close:active .cms-icon, .cms-pagetree-header-search-close:focus .cms-icon { - color: var(--dca-primary); -} -.cms-pagetree-header-search-close .cms-icon { - font-size: 12px; - color: var(--dca-gray); -} - -.cms-pagetree-header-search-reset { - font-size: 90%; - padding-block: 0; - padding-inline: 10px; -} - -.cms-pagetree-sites-list { - display: inline-block; - vertical-align: top; - padding-inline-start: 10px; - padding-inline-end: 10px; - margin-inline-start: 10px; - margin-inline-end: 10px; -} -.cms-pagetree-sites-list .cms-pagetree-dropdown-trigger { - display: inline-block; - color: var(--dca-gray) !important; - padding: 10px; - height: 16px; -} -.cms-pagetree-sites-list .cms-pagetree-dropdown-menu { - inset-inline-end: 50%; - margin-inline-end: -19px !important; -} - -.cms-pagetree-header-create { - display: inline-block; - vertical-align: top; - height: 36px !important; - box-sizing: border-box; -} - -.djangocms-admin-style .cms-pagetree-breadcrumbs { - margin-block-end: -30px !important; - border: none !important; -} - -.cms-pagetree-section { - position: relative; - z-index: auto; - min-height: 15px; - padding-block: 15px; - padding-inline: 20px; - box-shadow: none; - border-block-end: none; - background: var(--dca-white); -} -.cms-pagetree-section h2, -.cms-pagetree-section p { - padding: 0; - margin: 0; - border: none; -} -.cms-pagetree-section h2 { - float: left; - font-weight: bold; - color: var(--dca-gray-darkest); - background: none; -} -.cms-pagetree-section h2:dir(rtl) { - float: right; -} -.cms-pagetree-container + .cms-pagetree-section { - margin-block-start: -1px; -} - -.cms-pagetree-section-header { - display: flex; - flex-direction: row; - align-items: center; -} -.cms-pagetree-section-header select { - width: 120px !important; - margin-inline-start: 15px; -} - -.cms-pagetree-section-condensed { - padding-block: 10px; - padding-inline: 20px; -} - -.cms-pagetree-section-nav { - position: absolute; - inset-inline-end: 4px; - inset-block-start: 50%; - margin-block-start: -19px; -} -.cms-pagetree-section-nav .cms-pagetree-dropdown-menu { - inset-inline-end: 40px; - inset-block-start: -2px; - width: 205px; -} - -.cms-pagetree-dropdown-separator { - padding: 0; - margin-block: 4px !important; - margin-inline: 1px !important; - line-height: 1; - height: 1px; - background: var(--dca-gray-lighter); -} - -.cms-pagetree-container { - position: relative; - z-index: auto; - border: 1px solid var(--dca-gray-lighter); - border-block-end: none; -} - -.cms-tree-col { - float: right; - min-width: 32px; - height: 32px; -} -.cms-tree-col:dir(rtl) { - float: left; -} - -.cms-tree-col-padding-sm { - margin-block-start: -3px; - margin-inline-start: -4px; - margin-inline-start: -4px; - height: 38px; -} - -.cms-tree-item { - display: table; - width: 100%; - text-align: center; - height: 32px; -} -.cms-tree-item + .cms-tree-item { - padding-inline-start: 4px; -} - -.cms-tree-item-disabled { - opacity: 0.3; - pointer-events: none; -} - -.cms-tree-item-wrapper { - display: table-row; - min-width: 32px; -} -.cms-tree-item-wrapper .cms-tree-item { - display: table-cell; - vertical-align: middle; - text-align: center; - min-width: 32px; -} -.cms-tree-item-wrapper .cms-tree-item-inner { - display: block; -} - -.cms-tree-item-inner { - display: table-cell; - text-align: center; - vertical-align: middle; -} - -.cms-tree-reload { - float: right; -} -.cms-tree-reload:dir(rtl) { - float: left; -} - -.cms-tree-item-lang .cms-pagetree-dropdown-menu { - inset-inline-end: 32px; - inset-block-start: -5px; - min-width: 180px; -} - -.cms-tree-item-button .cms-pagetree-dropdown-menu { - inset-inline-end: 40px; - inset-block-start: -2px; - width: 205px; -} - -.cms-tree-item-button .cms-hover-tooltip:before, .cms-tree-item-button .cms-hover-tooltip:after { - inset-block-end: 23px; -} - -.cms-pagetree-alt-mode .cms-tree-item-button .cms-icon-settings:not(.cms-btn-disabled)[data-url]:before { - content: "\E001"; -} - -.cms-tree-item-icons { - position: relative; -} -.cms-tree-item-icons .cms-icon { - display: block; -} -.cms-tree-item-icons span:before { - color: var(--dca-gray-light); -} -.cms-tree-item-icons-dark span:before { - color: var(--dca-gray-darker); -} - -.cms-tree-item-preview { - position: relative; -} -.cms-tree-item-preview a { - font-size: 18px; - line-height: 18px; - height: 18px; - display: block; -} -.cms-tree-item-preview a:before { - display: block; - color: var(--dca-gray-darker); -} -.cms-tree-item-preview a:hover:before { - color: var(--dca-black); -} -.cms-tree-item-preview-active a:before { - color: var(--dca-black); -} - -.cms-tree-item-lang { - position: relative; -} -.cms-tree-item-lang .cms-tree-item-inner > a, .cms-tree-item-lang .cms-tree-item-inner > .cms-tree-lang-container { - display: block; - height: 16px; -} - -.cms-tree-item-menu { - position: relative; -} -.cms-tree-item-menu .cms-tree-item-inner > a { - display: block; - height: 14px; -} -.cms-tree-item-menu .cms-icon { - display: block; -} -.cms-tree-item-menu .cms-icon:before { - display: block; - font-size: 14px; - color: var(--dca-gray-darker); -} -.cms-tree-item-menu .cms-icon-check-square:before { - color: #693; -} - -.cms-tree-item-info { - position: relative; -} -.cms-tree-item-info .cms-tree-item-inner > a { - display: block; - height: 16px; -} -.cms-tree-item-info .cms-icon { - display: block; -} -.cms-tree-item-info .cms-icon:before { - display: block; - font-size: 16px; -} -.cms-tree-item-info p { - color: var(--dca-gray-darkest); - padding: 0; - margin: 0; -} -.cms-tree-item-info strong { - color: var(--dca-gray-light); - font-weight: normal; -} -.cms-tree-item-info a { - display: inline-block; - line-height: 1; -} - -.cms-tree-search { - float: left; - margin-inline-end: 5px; -} -.cms-tree-search:dir(rtl) { - float: right; -} -.cms-tree-search label { - display: none !important; -} -.cms-tree-search select { - position: relative; - inset-block-start: 2px; - width: 175px !important; -} -.cms-tree-search #toolbar form#changelist-search input[type=submit] { - margin-inline-start: 0 !important; -} -.cms-tree-search #toolbar form#changelist-search #searchbar { - width: 175px !important; -} - -.cms-tree-filters { - float: right; -} -.cms-tree-filters:dir(rtl) { - float: left; -} -.cms-tree-filters .js-cms-tree-filter-trigger { - position: absolute; - inset-block-start: 0; - inset-inline-end: 0; - padding-inline-end: 20px; - line-height: 30px; - width: 175px; - border-block-end: 1px solid var(--dca-gray-lighter); -} -.cms-tree-filters h2 { - display: none; -} - -.cms-pagetree-empty { - padding-block: 15px; - padding-inline: 20px; - border-block-start: none; -} -.cms-pagetree-empty .addlink { - padding-inline-start: 16px; -} - -.jstree-django-cms { - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -} -.jstree-django-cms .jstree-container-ul, -.jstree-django-cms .jstree-children, -.jstree-django-cms .jstree-leaf, -.jstree-django-cms .jstree-node { - list-style-type: none; - padding: 0; - margin: 0; - transform: translateZ(0); -} -.jstree-django-cms .jstree-is-dragging { - opacity: 0.1 !important; -} -.jstree-django-cms .jstree-is-dragging + .jstree-grid-cell { - height: 47px; - border-block-start: 1px solid var(--dca-gray-lighter); - margin-block-start: -1px; -} -.jstree-django-cms .jstree-is-dragging-copy { - opacity: 0.9 !important; -} -.jstree-django-cms .jstree-grid-header, -.jstree-django-cms .jstree-grid-separator { - padding: 0; - margin: 0; - margin-left: 0 !important; - border: none; - background: none; -} -.jstree-django-cms .jstree-grid-header { - z-index: 1; - position: relative; -} -.jstree-django-cms.jstree-grid-cell { - padding: 0; -} -.jstree-django-cms .jstree-grid-column, -.jstree-django-cms .jstree-grid-cell { - box-sizing: border-box; - overflow: visible; -} -.jstree-django-cms .jstree-grid-column { - vertical-align: top; -} - -.jstree-grid-wrapper { - box-sizing: border-box; - overflow: visible !important; - width: 100% !important; -} - -.jstree-grid-wrapper { - background: var(--dca-gray-lightest); -} - -.jstree-django-cms .jstree-grid-header { - font-size: 12px; - color: var(--dca-gray); - text-align: center; - padding-block: 3px; - padding-inline: 6px; - border-block-end: 1px solid var(--dca-gray-lighter); - background: var(--dca-white); -} -.jstree-django-cms .jstree-grid-header-fixed { - position: fixed; - z-index: 2; -} -.jstree-django-cms .jstree-grid-column-0 .jstree-grid-header { - text-align: start !important; -} -.jstree-django-cms .jstree-children { - margin-inline-start: 20px; -} -.jstree-django-cms .jstree-container-ul { - margin-inline-start: -1px; -} -.jstree-django-cms .jstree-ocl { - float: left; - position: relative; - inset-inline-start: 20px; - font-size: 12px; - color: var(--dca-gray-light); - padding-block-start: 18px; - padding-inline-end: 10px; - padding-block-end: 10px; - padding-inline-start: 0px; -} -.jstree-django-cms .jstree-ocl:dir(rtl) { - float: right; -} -.jstree-django-cms .jstree-ocl:before { - display: inline-block; -} -.jstree-django-cms .jstree-open > .jstree-ocl, -.jstree-django-cms .jstree-closed > .jstree-ocl { - cursor: pointer; -} -.jstree-django-cms .jstree-open > .jstree-ocl:before { - content: "\E005"; -} -.jstree-django-cms .jstree-open > .jstree-ocl:before { - transform: rotate(90deg); -} -.jstree-django-cms .jstree-closed > .jstree-ocl:before { - content: "\E005"; -} -.jstree-django-cms .jstree-loading > .jstree-ocl:before { - content: "\E01F"; -} -.jstree-django-cms .jstree-loading > .jstree-ocl:before { - animation: cms-spin 2s infinite linear; -} -.jstree-django-cms .jstree-anchor { - box-sizing: border-box; - display: block; - cursor: move; - outline: none; - color: var(--dca-gray-darkest); - height: 47px; - padding-block: 12px 12px; - padding-inline: 25px 25px; - margin-block-start: -1px; - border-block: 1px solid var(--dca-gray-lighter); - border-inline: 1px solid var(--dca-gray-lighter); - border-inline-end: none; - background-color: var(--dca-white); - background-image: url("../img/pagetree/tree-li-drag.gif"); - background-repeat: no-repeat; - background-position: 2px center; - image-rendering: pixelated; - white-space: normal; -} -.jstree-django-cms .jstree-anchor:hover { - color: var(--dca-gray) !important; -} -.jstree-django-cms .jstree-anchor:dir(rtl) { - background-position: calc(100% - 2px) center; -} -.jstree-django-cms .jstree-anchor em { - color: var(--dca-gray-light); -} -.jstree-django-cms .jstree-node[data-move-permission=false] > .jstree-anchor { - cursor: default; - background-image: none; -} -.jstree-django-cms .jstree-open > .jstree-anchor, -.jstree-django-cms .jstree-closed > .jstree-anchor, -.jstree-django-cms .jstree-loading > .jstree-anchor { - padding-inline: 45px 25px; - padding-block: 12px 12px; -} -.jstree-django-cms .cms-tree-node-filtered .jstree-anchor { - cursor: default; -} -.jstree-django-cms .jstree-grid-cell { - height: 46px; - border-block-end: 1px solid var(--dca-gray-lighter); - background: var(--dca-white); -} -.jstree-django-cms .jstree-grid-cell > span { - display: block; -} -.jstree-django-cms .jstree-grid-cell.jstree-django-cms { - height: auto; - border: none; - background: none; -} -.jstree-django-cms .jstree-grid-cell-regular { - text-align: center; - padding-block: 7px 7px; - padding-inline: 8px 8px; - border-inline-start: 1px solid var(--dca-gray-lighter); -} -.jstree-django-cms .jstree-grid-cell-regular-options { - border-inline-start: none; - text-align: end; -} -.jstree-django-cms .jstree-hovered, -.jstree-django-cms .jstree-clicked { - background-color: var(--dca-gray-lightest) !important; -} -.jstree-django-cms .jstree-grid-column-1 .jstree-grid-header { - text-align: end; -} -.jstree-django-cms .jstree-grid-column-1 .jstree-grid-header a { - font-size: 12px; - inset-block-start: 0; -} -.jstree-django-cms .jstree-grid-column-1 .jstree-grid-header span { - line-height: 1; -} -.jstree-django-cms .jstree-grid-column-1 .jstree-grid-header .cms-icon { - font-size: 12px; - padding-inline-end: 5px; - margin-inline-start: 5px; -} - -.jstree.jstree-loading .jstree-container-ul { - margin: 0; -} -.jstree.jstree-loading .jstree-anchor { - padding-block-start: 13px; - padding-inline-start: 15px; - border: none; - background: none; -} -.jstree.jstree-loading .jstree-loading > .jstree-ocl { - display: none; -} - -.cms-tree-node-success > .jstree-anchor .jstree-themeicon { - content: ""; - position: relative; - inset-block-start: 4px; - color: #693; -} -.cms-tree-node-success > .jstree-anchor .jstree-themeicon:before { - content: "\E00E"; -} - -.jstree-django-cms .cms-tree-node-shared-true > .jstree-anchor { - border-style: dashed; - border-inline-end: none; - color: #999; - background-color: #f2f2f2 !important; -} -.jstree-django-cms .cms-tree-node-shared-true > .jstree-anchor:hover { - color: #999 !important; -} -.jstree-django-cms .cms-tree-node-shared-true > .jstree-anchor.jstree-hovered, .jstree-django-cms .cms-tree-node-shared-true > .jstree-anchor.jstree-clicked { - background-color: #f2f2f2 !important; -} -.jstree-django-cms .jstree-grid-cell.jstree-hovered, -.jstree-django-cms .jstree-grid-cell.jstree-clicked { - background-color: #f2f2f2; -} -.jstree-django-cms .cms-tree-node-shared-false > .jstree-anchor { - border-style: solid; - border-inline-end: none; - background-color: var(--dca-white) !important; - border-block-end: 1px solid var(--dca-gray-lighter); -} -.jstree-django-cms .cms-tree-node-shared-false > .jstree-anchor.jstree-hovered, .jstree-django-cms .cms-tree-node-shared-false > .jstree-anchor.jstree-clicked { - background-color: var(--dca-gray-lightest) !important; -} -.jstree-django-cms .cms-tree-node-shared-false + .cms-tree-node-shared-true > .jstree-anchor { - border-block-start-style: solid; -} -.jstree-django-cms .cms-tree-node-shared-false + .cms-tree-node-shared-true.jstree-hovered, .jstree-django-cms .cms-tree-node-shared-false + .cms-tree-node-shared-true.jstree-clicked { - background-color: #f2f2f2; -} -.jstree-django-cms .cms-page-tree-cell-shared--true { - border-style: dashed; - background-color: #f2f2f2; -} -.jstree-django-cms .cms-page-tree-cell-shared--true.jstree-grid-cell-regular-options { - border-inline-start: none; -} -.jstree-django-cms .cms-page-tree-cell-shared--true .cms-btn-no-border { - background-color: transparent !important; -} -.jstree-django-cms .cms-page-tree-cell-shared--true .cms-btn-no-border:hover { - background-color: #fff !important; - border-block: 1px solid #ddd !important; - border-inline: 1px solid #ddd !important; -} -.jstree-django-cms .cms-page-tree-cell-shared--true.jstree-hovered, .jstree-django-cms .cms-page-tree-cell-shared--true.jstree-clicked { - background-color: #f2f2f2 !important; -} -.jstree-django-cms .cms-page-tree-cell-shared--false { - background-color: var(--dca-white); -} -.jstree-django-cms .cms-page-tree-cell-shared--false.jstree-hovered, .jstree-django-cms .cms-page-tree-cell-shared--false.jstree-clicked { - background-color: var(--dca-gray-lightest) !important; -} -.jstree-django-cms .cms-page-tree-cell-shared--true + .cms-page-tree-cell-shared--false { - border-block-start: 1px solid var(--dca-gray-lighter); - margin-block-start: -1px; - padding-block-start: 8px; - height: 47px; -} - -#jstree-dnd { - box-sizing: border-box; - display: block; - min-width: 300px; - height: 46px; - padding-block: 12px 12px !important; - padding-inline: 25px 25px !important; - margin-inline-start: -5px; - background-color: var(--dca-primary) !important; - color: var(--dca-white); -} -#jstree-dnd .jstree-copy { - color: var(--dca-white); - padding-block: 0px 2px; - padding-inline: 5px; - margin-inline-start: 5px; - border-radius: 3px; - background: var(--dca-gray-light); -} -#jstree-dnd .jstree-ok, -#jstree-dnd .jstree-er { - position: absolute; - inset-block-start: 0; - inset-inline-start: -5px; - inset-block-end: 0; - inset-inline-end: 0; - background-color: rgba(255, 255, 255, 0.6); -} -#jstree-dnd .jstree-ok { - display: none; -} - -.is-stacked #jstree-dnd { - box-shadow: 1px 1px 1px rgba(var(--dca-black), 0.2), 4px 4px 0 0 var(--dca-primary); -} -.is-stacked .jstree-er { - box-shadow: 4px 4px 0 0 rgba(255, 255, 255, 0.6); -} - -#jstree-marker { - position: absolute; - z-index: 102; - margin-inline-start: -6px; - margin-block-start: -6px; - border-inline-end: 0; - border-block-start: 6px solid transparent; - border-block-end: 6px solid transparent; - border-inline-start: 6px solid var(--dca-primary); - height: 0; -} -#jstree-marker:after { - content: ""; - display: block; - position: absolute; - height: 3px; - inset-block-start: -1px; - inset-inline-start: -6px; - inset-inline-end: 0px; - background-color: var(--dca-primary); -} -#jstree-marker.jstree-marker-child { - border: 2px solid var(--dca-primary); - height: 46px; - margin-block: -24px 0px; - margin-inline: 0; - background-color: rgba(var(--dca-primary), 0.1); - box-sizing: border-box; -} -#jstree-marker.jstree-marker-child:after { - display: none; -} - -.djangocms-admin-style.cms-pagetree-wrapper.cms-admin-sideframe .messagelist + #content { - margin-block-start: 117px !important; -} -.djangocms-admin-style.cms-pagetree-wrapper.cms-admin-sideframe #container .breadcrumbs { - inset-inline-start: 20px; -} -.djangocms-admin-style.cms-pagetree-wrapper.cms-admin-sideframe #container .breadcrumbs + .messagelist + #content, -.djangocms-admin-style.cms-pagetree-wrapper.cms-admin-sideframe #container .breadcrumbs + #content { - margin-inline-start: 0 !important; - margin-inline-end: 0 !important; -} -.djangocms-admin-style.cms-pagetree-wrapper.cms-admin-sideframe #jstree-marker { - inset-inline-end: 0; -} - -.cms-pagetree-header-lang, .cms-pagetree-header-lang:link, .cms-pagetree-header-lang:active, .cms-pagetree-header-lang:hover, .cms-pagetree-header-lang:focus { - color: var(--dca-gray-darkest) !important; -} -.cms-pagetree-header-lang.active { - font-weight: bold; -} - -.cms-pagetree-dropdown { - position: relative; -} - -.cms-pagetree-dropdown-menu { - display: none; - position: absolute; - inset-block-start: 30px; - inset-inline-end: -1px; - z-index: 1000; - min-width: 180px; - border-radius: 5px; - background: var(--dca-white); - box-shadow: 0 0 10px rgba(var(--dca-shadow), 0.25); - transform: translateZ(0); -} -.cms-pagetree-dropdown-menu a, .cms-pagetree-dropdown-menu a:visited, .cms-pagetree-dropdown-menu a:link, .cms-pagetree-dropdown-menu a:link:visited, -.cms-pagetree-dropdown-menu .cms-pagetree-dropdown-item { - display: block; - color: var(--dca-gray); - line-height: 1.5; - text-align: start; - padding-block: 10px; - padding-inline: 15px; -} -.cms-pagetree-dropdown-menu a:hover, -.cms-pagetree-dropdown-menu a:active, -.cms-pagetree-dropdown-menu a:focus { - color: var(--dca-white) !important; - background: var(--dca-primary); -} -.cms-pagetree-dropdown-menu li:first-child > a { - border-start-start-radius: 5px; - border-start-end-radius: 5px; -} -.cms-pagetree-dropdown-menu li:last-child > a { - border-end-start-radius: 5px; - border-end-end-radius: 5px; -} -.cms-pagetree-dropdown-menu .cms-pagetree-dropdown-item-disabled { - opacity: 0.2; - color: var(--dca-gray-light); - cursor: default; -} -.cms-pagetree-dropdown-menu .cms-pagetree-dropdown-item-disabled:hover, .cms-pagetree-dropdown-menu .cms-pagetree-dropdown-item-disabled:focus { - background: none !important; - color: var(--dca-gray-darker) !important; -} -.cms-pagetree-dropdown-menu.cms-pagetree-dropdown-menu-condensed a, -.cms-pagetree-dropdown-menu.cms-pagetree-dropdown-menu-condensed .cms-pagetree-dropdown-item { - padding-block: 5px; - padding-inline: 15px; -} -.cms-pagetree-dropdown-menu .active { - font-weight: bold; -} -.cms-pagetree-dropdown-menu .label { - display: block; - color: var(--dca-gray); - font-size: 90%; - font-weight: normal; - line-height: 1.5; - text-align: start; - text-transform: uppercase; - padding-block: 7px 5px; - padding-inline: 15px; - border-block-end: 1px solid var(--dca-gray-lighter); - margin-block-end: 4px; -} -.cms-pagetree-dropdown-menu li { - margin: 0; - padding: 0; - list-style-type: none; -} -.cms-pagetree-dropdown-menu p { - display: block; - overflow: hidden; - font-size: 12px !important; - text-align: start; - text-overflow: ellipsis; - margin: 0; - padding-block: 5px 3px !important; - padding-inline: 15px !important; -} -.cms-pagetree-dropdown-menu p strong { - font-size: 12px !important; -} -.cms-pagetree-dropdown-menu p + p { - margin-block-start: -2px; - padding-block-start: 0 !important; -} -.cms-pagetree-dropdown-menu p:last-child { - padding-block-end: 7px !important; -} -.cms-pagetree-dropdown-menu .cms-icon { - margin-inline-end: 10px; -} -.cms-pagetree-dropdown-menu span { - vertical-align: middle; -} -.cms-pagetree-dropdown-menu:before { - content: ""; - position: absolute; - inset-inline-start: 100%; - z-index: -1; - width: 10px; - height: 10px; - margin-inline-start: -5px; - background-color: var(--dca-white); - box-shadow: 0 0 10px rgba(var(--dca-shadow), 0.25); - transform: rotate(45deg) translateZ(0); -} -.cms-pagetree-dropdown-menu .cms-pagetree-dropdown-menu-inner { - margin: 0; - padding: 0 !important; - border-radius: 5px; - background-color: var(--dca-white); -} - -.cms-pagetree-dropdown-menu-open .cms-pagetree-dropdown-menu { - display: block; -} - -.cms-pagetree-dropdown-menu-arrow-block-start-inline-end:before { - margin-block-start: -5px; - margin-inline-start: -24px; -} - -.cms-pagetree-dropdown-menu-arrow-inline-end-block-start:before { - inset-block-start: 16px; -} - -.cms-pagetree-dropdown-menu-arrow-inline-end-block-end:before { - inset-block-end: 18px; -} - -.cms-pagetree-dropdown-loader { - position: absolute; - inset-block-start: 0; - inset-inline-start: 0; - inset-inline-end: 0; - inset-block-end: 0; - border-radius: 5px; -} - -.cms-pagetree-node-state { - box-sizing: border-box; - display: inline-block; - width: 16px; - height: 16px; - border-radius: 100%; - border: 2px solid var(--dca-white); - background: var(--dca-white); - vertical-align: top; -} - -.cms-btn .cms-pagetree-node-state { - vertical-align: middle; - margin-right: 5px; - margin-top: -4px; -} -.cms-btn:hover .cms-pagetree-node-state-dirty { - box-shadow: 0 0 0 1px white; -} - -.cms-pagetree-node-state-empty { - border-color: var(--dca-gray-light); - background-color: var(--dca-white); -} -.cms-pagetree-node-state-unpublished, .cms-pagetree-node-state-unpublished-parent { - border-color: var(--dca-gray-light); - background-color: var(--dca-gray-light); -} -.cms-pagetree-node-state-archived { - border-color: var(--dca-gray-light); - background-color: var(--dca-gray-lighter); -} -.cms-pagetree-node-state-published, .cms-pagetree-node-state-public { - border-color: #693; - background-color: #693; -} -.cms-pagetree-node-state-draft { - border-color: var(--dca-primary); - background-color: var(--dca-white); -} -.cms-pagetree-node-state-dirty { - animation: pulsate 2.5s ease-out infinite; - border-color: var(--dca-primary); - background-color: var(--dca-primary); -} -.cms-pagetree-node-state-deletion { - border-color: #f00; - background-color: #f00; -} - -@keyframes pulsate { - 0% { - opacity: 0.5; - } - 50% { - opacity: 1; - } - 100% { - opacity: 0.5; - } -} -.cms-pagetree-legend { - position: relative; - text-align: end; -} -.cms-pagetree-legend .cms-icon-info { - color: var(--dca-primary); - vertical-align: middle; - margin-inline-end: 5px; -} -.cms-pagetree-legend .cms-pagetree-dropdown { - display: inline-block; -} -.cms-pagetree-legend .cms-pagetree-dropdown-menu { - inset-block-start: auto; - inset-inline-end: 100%; - inset-block-end: 0; - inset-inline-start: auto; - margin-inline-end: 10px; - margin-block-end: -90px; -} -.cms-pagetree-legend .cms-pagetree-dropdown-menu:before { - inset-block-end: 96px; -} -.cms-pagetree-legend .cms-pagetree-dropdown-item .cms-pagetree-node-state { - margin-inline-end: 10px; -} -.cms-pagetree-legend .cms-pagetree-dropdown-item .cms-icon { - margin-inline-end: 8px; -} -.cms-pagetree-legend .cms-pagetree-dropdown-item .cms-icon-home, -.cms-pagetree-legend .cms-pagetree-dropdown-item .cms-icon-sitemap, -.cms-pagetree-legend .cms-pagetree-dropdown-item .cms-icon-apphook { - color: var(--dca-gray-light); -} -.cms-pagetree-legend .cms-icon { - text-align: center; - width: 20px; - margin-inline-start: -2px; -} -.cms-pagetree-legend .cms-icon-check-square { - color: #693; - font-size: 14px; -} -.cms-pagetree-legend .cms-icon-minus-square { - font-size: 14px; -} - -.cms-pagetree-legend-title-wrap { - cursor: pointer; -} - -.cms-pagetree-legend-title { - color: var(--dca-gray-light); - vertical-align: middle; - text-transform: uppercase; -} - -.cms-tooltip, -.cms-tooltip-touch { - visibility: hidden; - position: absolute; - top: 0; - left: 0; - font-size: 12px; - line-height: 22px; - font-weight: bold; - color: var(--dca-gray-lighter); - white-space: nowrap; - padding: 0 7px 0 22px; - margin: 0; - border-radius: 3px; - background: var(--dca-gray-darker); -} -.cms-tooltip:before, -.cms-tooltip-touch:before { - content: "\E016"; -} -.cms-tooltip:before, -.cms-tooltip-touch:before { - font-size: 14px; - top: 50%; - left: 4px; - margin-top: -7px; - position: absolute; -} -.cms-tooltip span, -.cms-tooltip-touch span { - position: absolute; - top: 100%; - right: 0; - margin-top: -4px; - float: right; - color: var(--dca-white); - font-weight: normal; - padding: 0 7px; - border-radius: 3px 0 3px 3px; - background: var(--dca-gray-darker); -} - -/*# sourceMappingURL=cms.pagetree.css.map */ + */:root,:root[data-theme=auto],:root[data-theme=light]{--dca-light-mode:1;--dca-dark-mode:0;--dca-white:#FFFFFF;--dca-black:#000000;--dca-shadow:0,0,0;--dca-primary:#00bbff;--dca-gray:#666;--dca-gray-lightest:#f2f2f2;--dca-gray-lighter:#ddd;--dca-gray-light:#999;--dca-gray-darker:#454545;--dca-gray-darkest:#333;--dca-gray-super-lightest:#f7f7f7;--active-brightness:0.9;--focus-brightness:0.95}:root,:root[data-theme=auto]{color-scheme:dark light}:root[data-theme=light]{color-scheme:light}:root[data-theme=dark]{color-scheme:dark;--dca-light-mode:0;--dca-dark-mode:1;--dca-white:#2A2C2E;--dca-black:#FFF;--dca-primary:#58D1FC;--dca-gray:#ccc;--dca-gray-lightest:#444;--dca-gray-lighter:#666;--dca-gray-light:#888;--dca-gray-darker:#ddd;--dca-gray-darkest:#eee;--dca-gray-super-lightest:#333;--active-brightness:2;--focus-brightness:1.5}@media (prefers-color-scheme:dark){:root:not([data-theme]),:root[data-theme=auto]{--dca-light-mode:0;--dca-dark-mode:1;--dca-white:#2A2C2E;--dca-black:#FFF;--dca-primary:#58D1FC;--dca-gray:#ccc;--dca-gray-lightest:#444;--dca-gray-lighter:#666;--dca-gray-light:#888;--dca-gray-darker:#ddd;--dca-gray-darkest:#eee;--dca-gray-super-lightest:#333;--active-brightness:2;--focus-brightness:1.5}}@font-face{font-family:django-cms-iconfont;src:url(/static/cms/fonts/5.0.8/django-cms-iconfont.eot);src:url(/static/cms/fonts/5.0.8/django-cms-iconfont.eot#iefix) format("eot"),url(/static/cms/fonts/5.0.8/django-cms-iconfont.woff2) format("woff2"),url(/static/cms/fonts/5.0.8/django-cms-iconfont.woff) format("woff"),url(/static/cms/fonts/5.0.8/django-cms-iconfont.ttf) format("truetype"),url(/static/cms/fonts/5.0.8/django-cms-iconfont.svg#django-cms-iconfont) format("svg");font-weight:400;font-style:normal}.cms-icon,.cms-tooltip-touch:before,.cms-tooltip:before,.cms-tree-item-preview a,.cms-tree-node-success>.jstree-anchor .jstree-themeicon,.jstree-django-cms .jstree-ocl{display:inline-block;font:normal normal normal 16px/1 django-cms-iconfont;text-rendering:auto;transform:translate(0,0);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.cms-icon-none{width:1em}.cms-icon-advanced-settings:before{content:"\E001"}.cms-icon-alias:before{content:"\E002"}.cms-icon-apphook:before{content:"\E003"}.cms-icon-archive:before{content:"\E004"}.cms-icon-arrow-right:before{content:"\E005"}.cms-icon-arrow-wide:before{content:"\E006"}.cms-icon-arrow:before{content:"\E007"}.cms-icon-bin:before{content:"\E008"}.cms-icon-broadcast-off:before{content:"\E009"}.cms-icon-broadcast-on:before{content:"\E00A"}.cms-icon-check-circle:before{content:"\E00B"}.cms-icon-check-o:before{content:"\E00C"}.cms-icon-check-square:before{content:"\E00D"}.cms-icon-check:before{content:"\E00E"}.cms-icon-close:before{content:"\E00F"}.cms-icon-cogs:before{content:"\E010"}.cms-icon-comment:before{content:"\E011"}.cms-icon-compare:before{content:"\E012"}.cms-icon-copy:before{content:"\E013"}.cms-icon-cut:before{content:"\E014"}.cms-icon-edit-new:before{content:"\E015"}.cms-icon-edit:before{content:"\E016"}.cms-icon-external-placeholder:before{content:"\E017"}.cms-icon-eye:before{content:"\E018"}.cms-icon-forbidden:before{content:"\E019"}.cms-icon-handler:before{content:"\E01A"}.cms-icon-highlight:before{content:"\E01B"}.cms-icon-home:before{content:"\E01C"}.cms-icon-info:before{content:"\E01D"}.cms-icon-layers:before{content:"\E01E"}.cms-icon-list-ol:before{content:"\E01F"}.cms-icon-loader:before{content:"\E020"}.cms-icon-lock:before{content:"\E021"}.cms-icon-logo:before{content:"\E022"}.cms-icon-manage-versions:before{content:"\E023"}.cms-icon-menu:before{content:"\E024"}.cms-icon-minimize:before{content:"\E025"}.cms-icon-minus-circle:before{content:"\E026"}.cms-icon-minus-square-o:before{content:"\E027"}.cms-icon-minus-square:before{content:"\E028"}.cms-icon-minus:before{content:"\E029"}.cms-icon-moderate:before{content:"\E02A"}.cms-icon-paste:before{content:"\E02B"}.cms-icon-pencil:before{content:"\E02C"}.cms-icon-pin:before{content:"\E02D"}.cms-icon-plugins:before{content:"\E02E"}.cms-icon-plus-circle:before{content:"\E02F"}.cms-icon-plus-square-o:before{content:"\E030"}.cms-icon-plus:before{content:"\E031"}.cms-icon-publish:before{content:"\E032"}.cms-icon-puzzle:before{content:"\E033"}.cms-icon-redo:before{content:"\E034"}.cms-icon-rename:before{content:"\E035"}.cms-icon-scissors:before{content:"\E036"}.cms-icon-search:before{content:"\E037"}.cms-icon-settings:before{content:"\E038"}.cms-icon-sitemap:before{content:"\E039"}.cms-icon-squares:before{content:"\E03A"}.cms-icon-theme-auto:before{content:"\E03B"}.cms-icon-theme-dark:before{content:"\E03C"}.cms-icon-theme-light:before{content:"\E03D"}.cms-icon-undo:before{content:"\E03E"}.cms-icon-unlock:before{content:"\E03F"}.cms-icon-unpublish:before{content:"\E040"}.cms-icon-view:before{content:"\E041"}.cms-icon-window:before{content:"\E042"}.cms-pagetree-wrapper .cms-btn{background-image:none!important;margin-bottom:0;border-radius:3px!important;color:var(--dca-gray)!important;background-color:var(--dca-white)!important;border:1px solid var(--dca-gray-lighter)!important;background-clip:padding-box;-webkit-appearance:none}.cms-pagetree-wrapper .cms-btn.focus,.cms-pagetree-wrapper .cms-btn:focus,.cms-pagetree-wrapper .cms-btn:hover{color:var(--dca-gray)!important;background-color:var(--dca-gray-lightest)!important;border-color:var(--dca-gray-lighter)!important}.cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn,.cms-pagetree-wrapper .cms-btn.cms-btn-active,.cms-pagetree-wrapper .cms-btn:active,.cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn{color:var(--dca-gray)!important;background-color:var(--dca-white)!important;border-color:var(--dca-gray-lighter)!important;filter:brightness(var(--active-brightness)) opacity(1)!important;box-shadow:inset 0 3px 5px rgba(var(--dca-shadow),.125)!important}.cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn.focus,.cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn:focus,.cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn:hover,.cms-pagetree-wrapper .cms-btn.cms-btn-active.focus,.cms-pagetree-wrapper .cms-btn.cms-btn-active:focus,.cms-pagetree-wrapper .cms-btn.cms-btn-active:hover,.cms-pagetree-wrapper .cms-btn:active.focus,.cms-pagetree-wrapper .cms-btn:active:focus,.cms-pagetree-wrapper .cms-btn:active:hover,.cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn.focus,.cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn:focus,.cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn:hover{color:var(--dca-gray)!important;background-color:var(--dca-white)!important;border-color:var(--dca-gray-lighter)!important;filter:brightness(calc(var(--focus-brightness) * var(--active-brightness))) opacity(1)!important}.cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn,.cms-pagetree-wrapper .cms-btn.cms-btn-active,.cms-pagetree-wrapper .cms-btn:active,.cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn{background-image:none!important}.cms-pagetree-wrapper .cms-btn.cms-btn-disabled,.cms-pagetree-wrapper .cms-btn.cms-btn-disabled.cms-btn-active,.cms-pagetree-wrapper .cms-btn.cms-btn-disabled.focus,.cms-pagetree-wrapper .cms-btn.cms-btn-disabled:active,.cms-pagetree-wrapper .cms-btn.cms-btn-disabled:focus,.cms-pagetree-wrapper .cms-btn.cms-btn-disabled:hover,.cms-pagetree-wrapper .cms-btn[disabled],.cms-pagetree-wrapper .cms-btn[disabled].cms-btn-active,.cms-pagetree-wrapper .cms-btn[disabled].focus,.cms-pagetree-wrapper .cms-btn[disabled]:active,.cms-pagetree-wrapper .cms-btn[disabled]:focus,.cms-pagetree-wrapper .cms-btn[disabled]:hover{background-color:var(--dca-white)!important;border-color:var(--dca-gray-lighter)!important;color:var(--dca-gray-lighter);cursor:not-allowed;box-shadow:none!important}.cms-pagetree-wrapper .cms-btn.cms-btn-disabled.cms-btn-active:before,.cms-pagetree-wrapper .cms-btn.cms-btn-disabled.focus:before,.cms-pagetree-wrapper .cms-btn.cms-btn-disabled:active:before,.cms-pagetree-wrapper .cms-btn.cms-btn-disabled:before,.cms-pagetree-wrapper .cms-btn.cms-btn-disabled:focus:before,.cms-pagetree-wrapper .cms-btn.cms-btn-disabled:hover:before,.cms-pagetree-wrapper .cms-btn[disabled].cms-btn-active:before,.cms-pagetree-wrapper .cms-btn[disabled].focus:before,.cms-pagetree-wrapper .cms-btn[disabled]:active:before,.cms-pagetree-wrapper .cms-btn[disabled]:before,.cms-pagetree-wrapper .cms-btn[disabled]:focus:before,.cms-pagetree-wrapper .cms-btn[disabled]:hover:before{color:var(--dca-gray-lighter)}.cms-pagetree-wrapper .cms-btn-action{background-image:none!important;margin-bottom:0;border-radius:3px!important;color:var(--dca-white)!important;background-color:var(--dca-primary)!important;border:1px solid var(--dca-primary)!important;background-clip:padding-box;-webkit-appearance:none}.cms-pagetree-wrapper .cms-btn-action.focus,.cms-pagetree-wrapper .cms-btn-action:focus,.cms-pagetree-wrapper .cms-btn-action:hover{color:var(--dca-white)!important;background-color:var(--dca-primary)!important;border-color:var(--dca-primary)!important;filter:brightness(var(--focus-brightness)) opacity(1)!important}.cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn-action.cms-btn,.cms-pagetree-wrapper .cms-btn-action.cms-btn-active,.cms-pagetree-wrapper .cms-btn-action:active,.cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn-action.cms-btn{color:var(--dca-white)!important;background-color:var(--dca-primary)!important;border-color:var(--dca-primary)!important;filter:brightness(var(--active-brightness)) opacity(1)!important;box-shadow:inset 0 3px 5px rgba(var(--dca-shadow),.125)!important}.cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn-action.cms-btn:focus,.cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn-action.cms-btn:hover,.cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn-action.focus.cms-btn,.cms-pagetree-wrapper .cms-btn-action.cms-btn-active.focus,.cms-pagetree-wrapper .cms-btn-action.cms-btn-active:focus,.cms-pagetree-wrapper .cms-btn-action.cms-btn-active:hover,.cms-pagetree-wrapper .cms-btn-action:active.focus,.cms-pagetree-wrapper .cms-btn-action:active:focus,.cms-pagetree-wrapper .cms-btn-action:active:hover,.cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn-action.cms-btn:focus,.cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn-action.cms-btn:hover,.cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn-action.focus.cms-btn{color:var(--dca-white)!important;background-color:var(--dca-primary)!important;border-color:var(--dca-primary)!important;filter:brightness(calc(var(--focus-brightness) * var(--active-brightness))) opacity(1)!important}.cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn-action.cms-btn,.cms-pagetree-wrapper .cms-btn-action.cms-btn-active,.cms-pagetree-wrapper .cms-btn-action:active,.cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn-action.cms-btn{background-image:none!important}.cms-pagetree-wrapper .cms-btn-action.cms-btn-disabled,.cms-pagetree-wrapper .cms-btn-action.cms-btn-disabled.cms-btn-active,.cms-pagetree-wrapper .cms-btn-action.cms-btn-disabled.focus,.cms-pagetree-wrapper .cms-btn-action.cms-btn-disabled:active,.cms-pagetree-wrapper .cms-btn-action.cms-btn-disabled:focus,.cms-pagetree-wrapper .cms-btn-action.cms-btn-disabled:hover,.cms-pagetree-wrapper .cms-btn-action[disabled],.cms-pagetree-wrapper .cms-btn-action[disabled].cms-btn-active,.cms-pagetree-wrapper .cms-btn-action[disabled].focus,.cms-pagetree-wrapper .cms-btn-action[disabled]:active,.cms-pagetree-wrapper .cms-btn-action[disabled]:focus,.cms-pagetree-wrapper .cms-btn-action[disabled]:hover{background-color:var(--dca-primary)!important;border-color:var(--dca-primary)!important;color:var(--dca-white);filter:brightness(.6) opacity(1);cursor:not-allowed;box-shadow:none!important}.cms-pagetree-wrapper .cms-btn-action.cms-btn-disabled.cms-btn-active:before,.cms-pagetree-wrapper .cms-btn-action.cms-btn-disabled.focus:before,.cms-pagetree-wrapper .cms-btn-action.cms-btn-disabled:active:before,.cms-pagetree-wrapper .cms-btn-action.cms-btn-disabled:before,.cms-pagetree-wrapper .cms-btn-action.cms-btn-disabled:focus:before,.cms-pagetree-wrapper .cms-btn-action.cms-btn-disabled:hover:before,.cms-pagetree-wrapper .cms-btn-action[disabled].cms-btn-active:before,.cms-pagetree-wrapper .cms-btn-action[disabled].focus:before,.cms-pagetree-wrapper .cms-btn-action[disabled]:active:before,.cms-pagetree-wrapper .cms-btn-action[disabled]:before,.cms-pagetree-wrapper .cms-btn-action[disabled]:focus:before,.cms-pagetree-wrapper .cms-btn-action[disabled]:hover:before{color:var(--dca-white);filter:brightness(.6) opacity(1)}.cms-pagetree-wrapper .cms-btn-caution{background-image:none!important;margin-bottom:0;border-radius:3px!important;color:var(--dca-white)!important;background-color:rgb(255,63.75,0)!important;border:1px solid rgb(255,63.75,0)!important;background-clip:padding-box;-webkit-appearance:none}.cms-pagetree-wrapper .cms-btn-caution.focus,.cms-pagetree-wrapper .cms-btn-caution:focus,.cms-pagetree-wrapper .cms-btn-caution:hover{color:var(--dca-white)!important;background-color:rgb(255,63.75,0)!important;border-color:rgb(255,63.75,0)!important;filter:brightness(var(--focus-brightness)) opacity(1)!important}.cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn-caution.cms-btn,.cms-pagetree-wrapper .cms-btn-caution.cms-btn-active,.cms-pagetree-wrapper .cms-btn-caution:active,.cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn-caution.cms-btn{color:var(--dca-white)!important;background-color:rgb(255,63.75,0)!important;border-color:rgb(255,63.75,0)!important;filter:brightness(var(--active-brightness)) opacity(1)!important;box-shadow:inset 0 3px 5px rgba(var(--dca-shadow),.125)!important}.cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn-caution.cms-btn:focus,.cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn-caution.cms-btn:hover,.cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn-caution.focus.cms-btn,.cms-pagetree-wrapper .cms-btn-caution.cms-btn-active.focus,.cms-pagetree-wrapper .cms-btn-caution.cms-btn-active:focus,.cms-pagetree-wrapper .cms-btn-caution.cms-btn-active:hover,.cms-pagetree-wrapper .cms-btn-caution:active.focus,.cms-pagetree-wrapper .cms-btn-caution:active:focus,.cms-pagetree-wrapper .cms-btn-caution:active:hover,.cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn-caution.cms-btn:focus,.cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn-caution.cms-btn:hover,.cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn-caution.focus.cms-btn{color:var(--dca-white)!important;background-color:rgb(255,63.75,0)!important;border-color:rgb(255,63.75,0)!important;filter:brightness(calc(var(--focus-brightness) * var(--active-brightness))) opacity(1)!important}.cms-pagetree .cms-pagetree-dropdown-menu-open .cms-pagetree-wrapper .cms-btn-caution.cms-btn,.cms-pagetree-wrapper .cms-btn-caution.cms-btn-active,.cms-pagetree-wrapper .cms-btn-caution:active,.cms-pagetree-wrapper .cms-pagetree .cms-pagetree-dropdown-menu-open .cms-btn-caution.cms-btn{background-image:none!important}.cms-pagetree-wrapper .cms-btn-caution.cms-btn-disabled,.cms-pagetree-wrapper .cms-btn-caution.cms-btn-disabled.cms-btn-active,.cms-pagetree-wrapper .cms-btn-caution.cms-btn-disabled.focus,.cms-pagetree-wrapper .cms-btn-caution.cms-btn-disabled:active,.cms-pagetree-wrapper .cms-btn-caution.cms-btn-disabled:focus,.cms-pagetree-wrapper .cms-btn-caution.cms-btn-disabled:hover,.cms-pagetree-wrapper .cms-btn-caution[disabled],.cms-pagetree-wrapper .cms-btn-caution[disabled].cms-btn-active,.cms-pagetree-wrapper .cms-btn-caution[disabled].focus,.cms-pagetree-wrapper .cms-btn-caution[disabled]:active,.cms-pagetree-wrapper .cms-btn-caution[disabled]:focus,.cms-pagetree-wrapper .cms-btn-caution[disabled]:hover{background-color:rgb(255,63.75,0)!important;border-color:rgb(255,63.75,0)!important;color:var(--dca-white);filter:brightness(.6) opacity(1);cursor:not-allowed;box-shadow:none!important}.cms-pagetree-wrapper .cms-btn-caution.cms-btn-disabled.cms-btn-active:before,.cms-pagetree-wrapper .cms-btn-caution.cms-btn-disabled.focus:before,.cms-pagetree-wrapper .cms-btn-caution.cms-btn-disabled:active:before,.cms-pagetree-wrapper .cms-btn-caution.cms-btn-disabled:before,.cms-pagetree-wrapper .cms-btn-caution.cms-btn-disabled:focus:before,.cms-pagetree-wrapper .cms-btn-caution.cms-btn-disabled:hover:before,.cms-pagetree-wrapper .cms-btn-caution[disabled].cms-btn-active:before,.cms-pagetree-wrapper .cms-btn-caution[disabled].focus:before,.cms-pagetree-wrapper .cms-btn-caution[disabled]:active:before,.cms-pagetree-wrapper .cms-btn-caution[disabled]:before,.cms-pagetree-wrapper .cms-btn-caution[disabled]:focus:before,.cms-pagetree-wrapper .cms-btn-caution[disabled]:hover:before{color:var(--dca-white);filter:brightness(.6) opacity(1)}.cms-pagetree-wrapper .cms-btn-disabled img{opacity:.2!important}.cms-admin-sideframe:not(.djangocms-admin-style){margin-block-start:46px}.cms-btn-toolbar{display:inline-block;line-height:32px;height:32px;font-size:12px;padding-block:0;padding-inline:22px}.cms-hidden{display:none!important}.cms-clear{clear:both;overflow:hidden}.cms-clear-mobile{clear:both;overflow:hidden}@media (min-width:520px){.cms-clear-mobile{display:none}}.jstree li{-ms-touch-action:none;touch-action:none}.cms-pagetree-root#changelist{display:block;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between}.cms-pagetree-root,.cms-pagetree-root.filtered{min-height:0;padding:0!important;box-shadow:0 0 5px 0 rgba(var(--dca-shadow),.2)!important}#content-main .cms-pagetree.cms-pagetree.cms-pagetree a{text-decoration:none}.cms-pagetree{border:1px solid var(--dca-gray-lighter)}.cms-pagetree .cms-hover-tooltip{z-index:auto}.cms-pagetree .sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0}.cms-pagetree .cms-btn.cms-icon{display:inline-block;box-sizing:border-box;width:39px;height:37px;position:relative}.cms-pagetree .cms-btn.cms-icon:before{position:absolute;inset-inline-start:50%;inset-block-start:50%;margin-block-start:-8px;margin-inline-start:-8px}.cms-pagetree .cms-btn.cms-btn-no-border{border:none!important}.cms-pagetree-header{position:relative;display:-ms-flexbox;display:flex;width:100%;box-sizing:border-box;z-index:auto;min-height:36px;padding-block:15px;padding-inline:20px;box-shadow:none;border-block-end:none;background:var(--dca-gray-super-lightest)}.cms-pagetree-header:before{content:"";position:absolute;inset-block-end:-6px;inset-inline-start:0;inset-inline-end:0;height:5px;background-color:var(--dca-white)}.cms-pagetree-header+.module{padding-block-start:0!important}.djangocms-admin-style .cms-pagetree-header{margin-block:-40px 0;margin-inline:0}.djangocms-admin-style.cms-admin-sideframe .cms-pagetree-header{margin-block-start:-25px}.cms-pagetree-header-title{display:table-cell;width:100%;line-height:36px;height:36px}.cms-pagetree-header-title h1{line-height:36px;margin:0!important}.cms-pagetree-header-title h1 span{font-weight:400}.cms-pagetree-header-row{display:table;width:100%;table-layout:fixed}@media (max-width:520px){.cms-pagetree-header-row{display:block}}.cms-pagetree-header-cell{display:table-cell;white-space:nowrap;vertical-align:middle}@media (max-width:520px){.cms-pagetree-header-cell{display:block}}.cms-pagetree-header-cell-fluid{width:100%}.cms-pagetree-header-cell-search{width:220px!important;position:relative}.cms-pagetree-header-filter-active .cms-pagetree-header-cell-search{position:static}@media (max-width:520px){.cms-pagetree-header-cell-search{width:100%!important}}@media (max-width:520px){.cms-pagetree-header-body{text-align:end;padding-block-start:10px}}.cms-pagetree-header-filter-active .cms-pagetree-search-container{position:relative}.cms-pagetree-header-search{position:absolute;inset-block-start:0;display:table;width:100%;inset-inline-start:0;inset-inline-end:0;padding:0;box-shadow:none;background:0 0}@media (max-width:520px){.cms-pagetree-header-search{position:relative}}.cms-pagetree-header-search .cms-pagetree-header-search-btn{display:table-cell;vertical-align:middle;position:relative;outline:0;color:var(--dca-white)!important;padding-block:8px!important;padding-inline:10px!important;border:none!important;background:var(--dca-primary)!important;height:36px;border-radius:3px}.cms-pagetree-header-search .cms-pagetree-header-search-btn:hover{background:rgb(0,168.3,229.5)!important}.cms-pagetree-header-search .cms-pagetree-header-search-btn:active,.cms-pagetree-header-search .cms-pagetree-header-search-btn:focus{color:var(--dca-white)!important;background:rgb(0,130.9,178.5)!important}.cms-pagetree-header-search .cms-pagetree-header-filter{position:relative;display:table-cell;width:100%;padding-inline-end:5px;vertical-align:middle}.cms-pagetree-header-search .cms-pagetree-header-filter input{width:100%;box-sizing:border-box;width:100%;display:inline-block;height:36px;padding:6px;line-height:24px;padding-inline-end:35px!important;transition:width .2s}.cms-pagetree-header-filter-active .cms-pagetree-header-search{inset-inline-start:0;inset-inline-end:0;inset-block-start:0}.djangocms-admin-style .cms-pagetree-header-search{margin-block:5px 10px;margin-inline:0}.cms-pagetree-header-filter-trigger{position:absolute;inset-inline-end:5px;inset-block-start:0;cursor:pointer;padding:10px;transform:rotate(270deg)}.cms-pagetree-header-filter-trigger:active .cms-icon,.cms-pagetree-header-filter-trigger:focus .cms-icon,.cms-pagetree-header-filter-trigger:hover .cms-icon{color:var(--dca-primary)}.cms-pagetree-header-filter-trigger .cms-icon{font-size:11px;color:var(--dca-gray)}.cms-pagetree-header-filter-container{display:none;overflow:auto;position:absolute;inset-inline-start:0;inset-block-start:38px;width:100%;padding-block:5px 15px;padding-inline:20px;max-height:400px;box-shadow:0 0 5px 0 rgba(var(--dca-shadow),.2);box-sizing:border-box;border-radius:3px;background:var(--dca-white);z-index:1000}.cms-pagetree-header-filter-container h3{color:var(--dca-gray);font-size:14px;font-weight:400;text-transform:uppercase;padding-block:10px 0;padding-inline:0}.cms-pagetree-header-filter-container li,.cms-pagetree-header-filter-container ul{list-style-type:none;padding-block:2px;padding-inline:0;margin:0}.cms-pagetree-header-filter-container li a{color:var(--dca-gray-light)}.cms-pagetree-header-filter-container .selected a{color:var(--dca-primary);font-weight:700}.cms-pagetree-header-search-close{position:absolute;inset-inline-end:0;inset-block-start:0;padding:10px}.cms-pagetree-header-search-close:active .cms-icon,.cms-pagetree-header-search-close:focus .cms-icon,.cms-pagetree-header-search-close:hover .cms-icon{color:var(--dca-primary)}.cms-pagetree-header-search-close .cms-icon{font-size:12px;color:var(--dca-gray)}.cms-pagetree-header-search-reset{font-size:90%;padding-block:0;padding-inline:10px}.cms-pagetree-sites-list{display:inline-block;vertical-align:top;padding-inline-start:10px;padding-inline-end:10px;margin-inline-start:10px;margin-inline-end:10px}.cms-pagetree-sites-list .cms-pagetree-dropdown-trigger{display:inline-block;color:var(--dca-gray)!important;padding:10px;height:16px}.cms-pagetree-sites-list .cms-pagetree-dropdown-menu{inset-inline-end:50%;margin-inline-end:-19px!important}.cms-pagetree-header-create{display:inline-block;vertical-align:top;height:36px!important;box-sizing:border-box}.djangocms-admin-style .cms-pagetree-breadcrumbs{margin-block-end:-30px!important;border:none!important}.cms-pagetree-section{position:relative;z-index:auto;min-height:15px;padding-block:15px;padding-inline:20px;box-shadow:none;border-block-end:none;background:var(--dca-white)}.cms-pagetree-section h2,.cms-pagetree-section p{padding:0;margin:0;border:none}.cms-pagetree-section h2{float:left;font-weight:700;color:var(--dca-gray-darkest);background:0 0}.cms-pagetree-section h2:dir(rtl){float:right}.cms-pagetree-container+.cms-pagetree-section{margin-block-start:-1px}.cms-pagetree-section-header{display:-ms-flexbox;display:flex;-ms-flex-direction:row;flex-direction:row;-ms-flex-align:center;align-items:center}.cms-pagetree-section-header select{width:120px!important;margin-inline-start:15px}.cms-pagetree-section-condensed{padding-block:10px;padding-inline:20px}.cms-pagetree-section-nav{position:absolute;inset-inline-end:4px;inset-block-start:50%;margin-block-start:-19px}.cms-pagetree-section-nav .cms-pagetree-dropdown-menu{inset-inline-end:40px;inset-block-start:-2px;width:205px}.cms-pagetree-dropdown-separator{padding:0;margin-block:4px!important;margin-inline:1px!important;line-height:1;height:1px;background:var(--dca-gray-lighter)}.cms-pagetree-container{position:relative;z-index:auto;border:1px solid var(--dca-gray-lighter);border-block-end:none}.cms-tree-col{float:right;min-width:32px;height:32px}.cms-tree-col:dir(rtl){float:left}.cms-tree-col-padding-sm{margin-block-start:-3px;margin-inline-start:-4px;margin-inline-start:-4px;height:38px}.cms-tree-item{display:table;width:100%;text-align:center;height:32px}.cms-tree-item+.cms-tree-item{padding-inline-start:4px}.cms-tree-item-disabled{opacity:.3;pointer-events:none}.cms-tree-item-wrapper{display:table-row;min-width:32px}.cms-tree-item-wrapper .cms-tree-item{display:table-cell;vertical-align:middle;text-align:center;min-width:32px}.cms-tree-item-wrapper .cms-tree-item-inner{display:block}.cms-tree-item-inner{display:table-cell;text-align:center;vertical-align:middle}.cms-tree-reload{float:right}.cms-tree-reload:dir(rtl){float:left}.cms-tree-item-lang .cms-pagetree-dropdown-menu{inset-inline-end:32px;inset-block-start:-5px;min-width:180px}.cms-tree-item-button .cms-pagetree-dropdown-menu{inset-inline-end:40px;inset-block-start:-2px;width:205px}.cms-tree-item-button .cms-hover-tooltip:after,.cms-tree-item-button .cms-hover-tooltip:before{inset-block-end:23px}.cms-pagetree-alt-mode .cms-tree-item-button .cms-icon-settings:not(.cms-btn-disabled)[data-url]:before{content:"\E001"}.cms-tree-item-icons{position:relative}.cms-tree-item-icons .cms-icon{display:block}.cms-tree-item-icons span:before{color:var(--dca-gray-light)}.cms-tree-item-icons-dark span:before{color:var(--dca-gray-darker)}.cms-tree-item-preview{position:relative}.cms-tree-item-preview a{font-size:18px;line-height:18px;height:18px;display:block}.cms-tree-item-preview a:before{display:block;color:var(--dca-gray-darker)}.cms-tree-item-preview a:hover:before{color:var(--dca-black)}.cms-tree-item-preview-active a:before{color:var(--dca-black)}.cms-tree-item-lang{position:relative}.cms-tree-item-lang .cms-tree-item-inner>.cms-tree-lang-container,.cms-tree-item-lang .cms-tree-item-inner>a{display:block;height:16px}.cms-tree-item-menu{position:relative}.cms-tree-item-menu .cms-tree-item-inner>a{display:block;height:14px}.cms-tree-item-menu .cms-icon{display:block}.cms-tree-item-menu .cms-icon:before{display:block;font-size:14px;color:var(--dca-gray-darker)}.cms-tree-item-menu .cms-icon-check-square:before{color:#693}.cms-tree-item-info{position:relative}.cms-tree-item-info .cms-tree-item-inner>a{display:block;height:16px}.cms-tree-item-info .cms-icon{display:block}.cms-tree-item-info .cms-icon:before{display:block;font-size:16px}.cms-tree-item-info p{color:var(--dca-gray-darkest);padding:0;margin:0}.cms-tree-item-info strong{color:var(--dca-gray-light);font-weight:400}.cms-tree-item-info a{display:inline-block;line-height:1}.cms-tree-search{float:left;margin-inline-end:5px}.cms-tree-search:dir(rtl){float:right}.cms-tree-search label{display:none!important}.cms-tree-search select{position:relative;inset-block-start:2px;width:175px!important}.cms-tree-search #toolbar form#changelist-search input[type=submit]{margin-inline-start:0!important}.cms-tree-search #toolbar form#changelist-search #searchbar{width:175px!important}.cms-tree-filters{float:right}.cms-tree-filters:dir(rtl){float:left}.cms-tree-filters .js-cms-tree-filter-trigger{position:absolute;inset-block-start:0;inset-inline-end:0;padding-inline-end:20px;line-height:30px;width:175px;border-block-end:1px solid var(--dca-gray-lighter)}.cms-tree-filters h2{display:none}.cms-pagetree-empty{padding-block:15px;padding-inline:20px;border-block-start:none}.cms-pagetree-empty .addlink{padding-inline-start:16px}.jstree-django-cms{-webkit-tap-highlight-color:transparent}.jstree-django-cms .jstree-children,.jstree-django-cms .jstree-container-ul,.jstree-django-cms .jstree-leaf,.jstree-django-cms .jstree-node{list-style-type:none;padding:0;margin:0;transform:translateZ(0)}.jstree-django-cms .jstree-is-dragging{opacity:.1!important}.jstree-django-cms .jstree-is-dragging+.jstree-grid-cell{height:47px;border-block-start:1px solid var(--dca-gray-lighter);margin-block-start:-1px}.jstree-django-cms .jstree-is-dragging-copy{opacity:.9!important}.jstree-django-cms .jstree-grid-header,.jstree-django-cms .jstree-grid-separator{padding:0;margin:0;margin-left:0!important;border:none;background:0 0}.jstree-django-cms .jstree-grid-header{z-index:1;position:relative}.jstree-django-cms.jstree-grid-cell{padding:0}.jstree-django-cms .jstree-grid-cell,.jstree-django-cms .jstree-grid-column{box-sizing:border-box;overflow:visible}.jstree-django-cms .jstree-grid-column{vertical-align:top}.jstree-grid-wrapper{box-sizing:border-box;overflow:visible!important;width:100%!important}.jstree-grid-wrapper{background:var(--dca-gray-lightest)}.jstree-django-cms .jstree-grid-header{font-size:12px;color:var(--dca-gray);text-align:center;padding-block:3px;padding-inline:6px;border-block-end:1px solid var(--dca-gray-lighter);background:var(--dca-white)}.jstree-django-cms .jstree-grid-header-fixed{position:fixed;z-index:2}.jstree-django-cms .jstree-grid-column-0 .jstree-grid-header{text-align:start!important}.jstree-django-cms .jstree-children{margin-inline-start:20px}.jstree-django-cms .jstree-container-ul{margin-inline-start:-1px}.jstree-django-cms .jstree-ocl{float:left;position:relative;inset-inline-start:20px;font-size:12px;color:var(--dca-gray-light);padding-block-start:18px;padding-inline-end:10px;padding-block-end:10px;padding-inline-start:0}.jstree-django-cms .jstree-ocl:dir(rtl){float:right}.jstree-django-cms .jstree-ocl:before{display:inline-block}.jstree-django-cms .jstree-closed>.jstree-ocl,.jstree-django-cms .jstree-open>.jstree-ocl{cursor:pointer}.jstree-django-cms .jstree-open>.jstree-ocl:before{content:"\E005"}.jstree-django-cms .jstree-open>.jstree-ocl:before{transform:rotate(90deg)}.jstree-django-cms .jstree-closed>.jstree-ocl:before{content:"\E005"}.jstree-django-cms .jstree-loading>.jstree-ocl:before{content:"\E020"}.jstree-django-cms .jstree-loading>.jstree-ocl:before{animation:cms-spin 2s infinite linear}.jstree-django-cms .jstree-anchor{box-sizing:border-box;display:block;cursor:move;outline:0;color:var(--dca-gray-darkest);height:47px;padding-block:12px 12px;padding-inline:25px 25px;margin-block-start:-1px;border-block:1px solid var(--dca-gray-lighter);border-inline:1px solid var(--dca-gray-lighter);border-inline-end:none;background-color:var(--dca-white);background-image:url(/static/cms/img/pagetree/tree-li-drag.gif);background-repeat:no-repeat;background-position:2px center;-ms-interpolation-mode:nearest-neighbor;image-rendering:pixelated;white-space:normal}.jstree-django-cms .jstree-anchor:hover{color:var(--dca-gray)!important}.jstree-django-cms .jstree-anchor:dir(rtl){background-position:calc(100% - 2px) center}.jstree-django-cms .jstree-anchor em{color:var(--dca-gray-light)}.jstree-django-cms .jstree-node[data-move-permission=false]>.jstree-anchor{cursor:default;background-image:none}.jstree-django-cms .jstree-closed>.jstree-anchor,.jstree-django-cms .jstree-loading>.jstree-anchor,.jstree-django-cms .jstree-open>.jstree-anchor{padding-inline:45px 25px;padding-block:12px 12px}.jstree-django-cms .cms-tree-node-filtered .jstree-anchor{cursor:default}.jstree-django-cms .jstree-grid-cell{height:46px;border-block-end:1px solid var(--dca-gray-lighter);background:var(--dca-white)}.jstree-django-cms .jstree-grid-cell>span{display:block}.jstree-django-cms .jstree-grid-cell.jstree-django-cms{height:auto;border:none;background:0 0}.jstree-django-cms .jstree-grid-cell-regular{text-align:center;padding-block:7px 7px;padding-inline:8px 8px;border-inline-start:1px solid var(--dca-gray-lighter)}.jstree-django-cms .jstree-grid-cell-regular-options{border-inline-start:none;text-align:end}.jstree-django-cms .jstree-clicked,.jstree-django-cms .jstree-hovered{background-color:var(--dca-gray-lightest)!important}.jstree-django-cms .jstree-grid-column-1 .jstree-grid-header{text-align:end}.jstree-django-cms .jstree-grid-column-1 .jstree-grid-header a{font-size:12px;inset-block-start:0}.jstree-django-cms .jstree-grid-column-1 .jstree-grid-header span{line-height:1}.jstree-django-cms .jstree-grid-column-1 .jstree-grid-header .cms-icon{font-size:12px;padding-inline-end:5px;margin-inline-start:5px}.jstree.jstree-loading .jstree-container-ul{margin:0}.jstree.jstree-loading .jstree-anchor{padding-block-start:13px;padding-inline-start:15px;border:none;background:0 0}.jstree.jstree-loading .jstree-loading>.jstree-ocl{display:none}.cms-tree-node-success>.jstree-anchor .jstree-themeicon{content:"";position:relative;inset-block-start:4px;color:#693}.cms-tree-node-success>.jstree-anchor .jstree-themeicon:before{content:"\E00E"}.jstree-django-cms .cms-tree-node-shared-true>.jstree-anchor{border-style:dashed;border-inline-end:none;color:#999;background-color:#f2f2f2!important}.jstree-django-cms .cms-tree-node-shared-true>.jstree-anchor:hover{color:#999!important}.jstree-django-cms .cms-tree-node-shared-true>.jstree-anchor.jstree-clicked,.jstree-django-cms .cms-tree-node-shared-true>.jstree-anchor.jstree-hovered{background-color:#f2f2f2!important}.jstree-django-cms .jstree-grid-cell.jstree-clicked,.jstree-django-cms .jstree-grid-cell.jstree-hovered{background-color:#f2f2f2}.jstree-django-cms .cms-tree-node-shared-false>.jstree-anchor{border-style:solid;border-inline-end:none;background-color:var(--dca-white)!important;border-block-end:1px solid var(--dca-gray-lighter)}.jstree-django-cms .cms-tree-node-shared-false>.jstree-anchor.jstree-clicked,.jstree-django-cms .cms-tree-node-shared-false>.jstree-anchor.jstree-hovered{background-color:var(--dca-gray-lightest)!important}.jstree-django-cms .cms-tree-node-shared-false+.cms-tree-node-shared-true>.jstree-anchor{border-block-start-style:solid}.jstree-django-cms .cms-tree-node-shared-false+.cms-tree-node-shared-true.jstree-clicked,.jstree-django-cms .cms-tree-node-shared-false+.cms-tree-node-shared-true.jstree-hovered{background-color:#f2f2f2}.jstree-django-cms .cms-page-tree-cell-shared--true{border-style:dashed;background-color:#f2f2f2}.jstree-django-cms .cms-page-tree-cell-shared--true.jstree-grid-cell-regular-options{border-inline-start:none}.jstree-django-cms .cms-page-tree-cell-shared--true .cms-btn-no-border{background-color:transparent!important}.jstree-django-cms .cms-page-tree-cell-shared--true .cms-btn-no-border:hover{background-color:#fff!important;border-block:1px solid #ddd!important;border-inline:1px solid #ddd!important}.jstree-django-cms .cms-page-tree-cell-shared--true.jstree-clicked,.jstree-django-cms .cms-page-tree-cell-shared--true.jstree-hovered{background-color:#f2f2f2!important}.jstree-django-cms .cms-page-tree-cell-shared--false{background-color:var(--dca-white)}.jstree-django-cms .cms-page-tree-cell-shared--false.jstree-clicked,.jstree-django-cms .cms-page-tree-cell-shared--false.jstree-hovered{background-color:var(--dca-gray-lightest)!important}.jstree-django-cms .cms-page-tree-cell-shared--true+.cms-page-tree-cell-shared--false{border-block-start:1px solid var(--dca-gray-lighter);margin-block-start:-1px;padding-block-start:8px;height:47px}#jstree-dnd{box-sizing:border-box;display:block;min-width:300px;height:46px;padding-block:12px 12px!important;padding-inline:25px 25px!important;margin-inline-start:-5px;background-color:var(--dca-primary)!important;color:var(--dca-white)}#jstree-dnd .jstree-copy{color:var(--dca-white);padding-block:0 2px;padding-inline:5px;margin-inline-start:5px;border-radius:3px;background:var(--dca-gray-light)}#jstree-dnd .jstree-er,#jstree-dnd .jstree-ok{position:absolute;inset-block-start:0;inset-inline-start:-5px;inset-block-end:0;inset-inline-end:0;background-color:rgba(255,255,255,.6)}#jstree-dnd .jstree-ok{display:none}.is-stacked #jstree-dnd{box-shadow:1px 1px 1px rgba(var(--dca-black),.2),4px 4px 0 0 var(--dca-primary)}.is-stacked .jstree-er{box-shadow:4px 4px 0 0 rgba(255,255,255,.6)}#jstree-marker{position:absolute;z-index:102;margin-inline-start:-6px;margin-block-start:-6px;border-inline-end:0;border-block-start:6px solid transparent;border-block-end:6px solid transparent;border-inline-start:6px solid var(--dca-primary);height:0}#jstree-marker:after{content:"";display:block;position:absolute;height:3px;inset-block-start:-1px;inset-inline-start:-6px;inset-inline-end:0;background-color:var(--dca-primary)}#jstree-marker.jstree-marker-child{border:2px solid var(--dca-primary);height:46px;margin-block:-24px 0;margin-inline:0;background-color:rgba(var(--dca-primary),.1);box-sizing:border-box}#jstree-marker.jstree-marker-child:after{display:none}.djangocms-admin-style.cms-pagetree-wrapper.cms-admin-sideframe .messagelist+#content{margin-block-start:117px!important}.djangocms-admin-style.cms-pagetree-wrapper.cms-admin-sideframe #container .breadcrumbs{inset-inline-start:20px}.djangocms-admin-style.cms-pagetree-wrapper.cms-admin-sideframe #container .breadcrumbs+#content,.djangocms-admin-style.cms-pagetree-wrapper.cms-admin-sideframe #container .breadcrumbs+.messagelist+#content{margin-inline-start:0!important;margin-inline-end:0!important}.djangocms-admin-style.cms-pagetree-wrapper.cms-admin-sideframe #jstree-marker{inset-inline-end:0}.cms-pagetree-header-lang,.cms-pagetree-header-lang:active,.cms-pagetree-header-lang:focus,.cms-pagetree-header-lang:hover,.cms-pagetree-header-lang:link{color:var(--dca-gray-darkest)!important}.cms-pagetree-header-lang.active{font-weight:700}.cms-pagetree-dropdown{position:relative}.cms-pagetree-dropdown-menu{display:none;position:absolute;inset-block-start:30px;inset-inline-end:-1px;z-index:1000;min-width:180px;border-radius:5px;background:var(--dca-white);box-shadow:0 0 10px rgba(var(--dca-shadow),.25);transform:translateZ(0)}.cms-pagetree-dropdown-menu .cms-pagetree-dropdown-item,.cms-pagetree-dropdown-menu a,.cms-pagetree-dropdown-menu a:link,.cms-pagetree-dropdown-menu a:link:visited,.cms-pagetree-dropdown-menu a:visited{display:block;color:var(--dca-gray);line-height:1.5;text-align:start;padding-block:10px;padding-inline:15px}.cms-pagetree-dropdown-menu a:active,.cms-pagetree-dropdown-menu a:focus,.cms-pagetree-dropdown-menu a:hover{color:var(--dca-white)!important;background:var(--dca-primary)}.cms-pagetree-dropdown-menu li:first-child>a{border-start-start-radius:5px;border-start-end-radius:5px}.cms-pagetree-dropdown-menu li:last-child>a{border-end-start-radius:5px;border-end-end-radius:5px}.cms-pagetree-dropdown-menu .cms-pagetree-dropdown-item-disabled{opacity:.2;color:var(--dca-gray-light);cursor:default}.cms-pagetree-dropdown-menu .cms-pagetree-dropdown-item-disabled:focus,.cms-pagetree-dropdown-menu .cms-pagetree-dropdown-item-disabled:hover{background:0 0!important;color:var(--dca-gray-darker)!important}.cms-pagetree-dropdown-menu.cms-pagetree-dropdown-menu-condensed .cms-pagetree-dropdown-item,.cms-pagetree-dropdown-menu.cms-pagetree-dropdown-menu-condensed a{padding-block:5px;padding-inline:15px}.cms-pagetree-dropdown-menu .active{font-weight:700}.cms-pagetree-dropdown-menu .label{display:block;color:var(--dca-gray);font-size:90%;font-weight:400;line-height:1.5;text-align:start;text-transform:uppercase;padding-block:7px 5px;padding-inline:15px;border-block-end:1px solid var(--dca-gray-lighter);margin-block-end:4px}.cms-pagetree-dropdown-menu li{margin:0;padding:0;list-style-type:none}.cms-pagetree-dropdown-menu p{display:block;overflow:hidden;font-size:12px!important;text-align:start;text-overflow:ellipsis;margin:0;padding-block:5px 3px!important;padding-inline:15px!important}.cms-pagetree-dropdown-menu p strong{font-size:12px!important}.cms-pagetree-dropdown-menu p+p{margin-block-start:-2px;padding-block-start:0!important}.cms-pagetree-dropdown-menu p:last-child{padding-block-end:7px!important}.cms-pagetree-dropdown-menu .cms-icon{margin-inline-end:10px}.cms-pagetree-dropdown-menu span{vertical-align:middle}.cms-pagetree-dropdown-menu:before{content:"";position:absolute;inset-inline-start:100%;z-index:-1;width:10px;height:10px;margin-inline-start:-5px;background-color:var(--dca-white);box-shadow:0 0 10px rgba(var(--dca-shadow),.25);transform:rotate(45deg) translateZ(0)}.cms-pagetree-dropdown-menu .cms-pagetree-dropdown-menu-inner{margin:0;padding:0!important;border-radius:5px;background-color:var(--dca-white)}.cms-pagetree-dropdown-menu-open .cms-pagetree-dropdown-menu{display:block}.cms-pagetree-dropdown-menu-arrow-block-start-inline-end:before{margin-block-start:-5px;margin-inline-start:-24px}.cms-pagetree-dropdown-menu-arrow-inline-end-block-start:before{inset-block-start:16px}.cms-pagetree-dropdown-menu-arrow-inline-end-block-end:before{inset-block-end:18px}.cms-pagetree-dropdown-loader{position:absolute;inset-block-start:0;inset-inline-start:0;inset-inline-end:0;inset-block-end:0;border-radius:5px}.cms-pagetree-node-state{box-sizing:border-box;display:inline-block;width:16px;height:16px;border-radius:100%;border:2px solid var(--dca-white);background:var(--dca-white);vertical-align:top}.cms-btn .cms-pagetree-node-state{vertical-align:middle;margin-right:5px;margin-top:-4px}.cms-btn:hover .cms-pagetree-node-state-dirty{box-shadow:0 0 0 1px #fff}.cms-pagetree-node-state-empty{border-color:var(--dca-gray-light);background-color:var(--dca-white)}.cms-pagetree-node-state-unpublished,.cms-pagetree-node-state-unpublished-parent{border-color:var(--dca-gray-light);background-color:var(--dca-gray-light)}.cms-pagetree-node-state-archived{border-color:var(--dca-gray-light);background-color:var(--dca-gray-lighter)}.cms-pagetree-node-state-public,.cms-pagetree-node-state-published{border-color:#693;background-color:#693}.cms-pagetree-node-state-draft{border-color:var(--dca-primary);background-color:var(--dca-white);border-width:2px}.cms-pagetree-node-state-dirty{animation:pulsate 2.5s ease-out infinite;border-color:var(--dca-primary);background-color:var(--dca-primary)}.cms-pagetree-node-state-deletion{border-color:red;background-color:red}@keyframes pulsate{0%{opacity:.5}50%{opacity:1}100%{opacity:.5}}.cms-pagetree-legend{position:relative;text-align:end}.cms-pagetree-legend .cms-icon-info{color:var(--dca-primary);vertical-align:middle;margin-inline-end:5px}.cms-pagetree-legend .cms-pagetree-dropdown{display:inline-block}.cms-pagetree-legend .cms-pagetree-dropdown-menu{inset-block-start:auto;inset-inline-end:100%;inset-block-end:0;inset-inline-start:auto;margin-inline-end:10px;margin-block-end:-90px}.cms-pagetree-legend .cms-pagetree-dropdown-menu:before{inset-block-end:96px}.cms-pagetree-legend .cms-pagetree-dropdown-item .cms-pagetree-node-state{margin-inline-end:10px}.cms-pagetree-legend .cms-pagetree-dropdown-item .cms-icon{margin-inline-end:8px}.cms-pagetree-legend .cms-pagetree-dropdown-item .cms-icon-apphook,.cms-pagetree-legend .cms-pagetree-dropdown-item .cms-icon-home,.cms-pagetree-legend .cms-pagetree-dropdown-item .cms-icon-sitemap{color:var(--dca-gray-light)}.cms-pagetree-legend .cms-icon{text-align:center;width:20px;margin-inline-start:-2px}.cms-pagetree-legend .cms-icon-check-square{color:#693;font-size:14px}.cms-pagetree-legend .cms-icon-minus-square{font-size:14px}.cms-pagetree-legend-title-wrap{cursor:pointer}.cms-pagetree-legend-title{color:var(--dca-gray-light);vertical-align:middle;text-transform:uppercase}.cms-tooltip,.cms-tooltip-touch{visibility:hidden;position:absolute;top:0;left:0;font-size:12px;line-height:22px;font-weight:700;color:var(--dca-gray-lighter);white-space:nowrap;padding:0 7px 0 22px;margin:0;border-radius:3px;background:var(--dca-gray-darker)}.cms-tooltip-touch:before,.cms-tooltip:before{content:"\E016"}.cms-tooltip-touch:before,.cms-tooltip:before{font-size:14px;top:50%;left:4px;margin-top:-7px;position:absolute}.cms-tooltip span,.cms-tooltip-touch span{position:absolute;top:100%;right:0;margin-top:-4px;float:right;color:var(--dca-white);font-weight:400;padding:0 7px;border-radius:3px 0 3px 3px;background:var(--dca-gray-darker)} \ No newline at end of file diff --git a/unfold_extra/static/unfold_extra/cms/css/cms.pagetree.css.map b/unfold_extra/static/unfold_extra/cms/css/cms.pagetree.css.map deleted file mode 100644 index 9fd77f4..0000000 --- a/unfold_extra/static/unfold_extra/cms/css/cms.pagetree.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sourceRoot":"","sources":["../../../src/sass/cms.pagetree.scss","../../../src/sass/settings/_cms.scss","../../../src/sass/components/_iconography.scss","../../../src/sass/components/_button.scss","../../../src/sass/mixins/_buttons.scss","../../../src/sass/components/pagetree/_tree.scss","../../../src/sass/settings/_pagetree.scss","../../../src/sass/components/pagetree/_dropdown.scss","../../../src/sass/components/pagetree/_node-state.scss","../../../src/sass/components/pagetree/_legend.scss","../../../src/sass/components/_tooltip.scss"],"names":[],"mappings":"AACA;AAAA;AAAA;ACQA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;;;AAGJ;EACI;IACI;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IAEA;IACA;;;ACnER;EACI;EACA;EACA;EAKA;EACA;;AAGJ;AAAA;EACI;EACA;EACA;EACA;EACA;EACA;;;AAgNJ;EACI;;;AAGA;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;AADJ;EACI;;;ACvOR;ECQI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EAGI;EAEI;EACA;;AAQR;EAEI;EACA;EACA;EACA;EAEA;;AAEA;EAGI;EACA;EACA;EACA;;AAGR;EAEI;;AAIA;EAMI;EACA;EAEI;EAKJ;EACA;;AACA;EAEQ;;ADpEpB;ECII;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EAGI;EAKI;EACA;EACA;;AAIR;EAEI;EACA;EACA;EACA;EAEA;;AAEA;EAGI;EACA;EACA;EACA;;AAGR;EAEI;;AAIA;EAMI;EACA;EAII;EACA;EAEJ;EACA;;AACA;EAIQ;EACA;;ADpEpB;ECCI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EAGI;EAKI;EACA;EACA;;AAIR;EAEI;EACA;EACA;EACA;EAEA;;AAEA;EAGI;EACA;EACA;EACA;;AAGR;EAEI;;AAIA;EAMI;EACA;EAII;EACA;EAEJ;EACA;;AACA;EAIQ;EACA;;ADhEpB;EACI;;;AETJ;EACE,oBJ+Qe;;;AI3QjB;EACE;EACA;EACA;EACA,WJmSyB;EIlSzB;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;AACA;EAHF;IAII;;;;AAMJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAMF;AAAA;EAEE;EAEA;EACA;;;AAGF;EACE,QC1DgB;;AD6DhB;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAIJ;EACE;;AAWJ;EACE;EACA;EACA;EACA;EACA;EACA,YC1GuB;ED2GvB,eCzG8B;ED0G9B,gBCzG+B;ED0G/B;EACA;EACA,YJ7BoB;;AI+BpB;EACE;EACA;EACA;EACA;EACA;EACA;EACA,kBJ1HI;;AI6HN;EACE;;AAGF;EACE;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;EACA,aC5IuB;ED6IvB,QC7IuB;;AD+IvB;EACE,aChJqB;EDiJrB;;AAEA;EACE;;;AAKN;EACE;EACA;EACA;;AACA;EAJF;IAKI;;;;AAIJ;EACE;EACA;EACA;;AACA;EAJF;IAKI;;;;AAIJ;EACE;;;AAGF;EACE;EACA;;AAEA;EACE;;AAGF;EARF;IASI;;;;AAKF;EADF;IAEI;IACA;;;;AAKF;EACE;;;AAKJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EAVF;IAWI;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,eJ9HiB;;AIgIjB;EACE;;AAGF;EAEE;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;;AAGF;EACE;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EAGE,OJxNY;;AI2Nd;EACE;EACA,OJtNc;;;AI0NlB;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,YCvS8B;EDwS9B,YCnToB;EDoTpB;EACA,eJ3MmB;EI4MnB,YJnTM;EIoTN;;AAEA;EACE,OJ3Oc;EI4Od;EACA;EACA;EACA;EACA;;AAGF;AAAA;EAEE;EACA;EACA;EACA;;AAIF;EACE,OJ1Pc;;AI6PhB;EACE,OJxQY;EIyQZ;;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EAGE,OJtRY;;AIyRd;EACE;EACA,OJpRc;;;AIwRlB;EACE;EACA;EACA,gBCjWsB;;;ADqWxB;EACE;EACA;EACA;EACA;EACA,qBC1WsB;ED2WtB,mBC3WsB;;AD6WtB;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;;;AAIJ;EACE;EACA;EACA;EACA;;;AAIF;EACE;EACA;;;AAMF;EACE;EACA;EACA,YC7XwB;ED8XxB,eCnY+B;EDoY/B,gBCnYgC;EDoYhC;EACA;EACA,YJxZM;;AI0ZN;AAAA;EAEE;EACA;EACA;;AAGF;EACE;EACA;EACA,OJnVc;EIoVd;;AAEA;EAEE;;AAIJ;EACE;;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;;;AAIJ;EACE,eC1ayC;ED2azC,gBC1a0C;;;AD6a5C;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA,YJvYgB;;;AI4YlB;EAEE;EACA;EACA;EACA;;;AAGF;EACE;EACA,WCpa2B;EDqa3B,QCra2B;;ADua3B;EAEE;;;AAIJ;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA,QCxb2B;;AD0b3B;EACE;;;AAKJ;EACE;EACA;;;AAGF;EACE;EACA,WCvc2B;;ADyc3B;EACE;EACA;EACA;EACA,WC7cyB;;ADgd3B;EACE;;;AAIJ;EACE;EACA;EACA;;;AAGF;EACE;;AAEA;EAEE;;;AAIJ;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAIA;EAEE;;;AHhVA;EACI;;;AG0VR;EACE;;AAEA;EACE;;AAGF;EACE,OJvfc;;AI0fhB;EACE,OJ1fc;;;AIigBlB;EACE;;AAEA;EAEE,WCtiBiB;EDuiBjB,aCviBiB;EDwiBjB,QCxiBiB;EDyiBjB;;AAEA;EACE;EACA,OJ7gBY;;AIghBd;EACE,OJhmBE;;AIqmBN;EACE,OJtmBI;;;AI6mBR;EAEE;;AAEA;EACE;EACA,QChkBiB;;;ADukBrB;EACE;;AAEA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA,OJ1jBc;;AI6jBhB;EACE,OJvkBY;;;AI8kBhB;EACE;;AAEA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;;AAGF;EACE,OJtlBc;EIulBd;EACA;;AAGF;EACE,OJ9lBc;EI+lBd;;AAGF;EACE;EACA;;;AAOJ;EACE;EACA;;AAEA;EAEE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;;AAIF;EACE;;AAGF;EACE;;;AAIJ;EACE;;AAEA;EAEE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;;;AAQJ;EACE,eC/uB8B;EDgvB9B,gBC/uB+B;EDgvB/B;;AAEA;EACE;;;AAUJ;EA8DE;;AA5DA;AAAA;AAAA;AAAA;EAKE;EACA;EACA;EACA;;AAGF;EACE;;AAEA;EACE;EACA;EACA;;AAIJ;EACE;;AAIF;AAAA;EAEE;EACA;EAIA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;;AAIF;AAAA;EAEE;EACA;;AAGF;EAEE;;;AAQJ;EACE;EACA;EACA;;;AAOF;EACE,YJlwBgB;;;AI2wBhB;EACE,WJ1vBc;EI2vBd,OJ9wBc;EI+wBd;EACA,eC5zBmC;ED6zBnC,gBC5zBoC;ED6zBpC;EACA,YJ/1BI;;AIk2BN;EACE;EACA;;AAIF;EACE;;AAKF;EACE,qBCv0BuB;;AD00BzB;EACE;;AAGF;EACE;EACA;EACA,oBCj1BuB;EDk1BvB;EACA,OJ5yBc;EI6yBd;EACA;EACA;EACA;;AAEA;EAEE;;AAKF;EAEE;;AAIJ;AAAA;EAEE;;AH7qBA;EACI;;AGkrBJ;EACE;;AHprBF;EACI;;AADJ;EACI;;AG8rBJ;EACE;;AAOJ;EACE;EACA;EACA;EACA;EACA,OJ91Bc;EIi2Bd;EACA,eCh5B4B;EDi5B5B,gBCh5B6B;EDi5B7B;EACA;EACA;EACA;EACA,kBJz7BI;EI07BJ;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EAEE;;AAGF;EACE,OJ33BY;;AI+3BhB;EACE;EACA;;AAMA;AAAA;AAAA;EACE,gBCh7BqC;EDi7BrC,eCl7BoC;;ADs7BxC;EACE;;AAIF;EACE,QCt8BmB;EDu8BnB;EACA,YJp+BI;;AIs+BJ;EACE;;AAIF;EACE;EACA;EACA;;AAIJ;EACE;EACA,eC18B0B;ED28B1B,gBC18B2B;ED28B3B;;AAGF;EACE;EACA;;AAIF;AAAA;EAEE;;AAIF;EACE;;AAEA;EACE,WJ16BY;EI26BZ;;AAGF;EACE;;AAIF;EACE,WJp7BY;EIq7BZ,oBCn+BiB;EDo+BjB,qBCp+BiB;;;AD4+BrB;EACE;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;;;AAQF;EACE;EACA;EACA;EACA,OJ9+BY;;AC4JZ;EACI;;;AG61BJ;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EAEE;;AAKN;AAAA;EAEE,kBAtBU;;AA0BV;EACE;EACA;EACA;EACA,kBC9lCY;;ADgmCZ;EAEE;;AAKF;EACE;;AAGF;EAEE,kBA7CM;;AAkDZ;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAEA;EACE;EACA;EACA;;AAIJ;EAEE;;AAIJ;EACE,kBJzoCI;;AI2oCJ;EAEE;;AAIJ;EACE,oBCppCc;EDqpCd;EACA;EACA;;;AAOJ;EACE;EACA;EACA;EACA,QCpoCqB;EDqoCrB;EACA;EACA;EACA;EACA,OJrqCM;;AIuqCN;EACE,OJxqCI;EIyqCJ;EACA;EACA;EACA,eJrkCiB;EIskCjB,YJ9lCc;;AIkmChB;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;;;AAMF;EACE;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA,kBJzpCY;;AI4pCd;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;;AAOJ;EACE;;AAGF;EACE;;AAGF;AAAA;EAEE;EACA;;AAGF;EACE;;;AAKF;EAKE;;AAGF;EACE;;;AEnxCJ;EACI;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA,WN4Xa;EM3Xb,eNkGmB;EMjGnB,YNPI;EMQJ,YN0Xc;EMzXd;;AAEA;AAAA;EAEI;EACA,ON8DU;EM7DV;EACA;EACA;EACA,gBDL+B;;ACOnC;AAAA;AAAA;EAII;EACA,YN4CQ;;AM1CZ;EACI,2BN4Ee;EM3Ef,yBN2Ee;;AMzEnB;EACI,yBNwEe;EMvEf,uBNuEe;;AMrEnB;EACI;EACA,ON0CU;EMzCV;;AACA;EAEI;EACA;;AAIJ;AAAA;EAEI;EACA,gBDpC2B;;ACuCnC;EACI;;AAGJ;EACI;EACA,ONkBU;EMjBV;EACA;EACA;EACA;EACA;EACA;EACA,gBDpD+B;ECqD/B;EACA;;AAGJ;EACI;EACA;EACA;;AAEJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACI;;AAGR;EACI;EACA;;AAEJ;EACI;;AAGJ;EACI;;AAGJ;EACI;;AAIJ;EACI;EACA;EACA;EACA;EACA,ONwRc;EMvRd,QNuRc;EMtRd;EACA,kBNjHA;EMkHA,YNgRU;EM/QV;;AAGJ;EACI;EACA;EACA,eNjBe;EMkBf,kBN1HA;;;AM8HR;EACI;;;AAIA;EACI;EACA;;;AAKJ;EACI;;;AAIJ;EACI;;;AAIR;EACI;EACA;EACA;EACA;EACA;EACA,eNlDmB;;;AO/GvB;EACI;EACA;EACA,OFwDiB;EEvDjB,QFuDiB;EEtDjB;EAEA;EACA,YPDI;EOEJ;;;AAGA;EACI;EACA;EACA;;AAGA;EACI;;;AAMR;EACI,cP4DU;EO3DV,kBPpBA;;AOsBJ;EAEI,cPuDU;EOtDV,kBPsDU;;AOpDf;EACK,cPmDU;EOlDV,kBPiDU;;AO/Cd;EACI,cPuCQ;EOtCR,kBPsCQ;;AOpCZ;EACI,cPiCQ;EOhCR,kBPrCA;;AOuCJ;EACI;EACA,cP4BQ;EO3BR,kBP2BQ;;AOzBZ;EACI,cP2BO;EO1BP,kBP0BO;;;AOrBf;EACI;IACI;;EAEJ;IACI;;EAEJ;IACI;;;AClER;EACI;EACA;;AAEA;EACI,ORuEQ;EQtER;EACA;;AAGJ;EACI;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;;AACA;EACI;;AAIJ;EACI;;AAEJ;EACI;;AAEJ;AAAA;AAAA;EAGI,ORmDM;;AQ/Cd;EACI;EACA;EACA;;AAEJ;EACI,ORiCQ;EQhCR;;AAEJ;EACI;;;AAGR;EACI;;;AAEJ;EACI,OR8Bc;EQ7Bd;EACA;;;ACrDJ;AAAA;EAGI;EACA;EACA;EACA;EACA,WTgQgB;ES/PhB,aTiQkB;EShQlB;EACA,OTsEc;ESrEd;EACA;EACA;EACA,eT2FiB;ES1FjB,YTmEc;;ACmJd;AAAA;EACI;;AQrNJ;AAAA;EAEI,WTwPY;ESvPZ;EACA;EACA;EACA;;AAGJ;AAAA;EACI;EACA;EACA;EACA;EACA;EACA,OT9BA;ES+BA;EACA;EACA;EACA,YT8CU","file":"cms.pagetree.css"} \ No newline at end of file diff --git a/unfold_extra/static/unfold_extra/cms/fonts/django-cms-iconfont.eot b/unfold_extra/static/unfold_extra/cms/fonts/django-cms-iconfont.eot deleted file mode 100644 index 7b58dee..0000000 --- a/unfold_extra/static/unfold_extra/cms/fonts/django-cms-iconfont.eot +++ /dev/null @@ -1,1908 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - django-cms/cms/static/cms/fonts/5.0.0a1/django-cms-iconfont.eot at develop-4 · django-cms/django-cms - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- Skip to content - - - - - - - - - - - - -
-
- - - - - - - - - - - - - -
- -
- - - - - - - - -
- - - - - -
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - - - -
- Open in github.dev - Open in a new github.dev tab - Open in codespace - - - - - - - - - - - - - - - - - - -

Files

Latest commit

 

History

History
14.8 KB

django-cms-iconfont.eot

File metadata and controls

14.8 KB
-
- - - - -
- -
- -
-
- -
- -
-

Footer

- - - - -
-
- - - - - © 2025 GitHub, Inc. - -
- - -
-
- - - - - - - - - - - - - - - - - - - - -
- -
-
- - - diff --git a/unfold_extra/static/unfold_extra/cms/fonts/django-cms-iconfont.svg b/unfold_extra/static/unfold_extra/cms/fonts/django-cms-iconfont.svg deleted file mode 100644 index 633fd43..0000000 --- a/unfold_extra/static/unfold_extra/cms/fonts/django-cms-iconfont.svg +++ /dev/null @@ -1,1911 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - django-cms/cms/static/cms/fonts/5.0.0a1/django-cms-iconfont.svg at develop-4 · django-cms/django-cms - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- Skip to content - - - - - - - - - - - - -
-
- - - - - - - - - - - - - -
- -
- - - - - - - - -
- - - - - -
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - - - -
- Open in github.dev - Open in a new github.dev tab - Open in codespace - - - - - - - - - - - - - - - - - - -

Files

Latest commit

 

History

History
207 lines (207 loc) · 92.1 KB

django-cms-iconfont.svg

File metadata and controls

207 lines (207 loc) · 92.1 KB
-
- - - - -
- -
- -
-
- -
- -
-

Footer

- - - - -
-
- - - - - © 2025 GitHub, Inc. - -
- - -
-
- - - - - - - - - - - - - - - - - - - - -
- -
-
- - - diff --git a/unfold_extra/static/unfold_extra/cms/fonts/django-cms-iconfont.ttf b/unfold_extra/static/unfold_extra/cms/fonts/django-cms-iconfont.ttf deleted file mode 100644 index af50510..0000000 --- a/unfold_extra/static/unfold_extra/cms/fonts/django-cms-iconfont.ttf +++ /dev/null @@ -1,1908 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - django-cms/cms/static/cms/fonts/5.0.0a1/django-cms-iconfont.ttf at develop-4 · django-cms/django-cms - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- Skip to content - - - - - - - - - - - - -
-
- - - - - - - - - - - - - -
- -
- - - - - - - - -
- - - - - -
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - - - -
- Open in github.dev - Open in a new github.dev tab - Open in codespace - - - - - - - - - - - - - - - - - - -

Files

Latest commit

 

History

History
14.6 KB

django-cms-iconfont.ttf

File metadata and controls

14.6 KB
-
- - - - -
- -
- -
-
- -
- -
-

Footer

- - - - -
-
- - - - - © 2025 GitHub, Inc. - -
- - -
-
- - - - - - - - - - - - - - - - - - - - -
- -
-
- - - diff --git a/unfold_extra/static/unfold_extra/cms/fonts/django-cms-iconfont.woff b/unfold_extra/static/unfold_extra/cms/fonts/django-cms-iconfont.woff deleted file mode 100644 index d4bcddd..0000000 --- a/unfold_extra/static/unfold_extra/cms/fonts/django-cms-iconfont.woff +++ /dev/null @@ -1,1910 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - django-cms/cms/static/cms/fonts/5.0.0a1/django-cms-iconfont.woff at develop-4 · django-cms/django-cms - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- Skip to content - - - - - - - - - - - - -
-
- - - - - - - - - - - - - -
- -
- - - - - - - - -
- - - - - -
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - - - -
- Open in github.dev - Open in a new github.dev tab - Open in codespace - - - - - - - - - - - - - - - - - - -

Files

Latest commit

 

History

History
8.04 KB

django-cms-iconfont.woff

File metadata and controls

8.04 KB
-
- - - - -
- -
- -
-
- -
- -
-

Footer

- - - - -
-
- - - - - © 2025 GitHub, Inc. - -
- - -
-
- - - - - - - - - - - - - - - - - - - - -
- -
-
- - - diff --git a/unfold_extra/static/unfold_extra/cms/fonts/django-cms-iconfont.woff2 b/unfold_extra/static/unfold_extra/cms/fonts/django-cms-iconfont.woff2 deleted file mode 100644 index f5477bd..0000000 --- a/unfold_extra/static/unfold_extra/cms/fonts/django-cms-iconfont.woff2 +++ /dev/null @@ -1,1908 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - django-cms/cms/static/cms/fonts/5.0.0a1/django-cms-iconfont.woff2 at develop-4 · django-cms/django-cms - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- Skip to content - - - - - - - - - - - - -
-
- - - - - - - - - - - - - -
- -
- - - - - - - - -
- - - - - -
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - - - -
- Open in github.dev - Open in a new github.dev tab - Open in codespace - - - - - - - - - - - - - - - - - - -

Files

Latest commit

 

History

History
6.67 KB

django-cms-iconfont.woff2

File metadata and controls

6.67 KB
-
- - - - -
- -
- -
-
- -
- -
-

Footer

- - - - -
-
- - - - - © 2025 GitHub, Inc. - -
- - -
-
- - - - - - - - - - - - - - - - - - - - -
- -
-
- - - diff --git a/unfold_extra/static/unfold_extra/cms/img/pagetree/tree-li-drag.gif b/unfold_extra/static/unfold_extra/cms/img/pagetree/tree-li-drag.gif deleted file mode 100644 index e79bf6f..0000000 Binary files a/unfold_extra/static/unfold_extra/cms/img/pagetree/tree-li-drag.gif and /dev/null differ diff --git a/unfold_extra/static/unfold_extra/cms/js/pageselect_inline.js b/unfold_extra/static/unfold_extra/cms/js/pageselect_inline.js new file mode 100644 index 0000000..27c2585 --- /dev/null +++ b/unfold_extra/static/unfold_extra/cms/js/pageselect_inline.js @@ -0,0 +1,104 @@ +(function () { + "use strict"; + + var cmsSiteReloadPending = false; + + function deriveWidgetName(widget) { + // Django admin rewrites `__prefix__` in id/name attributes but leaves the + // JSON inside `') \ No newline at end of file diff --git a/unfold_extra/views.py b/unfold_extra/views.py new file mode 100644 index 0000000..0086407 --- /dev/null +++ b/unfold_extra/views.py @@ -0,0 +1,43 @@ +from django.apps import apps +from django.conf import settings +from django.utils.translation import activate, check_for_language +from django.views.i18n import LANGUAGE_QUERY_PARAMETER, set_language + + +def cms_set_language(request): + """Set Django's language and keep CMS ``UserSettings.language`` in sync.""" + param = getattr(settings, "LANGUAGE_QUERY_PARAMETER", LANGUAGE_QUERY_PARAMETER) + raw_language = request.POST.get(param) if request.method == "POST" else None + language = raw_language if raw_language and check_for_language(raw_language) else None + + if raw_language and param != LANGUAGE_QUERY_PARAMETER and param in request.POST: + post = request.POST.copy() + post[LANGUAGE_QUERY_PARAMETER] = raw_language + request._post = post + + if ( + language + and getattr(request, "user", None) + and request.user.is_staff + and apps.is_installed("cms") + ): + from cms.models import UserSettings + + UserSettings.objects.update_or_create( + user=request.user, + defaults={"language": language}, + ) + + if language and request.method == "POST": + # Keep the source language active so Django can translate the referer URL. + cookie_lang = request.COOKIES.get(settings.LANGUAGE_COOKIE_NAME) + if cookie_lang and check_for_language(cookie_lang): + activate(cookie_lang) + + response = set_language(request) + + # Make sure middleware writes the target language back to the cookie. + if language and request.method == "POST": + activate(language) + + return response diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..2c04cb1 --- /dev/null +++ b/uv.lock @@ -0,0 +1,637 @@ +version = 1 +revision = 2 +requires-python = ">=3.12" + +[[package]] +name = "asgiref" +version = "3.11.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/63/40/f03da1264ae8f7cfdbf9146542e5e7e8100a4c66ab48e791df9a03d3f6c0/asgiref-3.11.1.tar.gz", hash = "sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce", size = 38550, upload-time = "2026-02-03T13:30:14.33Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/0a/a72d10ed65068e115044937873362e6e32fab1b7dce0046aeb224682c989/asgiref-3.11.1-py3-none-any.whl", hash = "sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133", size = 24345, upload-time = "2026-02-03T13:30:13.039Z" }, +] + +[[package]] +name = "certifi" +version = "2026.6.17" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/c7/424b75da314c1045981bd9777432fad05a9e0c69daa4ed7e308bbaffe405/certifi-2026.6.17.tar.gz", hash = "sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432", size = 134594, upload-time = "2026-06-17T10:31:07.894Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/2f/c5464532e965badff2f4c4c1a3a83f5697f0d7c407ed0cda44aaa99bb451/certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db", size = 133289, upload-time = "2026-06-17T10:31:06.348Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, + { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, + { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, + { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, + { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, + { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, + { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, + { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, + { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, + { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, + { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, + { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, + { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, + { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, + { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, + { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, + { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, + { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, + { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, + { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, + { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, + { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, + { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, + { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, + { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, + { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, + { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, + { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, + { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, + { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, + { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, + { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, + { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, + { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, + { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, + { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, + { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, + { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "django" +version = "6.0.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asgiref" }, + { name = "sqlparse" }, + { name = "tzdata", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/29/ac41e16097af67066d97a7d5775c5d8e7efc5d0284f6b0a159e07b9adb92/django-6.0.6.tar.gz", hash = "sha256:ad03916ba59523d781ae5c3f631960c23d69a9d9c43cecda52fc23b47e953713", size = 10905525, upload-time = "2026-06-03T13:02:46.503Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/50/23f9dc45483419a3cc2085b498b25adfbf10642b2941c73e6d2dfaffc9ab/django-6.0.6-py3-none-any.whl", hash = "sha256:25148b1194c47c2e685e5f5e9c5d59c78b075dfd282cb9618861ba6c1708f4d2", size = 8373354, upload-time = "2026-06-03T13:02:41.72Z" }, +] + +[[package]] +name = "django-classy-tags" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/91/7f/18da9d8fcc469fc7aa74abc526aacaa3cc7180ad2208f80d43e8b670aed6/django-classy-tags-4.1.0.tar.gz", hash = "sha256:c8d9d1aa2fa6e71c4d866df4dd11d23a69b8d25bbb750b2490a17b161774ee59", size = 24692, upload-time = "2023-07-29T09:06:18.646Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/10/13afc12b7d9657ed67f8d49c1cf0585a4331b197ed98ea654e9ed8034e79/django_classy_tags-4.1.0-py3-none-any.whl", hash = "sha256:1c784cf1bac49c20a77b8f7d1541867c64076642a160a847ff449588d4e01e55", size = 14095, upload-time = "2023-07-29T09:06:16.951Z" }, +] + +[[package]] +name = "django-cms" +version = "5.0.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, + { name = "django-classy-tags" }, + { name = "django-formtools" }, + { name = "django-sekizai" }, + { name = "django-treebeard" }, + { name = "djangocms-admin-style" }, + { name = "packaging" }, + { name = "setuptools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d0/03/78e5ef5dcec25ac1825bb8bdb6c09dfdb1ea1e28aec3061373715fb3d2eb/django_cms-5.0.8.tar.gz", hash = "sha256:ad3c640cfd784d93f0fb33fd3ba8ecfbd024c2e65b01a86a14379a0a5a895283", size = 5159477, upload-time = "2026-06-10T13:48:06.983Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/5f/a3ad0695c0b943f9b5f955308ad65b31cde2256a29faf71aa927a48203af/django_cms-5.0.8-py2.py3-none-any.whl", hash = "sha256:9a3af14cde4d2d94331b93177d611bac682a9d39f47c906c14e5ede3f2892ded", size = 3103027, upload-time = "2026-06-10T13:48:04.085Z" }, +] + +[[package]] +name = "django-formtools" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7a/fe/0b3c56822d4ba217a169d5a6493b4e3610c6efcda3081d68dcbb2a3dd994/django_formtools-2.6.1.tar.gz", hash = "sha256:603ebc69a4b76b9bf38534f6e0245face007c7ed31bdfa073f8bf45cff5d7122", size = 91174, upload-time = "2026-05-16T13:30:07.26Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/b8/0f711b0953d8234c6084ab071108a73b755da81de445e1296d83f0261ab1/django_formtools-2.6.1-py3-none-any.whl", hash = "sha256:2848516dc66a7acde1cb595d225b30c1b837528948cad32bf0faafc6d8cc7af8", size = 157971, upload-time = "2026-05-16T13:30:06Z" }, +] + +[[package]] +name = "django-fsm-2" +version = "4.2.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, + { name = "django-stubs-ext" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fa/eb/594712952364f0b1f009b9c414940a09031d07fe95bb888e92f50aa9a550/django_fsm_2-4.2.4.tar.gz", hash = "sha256:0e395afd03ff504afd476e8568ca213aafc3324bd4e5427e5b3287f7b1e7d979", size = 21544, upload-time = "2026-03-16T20:33:33.476Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/f6/75b74d0b631abfccfb71dda88b530ccccfba48cd6db674f7c27f027b827b/django_fsm_2-4.2.4-py3-none-any.whl", hash = "sha256:0072be359520075eee4baead3873e5eb8f1700b4f91e41208058cbea0ceae9b9", size = 23282, upload-time = "2026-03-16T20:33:31.97Z" }, +] + +[[package]] +name = "django-parler" +version = "2.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8c/2b/2423d31620efe8ab0d0390e60afab4f9cc2e62d4bf39fe0e05df0eef1b93/django-parler-2.3.tar.gz", hash = "sha256:2c8f5012ceb5e49af93b16ea3fe4d0c83d70b91b2d0f470c05d7d742b6f3083d", size = 69167, upload-time = "2021-11-18T09:32:28.959Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/38/11f1a7e3d56f3a6b74cf99e307f2554b741cadebc9b1c45b05e2ec1f35a2/django_parler-2.3-py3-none-any.whl", hash = "sha256:8f6c8061e4b5690f1ee2d8e5760940ef06bf78a5bfa033d11178377559c749cf", size = 83288, upload-time = "2021-11-18T09:32:27.193Z" }, +] + +[[package]] +name = "django-sekizai" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, + { name = "django-classy-tags" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/be/b7/399f7d84fa34c67e7c8e1da39744c9a2f83b61fa600c854cbe43bf6aab56/django-sekizai-4.1.0.tar.gz", hash = "sha256:2aca36cbae0b5c0cefed9565416ec442335767fb3145bff11e58622fc653cdad", size = 14591, upload-time = "2023-05-02T15:39:23.417Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/4f/8d1df68be504d7664cdeae7279134e77cf15babe2a5911b50f3e31d493f2/django_sekizai-4.1.0-py3-none-any.whl", hash = "sha256:aa12e66ba0335fbe726b7d74cf4e8716b89a0be99a1304a9b9e8b191229e2e4a", size = 8597, upload-time = "2023-05-02T15:39:21.655Z" }, +] + +[[package]] +name = "django-stubs-ext" +version = "6.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d8/01/419bc3cd882f3ec645d5a4511085202dd6bd3ef8d385871dcd2d32cc15b3/django_stubs_ext-6.0.5.tar.gz", hash = "sha256:1cc325e991303849bce70e19748981b225ef08b37256f263e113caf97cd3bcc0", size = 6666, upload-time = "2026-05-25T08:46:36.012Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/bf/7ee71071d84ad4e0efcd77e2681afed254a5f65c27524441a9caf8c60467/django_stubs_ext-6.0.5-py3-none-any.whl", hash = "sha256:a9932c8233d6dd4e34ae0b192026379c1a9be8f0b7c27aa1fa09ae215169773e", size = 10362, upload-time = "2026-05-25T08:46:34.467Z" }, +] + +[[package]] +name = "django-treebeard" +version = "4.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7a/d2/615c2d33b3b55e106ee590b164c0d4a8372527293c85c61175e81ded2aea/django_treebeard-4.8.0.tar.gz", hash = "sha256:61b8076b576107da21f6f6040774c0d17025200c2efdb70dd1f14b18c9206c3a", size = 292517, upload-time = "2025-12-03T03:58:40.625Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/3a/3731f6d5696b5dfd1c7830a64450e1f49b512cb192b2d3e37b72cc75470b/django_treebeard-4.8.0-py3-none-any.whl", hash = "sha256:ecaa7a0bbfa86a48fc91ff335513e83c841cdfe1add6229d04ac7096198d5d51", size = 70997, upload-time = "2025-12-03T03:58:37.292Z" }, +] + +[[package]] +name = "django-unfold" +version = "0.93.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/55/0649c4e7b540528f80078c300db7ae299502f3c2fccd4d6882193fa104d1/django_unfold-0.93.0.tar.gz", hash = "sha256:bce719989d199484c989e4042f338c287394c4562af05785f4fa511a8e3be859", size = 1124076, upload-time = "2026-05-11T10:13:07.855Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/6c/c6c63af9eaabf85877005ba307a7af80088e9d28efd77c6431ec8c61c3c3/django_unfold-0.93.0-py3-none-any.whl", hash = "sha256:d0a09ebe906278fa994d6eb808880ba3f24c20d8c3a5ca37f82ee89969fd459c", size = 1249541, upload-time = "2026-05-11T10:13:09.185Z" }, +] + +[[package]] +name = "django-unfold-extra" +source = { virtual = "." } +dependencies = [ + { name = "django-cms" }, + { name = "django-parler" }, + { name = "django-unfold" }, +] + +[package.optional-dependencies] +test = [ + { name = "pytest" }, + { name = "pytest-django" }, + { name = "pytest-env" }, +] + +[package.dev-dependencies] +dev = [ + { name = "ruff" }, +] +test = [ + { name = "djangocms-versioning" }, + { name = "pytest" }, + { name = "pytest-django" }, + { name = "pytest-env" }, + { name = "pytest-playwright" }, +] + +[package.metadata] +requires-dist = [ + { name = "django-cms", specifier = ">=5.0,<5.1" }, + { name = "django-parler", specifier = "~=2.3" }, + { name = "django-unfold", specifier = ">=0.92" }, + { name = "pytest", marker = "extra == 'test'", specifier = ">=8.0" }, + { name = "pytest-django", marker = "extra == 'test'", specifier = ">=4.5" }, + { name = "pytest-env", marker = "extra == 'test'", specifier = ">=1.0" }, +] +provides-extras = ["test"] + +[package.metadata.requires-dev] +dev = [{ name = "ruff", specifier = ">=0.4" }] +test = [ + { name = "djangocms-versioning", specifier = ">=2.5.1" }, + { name = "pytest", specifier = ">=8.0" }, + { name = "pytest-django", specifier = ">=4.5" }, + { name = "pytest-env", specifier = ">=1.0" }, + { name = "pytest-playwright", specifier = ">=0.7.2,<0.8" }, +] + +[[package]] +name = "djangocms-admin-style" +version = "3.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6c/1e/47b2d031c6d8f4ac5394b4b3dc863a8f21fd6d92aadf260ba1fc633b286f/djangocms-admin-style-3.3.1.tar.gz", hash = "sha256:da20de3831f1a1a74de33fe12bbd3865a0027a982878c02aa1d102b1c799d767", size = 319126, upload-time = "2024-02-29T22:12:45.167Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/f2/e0ed0cf8036708f7b7a26a4586a27b5e53891cf8d965f1e4751bf4f50de3/djangocms_admin_style-3.3.1-py3-none-any.whl", hash = "sha256:9b76aafda245205e0a88d57efd5097a96c1a7fcb78d7f3efa50955eb980079de", size = 408789, upload-time = "2024-02-29T22:12:42.557Z" }, +] + +[[package]] +name = "djangocms-versioning" +version = "2.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django-cms" }, + { name = "django-fsm-2" }, + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/45/b5/1d45a428b77d2c111cec40127d7332d82ee01e08507effca86a7bc6ef81d/djangocms_versioning-2.5.1.tar.gz", hash = "sha256:8e4d69e583918a8bb6264049a865c68abd2adc8cd14aa150b8ab1ad4d852a226", size = 575272, upload-time = "2026-02-09T16:27:03.595Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/63/68ef96671ceacdc00667170f5e57063d0919cdf6dd19dcd7cd8fd0e6a3ae/djangocms_versioning-2.5.1-py3-none-any.whl", hash = "sha256:f396014400dab3a561227d5395496aec40666f5e36aa8337f99aff8752249581", size = 609196, upload-time = "2026-02-09T16:27:01.669Z" }, +] + +[[package]] +name = "greenlet" +version = "3.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dd/8b/befc3cb36965f397d87e86fb3b00e3ec0dc67c1ecb0986d7f54ee528f018/greenlet-3.5.2.tar.gz", hash = "sha256:c1b906220d83c140361cdd12eef970fb5881a168b98ee58a43786426173da14c", size = 199243, upload-time = "2026-06-17T20:19:01.317Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/7a/6bc2a7835731387ed303b9390ce68a116ab053df05450a59181239200454/greenlet-3.5.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:76dae33e97b52743a19210931ee3e78a88fe1438bc2fc4ee5e7512d289bfad4f", size = 288351, upload-time = "2026-06-17T17:36:17.019Z" }, + { url = "https://files.pythonhosted.org/packages/57/1b/bd98062fcef6d0e9d0873ab6f2d029772e6ea342972ae43275bd6177900f/greenlet-3.5.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:30252d191d6959df1d040b559a38fc017139606c5ecc2ad00416557c0355d742", size = 604273, upload-time = "2026-06-17T18:07:20.296Z" }, + { url = "https://files.pythonhosted.org/packages/25/e6/fe392c522bf45d976abe7db2793f6ef4e87b053ebb869deeaae46aeb54da/greenlet-3.5.2-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1adc23c50f22b0f5979521909a8360ab4a3d3bef8b641ce633a04cf1b1c967ea", size = 616536, upload-time = "2026-06-17T18:29:43.205Z" }, + { url = "https://files.pythonhosted.org/packages/42/df/cdb1f75f07214f13110e7e3879531f11c26083bd480a56a9474c430ec44c/greenlet-3.5.2-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:87359c23eb4e8f1b16da68faad29bf5aeb80e3628d7d8e4aa2e41c36879ddedd", size = 621843, upload-time = "2026-06-17T18:39:27.507Z" }, + { url = "https://files.pythonhosted.org/packages/68/4a/399ff81fa93a19d6a9df394cef0355f082dbc19ad41aba9593cd0ad444e2/greenlet-3.5.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f052fff492c52fdfa99bd3b3c1389a53de37dae76a0562741417f0d018f02b3", size = 613749, upload-time = "2026-06-17T17:39:28.148Z" }, + { url = "https://files.pythonhosted.org/packages/2e/25/36a3628a7edcfeefddd3101dc88039c79721c5f8d688db7ebed1cbaaa789/greenlet-3.5.2-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:f4d67c1684db3f9782c37ee4bade3f86f5a23a8fcf3f8359224106018ca40728", size = 424889, upload-time = "2026-06-17T18:41:19.469Z" }, + { url = "https://files.pythonhosted.org/packages/a5/75/f519593f12ad43d08e28c03a95cfe2eeae011707dbc9dab0c4a263ce90f9/greenlet-3.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:120b77c2a18ebf629c3a7886f68c6d01e065654844ad468f15bb93ace66f2094", size = 1573725, upload-time = "2026-06-17T18:22:12.023Z" }, + { url = "https://files.pythonhosted.org/packages/f1/bc/bc1ea4b0754c6c51bbf9d94677b0b1f7fbda8cbb404e44a896854fc0a940/greenlet-3.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a850f6224088ef7dcc70f1a545cb6b3d119c35d6dca63b925b9f35da0635cdad", size = 1638132, upload-time = "2026-06-17T17:40:06.971Z" }, + { url = "https://files.pythonhosted.org/packages/36/c0/f0f5a34247df60de285f75f22e57f14027f4b3c43820981854b5b643ca6d/greenlet-3.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:89da99ee8345b458ea2f16831dad31c88ddcdec454b48704d569a0b8fb28f146", size = 239393, upload-time = "2026-06-17T17:33:47.09Z" }, + { url = "https://files.pythonhosted.org/packages/09/17/a8544e165445f30aea67a8d9cf2786d2bb0eb1b0e0d224b4d9bd80e2d587/greenlet-3.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:ca92411942154023c65851e6077d8ca0d00f19de5fa80bb2c6f196ff6c920ba9", size = 237723, upload-time = "2026-06-17T17:36:47.776Z" }, + { url = "https://files.pythonhosted.org/packages/d0/3c/bb37b9d40d65b0741a8b040ca5c307034d0a9822994dff5f825c88dd7a6b/greenlet-3.5.2-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:0629377725977252159de1ebd3c6e49c170a63856e585446797bb3d66d4d9c34", size = 287178, upload-time = "2026-06-17T17:35:25.132Z" }, + { url = "https://files.pythonhosted.org/packages/f0/a6/0c5902393f492f8ceb19d0b5cf139284e3a11b333a049739643b1036b6f8/greenlet-3.5.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2ddf9eddc617681108dd071b3feabf3f4a4cd64846254aec4d4ceda098b639a", size = 606900, upload-time = "2026-06-17T18:07:21.692Z" }, + { url = "https://files.pythonhosted.org/packages/d8/7c/42899c31d4b87148ae4e3f87f63e13398824be6241f4dde42ded95768a34/greenlet-3.5.2-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f41feb9f2b59e2e61ac9bea4e344ddd9396bf3cacb2583f73a3595ed7df6f8e7", size = 619265, upload-time = "2026-06-17T18:29:44.837Z" }, + { url = "https://files.pythonhosted.org/packages/6a/7e/28f991affb413b232b1e7d768db24c37b3f4d5daecc3f19b455d40bd2dea/greenlet-3.5.2-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9dc23f0e5ad76415457212a4b947d22ebe4dc80baf02adf7dd5647a90f38bb4e", size = 625044, upload-time = "2026-06-17T18:39:29.046Z" }, + { url = "https://files.pythonhosted.org/packages/d3/52/4ff8c98d3cfe62b4515f8584ae14510a58f35c549cc5292b78d9b7a40b70/greenlet-3.5.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:09201fa698768db245920b00fdc86ee3e73540f01ca6db162be9632642e1a473", size = 616187, upload-time = "2026-06-17T17:39:29.473Z" }, + { url = "https://files.pythonhosted.org/packages/29/05/0cc9ec660e7acff85f93b0a048b6654371c822c884add44c02a465cf70e0/greenlet-3.5.2-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:423167363c510a75b649f5cd58d873c29498ea03598b9e4b1c3b73e0f899f3d5", size = 427322, upload-time = "2026-06-17T18:41:20.892Z" }, + { url = "https://files.pythonhosted.org/packages/c9/a6/269c8bf9aefc13361ce1088f0e392b154cb21005de7862e42b5d782b81fd/greenlet-3.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a1759fa4f14c398508cf20dc8037de55cc23ae8bd14c185c2718257837195ca5", size = 1573778, upload-time = "2026-06-17T18:22:13.497Z" }, + { url = "https://files.pythonhosted.org/packages/1f/9b/391d015cbc6323e81b14c02cf825fdca7e0049c9bb489bf4ac72883118ba/greenlet-3.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b9318cdeb9abdbfdd8bc8464ee4a06dffde2c7846e1def138365a6240ab2c9a5", size = 1638092, upload-time = "2026-06-17T17:40:08.163Z" }, + { url = "https://files.pythonhosted.org/packages/49/53/5b4df711f4356c62e85d9f819d87966d526d1cfb32bae49a8f7d6fc36ea4/greenlet-3.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:2c3b3311af72b3d3b03cc0f1ffd11f072e834be5d0444105cf715fc44434e39c", size = 239352, upload-time = "2026-06-17T17:38:51.593Z" }, + { url = "https://files.pythonhosted.org/packages/bb/b6/18efc3a329ec035c3f344b8f2b60356451950ddf9b7b64ff00023778a1dd/greenlet-3.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:f9bbd6216c45a563c2a61e478e038b439d9f248bde44f775ea37d339da643af4", size = 237635, upload-time = "2026-06-17T17:35:36.632Z" }, + { url = "https://files.pythonhosted.org/packages/c7/89/aaafc8e14de4ac882e02ccb963225329b0e8578aba4365e71eb678e45722/greenlet-3.5.2-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:1c31219badba285858ba8ed117f403dea7fafee6bade9a1991875aae530c3ceb", size = 287676, upload-time = "2026-06-17T17:33:31.514Z" }, + { url = "https://files.pythonhosted.org/packages/b8/fc/2308249206c12ac70de7b9a00970f84f07d10b3cd60e05d2fbcaa84124e8/greenlet-3.5.2-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6f96ed6f4adc1066954ae95f45717657cb67468ef3b89e9a3632e14a625a8f39", size = 653552, upload-time = "2026-06-17T18:07:23.493Z" }, + { url = "https://files.pythonhosted.org/packages/7c/24/47730d1f8f1336b9b089237521ed7a26eee997065dcb4cab81cdca333abc/greenlet-3.5.2-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5795e883e915333c0d5648faaa691857fbc7180136883edc377f50f0d509c2a8", size = 665756, upload-time = "2026-06-17T18:29:46.616Z" }, + { url = "https://files.pythonhosted.org/packages/23/5c/2664d290cbd1fef9eb3f69b5d3bc5aa91b6fa907519298ca6af93a90c6cb/greenlet-3.5.2-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6e9e49d732ee92a189bb7035e293029244aeba648297a9b856dc733d17ca7f0d", size = 669989, upload-time = "2026-06-17T18:39:30.79Z" }, + { url = "https://files.pythonhosted.org/packages/99/69/d6c99db15dc0b5e892ac3cc7b942c8b21f4a9cc3bd9ea0bc3b0f339ffbd4/greenlet-3.5.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:26aed8d9503ca78889141a9739d71b383efea5f472a7c522b5410f7eb2a1b163", size = 663228, upload-time = "2026-06-17T17:39:31.073Z" }, + { url = "https://files.pythonhosted.org/packages/42/d4/fcb53fa9847d7fbd4723fbed9469c3869b9e3544c4e001d9d5aa2f66162d/greenlet-3.5.2-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:537c5c4f30395020bb9f48f53146070e3b997c3c75da14011ab732aaa19ce3ef", size = 472888, upload-time = "2026-06-17T18:41:22.511Z" }, + { url = "https://files.pythonhosted.org/packages/4f/88/9e603f448e2bc107c883e95817b980fb9b45ba6aea0299b2e9978124bea2/greenlet-3.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:dbebc038fcdda8f8f21cce985fd04e34e0f42007e7fc7ab7ad285caf77974b95", size = 1620723, upload-time = "2026-06-17T18:22:14.817Z" }, + { url = "https://files.pythonhosted.org/packages/11/91/26da17e3777858c16fdb8d020a4c68f3a03cb92f238de8f5351d5d5186e9/greenlet-3.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a207023f1cf8695fd82580b8099c09c5809be18bc2282362cdfb965dd884a317", size = 1684227, upload-time = "2026-06-17T17:40:09.536Z" }, + { url = "https://files.pythonhosted.org/packages/2d/44/b3a11f7aa34cb38f1b7f3df8bcd9fcd09bac9d342c2a2c9b8686c804bcd2/greenlet-3.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:c674a1dd4fe41f6a93febe7ab366ceabf15080ea31a9307811c56dac5f435f73", size = 240257, upload-time = "2026-06-17T17:35:23.359Z" }, + { url = "https://files.pythonhosted.org/packages/de/e3/3b62145fe917311732041a258adb218248add00542e3131c48bd047fbed5/greenlet-3.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:3c417cd6c593bbbef6f7aa31a79f37d3db7d18832fc56b694a2150130bde784e", size = 239038, upload-time = "2026-06-17T17:37:56.792Z" }, + { url = "https://files.pythonhosted.org/packages/47/ac/d3bad483e9f6cd1848604fdffa32cac25846dd6dfcec0e6f81c790185518/greenlet-3.5.2-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:a96457a30384de52d9c5d2fd33abf6c1daae3db392cd556738f408b1a79a1cf0", size = 295668, upload-time = "2026-06-17T17:36:02.293Z" }, + { url = "https://files.pythonhosted.org/packages/00/e9/3a7e557b895fd0469b00cd0b2bd498ba950e8bfdf6d7adeecf2c5e4130a6/greenlet-3.5.2-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4af5d4961818ab651d09c1448a03b1ba2a1726a076266ebb62330bab9f3238c", size = 652820, upload-time = "2026-06-17T18:07:24.95Z" }, + { url = "https://files.pythonhosted.org/packages/78/67/6225d5c5e4afc04be0fd161eec82e4b72017e8a100d222f25d7b42b0140d/greenlet-3.5.2-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a1789a6244ea1ba61fd4386c9a6a31873e9b0234762103364be98ef87dcb19f3", size = 658697, upload-time = "2026-06-17T18:29:48.365Z" }, + { url = "https://files.pythonhosted.org/packages/35/ad/9b3058f999b81750a9c6d9ec424f509462d232b58002086fe2ba63b66407/greenlet-3.5.2-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2ee6288f1933d698b4f098127ed17bda2910a75d2807915bd16294a972055d6c", size = 658945, upload-time = "2026-06-17T18:39:32.509Z" }, + { url = "https://files.pythonhosted.org/packages/fa/99/6324b8ef916dcaddccb340b304c992ca3f947614ce0f2685d438187300b8/greenlet-3.5.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3be00501fb4a8c37f6b4b3c4773808ceb26ea65c7ea64fd5735d0f330b3786de", size = 656436, upload-time = "2026-06-17T17:39:32.509Z" }, + { url = "https://files.pythonhosted.org/packages/92/75/1b6ecd8c027b69ab1b6798a84094df79aab5e69ac7e249c78b9d361dd1fa/greenlet-3.5.2-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:b4cad42662c796334c2d24607c411e3ed82481c1fb4e1e8ec3a5a8416060092e", size = 490529, upload-time = "2026-06-17T18:41:23.954Z" }, + { url = "https://files.pythonhosted.org/packages/a9/ee/f5bf9daac27c5e1b011965f64b5630a32b415daf7381b312943629e12c2a/greenlet-3.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1d554cd96841a68d464d75a3736f8e87408a7b02b1930a75fa32feb408ad62f8", size = 1617193, upload-time = "2026-06-17T18:22:16.252Z" }, + { url = "https://files.pythonhosted.org/packages/8a/21/b05d5b12715bda92ce27c118d64971d21e9b8f3563ed959a7d271e2d4223/greenlet-3.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3dff6cd3aac35f6cd3fc23460105acf576f5faf6c378de0bc088bf37c913864a", size = 1677512, upload-time = "2026-06-17T17:40:10.771Z" }, + { url = "https://files.pythonhosted.org/packages/b8/97/1b8f1314b868041b327dc1051603e8142b826480cb0ecb8a7b7632aee9c4/greenlet-3.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:36cfea2aa075d544617176b2e84450480f0797070ad8799a8c41ada2fe449d32", size = 243145, upload-time = "2026-06-17T17:34:37.502Z" }, + { url = "https://files.pythonhosted.org/packages/36/07/1b5311775e04c718a118c504d7a3a312430e2a1bd1347226aff4774e4549/greenlet-3.5.2-cp315-cp315-macosx_11_0_universal2.whl", hash = "sha256:a0314aa832c94633355dc6f3ee54f195159533355a323f26926fc63b98b2ccbb", size = 288315, upload-time = "2026-06-17T17:34:34.04Z" }, + { url = "https://files.pythonhosted.org/packages/ed/cc/6abcd2a486b58b9f77b7a93b690d59cb2c11a5906ed2ad4c63c7b9c1113d/greenlet-3.5.2-cp315-cp315-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24c59cb7db9d5c694cb8fd0c76eef8e456b2123afdfa7e4b8f2a67a0860d7682", size = 659130, upload-time = "2026-06-17T18:07:26.354Z" }, + { url = "https://files.pythonhosted.org/packages/f2/12/f4aaad6d3d383233f700ab322568a4f29f2c701a4861d85f4811d99689b2/greenlet-3.5.2-cp315-cp315-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7bb811753703739ad318112f16eccfaabdac050037b6d092debaa8b23566b4ce", size = 669724, upload-time = "2026-06-17T18:29:50.13Z" }, + { url = "https://files.pythonhosted.org/packages/53/e0/4ce3a046b51e53934eae93d7f9c13975a97285741e9e1fcadf8751314c37/greenlet-3.5.2-cp315-cp315-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2debcd0ef9455b7d4879589903efc8e497d4b8fb8c0ae772309e44d1ca5e957f", size = 673494, upload-time = "2026-06-17T18:39:34.196Z" }, + { url = "https://files.pythonhosted.org/packages/91/2a/a089811fc31c6bf8742f40a4e73470d6d401cef18e4314eb20dc399b377c/greenlet-3.5.2-cp315-cp315-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6d78b5c1c178dad90447f1b8452262709d3eef4c98f825569e74c9d0b2260ac9", size = 668089, upload-time = "2026-06-17T17:39:33.808Z" }, + { url = "https://files.pythonhosted.org/packages/52/e0/9c18721e63445dce02ee67e4c81c0f281626604ff55ae6f7b7f4354d7129/greenlet-3.5.2-cp315-cp315-manylinux_2_39_riscv64.whl", hash = "sha256:9558cae989faeab6fbb425cd98a0cfa4190a47fba6443973fbee0a1eb0b0b6c3", size = 479721, upload-time = "2026-06-17T18:41:25.726Z" }, + { url = "https://files.pythonhosted.org/packages/0f/1c/2f47c7d5fcfa98a62b705bf9a0505d86f4563c0d81cab1f7159ff1e743b7/greenlet-3.5.2-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:0977af2df83136f81c1f76e76d4e2fe7d0dc56ea9c101a86af26a95190b9ca32", size = 1625684, upload-time = "2026-06-17T18:22:17.664Z" }, + { url = "https://files.pythonhosted.org/packages/b9/bf/661dd24624f70b7b32972d7693d0344ecde10278f647d7b828baf739899c/greenlet-3.5.2-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:f9ed777c6891d8253e54468576f55e27f8fc1a662a664f946a191003574c0a74", size = 1688043, upload-time = "2026-06-17T17:40:12.403Z" }, + { url = "https://files.pythonhosted.org/packages/60/49/d9bde1d15a21296b3b521fe083eb8aabd54ac05d15de9832918f3d639543/greenlet-3.5.2-cp315-cp315-win_amd64.whl", hash = "sha256:c0ea4eb3de23f0bac1d75205e10ccfa9b418b17b01a2d7bf19e3b69dda08900a", size = 240531, upload-time = "2026-06-17T17:35:47.448Z" }, + { url = "https://files.pythonhosted.org/packages/7f/4d/86d7768bd53e9907de0333df215c2018cd01a593b3715cbd79aa82dd94b7/greenlet-3.5.2-cp315-cp315-win_arm64.whl", hash = "sha256:7a7bfc200be40d04961d7e80e8337d726c0c1a50777e588123c3ed8ba731dcb9", size = 239579, upload-time = "2026-06-17T17:39:39.954Z" }, + { url = "https://files.pythonhosted.org/packages/92/15/907be5e8900901039bae752fa9a31c03a3c1e064833f35a4e49449184581/greenlet-3.5.2-cp315-cp315t-macosx_11_0_universal2.whl", hash = "sha256:98a52d6a50d4deaba304331d83ee3e10ebbdc1517fcca40b2715d1de4534065c", size = 296697, upload-time = "2026-06-17T17:37:15.887Z" }, + { url = "https://files.pythonhosted.org/packages/95/5c/08c57be575c3d6a3c023bbf22144a1c7dc6ed4d134527bb36ded4dbf04a8/greenlet-3.5.2-cp315-cp315t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1587ff8b58fdf806993ed1490a06ac19c22d47b219c68b30954380029045d8d4", size = 656710, upload-time = "2026-06-17T18:07:28.046Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d0/749f917bdc9fc90fceea4aa65fbf6556e617a50714d1496bdc8ad190bb36/greenlet-3.5.2-cp315-cp315t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:feb721811d2754bfd16b48de151dd6b1f222c048e625151f2ca44cfdfd69f59c", size = 662629, upload-time = "2026-06-17T18:29:51.728Z" }, + { url = "https://files.pythonhosted.org/packages/55/87/10776cd88df54d0f563e9e21e98363f2d6af94bedc553b1da0972fa87f80/greenlet-3.5.2-cp315-cp315t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a9476cbead736dc48ce89e3cd97acff95ecc48cbf21273603a438f9870c4a014", size = 663191, upload-time = "2026-06-17T18:39:35.639Z" }, + { url = "https://files.pythonhosted.org/packages/5a/a5/68cefae3a07f6d0093a490cf28ab604f14578f3e60205a2a2b2d5cd70af2/greenlet-3.5.2-cp315-cp315t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7fe6062b1f35534e1e8fb28dfed406cf4eeff3e0bca3a0d9f8ff69f20a4abb00", size = 660147, upload-time = "2026-06-17T17:39:35.068Z" }, + { url = "https://files.pythonhosted.org/packages/02/aa/26ddf92826a99d87bfb8fdb8f3a262a6f16495a5d8e579737baa92fb4543/greenlet-3.5.2-cp315-cp315t-manylinux_2_39_riscv64.whl", hash = "sha256:5930d3946ecae99fa7fc0e3f3ae515426ad85058ebd9bfc6c00cca8016e6206b", size = 498199, upload-time = "2026-06-17T18:41:27.464Z" }, + { url = "https://files.pythonhosted.org/packages/d2/6b/b9156d8397e4750220f54c7c5c34650f1e740a8d2f66eab9cfd1b7b53b69/greenlet-3.5.2-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:b4ac902af825cbac8e9b2fccab8122236fd2ba6c8b71a080116d2c2ec72671b1", size = 1621675, upload-time = "2026-06-17T18:22:18.873Z" }, + { url = "https://files.pythonhosted.org/packages/b0/e3/d3250f4fa01c211a93d04e34fded63187e648dbec17b9b1a14d388040593/greenlet-3.5.2-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:6f1e473c06ae8be00c9034c2bb10fa277b08a93287e3111c395b839f01d27e1f", size = 1680577, upload-time = "2026-06-17T17:40:14.055Z" }, + { url = "https://files.pythonhosted.org/packages/55/ba/eaee8bda4419770d7096b5a009ebff0ab20a2a28cdd83c4b591bfdf36fa9/greenlet-3.5.2-cp315-cp315t-win_amd64.whl", hash = "sha256:3c2315045f9983e2e50d7e89d95405c21bddb8745f2da4487bc080ab3525f904", size = 243482, upload-time = "2026-06-17T17:37:34.741Z" }, + { url = "https://files.pythonhosted.org/packages/37/45/f794a81c91e9942c61f9110bd1f9a38a0ea565eab57f8b08cd53d3131e48/greenlet-3.5.2-cp315-cp315t-win_arm64.whl", hash = "sha256:db548d5ab6c2a8ead82c013f875090d79b5d7d2b67fc513934ce6cf66492ad7f", size = 242062, upload-time = "2026-06-17T17:35:39.814Z" }, +] + +[[package]] +name = "idna" +version = "3.18" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "packaging" +version = "26.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, +] + +[[package]] +name = "playwright" +version = "1.60.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "greenlet" }, + { name = "pyee" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/f0/832bd9677194908da118064eef20082f2791e3d18215cc6d9391ee2c5a67/playwright-1.60.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:6a8cd0fec171fb3089e95e898c8bc8a6f35dea0b78b399e12fcc19427e91b1d7", size = 43474635, upload-time = "2026-05-18T12:00:31.969Z" }, + { url = "https://files.pythonhosted.org/packages/59/7b/e1d32ae8a3ed937ec2be3721c5f728b13d731a0b7c6442e0b3bec5094ac0/playwright-1.60.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:39b5420ba6145045b69ced4c5c47d4d9fe5bddfc8ff816c518913afcb25ec7a5", size = 42261327, upload-time = "2026-05-18T12:00:35.638Z" }, + { url = "https://files.pythonhosted.org/packages/d7/bc/23de499ded6411c188a20c5a0dea6f0cd4ed5d2b3cc6042a5dbd3ed609aa/playwright-1.60.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:2581d0e6a3392c71f91b27460c7fd093356818dc430f48153896c8aeeaef7705", size = 43474636, upload-time = "2026-05-18T12:00:39.294Z" }, + { url = "https://files.pythonhosted.org/packages/22/7b/1d679f4fced4ea94efadd17103856d8c565384f68382a1681264e46f5925/playwright-1.60.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:1c2bfae7884fb3fb05b853290eab8f343d524e5016f2f1def702acbbdf14c93e", size = 47467220, upload-time = "2026-05-18T12:00:43.179Z" }, + { url = "https://files.pythonhosted.org/packages/84/c2/1528d267d4442bd2c6b8eaeab819dd52c2030bf80e89293f0ba1f687473b/playwright-1.60.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43e66564125ee31b07a58cefb21e256d62d67d8d1713e6858df7a3019d8ed353", size = 47154856, upload-time = "2026-05-18T12:00:46.715Z" }, + { url = "https://files.pythonhosted.org/packages/bb/4e/b008b6440a7a1624378041da94829956d4b8f7ab9ef5aad22d0dc3f2e26d/playwright-1.60.0-py3-none-win32.whl", hash = "sha256:ec94e416ea320711e0ad4bf185dcbf41833672961e90773e1885255d7db7b7e7", size = 37902157, upload-time = "2026-05-18T12:00:50.374Z" }, + { url = "https://files.pythonhosted.org/packages/55/f0/0541524133104f9cc20bf900870ff4a736b76a23483f3a55295ddfa58409/playwright-1.60.0-py3-none-win_amd64.whl", hash = "sha256:9566821ce6030a1f9e7146a24e19355ab0d98805fd0f9be50bb3d8fef1750c02", size = 37902159, upload-time = "2026-05-18T12:00:53.728Z" }, + { url = "https://files.pythonhosted.org/packages/80/c8/210f282d278e4709cdd71b12a31af45a30a22ab3207b387e29b37e478713/playwright-1.60.0-py3-none-win_arm64.whl", hash = "sha256:6e4f6700a4c2250efff8e690a81d66e3855754fb587b6b87cf5c784014f91537", size = 34037981, upload-time = "2026-05-18T12:00:57.584Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pyee" +version = "13.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/04/e7c1fe4dc78a6fdbfd6c337b1c3732ff543b8a397683ab38378447baa331/pyee-13.0.1.tar.gz", hash = "sha256:0b931f7c14535667ed4c7e0d531716368715e860b988770fc7eb8578d1f67fc8", size = 31655, upload-time = "2026-02-14T21:12:28.044Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/c4/b4d4827c93ef43c01f599ef31453ccc1c132b353284fc6c87d535c233129/pyee-13.0.1-py3-none-any.whl", hash = "sha256:af2f8fede4171ef667dfded53f96e2ed0d6e6bd7ee3bb46437f77e3b57689228", size = 15659, upload-time = "2026-02-14T21:12:26.263Z" }, +] + +[[package]] +name = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, +] + +[[package]] +name = "pytest" +version = "9.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/84/0e/b5858858d74958632c49b72cb25a3976ff9f632397626715be71c89d3971/pytest-9.1.0.tar.gz", hash = "sha256:41dd9148c08072446394cefd3d79701701335a9f4cae69ba92e39f6c7f5c061c", size = 1634181, upload-time = "2026-06-13T18:52:45.983Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/5a/ba30a81239b909821b3153e303e7def45178bf353da4f72380e6c5e8793b/pytest-9.1.0-py3-none-any.whl", hash = "sha256:8ebb0e7888bdf2bdfc602ec51f8f62d50200af37356c74e503c79a94f5c81f32", size = 386453, upload-time = "2026-06-13T18:52:44.045Z" }, +] + +[[package]] +name = "pytest-base-url" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/1a/b64ac368de6b993135cb70ca4e5d958a5c268094a3a2a4cac6f0021b6c4f/pytest_base_url-2.1.0.tar.gz", hash = "sha256:02748589a54f9e63fcbe62301d6b0496da0d10231b753e950c63e03aee745d45", size = 6702, upload-time = "2024-01-31T22:43:00.81Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/1c/b00940ab9eb8ede7897443b771987f2f4a76f06be02f1b3f01eb7567e24a/pytest_base_url-2.1.0-py3-none-any.whl", hash = "sha256:3ad15611778764d451927b2a53240c1a7a591b521ea44cebfe45849d2d2812e6", size = 5302, upload-time = "2024-01-31T22:42:58.897Z" }, +] + +[[package]] +name = "pytest-django" +version = "4.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/13/2b/db9a193df89e5660137f5428063bcc2ced7ad790003b26974adf5c5ceb3b/pytest_django-4.12.0.tar.gz", hash = "sha256:df94ec819a83c8979c8f6de13d9cdfbe76e8c21d39473cfe2b40c9fc9be3c758", size = 91156, upload-time = "2026-02-14T18:40:49.235Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/a5/41d091f697c09609e7ef1d5d61925494e0454ebf51de7de05f0f0a728f1d/pytest_django-4.12.0-py3-none-any.whl", hash = "sha256:3ff300c49f8350ba2953b90297d23bf5f589db69545f56f1ec5f8cff5da83e85", size = 26123, upload-time = "2026-02-14T18:40:47.381Z" }, +] + +[[package]] +name = "pytest-env" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, + { name = "python-dotenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ff/69/4db1c30625af0621df8dbe73797b38b6d1b04e15d021dd5d26a6d297f78c/pytest_env-1.6.0.tar.gz", hash = "sha256:ac02d6fba16af54d61e311dd70a3c61024a4e966881ea844affc3c8f0bf207d3", size = 16163, upload-time = "2026-03-12T22:39:43.78Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/16/ad52f56b96d851a2bcfdc1e754c3531341885bd7177a128c13ff2ca72ab4/pytest_env-1.6.0-py3-none-any.whl", hash = "sha256:1e7f8a62215e5885835daaed694de8657c908505b964ec8097a7ce77b403d9a3", size = 10400, upload-time = "2026-03-12T22:39:41.887Z" }, +] + +[[package]] +name = "pytest-playwright" +version = "0.7.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "playwright" }, + { name = "pytest" }, + { name = "pytest-base-url" }, + { name = "python-slugify" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e8/6b/913e36aa421b35689ec95ed953ff7e8df3f2ee1c7b8ab2a3f1fd39d95faf/pytest_playwright-0.7.2.tar.gz", hash = "sha256:247b61123b28c7e8febb993a187a07e54f14a9aa04edc166f7a976d88f04c770", size = 16928, upload-time = "2025-11-24T03:43:22.53Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/61/4d333d8354ea2bea2c2f01bad0a4aa3c1262de20e1241f78e73360e9b620/pytest_playwright-0.7.2-py3-none-any.whl", hash = "sha256:8084e015b2b3ecff483c2160f1c8219b38b66c0d4578b23c0f700d1b0240ea38", size = 16881, upload-time = "2025-11-24T03:43:24.423Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, +] + +[[package]] +name = "python-slugify" +version = "8.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "text-unidecode" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/87/c7/5e1547c44e31da50a460df93af11a535ace568ef89d7a811069ead340c4a/python-slugify-8.0.4.tar.gz", hash = "sha256:59202371d1d05b54a9e7720c5e038f928f45daaffe41dd10822f3907b937c856", size = 10921, upload-time = "2024-02-08T18:32:45.488Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/62/02da182e544a51a5c3ccf4b03ab79df279f9c60c5e82d5e8bec7ca26ac11/python_slugify-8.0.4-py2.py3-none-any.whl", hash = "sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8", size = 10051, upload-time = "2024-02-08T18:32:43.911Z" }, +] + +[[package]] +name = "requests" +version = "2.34.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, +] + +[[package]] +name = "ruff" +version = "0.15.17" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/a9/3abdf488f1bf3d24c699415e454ed554a6350d5d89ce183be1ee0a3361ac/ruff-0.15.17.tar.gz", hash = "sha256:2ec446937fd16c8c4de2674a209cc5af64d9c6f17d21fbf1151054fa0bcf5219", size = 4743346, upload-time = "2026-06-11T17:54:47.663Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/4d/e11259f5da07cb6afb2d074c31bf09da9671993f7329d4f15d2fdc458301/ruff-0.15.17-py3-none-linux_armv6l.whl", hash = "sha256:d9feddb927fc68bd295f5eebc587a7e42cfaf9b65f60ca4a2386febff575da8f", size = 10856677, upload-time = "2026-06-11T17:54:49.533Z" }, + { url = "https://files.pythonhosted.org/packages/29/3e/772d679e1a0dc058e58875bd2c0cb713a0530877b4a76fee3c7966df0d49/ruff-0.15.17-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:25805a226d741c47d274a35ad5c10a7dde175fcddfa511d7cf3da0a21eb3eab7", size = 11223443, upload-time = "2026-06-11T17:55:00.573Z" }, + { url = "https://files.pythonhosted.org/packages/68/58/bd41f7688b2fd5623012605130ed70e60aa7f2244baa3d5066bdd61530c8/ruff-0.15.17-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f6ad73b14c2d18a3bf8ad7cb6974294d7f613a7898604826058e6ac64918ef4d", size = 10566458, upload-time = "2026-06-11T17:55:07.52Z" }, + { url = "https://files.pythonhosted.org/packages/d8/5b/733371013fcf1ec339e477ece6ab42bfe10bdd9bba8ee88a9516aa56bfc0/ruff-0.15.17-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ba0c1e4f95bcb3869d0d30cbd5917071ef2e28665abfec970cdab0492c713ed", size = 10914483, upload-time = "2026-06-11T17:55:05.501Z" }, + { url = "https://files.pythonhosted.org/packages/bd/cc/6f24251cc0252f7239391ccb85833f320efad14ebe5b443943f37ced6332/ruff-0.15.17-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:81647960f10bff57d2e51cadd0c3950fe598400c852863a038720ef5b8cca91e", size = 10647497, upload-time = "2026-06-11T17:54:57.733Z" }, + { url = "https://files.pythonhosted.org/packages/68/dd/0d10c17ce1a1624d6fc3156309c3f834fdb5dfaad026ec90c85684f3990e/ruff-0.15.17-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e01a84ddbc8c16c23055ba3924476850f1bbc1917cebbb9376665a63e74260d", size = 11416967, upload-time = "2026-06-11T17:54:51.461Z" }, + { url = "https://files.pythonhosted.org/packages/2f/91/556bfb156f6144f355e831c23db00b2fc4120f86b3ce81cc5f7fd2df51f3/ruff-0.15.17-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fe9f653152f8f294f9f7e03bf3a453d8b4a27f7a59c78c8666167f2b17b96c", size = 12335770, upload-time = "2026-06-11T17:54:45.793Z" }, + { url = "https://files.pythonhosted.org/packages/88/82/8b5999aa13355e926f06d9f42a32dcca862f623bf0363785ff89d607dffd/ruff-0.15.17-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c0fe88a7676e7a05b73174d4d4a59cb2ac21ff8263583f87a81a6018475a978", size = 11575441, upload-time = "2026-06-11T17:54:32.661Z" }, + { url = "https://files.pythonhosted.org/packages/11/93/f10377bb04109ca0e8cbc483ff1982c54b6d418210041776f93e8cdc7fa9/ruff-0.15.17-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecfc3c7878fff94633ab0348524e093f9ce3243080416dd7d14f8ba400174719", size = 11557614, upload-time = "2026-06-11T17:54:34.698Z" }, + { url = "https://files.pythonhosted.org/packages/c7/a6/eeeae7f7d5493df41649ab3db92f086b2d0a30199e4efdf8e3dd7a033f24/ruff-0.15.17-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:b8461180b22420b1bdc289909410930761629fddf2a5aaf60fae1ab26cedc4c4", size = 11544450, upload-time = "2026-06-11T17:54:39.042Z" }, + { url = "https://files.pythonhosted.org/packages/32/88/5991ce565129a24dd4a00db1254b3b5db2e53018cbe4018ea5a89738e727/ruff-0.15.17-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6eccbe50a038b503e7140b441aa9c7fc8c1f36edf23ebef9f4165c2f28f568b7", size = 10892524, upload-time = "2026-06-11T17:55:09.432Z" }, + { url = "https://files.pythonhosted.org/packages/f5/1d/0fdd248313425f55223968af04b0a42125466a8d88d21c1d99c6af0a51e8/ruff-0.15.17-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:382fc0521025f5a8ad447d8bdd523545d0d7646adb718eb1c2dac5065ec27c0f", size = 10659573, upload-time = "2026-06-11T17:54:36.824Z" }, + { url = "https://files.pythonhosted.org/packages/9e/0e/072e8260deb9461062ce9311ced27a8e541229a6ffd483013dd37661e43e/ruff-0.15.17-py3-none-musllinux_1_2_i686.whl", hash = "sha256:456d41fcd1b2777ad63f09a6e7121d43f7b688bbc76a800c10f7f8fb1f912c3f", size = 11127818, upload-time = "2026-06-11T17:55:03.124Z" }, + { url = "https://files.pythonhosted.org/packages/ab/b4/55060a34163121498014696b5f656db5b8c6963768f227dbf0d76b311073/ruff-0.15.17-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b1a04bcc94ae6194e9db05d16ad31f298a7194bfbcb08258bbe589cee1d587b8", size = 11655901, upload-time = "2026-06-11T17:54:53.562Z" }, + { url = "https://files.pythonhosted.org/packages/49/71/9b29d6b87cef468d697f43c6a91e3fae4a80185779d7d5a4ef27d173439f/ruff-0.15.17-py3-none-win32.whl", hash = "sha256:596065960ab1ff593f744220c9fe6580eda00a95003cffa9f4048bb5b1bf0392", size = 10925574, upload-time = "2026-06-11T17:54:55.723Z" }, + { url = "https://files.pythonhosted.org/packages/3d/b2/8fc77f3723228836fa5d12497eb71c808f83782e10d058d2b15cfa14640b/ruff-0.15.17-py3-none-win_amd64.whl", hash = "sha256:6769e5fa1710b179b92e0bfa5a51735b35baea9013dadb06d5f44cbcf9547084", size = 12058788, upload-time = "2026-06-11T17:54:41.042Z" }, + { url = "https://files.pythonhosted.org/packages/2d/c7/c53e8dbff9c9dc4b7928773421ae294a5d28fcb8dcda1a089579d3a7e510/ruff-0.15.17-py3-none-win_arm64.whl", hash = "sha256:f3be1fbb34bcdfd146240d8fb92a709d4c2c8191348580a3c044ec60fa0b4456", size = 11355275, upload-time = "2026-06-11T17:54:43.635Z" }, +] + +[[package]] +name = "setuptools" +version = "82.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316, upload-time = "2026-03-09T12:47:17.221Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223, upload-time = "2026-03-09T12:47:15.026Z" }, +] + +[[package]] +name = "sqlparse" +version = "0.5.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/90/76/437d71068094df0726366574cf3432a4ed754217b436eb7429415cf2d480/sqlparse-0.5.5.tar.gz", hash = "sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e", size = 120815, upload-time = "2025-12-19T07:17:45.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/4b/359f28a903c13438ef59ebeee215fb25da53066db67b305c125f1c6d2a25/sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba", size = 46138, upload-time = "2025-12-19T07:17:46.573Z" }, +] + +[[package]] +name = "text-unidecode" +version = "1.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ab/e2/e9a00f0ccb71718418230718b3d900e71a5d16e701a3dae079a21e9cd8f8/text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93", size = 76885, upload-time = "2019-08-30T21:36:45.405Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/a5/c0b6468d3824fe3fde30dbb5e1f687b291608f9473681bbf7dabbf5a87d7/text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", size = 78154, upload-time = "2019-08-30T21:37:03.543Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "tzdata" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254, upload-time = "2026-04-24T15:22:08.651Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" }, +] + +[[package]] +name = "urllib3" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, +]