diff --git a/.circleci/Dockerfile b/.circleci/Dockerfile deleted file mode 100644 index 421f6bb..0000000 --- a/.circleci/Dockerfile +++ /dev/null @@ -1,7 +0,0 @@ -ARG PYTHON_VERSION=3.6 -FROM circleci/python:$PYTHON_VERSION - -WORKDIR /home/circleci/app/ -COPY . /home/circleci/app/ -RUN sudo chown -R circleci:circleci . -RUN sudo pip install tox diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 8a21044..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,135 +0,0 @@ -version: 2.0 - -py38default: &py38default - docker: - - image: circleci/python:3.8 - steps: - - setup_remote_docker: - docker_layer_caching: false - - checkout - - attach_workspace: - at: /tmp/images - - run: docker load -i /tmp/images/py38.tar || true - - run: docker run py38 tox -e $CIRCLE_JOB - -py39default: &py39default - docker: - - image: circleci/python:3.9 - steps: - - setup_remote_docker: - docker_layer_caching: false - - checkout - - attach_workspace: - at: /tmp/images - - run: docker load -i /tmp/images/py39.tar || true - - run: docker run py39 tox -e $CIRCLE_JOB - -py310default: &py310default - docker: - - image: circleci/python:3.10 - steps: - - setup_remote_docker: - docker_layer_caching: false - - checkout - - attach_workspace: - at: /tmp/images - - run: docker load -i /tmp/images/py310.tar || true - - run: docker run py310 tox -e $CIRCLE_JOB - -py38_requires: &py38_requires - requires: - - py38_base - -py39_requires: &py39_requires - requires: - - py39_base - -py310_requires: &py310_requires - requires: - - py310_base - -jobs: - py38_base: - docker: - - image: circleci/python:3.8 - steps: - - checkout - - setup_remote_docker: - docker_layer_caching: false - - run: docker build -f .circleci/Dockerfile --build-arg PYTHON_VERSION=3.8 -t py38 . - - run: mkdir images - - run: docker save -o images/py38.tar py38 - - persist_to_workspace: - root: images - paths: py38.tar - py39_base: - docker: - - image: circleci/python:3.9 - steps: - - checkout - - setup_remote_docker: - docker_layer_caching: false - - run: docker build -f .circleci/Dockerfile --build-arg PYTHON_VERSION=3.9 -t py39 . - - run: mkdir images - - run: docker save -o images/py39.tar py39 - - persist_to_workspace: - root: images - paths: py39.tar - py310_base: - docker: - - image: circleci/python:3.10 - steps: - - checkout - - setup_remote_docker: - docker_layer_caching: false - - run: docker build -f .circleci/Dockerfile --build-arg PYTHON_VERSION=3.10 -t py310 . - - run: mkdir images - - run: docker save -o images/py310.tar py310 - - persist_to_workspace: - root: images - paths: py310.tar - - flake8: - <<: *py310default - isort: - <<: *py310default - - py38-dj32-sqlite-cms40: - <<: *py38default - py39-dj32-sqlite-cms40: - <<: *py39default - - py39-dj42-sqlite-cms40: - <<: *py39default - py310-dj42-sqlite-cms40: - <<: *py310default - -####################### - -workflows: - version: 2 - build: - jobs: - - py38_base - - py39_base - - py310_base - - flake8: - requires: - - py310_base - - isort: - requires: - - py310_base - - - py38-dj32-sqlite-cms40: - requires: - - py38_base - - py39-dj32-sqlite-cms40: - requires: - - py39_base - - - py39-dj42-sqlite-cms40: - requires: - - py39_base - - py310-dj42-sqlite-cms40: - requires: - - py310_base diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml new file mode 100644 index 0000000..d1db92d --- /dev/null +++ b/.github/workflows/linting.yml @@ -0,0 +1,39 @@ +name: django CMS References linters.yml + +on: [pull_request, push] + +jobs: + flake8: + name: flake8 + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - run: pip install --upgrade flake8 + - name: flake8 + uses: liskin/gh-problem-matcher-wrap@v1 + with: + linters: flake8 + run: | + flake8 + isort: + name: isort + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - run: python -m pip install isort + - name: isort + uses: liskin/gh-problem-matcher-wrap@v1 + with: + linters: isort + run: isort -c -rc -df ./ + diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..0654882 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,39 @@ +name: django CMS references test.yml + +on: [pull_request, push] + +jobs: + database-sqlite: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: [ 3.8, 3.9, '3.10'] # latest release minus two + requirements-file: [ + dj32_cms40.txt, + dj42_cms40.txt, + dj42_cms41.txt, + ] + exclude: + - requirements-file: dj42_cms41.txt + python-version: 3.8 + + steps: + - uses: actions/checkout@v1 + - name: Set up Python ${{ matrix.python-version }} + + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + sudo apt install gettext gcc -y + python -m pip install --upgrade pip + pip install pytest + pip install -r tests/requirements/${{ matrix.requirements-file }} + python setup.py install + - name: Test with python test runner + run: | + python setup.py test + env: + DATABASE_URL: sqlite://localhost/testdb.sqlite diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3a442d2..aaaaba1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,8 @@ Changelog Unreleased ========== +* Django CMS 4.1 support added + 1.5.0 (2024-05-16) ================== diff --git a/djangocms_references/compat.py b/djangocms_references/compat.py index bef3dce..f5d98c8 100644 --- a/djangocms_references/compat.py +++ b/djangocms_references/compat.py @@ -1,3 +1,12 @@ +from cms import __version__ as cms_version + + +try: + from packaging.version import Version +except ModuleNotFoundError: + from distutils.version import LooseVersion as Version + + def is_versioning_installed(): try: import djangocms_versioning # noqa: F401 @@ -7,4 +16,7 @@ def is_versioning_installed(): return True +DJANGO_CMS_4_1 = Version(cms_version) >= Version('4.1') + + VERSIONING_INSTALLED = is_versioning_installed() diff --git a/djangocms_references/monkeypatch/admin.py b/djangocms_references/monkeypatch/admin.py index b0af466..d1a22f9 100644 --- a/djangocms_references/monkeypatch/admin.py +++ b/djangocms_references/monkeypatch/admin.py @@ -5,10 +5,12 @@ from djangocms_alias import admin as AliasOriginalAdmin from djangocms_snippet import admin as SnippetOriginalAdmin +from djangocms_references.compat import DJANGO_CMS_4_1 -def generate_get_references_link(content_grouper): + +def generate_get_references_link(content_grouper=None): def _get_references_link(self, obj, request): - obj_grouper = getattr(obj, content_grouper) + obj_grouper = obj if not content_grouper else getattr(obj, content_grouper) _obj_grouper_meta = obj_grouper._meta content_type = ContentType.objects.get( app_label=_obj_grouper_meta.app_label, @@ -24,7 +26,7 @@ def _get_references_link(self, obj, request): return _get_references_link -def get_list_actions(func): +def _get_actions_list(func): """ Add references action to alias list display """ @@ -35,12 +37,21 @@ def inner(self, *args, **kwargs): return inner -AliasOriginalAdmin.AliasContentAdmin._get_references_link = generate_get_references_link('alias') -AliasOriginalAdmin.AliasContentAdmin.get_list_actions = get_list_actions( - AliasOriginalAdmin.AliasContentAdmin.get_list_actions -) - -SnippetOriginalAdmin.SnippetAdmin._get_references_link = generate_get_references_link('snippet_grouper') -SnippetOriginalAdmin.SnippetAdmin.get_list_actions = get_list_actions( - SnippetOriginalAdmin.SnippetAdmin.get_list_actions -) +if not DJANGO_CMS_4_1: + AliasOriginalAdmin.AliasContentAdmin._get_references_link = generate_get_references_link('alias') + AliasOriginalAdmin.AliasContentAdmin.get_list_actions = _get_actions_list( + AliasOriginalAdmin.AliasContentAdmin.get_list_actions + ) + + SnippetOriginalAdmin.SnippetAdmin._get_references_link = generate_get_references_link('snippet_grouper') + SnippetOriginalAdmin.SnippetAdmin.get_list_actions = _get_actions_list( + SnippetOriginalAdmin.SnippetAdmin.get_list_actions + ) +else: + AliasOriginalAdmin.AliasAdmin._get_references_link = generate_get_references_link() + AliasOriginalAdmin.AliasAdmin.get_actions_list = _get_actions_list(AliasOriginalAdmin.AliasAdmin.get_actions_list) + + SnippetOriginalAdmin.SnippetAdmin._get_references_link = generate_get_references_link('snippet_grouper') + SnippetOriginalAdmin.SnippetAdmin.get_actions_list = _get_actions_list( + SnippetOriginalAdmin.SnippetAdmin.get_actions_list + ) diff --git a/djangocms_references/templates/djangocms_references/references.html b/djangocms_references/templates/djangocms_references/references.html index 74e8e2c..b74468a 100644 --- a/djangocms_references/templates/djangocms_references/references.html +++ b/djangocms_references/templates/djangocms_references/references.html @@ -4,6 +4,11 @@ {% block extrastyle %} {{ block.super }} + {% endblock %} {% block extrahead %} diff --git a/djangocms_references/templates/djangocms_references/references_icon.html b/djangocms_references/templates/djangocms_references/references_icon.html index ae2649f..ae784e3 100644 --- a/djangocms_references/templates/djangocms_references/references_icon.html +++ b/djangocms_references/templates/djangocms_references/references_icon.html @@ -1,2 +1,4 @@ {% load static i18n %} - + + + diff --git a/djangocms_references/templates/djangocms_versioning_filer/admin/action_buttons/show_references.html b/djangocms_references/templates/djangocms_versioning_filer/admin/action_buttons/show_references.html index 38a1d60..fa104d7 100644 --- a/djangocms_references/templates/djangocms_versioning_filer/admin/action_buttons/show_references.html +++ b/djangocms_references/templates/djangocms_versioning_filer/admin/action_buttons/show_references.html @@ -1,5 +1,9 @@ {% load i18n djangocms_references_tags %} {% get_versioning_filer_references_url file as references_url %} - \ No newline at end of file + + + + + + diff --git a/tests/requirements/dj32_cms40.txt b/tests/requirements/dj32_cms40.txt index c2ef6e2..1bb85a3 100644 --- a/tests/requirements/dj32_cms40.txt +++ b/tests/requirements/dj32_cms40.txt @@ -3,5 +3,7 @@ Django>=3.2,<4.0 # Unreleased django-cms 4.0 compatible packages +https://github.com/django-cms/django-cms/tarball/release/4.0.1.x#egg=django-cms +https://github.com/django-cms/djangocms-snippet/tarball/support/django-cms-4.0.x#egg=djangocms-snippet https://github.com/django-cms/djangocms-versioning/tarball/1.2.2#egg=djangocms-versioning https://github.com/django-cms/djangocms-alias/tarball/1.11.0#egg=djangocms-alias \ No newline at end of file diff --git a/tests/requirements/dj42_cms40.txt b/tests/requirements/dj42_cms40.txt index b91fba6..0c6f46a 100644 --- a/tests/requirements/dj42_cms40.txt +++ b/tests/requirements/dj42_cms40.txt @@ -2,5 +2,7 @@ Django>=4.2,<5.0 +https://github.com/django-cms/django-cms/tarball/release/4.0.1.x#egg=django-cms +https://github.com/django-cms/djangocms-snippet/tarball/support/django-cms-4.0.x#egg=djangocms-snippet https://github.com/django-cms/djangocms-versioning/tarball/support/django-cms-4.0.x#egg=djangocms-versioning https://github.com/django-cms/djangocms-alias/tarball/support/django-cms-4.0.x#egg=djangocms-alias \ No newline at end of file diff --git a/tests/requirements/dj42_cms41.txt b/tests/requirements/dj42_cms41.txt new file mode 100644 index 0000000..fc7346d --- /dev/null +++ b/tests/requirements/dj42_cms41.txt @@ -0,0 +1,8 @@ +-r ./requirements_base.txt + +Django>=4.2,<5.0 +djangocms-versioning==2.1.0 +django-cms>=4.1.0 + +https://github.com/django-cms/djangocms-snippet/tarball/master#egg=djangocms-snippet +https://github.com/django-cms/djangocms-alias/tarball/master#egg=djangocms-alias \ No newline at end of file diff --git a/tests/requirements/requirements_base.txt b/tests/requirements/requirements_base.txt index 2534e78..95964b6 100644 --- a/tests/requirements/requirements_base.txt +++ b/tests/requirements/requirements_base.txt @@ -4,6 +4,3 @@ factory-boy flake8 isort tox - -https://github.com/django-cms/django-cms/tarball/release/4.0.1.x#egg=django-cms -https://github.com/django-cms/djangocms-snippet/tarball/support/django-cms-4.0.x#egg=djangocms-snippet diff --git a/tests/settings.py b/tests/settings.py index 283f25d..79a7f08 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -22,6 +22,7 @@ }, "DEFAULT_AUTO_FIELD": "django.db.models.AutoField", "ROOT_URLCONF": "tests.urls", + "VERSIONING_ALIAS_MODELS_ENABLED": True, } diff --git a/tests/test_admin.py b/tests/test_admin.py index fa0bf5e..b7d3db6 100644 --- a/tests/test_admin.py +++ b/tests/test_admin.py @@ -1,17 +1,27 @@ +from unittest import skipIf + from django.contrib import admin from django.contrib.contenttypes.models import ContentType from django.urls import reverse_lazy from cms.test_utils.testcases import CMSTestCase -from djangocms_alias.admin import AliasContentAdmin -from djangocms_alias.models import Alias, AliasContent, Category +from djangocms_alias.models import Alias as AliasGouper, AliasContent, Category from djangocms_snippet.admin import SnippetAdmin as SnippetContentAdmin from djangocms_snippet.models import Snippet as SnippetContent, SnippetGrouper from djangocms_versioning.models import Version +from djangocms_references.compat import DJANGO_CMS_4_1 + + +if DJANGO_CMS_4_1: + from djangocms_alias.admin import AliasAdmin as AliasGouperAdmin +else: + from djangocms_alias.admin import AliasContentAdmin + class AliasAdminReferencesMonkeyPatchTestCase(CMSTestCase): + @skipIf(DJANGO_CMS_4_1, "skip for now, KeyError at line 52: 'language'.") def test_list_display(self): """ The monkeypatch extends the alias admin, adding the show references link @@ -19,7 +29,7 @@ def test_list_display(self): request = self.get_request("/") request.user = self.get_superuser() category = Category.objects.create(name="Alias Reference Monkey Patch Category") - alias = Alias.objects.create(category=category, position=0) + alias = AliasGouper.objects.create(category=category, position=0) alias_content = AliasContent.objects.create( alias=alias, name="Alias Reference Monkey Patch Content", @@ -28,7 +38,10 @@ def test_list_display(self): Version.objects.create(content=alias_content, created_by=request.user) content_type = ContentType.objects.get(app_label=alias._meta.app_label, model=alias._meta.model_name) - alias_admin = AliasContentAdmin(AliasContent, admin.AdminSite()) + if DJANGO_CMS_4_1: + alias_admin = AliasGouperAdmin(AliasGouper, admin.AdminSite()) + else: + alias_admin = AliasContentAdmin(AliasContent, admin.AdminSite()) func = alias_admin.get_list_display(request)[-1] references_url = reverse_lazy( "djangocms_references:references-index", diff --git a/tox.ini b/tox.ini index 60fcca8..4267f26 100644 --- a/tox.ini +++ b/tox.ini @@ -2,7 +2,7 @@ envlist = flake8 isort - py{38,39,310}-dj{32,42}-sqlite-cms{40,41} + py{38,39,310}-django{32,42}-cms{40,41} skip_missing_interpreters=True @@ -11,8 +11,9 @@ deps = flake8: -r{toxinidir}/tests/requirements/requirements_base.txt isort: -r{toxinidir}/tests/requirements/requirements_base.txt - dj32: -r{toxinidir}/tests/requirements/dj32_cms40.txt - dj42: -r{toxinidir}/tests/requirements/dj42_cms40.txt + dj32_cms40: -r{toxinidir}/tests/requirements/dj32_cms40.txt + dj42_cms40: -r{toxinidir}/tests/requirements/dj42_cms40.txt + dj42_cms41: -r{toxinidir}/tests/requirements/dj42_cms41.txt basepython = py38: python3.8