From fc40d6c26272e2787678dd774b1935c1a50fa932 Mon Sep 17 00:00:00 2001 From: Andrea Cecchi Date: Thu, 29 Sep 2022 10:03:00 +0200 Subject: [PATCH 01/19] Plone 6 support --- .github/workflows/ci.yml | 8 +-- .gitignore | 1 + CHANGES.rst | 3 +- .../formwidget/masterselect/configure.zcml | 66 ++++++++++++++----- .../profiles/default/registry.xml | 1 + src/plone/formwidget/masterselect/widget.py | 2 +- 6 files changed, 57 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3fc34e4..589c912 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,8 +6,8 @@ jobs: strategy: fail-fast: false matrix: - python-version: [3.8] - plone-version: [5.2] + python-version: [3.8, 3.7] + plone-version: [5.2, 6.0] steps: # git checkout @@ -23,7 +23,7 @@ jobs: - uses: actions/cache@v1 with: path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements-${{ matrix.python-version }}.txt') }} restore-keys: | ${{ runner.os }}-pip- @@ -37,7 +37,7 @@ jobs: - run: pip install virtualenv - run: pip install wheel - name: pip install - run: pip install -r requirements.txt + run: pip install -r requirements-${{ matrix.python-version }}.txt # Xvfb - run: Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & diff --git a/.gitignore b/.gitignore index 79f3d25..07634d8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.py[co] +.python-version # Packages *.egg diff --git a/CHANGES.rst b/CHANGES.rst index e4bdf93..89b958c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,7 +4,8 @@ Changelog 3.0.1 (unreleased) ------------------ -- Nothing changed yet. +- Plone6 compatibility. + [cekk] 3.0.0 (2024-06-05) diff --git a/src/plone/formwidget/masterselect/configure.zcml b/src/plone/formwidget/masterselect/configure.zcml index 9b5c8b3..8afdafd 100644 --- a/src/plone/formwidget/masterselect/configure.zcml +++ b/src/plone/formwidget/masterselect/configure.zcml @@ -8,6 +8,7 @@ + @@ -27,26 +28,55 @@ directory="static" /> - + + + + + + + + + + + + + + + + + - - ++resource++plone.formwidget.masterselect/masterselect.js 2021-06-18 00:00:00 + plone False True diff --git a/src/plone/formwidget/masterselect/widget.py b/src/plone/formwidget/masterselect/widget.py index 94206e9..58ba1a1 100644 --- a/src/plone/formwidget/masterselect/widget.py +++ b/src/plone/formwidget/masterselect/widget.py @@ -130,7 +130,7 @@ class MasterSelectWidget(select.SelectWidget, MasterSelect): """Master Select Widget """ - klass = u'masterselect-widget' + klass = u'form-select masterselect-widget' @implementer(IMasterSelectBoolWidget) From 42cfe3a99517f9dec17d4cf486a03663ef6278d5 Mon Sep 17 00:00:00 2001 From: Andrea Cecchi Date: Thu, 29 Sep 2022 10:12:09 +0200 Subject: [PATCH 02/19] fix requirements deps --- .github/workflows/ci.yml | 2 +- constraints_plone60.txt | 16 ++++++++++++++++ plone-6.0.x.cfg | 19 +++++++++++++++++++ requirements-5.2.txt | 5 ++++- requirements-6.0.txt | 3 +++ 5 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 constraints_plone60.txt create mode 100644 plone-6.0.x.cfg create mode 100644 requirements-6.0.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 589c912..b94f01a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: - run: pip install virtualenv - run: pip install wheel - name: pip install - run: pip install -r requirements-${{ matrix.python-version }}.txt + run: pip install -r requirements-${{ matrix.plone-version }}.txt # Xvfb - run: Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & diff --git a/constraints_plone60.txt b/constraints_plone60.txt new file mode 100644 index 0000000..369b88b --- /dev/null +++ b/constraints_plone60.txt @@ -0,0 +1,16 @@ +-c https://dist.plone.org/release/6.0-dev/requirements.txt + +#setuptools==54.0.0 +#zc.buildout==3.0.0b2 +#pip==21.0.1 +# +## Windows specific down here (has to be installed here, fails in buildout) +## Dependency of zope.sendmail: +#pywin32 ; platform_system == 'Windows' +# +## SSL Certs on windows, because Python is missing them otherwise: +#certifi ; platform_system == 'Windows' + +isort>=5 +black==22.3.0 +flake8>=4.0.1 diff --git a/plone-6.0.x.cfg b/plone-6.0.x.cfg new file mode 100644 index 0000000..dee0b54 --- /dev/null +++ b/plone-6.0.x.cfg @@ -0,0 +1,19 @@ +[buildout] +extends = + https://dist.plone.org/release/6-latest/versions.cfg + base.cfg +find-links += https://dist.plone.org/thirdparty/ +versions=versions + +[versions] +black = 21.7b0 + +# Error: The requirement ('virtualenv>=20.0.35') is not allowed by your [versions] constraint (20.0.26) +virtualenv = 20.0.35 + +# Error: The requirement ('importlib-metadata>=1') is not allowed by your [versions] constraint (0.23) +importlib-metadata = 2.0.0 + +# cffi 1.14.3 fails on apple m1 +# cffi 1.14.4 fails with "ModuleNotFoundError: No module named '_cffi_backend'" +cffi = 1.14.6 diff --git a/requirements-5.2.txt b/requirements-5.2.txt index 37660fa..8466fed 100644 --- a/requirements-5.2.txt +++ b/requirements-5.2.txt @@ -1 +1,4 @@ --r https://dist.plone.org/release/5.2.14/requirements.txt +# Keep this file in sync with: https://github.com/kitconcept/buildout/edit/master/requirements.txt +setuptools==42.0.2 +zc.buildout==2.13.3 +wheel diff --git a/requirements-6.0.txt b/requirements-6.0.txt new file mode 100644 index 0000000..cab798d --- /dev/null +++ b/requirements-6.0.txt @@ -0,0 +1,3 @@ +-c constraints_plone60.txt +setuptools +zc.buildout From 6917cc29a95881c402b9d9ca5b6ca9d573b330bf Mon Sep 17 00:00:00 2001 From: Andrea Cecchi Date: Thu, 29 Sep 2022 10:13:38 +0200 Subject: [PATCH 03/19] fix file name --- requirements-6.0.txt => requirements-6.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename requirements-6.0.txt => requirements-6.txt (100%) diff --git a/requirements-6.0.txt b/requirements-6.txt similarity index 100% rename from requirements-6.0.txt rename to requirements-6.txt From b41341390e64c3b5a2d2e1972f15a8a707271050 Mon Sep 17 00:00:00 2001 From: Andrea Cecchi Date: Thu, 29 Sep 2022 10:17:14 +0200 Subject: [PATCH 04/19] fix cfg filename for plone 6 --- plone-6.0.x.cfg | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 plone-6.0.x.cfg diff --git a/plone-6.0.x.cfg b/plone-6.0.x.cfg deleted file mode 100644 index dee0b54..0000000 --- a/plone-6.0.x.cfg +++ /dev/null @@ -1,19 +0,0 @@ -[buildout] -extends = - https://dist.plone.org/release/6-latest/versions.cfg - base.cfg -find-links += https://dist.plone.org/thirdparty/ -versions=versions - -[versions] -black = 21.7b0 - -# Error: The requirement ('virtualenv>=20.0.35') is not allowed by your [versions] constraint (20.0.26) -virtualenv = 20.0.35 - -# Error: The requirement ('importlib-metadata>=1') is not allowed by your [versions] constraint (0.23) -importlib-metadata = 2.0.0 - -# cffi 1.14.3 fails on apple m1 -# cffi 1.14.4 fails with "ModuleNotFoundError: No module named '_cffi_backend'" -cffi = 1.14.6 From cddb22c8a78ac4e407e9d9589a0e6fd0642c67f9 Mon Sep 17 00:00:00 2001 From: Andrea Cecchi Date: Thu, 29 Sep 2022 11:46:11 +0200 Subject: [PATCH 05/19] add missing file --- plone-6.x.cfg | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 plone-6.x.cfg diff --git a/plone-6.x.cfg b/plone-6.x.cfg new file mode 100644 index 0000000..dee0b54 --- /dev/null +++ b/plone-6.x.cfg @@ -0,0 +1,19 @@ +[buildout] +extends = + https://dist.plone.org/release/6-latest/versions.cfg + base.cfg +find-links += https://dist.plone.org/thirdparty/ +versions=versions + +[versions] +black = 21.7b0 + +# Error: The requirement ('virtualenv>=20.0.35') is not allowed by your [versions] constraint (20.0.26) +virtualenv = 20.0.35 + +# Error: The requirement ('importlib-metadata>=1') is not allowed by your [versions] constraint (0.23) +importlib-metadata = 2.0.0 + +# cffi 1.14.3 fails on apple m1 +# cffi 1.14.4 fails with "ModuleNotFoundError: No module named '_cffi_backend'" +cffi = 1.14.6 From 383c48840304abc643401c7dc23cc296c54ebaad Mon Sep 17 00:00:00 2001 From: Andrea Cecchi Date: Thu, 29 Sep 2022 11:47:09 +0200 Subject: [PATCH 06/19] import zcml domain --- src/plone/formwidget/masterselect/configure.zcml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plone/formwidget/masterselect/configure.zcml b/src/plone/formwidget/masterselect/configure.zcml index 8afdafd..cd7e5c4 100644 --- a/src/plone/formwidget/masterselect/configure.zcml +++ b/src/plone/formwidget/masterselect/configure.zcml @@ -4,6 +4,7 @@ xmlns:z3c="http://namespaces.zope.org/z3c" xmlns:plone="http://namespaces.plone.org/plone" xmlns:gs="http://namespaces.zope.org/genericsetup" + xmlns:zcml="http://namespaces.zope.org/zcml" i18n_domain="plone.formwidget.masterselect"> From 4104ad566098d554260a6bcb188c14e22d795505 Mon Sep 17 00:00:00 2001 From: Andrea Cecchi Date: Thu, 29 Sep 2022 12:09:22 +0200 Subject: [PATCH 07/19] fix version --- plone-6.x.cfg | 3 --- 1 file changed, 3 deletions(-) diff --git a/plone-6.x.cfg b/plone-6.x.cfg index dee0b54..e5ed982 100644 --- a/plone-6.x.cfg +++ b/plone-6.x.cfg @@ -11,9 +11,6 @@ black = 21.7b0 # Error: The requirement ('virtualenv>=20.0.35') is not allowed by your [versions] constraint (20.0.26) virtualenv = 20.0.35 -# Error: The requirement ('importlib-metadata>=1') is not allowed by your [versions] constraint (0.23) -importlib-metadata = 2.0.0 - # cffi 1.14.3 fails on apple m1 # cffi 1.14.4 fails with "ModuleNotFoundError: No module named '_cffi_backend'" cffi = 1.14.6 From 2df7f241142c057b2ef05ee2042e5b7a08ac0546 Mon Sep 17 00:00:00 2001 From: Andrea Cecchi Date: Thu, 29 Sep 2022 12:22:50 +0200 Subject: [PATCH 08/19] fix importlib-metadata version --- plone-6.x.cfg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plone-6.x.cfg b/plone-6.x.cfg index e5ed982..e990d9c 100644 --- a/plone-6.x.cfg +++ b/plone-6.x.cfg @@ -11,6 +11,9 @@ black = 21.7b0 # Error: The requirement ('virtualenv>=20.0.35') is not allowed by your [versions] constraint (20.0.26) virtualenv = 20.0.35 +# Error: The requirement ('importlib-metadata<4.3,>=1.1.0') is not allowed by your [versions] constraint (4.12.0) +importlib-metadata = 4.2.0 + # cffi 1.14.3 fails on apple m1 # cffi 1.14.4 fails with "ModuleNotFoundError: No module named '_cffi_backend'" cffi = 1.14.6 From 618d609a402b3d283e1dbb66aabc207231dcb03f Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Mon, 19 Jun 2023 16:50:36 +0200 Subject: [PATCH 09/19] set testing matrix --- .github/workflows/ci.yml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b94f01a..2f964f5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,8 +6,20 @@ jobs: strategy: fail-fast: false matrix: - python-version: [3.8, 3.7] - plone-version: [5.2, 6.0] + include: + - python-version: '3.7' + plone-version: '5.2' + - python-version: '3.8' + plone-version: '5.2' + - python-version: '3.8' + plone-version: '6.0' + - python-version: '3.9' + plone-version: '6.0' + - python-version: '3.10' + plone-version: '6.0' + - python-version: '3.11' + plone-version: '6.0' + steps: # git checkout From 75cf624193e2b35976ed2385f3eb4a84f2654444 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Mon, 19 Jun 2023 16:53:33 +0200 Subject: [PATCH 10/19] rename file --- plone-6.x.cfg => plone-6.0.cfg | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename plone-6.x.cfg => plone-6.0.cfg (100%) diff --git a/plone-6.x.cfg b/plone-6.0.cfg similarity index 100% rename from plone-6.x.cfg rename to plone-6.0.cfg From 43af0fa4a4363c0115089c9764bfd3a2500753ec Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Mon, 19 Jun 2023 16:55:40 +0200 Subject: [PATCH 11/19] rename --- requirements-6.txt => requirements-6.0.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename requirements-6.txt => requirements-6.0.txt (100%) diff --git a/requirements-6.txt b/requirements-6.0.txt similarity index 100% rename from requirements-6.txt rename to requirements-6.0.txt From c036a237f51bff8bdc1cd84226d04623bbe97e4c Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Mon, 19 Jun 2023 16:57:53 +0200 Subject: [PATCH 12/19] rename --- plone-6.0.cfg => plone-6.0.x.cfg | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename plone-6.0.cfg => plone-6.0.x.cfg (100%) diff --git a/plone-6.0.cfg b/plone-6.0.x.cfg similarity index 100% rename from plone-6.0.cfg rename to plone-6.0.x.cfg From db62eaaf84fd14b6fa1a9a3d73bda30c14191054 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Mon, 19 Jun 2023 17:06:08 +0200 Subject: [PATCH 13/19] version pin --- plone-6.0.x.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plone-6.0.x.cfg b/plone-6.0.x.cfg index e990d9c..6c5fb80 100644 --- a/plone-6.0.x.cfg +++ b/plone-6.0.x.cfg @@ -12,7 +12,7 @@ black = 21.7b0 virtualenv = 20.0.35 # Error: The requirement ('importlib-metadata<4.3,>=1.1.0') is not allowed by your [versions] constraint (4.12.0) -importlib-metadata = 4.2.0 +importlib-metadata = 4.13 # cffi 1.14.3 fails on apple m1 # cffi 1.14.4 fails with "ModuleNotFoundError: No module named '_cffi_backend'" From 94c329400963a928a5b3dd59b6f7ba1d39339198 Mon Sep 17 00:00:00 2001 From: "Bermudez Horcajada, Rafael" Date: Wed, 17 Jan 2024 16:32:19 +0100 Subject: [PATCH 14/19] custom version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e4a2580..4db0416 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ from setuptools import setup -version = '3.0.1.dev0' +version = '2.0.1+once.1.dev0' long_description = '\n\n'.join([ open('README.rst').read(), From 51c7b5418988f3a2491ea6d423b9ff4dcf510fe9 Mon Sep 17 00:00:00 2001 From: "Bermudez Horcajada, Rafael" Date: Wed, 17 Jan 2024 16:34:57 +0100 Subject: [PATCH 15/19] custom once release --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4db0416..d2e37e8 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ from setuptools import setup -version = '2.0.1+once.1.dev0' +version = '2.0.1+once.1' long_description = '\n\n'.join([ open('README.rst').read(), From 2b5e737a7b8596b9707c2a68a3421912da737c47 Mon Sep 17 00:00:00 2001 From: Laurent Lasudry Date: Fri, 12 Sep 2025 11:13:34 +0200 Subject: [PATCH 16/19] Update buildout to Plone 6.1 Also add uninstall profile & fix tests --- .coveragerc | 6 + .github/workflows/ci.yml | 69 ------- .github/workflows/main.yml | 91 +++++++++ .gitignore | 1 + .isort.cfg | 5 + Makefile | 180 ++++++++++-------- base.cfg | 159 ++++++++-------- checkouts.cfg | 13 ++ ci.cfg | 7 - constraints_plone60.txt | 16 -- gha.cfg | 5 + plone-5.2.x.cfg | 4 - plone-6.0.x.cfg | 19 -- requirements-5.2.txt | 6 +- requirements-6.0.txt | 2 +- requirements-6.1.txt | 3 + requirements.txt | 1 - setup.py | 11 +- .../formwidget/masterselect/configure.zcml | 10 +- .../profiles/default/jsregistry.xml-dist | 11 -- .../profiles/uninstall/registry.xml | 8 + .../masterselect/profiles/uninstall/types.xml | 4 + .../tests/robot/test_masterselect_demo.robot | 4 +- .../masterselect/tests/test_setup.py | 8 +- .../masterselect/tests/test_upgrade_steps.py | 6 + test-5.2.cfg | 12 ++ test-6.0.cfg | 30 +++ test-6.1.cfg | 30 +++ 28 files changed, 421 insertions(+), 300 deletions(-) create mode 100644 .coveragerc delete mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/main.yml create mode 100644 .isort.cfg create mode 100644 checkouts.cfg delete mode 100644 ci.cfg delete mode 100644 constraints_plone60.txt create mode 100644 gha.cfg delete mode 100644 plone-5.2.x.cfg delete mode 100644 plone-6.0.x.cfg create mode 100644 requirements-6.1.txt delete mode 100644 requirements.txt delete mode 100644 src/plone/formwidget/masterselect/profiles/default/jsregistry.xml-dist create mode 100644 src/plone/formwidget/masterselect/profiles/uninstall/registry.xml create mode 100644 src/plone/formwidget/masterselect/profiles/uninstall/types.xml create mode 100644 test-5.2.cfg create mode 100644 test-6.0.cfg create mode 100644 test-6.1.cfg diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..f0fdfe6 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,6 @@ +[report] +include = + src/plone/* +omit = + */test* + */upgrades/* diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 2f964f5..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,69 +0,0 @@ -name: plone.formwidget.masterselect CI -on: [push] -jobs: - build: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - include: - - python-version: '3.7' - plone-version: '5.2' - - python-version: '3.8' - plone-version: '5.2' - - python-version: '3.8' - plone-version: '6.0' - - python-version: '3.9' - plone-version: '6.0' - - python-version: '3.10' - plone-version: '6.0' - - python-version: '3.11' - plone-version: '6.0' - - - steps: - # git checkout - - uses: actions/checkout@v2 - - # python setup - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 - with: - python-version: ${{ matrix.python-version }} - - # python cache - - uses: actions/cache@v1 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements-${{ matrix.python-version }}.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - # Firefox - - uses: browser-actions/setup-firefox@latest - with: - firefox-version: '102.6.0esr' - - run: firefox --version - - # python install - - run: pip install virtualenv - - run: pip install wheel - - name: pip install - run: pip install -r requirements-${{ matrix.plone-version }}.txt - - # Xvfb - - run: Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & - - # buildout - - name: buildout - run: buildout -t 10 -c plone-${{ matrix.plone-version }}.x.cfg code-analysis:return-status-codes=True - - # code analysis - - name: code analysis - run: bin/code-analysis - - # test - - name: test - env: - DISPLAY: :99 - run: bin/test diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..9d73b05 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,91 @@ +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Push tests +# run-name: ${{ github.actor }} push tests + +on: + push: + pull_request: + +jobs: + test: + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + include: + - python: 3.7.14 + plone: 5.2 + - python: 3.10.11 + plone: "6.0" + - python: 3.13.1 + plone: "6.1" + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set up pyenv and Python + uses: "gabrielfalcao/pyenv-action@v18" + with: + default: "${{ matrix.python }}" + - name: Setup Env + run: | + pip install --upgrade pip + pip install -r requirements-${{ matrix.plone }}.txt + - name: Cache eggs + uses: actions/cache@v4 + env: + cache-name: cache-eggs + with: + path: ~/buildout-cache/eggs + key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ matrix.python }}-${{ matrix.plone }} + - name: buildout + run: | + sed -ie "s#test.cfg#test-${{matrix.plone}}.cfg#" gha.cfg + buildout -c gha.cfg annotate + buildout -c gha.cfg + - name: test + run: | + bin/test -t !robot + coverage: + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + include: + - python: 3.10.11 + plone: "6.0" + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set up pyenv and Python + uses: "gabrielfalcao/pyenv-action@v18" + with: + default: "${{ matrix.python }}" + - name: Setup Env + run: | + pip install --upgrade pip + pip install -r requirements-${{matrix.plone}}.txt + pip install -U coveralls coverage + - name: Cache eggs + uses: actions/cache@v4 + env: + cache-name: cache-eggs + with: + path: ~/buildout-cache/eggs + key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ matrix.python }}-${{ matrix.plone }} + - name: buildout + run: | + sed -ie "s#test.cfg#test-${{matrix.plone}}.cfg#" gha.cfg + buildout -c gha.cfg + - name: code-analysis + run: | + bin/code-analysis + - name: test coverage + run: | + coverage run bin/test -t !robot + - name: Publish to Coveralls + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + coveralls --service=github diff --git a/.gitignore b/.gitignore index 07634d8..b3706dd 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,4 @@ local.cfg /pyvenv.cfg /pip-selfcheck.json +/.plone.versioncheck.tracked.json diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 0000000..6f73421 --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,5 @@ +[settings] +force_alphabetical_sort = True +force_single_line = True +lines_after_imports = 2 +line_length = 120 diff --git a/Makefile b/Makefile index b4de552..8bbec36 100644 --- a/Makefile +++ b/Makefile @@ -1,85 +1,105 @@ -# keep in sync with: https://github.com/kitconcept/buildout/edit/master/Makefile -# update by running 'make update' -SHELL := /bin/bash -CURRENT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) +#!/usr/bin/make +# pyenv is a requirement, with 3.8, 3.10, 3.13 python versions, and virtualenv installed in each version +# plone parameter must be passed to create environment 'make setup plone=6.1' or after a make cleanall +# The original Makefile can be found on https://github.com/IMIO/scripts-buildout + +SHELL=/bin/bash +plones=5.2 6.0 6.1 +b_o= +old_plone=$(shell [ -e .plone-version ] && cat .plone-version) + +ifeq (, $(shell which pyenv)) + $(error "pyenv command not found! Aborting") +endif + +ifndef plone +ifeq (,$(filter setup,$(MAKECMDGOALS))) + plone=$(old_plone) +endif +endif + +ifneq ($(wildcard bin/instance),) + b_o=-N +endif + +ifndef python +ifeq ($(plone),5.2) + python=3.8 +endif +ifeq ($(plone),6.0) + python=3.10 +endif +ifeq ($(plone),6.1) + python=3.13 +endif +endif + +all: buildout -version = 3 - -# We like colors -# From: https://coderwall.com/p/izxssa/colored-makefile-for-golang-projects -RED=`tput setaf 1` -GREEN=`tput setaf 2` -RESET=`tput sgr0` -YELLOW=`tput setaf 3` - -all: .installed.cfg - -# Add the following 'help' target to your Makefile -# And add help text after each target name starting with '\#\#' .PHONY: help -help: ## This help message - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +help: + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n\nTargets:\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-10s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST) +<<<<<<< HEAD .installed.cfg: bin/buildout *.cfg bin/buildout - -bin/buildout: bin/pip - bin/pip install --upgrade pip - bin/pip install -r requirements.txt - bin/pip install black || true - @touch -c $@ - -bin/python bin/pip: - python$(version) -m venv . || virtualenv --python=python$(version) . - -.PHONY: Build Plone 5.2 -build-plone-5.2: .installed.cfg ## Build Plone 5.2 - bin/pip install --upgrade pip - bin/pip install -r requirements.txt - bin/buildout -c plone-5.2.x.cfg - -.PHONY: Build Plone 5.2 Performance -build-plone-5.2-performance: .installed.cfg ## Build Plone 5.2 - bin/pip install --upgrade pip - bin/pip install -r requirements.txt - bin/buildout -c plone-5.2.x-performance.cfg - -.PHONY: Test -test: ## Test - bin/test - -.PHONY: Test Performance -test-performance: - jmeter -n -t performance.jmx -l jmeter.jtl - -.PHONY: Code Analysis -code-analysis: ## Code Analysis - bin/code-analysis - if [ -f "bin/black" ]; then bin/black src/ --check ; fi - -.PHONY: Black -black: ## Black - bin/code-analysis - if [ -f "bin/black" ]; then bin/black src/ ; fi - -.PHONY: zpretty -zpretty: ## zpretty - if [ -f "bin/zpretty" ]; then zpretty -i ./**/*.zcml; fi - -.PHONY: Build Docs -docs: ## Build Docs - bin/sphinxbuilder - -.PHONY: Test Release -test-release: ## Run Pyroma and Check Manifest - bin/pyroma -n 10 -d . - -.PHONY: Release -release: ## Release - bin/fullrelease - -.PHONY: Clean -clean: ## Clean - git clean -Xdf - -.PHONY: all clean +======= +.python-version: ## Setups pyenv version + @pyenv local `pyenv versions |grep " $(python)" |tail -1 |xargs` + @echo "Local pyenv version is `cat .python-version`" + @ if [[ `pyenv which virtualenv` != `pyenv prefix`* ]] ; then echo "You need to install virtualenv in `cat .python-version` pyenv python (pip install virtualenv)"; exit 1; fi + +bin/buildout: .python-version ## Setups environment + virtualenv . + ./bin/pip install --upgrade pip + ./bin/pip install -r requirements-$(plone).txt + @echo "$(plone)" > .plone-version +>>>>>>> 261a812 (Update buildout to Plone 6.1) + +.PHONY: setup +setup: oneof-plone backup cleanall bin/buildout restore ## Setups environment + +.PHONY: buildout +buildout: oneof-plone bin/buildout ## Runs setup and buildout + rm -f .installed.cfg .mr.developer.cfg + bin/buildout -t 5 -c test-$(plone).cfg ${b_o} + +.PHONY: test +test: oneof-plone bin/buildout ## run bin/test without robot + # can be run by example with: make test opt='-t "settings"' + bin/test -t \!robot ${opt} + +.PHONY: cleanall +cleanall: ## Cleans all installed buildout files + rm -fr bin include lib local share develop-eggs downloads eggs parts .installed.cfg .mr.developer.cfg .python-version pyvenv.cfg + +.PHONY: backup +backup: ## Backups db files + @if [ '$(old_plone)' != '' ] && [ -f var/filestorage/Data.fs ]; then mv var/filestorage/Data.fs var/filestorage/Data.fs.$(old_plone); mv var/blobstorage var/blobstorage.$(old_plone); fi + +.PHONY: restore +restore: ## Restores db files + @if [ '$(plone)' != '' ] && [ -f var/filestorage/Data.fs.$(plone) ]; then mv var/filestorage/Data.fs.$(plone) var/filestorage/Data.fs; mv var/blobstorage.$(plone) var/blobstorage; fi + +.PHONY: which-python +which-python: oneof-plone ## Displays versions information + @echo "current plone = $(old_plone)" + @echo "current python = `cat .python-version`" + @echo "plone var = $(plone)" + @echo "python var = $(python)" + +.PHONY: vcr +vcr: ## Shows requirements in checkversion-r.html + @bin/versioncheck -rbo checkversion-r-$(plone).html test-$(plone).cfg + +.PHONY: vcn +vcn: ## Shows newer packages in checkversion-n.html + @bin/versioncheck -npbo checkversion-n-$(plone).html test-$(plone).cfg + +.PHONY: guard-% +guard-%: + @ if [ "${${*}}" = "" ]; then echo "You must give a value for variable '$*' : like $*=xxx"; exit 1; fi + +.PHONY: oneof-% +oneof-%: + @ if ! echo "${${*}s}" | tr " " '\n' |grep -Fqx "${${*}}"; then echo "Invalid '$*' parameter ('${${*}}') : must be one of '${${*}s}'"; exit 1; fi diff --git a/base.cfg b/base.cfg index 0a98034..009c492 100644 --- a/base.cfg +++ b/base.cfg @@ -1,60 +1,83 @@ [buildout] -index = https://pypi.org/simple/ -parts = +package-name = plone.formwidget.masterselect +package-extras = [test] + +extends = + https://raw.githubusercontent.com/collective/buildout.plonetest/master/qa.cfg + checkouts.cfg + +extensions = + mr.developer + plone.versioncheck + +parts += instance - dependencies - code-analysis - test - test-coverage - update-translations - find-untranslated omelette -versions = versions + ploneversioncheck + createcoverage + robot +# coverage +# test-coverage +# plone-helper-scripts + develop = . -[instance] -recipe = plone.recipe.zope2instance -user = admin:admin -http-address = 8080 -eggs = +eggs += Plone Pillow - plone.formwidget.masterselect [test] -zcml = plone.formwidget.masterselect +# Products.PDBDebugMode +# collective.profiler +# ipdb + pdbp +# plone.reload + +package-extras += + pdbp + +always-checkout = force + +[instance] +environment-vars += + PYTHONBREAKPOINT pdbp.set_trace +eggs += + ${buildout:eggs} +zcml += -[dependencies] +[test] +environment = testenv +initialization += + os.environ['PYTHONBREAKPOINT'] = 'pdbp.set_trace' + +[testenv] +zope_i18n_compile_mo_files = true + +[omelette] +recipe = collective.recipe.omelette +eggs = ${test:eggs} + +[ploneversioncheck] recipe = zc.recipe.egg -eggs = - i18ndude - zest.pocompile - zest.releaser[recommended] - zestreleaser.towncrier - towncrier - readme - docutils - coverage - pyroma +eggs = plone.versioncheck [code-analysis] recipe = plone.recipe.codeanalysis -directory = ${buildout:directory}/src/plone -flake8-max-line-length = 88 -flake8-exclude=docs,bin,*.egg,setup.py,overrides -flake8-max-complexity = 20 -flake8-ignore = E501, W503 +pre-commit-hook = True +return-status-codes = True +directory = ${buildout:directory}/src/plone/formwidget/masterselect +flake8-ignore = E123,E124,E501,E126,E127,E128,W391,C901,W503,W504 flake8-extensions = - flake8-debugger - flake8-print -# additional pep8/flake8 rules, see docs for details -# https://pep8.readthedocs.io/en/1.4.6/intro.html#error-codes -# - E123: closing bracket does not match indentation of opening bracket’s line -# flake8-select = E123 + flake8-isort -[test] -recipe = collective.xmltestreport -eggs = ${instance:eggs} -defaults = ['-s', 'plone.formwidget.masterselect', '--auto-color', '--auto-progress'] -environment = environment +[robot] +recipe = zc.recipe.egg +eggs = + Pillow + ${test:eggs} + plone.app.robotframework[reload, debug] + +[coverage] +recipe = zc.recipe.egg +eggs = coverage [test-coverage] recipe = collective.recipe.template @@ -69,44 +92,12 @@ input = inline: output = ${buildout:directory}/bin/test-coverage mode = 755 -[environment] -ROBOT_SELENIUM2LIBRARY_RUN_ON_FAILURE = Capture page screenshot and log source - -[update-translations] -recipe = collective.recipe.template -input = inline: - domain=plone.formwidget.masterselect - maindir=src/plone/formwidget/masterselect - locales=$maindir/locales - echo "Update translations for $domain" - if [ ! -f $locales/$domain.pot ]; then - # Create .pot file if it does not exist yet - touch $locales/$domain.pot - fi - if [ ! -f $locales/de/LC_MESSAGES ]; then - # Create de/LC_MESSAGES directory if it does not exist yet - mkdir -p $locales/de/LC_MESSAGES - fi - if [ ! -f $locales/de/LC_MESSAGES/$domain.po ]; then - # Create .po file if it does not exist yet - touch $locales/de/LC_MESSAGES/$domain.po - fi - ${buildout:directory}/bin/i18ndude rebuild-pot --pot $locales/$domain.pot --create $domain $maindir - ${buildout:directory}/bin/i18ndude sync --pot $locales/$domain.pot $locales/*/LC_MESSAGES/$domain.po -output = ${buildout:directory}/bin/updatetranslations -mode = 755 - -[find-untranslated] -recipe = collective.recipe.template -input = inline: - #!/bin/sh - bin/i18ndude find-untranslated `find -L src/plone/formwidget/masterselect -regex ".*\.[cz]?pt"` -output = ${buildout:directory}/bin/find-untranslated -mode = 755 - -[omelette] -recipe = collective.recipe.omelette -eggs = ${test:eggs} - -[versions] -plone.formwidget.masterselect = \ No newline at end of file +[plone-helper-scripts] +recipe = zc.recipe.egg +eggs = + Products.CMFPlone + ${instance:eggs} +interpreter = zopepy +scripts = + zopepy + plone-compile-resources diff --git a/checkouts.cfg b/checkouts.cfg new file mode 100644 index 0000000..b2d77a4 --- /dev/null +++ b/checkouts.cfg @@ -0,0 +1,13 @@ +[buildout] +always-checkout = force +auto-checkout += + +[remotes] +plone = https://github.com/plone +plone_push = git@github.com:plone +zopefoundation = https://github.com/zopefoundation +zopefoundation_push = git@github.com:zopefoundation +col = https://github.com/collective +col_push = git@github.com:collective + +[sources] diff --git a/ci.cfg b/ci.cfg deleted file mode 100644 index af1a4f2..0000000 --- a/ci.cfg +++ /dev/null @@ -1,7 +0,0 @@ -[buildout] -extends = plone-x.x.x.cfg - -[code-analysis] -recipe = plone.recipe.codeanalysis -pre-commit-hook = False -return-status-codes = True diff --git a/constraints_plone60.txt b/constraints_plone60.txt deleted file mode 100644 index 369b88b..0000000 --- a/constraints_plone60.txt +++ /dev/null @@ -1,16 +0,0 @@ --c https://dist.plone.org/release/6.0-dev/requirements.txt - -#setuptools==54.0.0 -#zc.buildout==3.0.0b2 -#pip==21.0.1 -# -## Windows specific down here (has to be installed here, fails in buildout) -## Dependency of zope.sendmail: -#pywin32 ; platform_system == 'Windows' -# -## SSL Certs on windows, because Python is missing them otherwise: -#certifi ; platform_system == 'Windows' - -isort>=5 -black==22.3.0 -flake8>=4.0.1 diff --git a/gha.cfg b/gha.cfg new file mode 100644 index 0000000..ef2e218 --- /dev/null +++ b/gha.cfg @@ -0,0 +1,5 @@ +[buildout] +extends = + test.cfg +eggs-directory = ~/buildout-cache/eggs +download-cache = ~/buildout-cache/downloads diff --git a/plone-5.2.x.cfg b/plone-5.2.x.cfg deleted file mode 100644 index dbc7af7..0000000 --- a/plone-5.2.x.cfg +++ /dev/null @@ -1,4 +0,0 @@ -[buildout] -extends = - https://dist.plone.org/release/5.2.14/versions.cfg - base.cfg diff --git a/plone-6.0.x.cfg b/plone-6.0.x.cfg deleted file mode 100644 index 6c5fb80..0000000 --- a/plone-6.0.x.cfg +++ /dev/null @@ -1,19 +0,0 @@ -[buildout] -extends = - https://dist.plone.org/release/6-latest/versions.cfg - base.cfg -find-links += https://dist.plone.org/thirdparty/ -versions=versions - -[versions] -black = 21.7b0 - -# Error: The requirement ('virtualenv>=20.0.35') is not allowed by your [versions] constraint (20.0.26) -virtualenv = 20.0.35 - -# Error: The requirement ('importlib-metadata<4.3,>=1.1.0') is not allowed by your [versions] constraint (4.12.0) -importlib-metadata = 4.13 - -# cffi 1.14.3 fails on apple m1 -# cffi 1.14.4 fails with "ModuleNotFoundError: No module named '_cffi_backend'" -cffi = 1.14.6 diff --git a/requirements-5.2.txt b/requirements-5.2.txt index 8466fed..65fbf37 100644 --- a/requirements-5.2.txt +++ b/requirements-5.2.txt @@ -1,4 +1,4 @@ -# Keep this file in sync with: https://github.com/kitconcept/buildout/edit/master/requirements.txt -setuptools==42.0.2 -zc.buildout==2.13.3 +-c https://dist.plone.org/release/5.2-latest/requirements.txt +setuptools +zc.buildout wheel diff --git a/requirements-6.0.txt b/requirements-6.0.txt index cab798d..75ae54c 100644 --- a/requirements-6.0.txt +++ b/requirements-6.0.txt @@ -1,3 +1,3 @@ --c constraints_plone60.txt +-c https://dist.plone.org/release/6.0-latest/requirements.txt setuptools zc.buildout diff --git a/requirements-6.1.txt b/requirements-6.1.txt new file mode 100644 index 0000000..aaf0495 --- /dev/null +++ b/requirements-6.1.txt @@ -0,0 +1,3 @@ +-c https://dist.plone.org/release/6.1-latest/requirements.txt +setuptools +zc.buildout diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 05fc900..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ --r requirements-5.2.txt diff --git a/setup.py b/setup.py index d2e37e8..dc2adfd 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ from setuptools import setup -version = '2.0.1+once.1' +version = '3.0.1.dev0' long_description = '\n\n'.join([ open('README.rst').read(), @@ -23,6 +23,15 @@ 'Environment :: Web Environment', 'Framework :: Plone', 'Framework :: Plone :: 5.2', + 'Framework :: Plone :: 6.0', + 'Framework :: Plone :: 6.1', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', 'Intended Audience :: End Users/Desktop', 'License :: OSI Approved :: GNU General Public License (GPL)', 'Operating System :: OS Independent', diff --git a/src/plone/formwidget/masterselect/configure.zcml b/src/plone/formwidget/masterselect/configure.zcml index cd7e5c4..a094700 100644 --- a/src/plone/formwidget/masterselect/configure.zcml +++ b/src/plone/formwidget/masterselect/configure.zcml @@ -89,13 +89,21 @@ post_handler=".setuphandlers.post_install" /> + + - - - \ No newline at end of file diff --git a/src/plone/formwidget/masterselect/profiles/uninstall/registry.xml b/src/plone/formwidget/masterselect/profiles/uninstall/registry.xml new file mode 100644 index 0000000..de7f673 --- /dev/null +++ b/src/plone/formwidget/masterselect/profiles/uninstall/registry.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/src/plone/formwidget/masterselect/profiles/uninstall/types.xml b/src/plone/formwidget/masterselect/profiles/uninstall/types.xml new file mode 100644 index 0000000..94dcbfd --- /dev/null +++ b/src/plone/formwidget/masterselect/profiles/uninstall/types.xml @@ -0,0 +1,4 @@ + + + diff --git a/src/plone/formwidget/masterselect/tests/robot/test_masterselect_demo.robot b/src/plone/formwidget/masterselect/tests/robot/test_masterselect_demo.robot index 1737c17..3102ddd 100644 --- a/src/plone/formwidget/masterselect/tests/robot/test_masterselect_demo.robot +++ b/src/plone/formwidget/masterselect/tests/robot/test_masterselect_demo.robot @@ -89,7 +89,7 @@ I am on the masterselect demo page as a ${role} Go To ${PLONE_URL}/++add++plone.formwidget.masterselect.demo I select '${value}' on master field '${id}' - Select From List css=#${id} ${value} + Select From List By Value css=#${id} ${value} Slave field '${id}' should be visible Element should become visible css=#formfield-form-widgets-slaveField${id} @@ -146,6 +146,6 @@ Add available language Enable Autologin As Manager Set Autologin Username ${TEST_USER_NAME} Go To ${PLONE_URL}/@@language-controlpanel - Select From List css=#form-widgets-available_languages-from ${available_language} + Select From List By Value css=#form-widgets-available_languages-from ${available_language} Click Button name=from2toButton Click Button css=#form-buttons-save diff --git a/src/plone/formwidget/masterselect/tests/test_setup.py b/src/plone/formwidget/masterselect/tests/test_setup.py index 839735a..b24c8da 100644 --- a/src/plone/formwidget/masterselect/tests/test_setup.py +++ b/src/plone/formwidget/masterselect/tests/test_setup.py @@ -22,9 +22,15 @@ def setUp(self): def test_product_installed(self): """Test if plone.formwidget.masterselect is installed.""" - self.assertTrue(self.installer.isProductInstalled( + self.assertTrue(self.installer.is_product_installed( 'plone.formwidget.masterselect')) + def test_uninstall(self): + self.installer.uninstall_product('plone.formwidget.masterselect') + self.assertFalse( + self.installer.is_product_installed('plone.formwidget.masterselect') + ) + def _get_profiles_upgrades(self): """Return the list o profiles of upgrade steps.""" profiles_upgrades = [] diff --git a/src/plone/formwidget/masterselect/tests/test_upgrade_steps.py b/src/plone/formwidget/masterselect/tests/test_upgrade_steps.py index 6bf519a..2f2bbaf 100644 --- a/src/plone/formwidget/masterselect/tests/test_upgrade_steps.py +++ b/src/plone/formwidget/masterselect/tests/test_upgrade_steps.py @@ -52,6 +52,7 @@ def _how_many_upgrades_to_do(self): class Upgrade4to5TestCase(UpgradeTestCaseBase): def setUp(self): + self.request = self.layer['request'] UpgradeTestCaseBase.setUp(self, '4', '5') def test_registrations(self): @@ -81,6 +82,11 @@ def test_fix_resource_not_found(self): self.assertTrue(bundle.compile) self._do_upgrade_step(step) + try: + from Products.CMFPlone.resources.browser.resource import REQUEST_CACHE_KEY + setattr(self.request, REQUEST_CACHE_KEY, None) + except ImportError: + pass self.assertNotIn( 'plone.formwidget.masterselect/master-compiled.css', diff --git a/test-5.2.cfg b/test-5.2.cfg new file mode 100644 index 0000000..de9f6f8 --- /dev/null +++ b/test-5.2.cfg @@ -0,0 +1,12 @@ +[buildout] + +extends = + https://raw.githubusercontent.com/collective/buildout.plonetest/master/test-5.2.x.cfg + base.cfg + +#update-versions-file = test-5.2.cfg + +[versions] +importlib-metadata = 2.1.3 +pdbp = 1.5.4 +pygments = 2.17.2 diff --git a/test-6.0.cfg b/test-6.0.cfg new file mode 100644 index 0000000..fbdb8a1 --- /dev/null +++ b/test-6.0.cfg @@ -0,0 +1,30 @@ +[buildout] + +extends = + https://raw.githubusercontent.com/collective/buildout.plonetest/master/test-6.0.x.cfg + base.cfg + +update-versions-file = test-6.0.cfg + +[versions] +# to keep prompt-toolkit < 3 +ipython = 8.3.0 + +ipdb = 0.13.9 +iw.debug = 0.3 +jedi = 0.18.1 +parso = 0.8.3 +pdbp = 1.7.0 + +# Required by: +# ipdb +asttokens = 2.0.8 +backcall = 0.2.0 +executing = 1.1.1 +matplotlib-inline = 0.1.6 +pexpect = 4.8.0 +pickleshare = 0.7.5 +ptyprocess = 0.7.0 +pure-eval = 0.2.2 +stack-data = 0.5.1 +traitlets = 5.4.0 diff --git a/test-6.1.cfg b/test-6.1.cfg new file mode 100644 index 0000000..d708207 --- /dev/null +++ b/test-6.1.cfg @@ -0,0 +1,30 @@ +[buildout] + +extends = + https://raw.githubusercontent.com/collective/buildout.plonetest/master/test-6.1.x.cfg + base.cfg + +#update-versions-file = test-6.1.cfg + +[versions] +# to keep prompt-toolkit < 3 +ipython = 8.3.0 + +ipdb = 0.13.9 +iw.debug = 0.3 +jedi = 0.18.1 +parso = 0.8.3 +pdbp = 1.7.0 + +# Required by: +# ipdb +asttokens = 2.0.8 +backcall = 0.2.0 +executing = 1.1.1 +matplotlib-inline = 0.1.6 +pexpect = 4.8.0 +pickleshare = 0.7.5 +ptyprocess = 0.7.0 +pure-eval = 0.2.2 +stack-data = 0.5.1 +traitlets = 5.4.0 From 4303eb8a94dff5a5e2a272a4ab7c737e6a055376 Mon Sep 17 00:00:00 2001 From: Laurent Lasudry Date: Fri, 12 Sep 2025 12:33:27 +0200 Subject: [PATCH 17/19] Fix typos --- README.rst | 2 +- src/plone/formwidget/masterselect/widget.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 0193aae..080e37d 100644 --- a/README.rst +++ b/README.rst @@ -18,7 +18,7 @@ Feel free to help edit this document to help explain things better! Example ======= -For more complex examples see ``demo.py`` in pacakge directory.:: +For more complex examples see ``demo.py`` in package directory.:: from zope import schema from plone.supermodel import model diff --git a/src/plone/formwidget/masterselect/widget.py b/src/plone/formwidget/masterselect/widget.py index 58ba1a1..4ea5ec4 100644 --- a/src/plone/formwidget/masterselect/widget.py +++ b/src/plone/formwidget/masterselect/widget.py @@ -187,8 +187,8 @@ def __init__(self, context, request): # from plone.transformchain.interfaces import DISABLE_TRANSFORM_REQUEST_KEY # request.environ[DISABLE_TRANSFORM_REQUEST_KEY] = True - def createVocaabulary(self, value): - """Create a simple vocubulary from provided value, list or tuple + def createVocabulary(self, value): + """Create a simple vocabulary from provided value, list or tuple """ terms = [] for token in value: @@ -241,7 +241,7 @@ def __call__(self): vocabulary = self.getVocabulary(slave, value) if isinstance(vocabulary, (tuple, list)): - vocabulary = self.createVocaabulary(vocabulary) + vocabulary = self.createVocabulary(vocabulary) widget = self.widget.form.widgets.get(slave['name']) if widget is None: From 786e3e29f07c2e50a748dff57f8605a57c96d53c Mon Sep 17 00:00:00 2001 From: Laurent Lasudry Date: Fri, 12 Sep 2025 12:33:41 +0200 Subject: [PATCH 18/19] Update CI badges --- README.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 080e37d..254dca1 100644 --- a/README.rst +++ b/README.rst @@ -1,8 +1,9 @@ -.. image:: https://travis-ci.org/collective/plone.formwidget.masterselect.svg - :target: https://travis-ci.org/collective/plone.formwidget.masterselect +.. image:: https://github.com/collective/plone.formwidget.masterselect/actions/workflows/main.yml/badge.svg + :target: https://github.com/collective/plone.formwidget.masterselect/actions/workflows/main.yml + +.. image:: https://coveralls.io/repos/github/collective/plone.formwidget.masterselect/badge.svg + :target: https://coveralls.io/github/collective/plone.formwidget.masterselect -.. image:: https://coveralls.io/repos/collective/plone.formwidget.masterselect/badge.png - :target: https://coveralls.io/r/collective/plone.formwidget.masterselect MasterSelectWidget ================== From 4ed187fa14c8e0481ed135952a1da6f908820c4c Mon Sep 17 00:00:00 2001 From: Laurent Lasudry Date: Fri, 12 Sep 2025 12:36:55 +0200 Subject: [PATCH 19/19] Black & isort --- setup.py | 102 +++--- src/plone/formwidget/masterselect/__init__.py | 15 +- src/plone/formwidget/masterselect/demo.py | 317 ++++++++++-------- .../formwidget/masterselect/interfaces.py | 10 +- .../formwidget/masterselect/setuphandlers.py | 4 +- src/plone/formwidget/masterselect/testing.py | 28 +- .../masterselect/tests/test_fields.py | 1 + .../masterselect/tests/test_intergration.py | 9 +- .../masterselect/tests/test_robot.py | 23 +- .../masterselect/tests/test_setup.py | 26 +- .../masterselect/tests/test_upgrade_steps.py | 37 +- src/plone/formwidget/masterselect/widget.py | 130 ++++--- 12 files changed, 370 insertions(+), 332 deletions(-) diff --git a/setup.py b/setup.py index dc2adfd..d50a61a 100644 --- a/setup.py +++ b/setup.py @@ -4,69 +4,71 @@ from setuptools import setup -version = '3.0.1.dev0' +version = "3.0.1.dev0" -long_description = '\n\n'.join([ - open('README.rst').read(), - open('CONTRIBUTORS.rst').read(), - open('CHANGES.rst').read(), -]) +long_description = "\n\n".join( + [ + open("README.rst").read(), + open("CONTRIBUTORS.rst").read(), + open("CHANGES.rst").read(), + ] +) setup( - name='plone.formwidget.masterselect', + name="plone.formwidget.masterselect", version=version, - description='A z3c.form widget that controls the vocabulary or ' - 'display of other fields on an edit page', + description="A z3c.form widget that controls the vocabulary or " + "display of other fields on an edit page", long_description=long_description, classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Environment :: Web Environment', - 'Framework :: Plone', - 'Framework :: Plone :: 5.2', - 'Framework :: Plone :: 6.0', - 'Framework :: Plone :: 6.1', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.12', - 'Programming Language :: Python :: 3.13', - 'Intended Audience :: End Users/Desktop', - 'License :: OSI Approved :: GNU General Public License (GPL)', - 'Operating System :: OS Independent', - 'Programming Language :: JavaScript', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3.8', - 'Topic :: Software Development :: Libraries :: Python Modules', - 'Topic :: Software Development :: Widget Sets', + "Development Status :: 5 - Production/Stable", + "Environment :: Web Environment", + "Framework :: Plone", + "Framework :: Plone :: 5.2", + "Framework :: Plone :: 6.0", + "Framework :: Plone :: 6.1", + "Programming Language :: Python", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Intended Audience :: End Users/Desktop", + "License :: OSI Approved :: GNU General Public License (GPL)", + "Operating System :: OS Independent", + "Programming Language :: JavaScript", + "Programming Language :: Python", + "Programming Language :: Python :: 3.8", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Software Development :: Widget Sets", ], - keywords='plone widget', - author='Jason Mehring', - author_email='nrgaway@yahoo.com', - url='https://github.com/collective/plone.formwidget.masterselect', - license='GPL', - packages=find_packages("src", exclude=['ez_setup']), - namespace_packages=['plone', 'plone.formwidget'], + keywords="plone widget", + author="Jason Mehring", + author_email="nrgaway@yahoo.com", + url="https://github.com/collective/plone.formwidget.masterselect", + license="GPL", + packages=find_packages("src", exclude=["ez_setup"]), + namespace_packages=["plone", "plone.formwidget"], package_dir={"": "src"}, include_package_data=True, zip_safe=False, install_requires=[ - 'plone.supermodel', - 'plone.z3cform', - 'Products.CMFPlone', - 'setuptools', - 'z3c.form', - 'zope.component', - 'zope.schema', + "plone.supermodel", + "plone.z3cform", + "Products.CMFPlone", + "setuptools", + "z3c.form", + "zope.component", + "zope.schema", ], extras_require={ - 'test': [ - 'plone.app.dexterity', - 'plone.app.robotframework', - 'plone.app.testing [robot] >= 4.2.2', - 'plone.registry', - 'Products.GenericSetup', + "test": [ + "plone.app.dexterity", + "plone.app.robotframework", + "plone.app.testing [robot] >= 4.2.2", + "plone.registry", + "Products.GenericSetup", ], }, entry_points=""" diff --git a/src/plone/formwidget/masterselect/__init__.py b/src/plone/formwidget/masterselect/__init__.py index f54a08f..98effce 100644 --- a/src/plone/formwidget/masterselect/__init__.py +++ b/src/plone/formwidget/masterselect/__init__.py @@ -1,14 +1,10 @@ from plone.formwidget.masterselect.interfaces import IMasterSelectBoolField from plone.formwidget.masterselect.interfaces import IMasterSelectField from plone.formwidget.masterselect.interfaces import IMasterSelectRadioField -from plone.formwidget.masterselect.widget import \ - MasterSelectBoolFieldWidget # noqa: F401 -from plone.formwidget.masterselect.widget import \ - MasterSelectFieldWidget # noqa: F401 -from plone.formwidget.masterselect.widget import \ - MasterSelectRadioFieldWidget # noqa: F401 -from plone.formwidget.masterselect.widget import \ - MasterSelectWidget # noqa: F401 +from plone.formwidget.masterselect.widget import MasterSelectBoolFieldWidget # noqa: F401 +from plone.formwidget.masterselect.widget import MasterSelectFieldWidget # noqa: F401 +from plone.formwidget.masterselect.widget import MasterSelectRadioFieldWidget # noqa: F401 +from plone.formwidget.masterselect.widget import MasterSelectWidget # noqa: F401 from zope.i18nmessageid import MessageFactory from zope.interface import implementer from zope.schema import Bool @@ -24,6 +20,7 @@ class MasterSelectField(Choice): """MasterSelectField that provides additional properties for widget (extends schema.Choice) """ + slave_fields = () def __init__(self, slave_fields=(), **kw): @@ -36,6 +33,7 @@ class MasterSelectBoolField(Bool): """MasterSelectBoolField that provides addtional properties for widget (extends schema.Bool) """ + slave_fields = () def __init__(self, slave_fields=(), **kw): @@ -48,6 +46,7 @@ class MasterSelectRadioField(Choice): """MasterSelectRadioField that provides additional properties for widget (extends schema.Choice) """ + slave_fields = () def __init__(self, slave_fields=(), **kw): diff --git a/src/plone/formwidget/masterselect/demo.py b/src/plone/formwidget/masterselect/demo.py index 4372c69..c0d2e1f 100644 --- a/src/plone/formwidget/masterselect/demo.py +++ b/src/plone/formwidget/masterselect/demo.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- +from plone.formwidget.masterselect import _ from plone.formwidget.masterselect import MasterSelectBoolField from plone.formwidget.masterselect import MasterSelectField -from plone.formwidget.masterselect import _ from plone.supermodel import model from zope import schema @@ -13,7 +13,7 @@ def getSlaveVocab(master): The displayed value has 'num: ' prepended. """ results = range(int(master) + 1, 10) - results = [(str(a), 'num: ' + str(a)) for a in results] + results = [(str(a), "num: " + str(a)) for a in results] return results @@ -46,218 +46,257 @@ class IMasterSelectDemo(model.Schema): """ masterField = MasterSelectField( - title=_(u'MasterField'), - description=_(u'This field controls the vocabulary of slaveField1,' - u'the available values in slaveField1 will be equal ' - u'to the numbers between the selected number and 10. ' - u'When the value 2 or 4 is selected, slaveField2 will ' - u'be hidden. When the value 1 or 5 is selected, ' - u'slaveField3 will be disabled. When value 6 is ' - u'selected, slaveField one will be hidden.'), + title=_("MasterField"), + description=_( + "This field controls the vocabulary of slaveField1," + "the available values in slaveField1 will be equal " + "to the numbers between the selected number and 10. " + "When the value 2 or 4 is selected, slaveField2 will " + "be hidden. When the value 1 or 5 is selected, " + "slaveField3 will be disabled. When value 6 is " + "selected, slaveField one will be hidden." + ), values=(1, 2, 3, 4, 5, 6), slave_fields=( # Controls the vocab of slaveField1 - {'name': 'slaveField1', - 'action': 'vocabulary', - 'vocab_method': getSlaveVocab, - 'control_param': 'master', - }, + { + "name": "slaveField1", + "action": "vocabulary", + "vocab_method": getSlaveVocab, + "control_param": "master", + }, # Controls the visibility of slaveField1 also - {'name': 'slaveField1', - 'action': 'hide', - 'hide_values': ('6',), - 'siblings': True, - }, + { + "name": "slaveField1", + "action": "hide", + "hide_values": ("6",), + "siblings": True, + }, # Controls the visibility of slaveField2 - {'name': 'slaveField2', - 'action': 'hide', - 'hide_values': ('2', '4'), - 'siblings': True, - }, + { + "name": "slaveField2", + "action": "hide", + "hide_values": ("2", "4"), + "siblings": True, + }, # Disables slaveField3 - {'name': 'slaveField3', - 'slaveID': '#form-widgets-slaveField3-0', - 'action': 'disable', - 'hide_values': ('1', '5'), - 'siblings': True, - }, + { + "name": "slaveField3", + "slaveID": "#form-widgets-slaveField3-0", + "action": "disable", + "hide_values": ("1", "5"), + "siblings": True, + }, ), required=True, ) slaveField1 = schema.Set( - title=_(u'SlaveField1'), - description=_(u'This field\'s vocabulary is controlled by the value ' - u'selected in masterField. The values available here ' - u'will be the numbers between the number selected in ' - u'masterField and 10. The field will be hidden when 6 ' - u'is selected in the masterField.'), + title=_("SlaveField1"), + description=_( + "This field's vocabulary is controlled by the value " + "selected in masterField. The values available here " + "will be the numbers between the number selected in " + "masterField and 10. The field will be hidden when 6 " + "is selected in the masterField." + ), value_type=schema.Choice(values=(1, 2, 3, 4, 5, 6)), required=False, ) slaveField2 = schema.Choice( - title=_(u'SlaveField2'), - description=_(u'This field\'s visibility is controlled by the value ' - u'selected in masterField. It will become invisible ' - u'when the values 2 or 4 are selected.'), - values=('10', '20', '30', '40', '50'), + title=_("SlaveField2"), + description=_( + "This field's visibility is controlled by the value " + "selected in masterField. It will become invisible " + "when the values 2 or 4 are selected." + ), + values=("10", "20", "30", "40", "50"), required=False, ) slaveField3 = schema.Bool( - title=_(u'SlaveField3'), - description=_(u'This field\'s availability is controlled by the value ' - u'selected in masterField. It will be deactivated when ' - u'the values 1 or 5 are selected.'), + title=_("SlaveField3"), + description=_( + "This field's availability is controlled by the value " + "selected in masterField. It will be deactivated when " + "the values 1 or 5 are selected." + ), required=False, ) masterField2 = MasterSelectField( - title=_(u'MasterField2'), - description=_(u'This field controls the vocabulary of ' - u'slaveMasterField, the available values in ' - u'slaveMasterField will be the 5 letters after the ' - u'selected letter. It also controls the current value ' - u'of the field SlaveValueField, which contains the ' - u'ROT13 transformed value of the selection.'), - values=('a', 'b', 'c', 'd', 'e', 'f'), + title=_("MasterField2"), + description=_( + "This field controls the vocabulary of " + "slaveMasterField, the available values in " + "slaveMasterField will be the 5 letters after the " + "selected letter. It also controls the current value " + "of the field SlaveValueField, which contains the " + "ROT13 transformed value of the selection." + ), + values=("a", "b", "c", "d", "e", "f"), slave_fields=( # Controls the vocab of slaveMasterField - {'name': 'slaveMasterField', - 'action': 'vocabulary', - 'vocab_method': getSlaveVocab2, - 'control_param': 'master', - }, + { + "name": "slaveMasterField", + "action": "vocabulary", + "vocab_method": getSlaveVocab2, + "control_param": "master", + }, # Controls the value of slaveValueField - {'name': 'slaveValueField', - 'action': 'value', - 'vocab_method': getSlaveValue, - 'control_param': 'master', - }, + { + "name": "slaveValueField", + "action": "value", + "vocab_method": getSlaveValue, + "control_param": "master", + }, ), required=True, ) slaveMasterField = MasterSelectField( - title=_(u'SlaveMasterField'), - description=_(u'This field\'s vocabulary is controlled by the value ' - u'selected in masterField2. The values available here ' - u'will be the 5 letters after the selected letter. ' - u'This field also controls the visibility of ' - u'slaveField4. If the values c or g are selected ' - u'slaveField4 will be hidden. Only the input field will ' - u'be hidden while the title and description will remain ' - u'visible'), + title=_("SlaveMasterField"), + description=_( + "This field's vocabulary is controlled by the value " + "selected in masterField2. The values available here " + "will be the 5 letters after the selected letter. " + "This field also controls the visibility of " + "slaveField4. If the values c or g are selected " + "slaveField4 will be hidden. Only the input field will " + "be hidden while the title and description will remain " + "visible" + ), values=(1, 2, 3, 4, 5, 6), slave_fields=( # Controls the visibility of slaveField4 - {'name': 'slaveField4', - 'action': 'hide', - 'hide_values': ('c', 'g'), - 'siblings': False, - }, + { + "name": "slaveField4", + "action": "hide", + "hide_values": ("c", "g"), + "siblings": False, + }, ), required=True, ) slaveField4 = schema.Choice( - title=_(u'SlaveField4'), - description=_(u'This field\'s visibility is controlled by the value ' - u'selected in slaveMasterField. It will become ' - u'invisible when the values c or g are selected. Notice ' - u'only the form field disappears while the label and ' - u'description remain (siblings set to False'), - values=('10', '20', '30', '40', '50'), + title=_("SlaveField4"), + description=_( + "This field's visibility is controlled by the value " + "selected in slaveMasterField. It will become " + "invisible when the values c or g are selected. Notice " + "only the form field disappears while the label and " + "description remain (siblings set to False" + ), + values=("10", "20", "30", "40", "50"), required=False, ) slaveValueField = schema.TextLine( - title=_(u'SlaveValueField'), - description=_(u'This field\'s value is controlled by the value ' - u'selected in MasterField2. It will display the ROT13 ' - u'transformation of the value selected. The field\'s ' - u'availability is controlled by the value selected in ' - u'masterField3. It only will be activated when the ' - u'values "one" is selected.'), + title=_("SlaveValueField"), + description=_( + "This field's value is controlled by the value " + "selected in MasterField2. It will display the ROT13 " + "transformation of the value selected. The field's " + "availability is controlled by the value selected in " + "masterField3. It only will be activated when the " + 'values "one" is selected.' + ), required=False, ) masterField3 = MasterSelectField( - title=_(u'MasterField3'), - description=_(u'This field controls the visibility of slaveField5. ' - u'It will become visible only when the value "other" ' - u'is selected. It also controls the availability of' - u'slaveValueField. It will be enabled when the value ' - u'"one" is selected.'), - values=('one', 'two', 'three', 'other'), + title=_("MasterField3"), + description=_( + "This field controls the visibility of slaveField5. " + 'It will become visible only when the value "other" ' + "is selected. It also controls the availability of" + "slaveValueField. It will be enabled when the value " + '"one" is selected.' + ), + values=("one", "two", "three", "other"), slave_fields=( # Controls the visibility of slaveField5 - {'name': 'slaveField5', - 'action': 'show', - 'hide_values': ('other',), - 'siblings': True, - }, + { + "name": "slaveField5", + "action": "show", + "hide_values": ("other",), + "siblings": True, + }, # Enable slaveValueField - {'name': 'slaveValueField', - 'action': 'enable', - 'hide_values': ('one',), - }, + { + "name": "slaveValueField", + "action": "enable", + "hide_values": ("one",), + }, ), required=True, ) slaveField5 = schema.TextLine( - title=_(u'SlaveField5'), - description=_(u'This field\'s visibility is controlled by the value ' - u'selected in masterField3. It will become visible ' - u'only when the value "other" is selected.'), + title=_("SlaveField5"), + description=_( + "This field's visibility is controlled by the value " + "selected in masterField3. It will become visible " + 'only when the value "other" is selected.' + ), required=False, ) masterBoolean = MasterSelectBoolField( - title=_(u'MasterBoolean'), - description=_(u'This field controls the visibility of slaveField6, ' - u'which will only become visible when this checkbox is ' - u'checked.'), + title=_("MasterBoolean"), + description=_( + "This field controls the visibility of slaveField6, " + "which will only become visible when this checkbox is " + "checked." + ), slave_fields=( - {'masterID': 'form-widgets-masterBoolean-0', - 'name': 'slaveField6', - 'action': 'show', - 'hide_values': 1, - 'siblings': True, - }, + { + "masterID": "form-widgets-masterBoolean-0", + "name": "slaveField6", + "action": "show", + "hide_values": 1, + "siblings": True, + }, ), required=True, ) slaveField6 = schema.TextLine( - title=_(u'SlaveField6'), - description=_(u'This field\'s visibility is controlled by the value ' - u'selected in masterBoolean. It will become visible ' - u'only when that checkbox is checked.'), + title=_("SlaveField6"), + description=_( + "This field's visibility is controlled by the value " + "selected in masterBoolean. It will become visible " + "only when that checkbox is checked." + ), required=False, ) masterField4 = MasterSelectField( - title=_(u'MasterField 4'), - description=_(u'This field controls the vocabulary of slaveField7.' - u'The selected value becomes the value of slaveField7.'), - values=('ok', 'nok'), + title=_("MasterField 4"), + description=_( + "This field controls the vocabulary of slaveField7." + "The selected value becomes the value of slaveField7." + ), + values=("ok", "nok"), slave_fields=( - {'name': 'slaveField7', - 'action': 'vocabulary', - 'vocab_method': getSlaveVocab3, - 'control_param': 'master', - }, + { + "name": "slaveField7", + "action": "vocabulary", + "vocab_method": getSlaveVocab3, + "control_param": "master", + }, ), required=True, ) slaveField7 = schema.Choice( - title=_(u'SlaveField7'), - description=_(u'This field\'s value is controlled by the value ' - u'selected in masterBoolean.'), + title=_("SlaveField7"), + description=_( + "This field's value is controlled by the value " + "selected in masterBoolean." + ), required=False, - values=('ok', 'nok') + values=("ok", "nok"), ) diff --git a/src/plone/formwidget/masterselect/interfaces.py b/src/plone/formwidget/masterselect/interfaces.py index 5647523..ca62acc 100644 --- a/src/plone/formwidget/masterselect/interfaces.py +++ b/src/plone/formwidget/masterselect/interfaces.py @@ -7,24 +7,22 @@ class IMasterSelectWidget(Interface): - """Marker interface for the multi select widget. - """ + """Marker interface for the multi select widget.""" class IMasterSelectBoolWidget(Interface): - """Marker interface for the multi select widget. - """ + """Marker interface for the multi select widget.""" class IMasterSelectRadioWidget(Interface): - """Marker interface for the radio button widget. - """ + """Marker interface for the radio button widget.""" class IMasterSelectField(IField): """ Additional Fields for MasterSelect """ + slave_fields = Tuple( title=_( "title_slave_fields", diff --git a/src/plone/formwidget/masterselect/setuphandlers.py b/src/plone/formwidget/masterselect/setuphandlers.py index 65e564d..480cc45 100644 --- a/src/plone/formwidget/masterselect/setuphandlers.py +++ b/src/plone/formwidget/masterselect/setuphandlers.py @@ -9,8 +9,8 @@ class HiddenProfiles(object): def getNonInstallableProfiles(self): """Hide uninstall profile from site-creation and quickinstaller.""" return [ - 'plone.formwidget.masterselect:uninstall', - 'plone.formwidget.masterselect.upgrades:5', + "plone.formwidget.masterselect:uninstall", + "plone.formwidget.masterselect.upgrades:5", ] diff --git a/src/plone/formwidget/masterselect/testing.py b/src/plone/formwidget/masterselect/testing.py index d956671..7a86785 100644 --- a/src/plone/formwidget/masterselect/testing.py +++ b/src/plone/formwidget/masterselect/testing.py @@ -15,25 +15,29 @@ class MasterSelectLayer(PloneSandboxLayer): def setUpZope(self, app, configurationContext): import plone.formwidget.masterselect + xmlconfig.file( - 'testing.zcml', - plone.formwidget.masterselect, - context=configurationContext + "testing.zcml", plone.formwidget.masterselect, context=configurationContext ) def setUpPloneSite(self, portal): - applyProfile(portal, 'plone.formwidget.masterselect:demo') + applyProfile(portal, "plone.formwidget.masterselect:demo") PLONE_FORMWIDGET_MASTERSELECT = MasterSelectLayer() PLONE_FORMWIDGET_MASTERSELECT_INTEGRATION = IntegrationTesting( - name='plone.formwidget.masterselect:Integration', - bases=(PLONE_FORMWIDGET_MASTERSELECT, )) + name="plone.formwidget.masterselect:Integration", + bases=(PLONE_FORMWIDGET_MASTERSELECT,), +) PLONE_FORMWIDGET_MASTERSELECT_FUNCTIONAL = FunctionalTesting( - name='plone.formwidget.masterselect:Functional', - bases=(PLONE_FORMWIDGET_MASTERSELECT, )) + name="plone.formwidget.masterselect:Functional", + bases=(PLONE_FORMWIDGET_MASTERSELECT,), +) PLONE_FORMWIDGET_MASTERSELECT_ROBOT = FunctionalTesting( - name='plone.formwidget.masterselect:Robot', - bases=(REMOTE_LIBRARY_BUNDLE_FIXTURE, - PLONE_FORMWIDGET_MASTERSELECT, - z2.ZSERVER_FIXTURE)) + name="plone.formwidget.masterselect:Robot", + bases=( + REMOTE_LIBRARY_BUNDLE_FIXTURE, + PLONE_FORMWIDGET_MASTERSELECT, + z2.ZSERVER_FIXTURE, + ), +) diff --git a/src/plone/formwidget/masterselect/tests/test_fields.py b/src/plone/formwidget/masterselect/tests/test_fields.py index c9e02a7..a4093a4 100644 --- a/src/plone/formwidget/masterselect/tests/test_fields.py +++ b/src/plone/formwidget/masterselect/tests/test_fields.py @@ -1,4 +1,5 @@ """Tests of package fields.""" + from plone.formwidget.masterselect import MasterSelectBoolField from plone.formwidget.masterselect import MasterSelectField from plone.formwidget.masterselect import MasterSelectRadioField diff --git a/src/plone/formwidget/masterselect/tests/test_intergration.py b/src/plone/formwidget/masterselect/tests/test_intergration.py index bf3d402..8a3494c 100644 --- a/src/plone/formwidget/masterselect/tests/test_intergration.py +++ b/src/plone/formwidget/masterselect/tests/test_intergration.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- -from plone.formwidget.masterselect.testing import \ - PLONE_FORMWIDGET_MASTERSELECT_INTEGRATION +from plone.formwidget.masterselect.testing import PLONE_FORMWIDGET_MASTERSELECT_INTEGRATION from Testing import ZopeTestCase as ztc import unittest @@ -24,9 +23,9 @@ def setUp(self): # client.mouseDown(jquery=u'(".deco-button-save")[0]') # client.waits.forPageLoad(timeout=u'20000') - # Edit the newly created page - # client.click(jquery=u'("#contentview-edit a")[0]') - # client.waits.forPageLoad(timeout=u'20000') + # Edit the newly created page + # client.click(jquery=u'("#contentview-edit a")[0]') + # client.waits.forPageLoad(timeout=u'20000') def test_suite(): diff --git a/src/plone/formwidget/masterselect/tests/test_robot.py b/src/plone/formwidget/masterselect/tests/test_robot.py index e79ff1d..f81a416 100644 --- a/src/plone/formwidget/masterselect/tests/test_robot.py +++ b/src/plone/formwidget/masterselect/tests/test_robot.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- +from plone.formwidget.masterselect.testing import PLONE_FORMWIDGET_MASTERSELECT_ROBOT from plone.testing import layered -from plone.formwidget.masterselect.testing import \ - PLONE_FORMWIDGET_MASTERSELECT_ROBOT import os import robotsuite @@ -12,16 +11,20 @@ def test_suite(): suite = unittest.TestSuite() current_dir = os.path.abspath(os.path.dirname(__file__)) - robot_dir = os.path.join(current_dir, 'robot') + robot_dir = os.path.join(current_dir, "robot") robot_tests = [ - os.path.join('robot', doc) for doc in os.listdir(robot_dir) - if doc.endswith('.robot') and doc.startswith('test_') + os.path.join("robot", doc) + for doc in os.listdir(robot_dir) + if doc.endswith(".robot") and doc.startswith("test_") ] for test in robot_tests: - suite.addTests([ - layered( - robotsuite.RobotTestSuite(test), - layer=PLONE_FORMWIDGET_MASTERSELECT_ROBOT), - ]) + suite.addTests( + [ + layered( + robotsuite.RobotTestSuite(test), + layer=PLONE_FORMWIDGET_MASTERSELECT_ROBOT, + ), + ] + ) return suite diff --git a/src/plone/formwidget/masterselect/tests/test_setup.py b/src/plone/formwidget/masterselect/tests/test_setup.py index b24c8da..faac6f4 100644 --- a/src/plone/formwidget/masterselect/tests/test_setup.py +++ b/src/plone/formwidget/masterselect/tests/test_setup.py @@ -5,7 +5,6 @@ from Products.CMFPlone.utils import get_installer from Products.GenericSetup.upgrade import listUpgradeSteps - import unittest @@ -16,29 +15,30 @@ class TestSetup(unittest.TestCase): def setUp(self): """Custom shared utility setup for tests.""" - self.portal = self.layer['portal'] - self.installer = get_installer(self.portal, self.layer['request']) - self.setup = self.portal['portal_setup'] + self.portal = self.layer["portal"] + self.installer = get_installer(self.portal, self.layer["request"]) + self.setup = self.portal["portal_setup"] def test_product_installed(self): """Test if plone.formwidget.masterselect is installed.""" - self.assertTrue(self.installer.is_product_installed( - 'plone.formwidget.masterselect')) + self.assertTrue( + self.installer.is_product_installed("plone.formwidget.masterselect") + ) def test_uninstall(self): - self.installer.uninstall_product('plone.formwidget.masterselect') + self.installer.uninstall_product("plone.formwidget.masterselect") self.assertFalse( - self.installer.is_product_installed('plone.formwidget.masterselect') + self.installer.is_product_installed("plone.formwidget.masterselect") ) def _get_profiles_upgrades(self): """Return the list o profiles of upgrade steps.""" profiles_upgrades = [] - profile_id = 'plone.formwidget.masterselect:default' - upgrades = listUpgradeSteps(self.setup, profile_id, '') + profile_id = "plone.formwidget.masterselect:default" + upgrades = listUpgradeSteps(self.setup, profile_id, "") for upgrade in upgrades: for step in upgrade: - import_profile = step['step'].import_profile + import_profile = step["step"].import_profile if import_profile: profiles_upgrades.append(import_profile) return profiles_upgrades @@ -52,6 +52,6 @@ def test_upgrade_seteps_hidden(self): self.assertIn( profile, hidden_profiles, - '{0} not in plone.formwidget.masterselect.setuphandlers.' - 'HiddenProfiles.getNonInstallableProfiles'.format(profile), + "{0} not in plone.formwidget.masterselect.setuphandlers." + "HiddenProfiles.getNonInstallableProfiles".format(profile), ) diff --git a/src/plone/formwidget/masterselect/tests/test_upgrade_steps.py b/src/plone/formwidget/masterselect/tests/test_upgrade_steps.py index 2f2bbaf..f8118bb 100644 --- a/src/plone/formwidget/masterselect/tests/test_upgrade_steps.py +++ b/src/plone/formwidget/masterselect/tests/test_upgrade_steps.py @@ -1,10 +1,8 @@ # -*- coding: utf-8 -*- -""" Upgrade steps tests. """ +"""Upgrade steps tests.""" from plone.app.testing import applyProfile -from plone.formwidget.masterselect.testing import ( - PLONE_FORMWIDGET_MASTERSELECT_INTEGRATION, -) +from plone.formwidget.masterselect.testing import PLONE_FORMWIDGET_MASTERSELECT_INTEGRATION from plone.registry.interfaces import IRegistry from Products.CMFPlone.interfaces import IBundleRegistry from zope.component import getUtility @@ -17,9 +15,9 @@ class UpgradeTestCaseBase(unittest.TestCase): layer = PLONE_FORMWIDGET_MASTERSELECT_INTEGRATION def setUp(self, from_version, to_version): - self.portal = self.layer['portal'] - self.setup = self.portal['portal_setup'] - self.profile_id = 'plone.formwidget.masterselect:default' + self.portal = self.layer["portal"] + self.setup = self.portal["portal_setup"] + self.profile_id = "plone.formwidget.masterselect:default" self.from_version = from_version self.to_version = to_version @@ -30,7 +28,7 @@ def _get_upgrade_step(self, title): """ self.setup.setLastVersionForProfile(self.profile_id, self.from_version) upgrades = self.setup.listUpgrades(self.profile_id) - steps = [s for s in upgrades[0] if s['title'] == title] + steps = [s for s in upgrades[0] if s["title"] == title] return steps[0] if steps else None def _do_upgrade_step(self, step): @@ -38,9 +36,9 @@ def _do_upgrade_step(self, step): Keyword arguments: step -- the step we want to run """ - request = self.layer['request'] - request.form['profile_id'] = self.profile_id - request.form['upgrades'] = [step['id']] + request = self.layer["request"] + request.form["profile_id"] = self.profile_id + request.form["upgrades"] = [step["id"]] self.setup.manage_doUpgrades(request=request) def _how_many_upgrades_to_do(self): @@ -52,8 +50,8 @@ def _how_many_upgrades_to_do(self): class Upgrade4to5TestCase(UpgradeTestCaseBase): def setUp(self): - self.request = self.layer['request'] - UpgradeTestCaseBase.setUp(self, '4', '5') + self.request = self.layer["request"] + UpgradeTestCaseBase.setUp(self, "4", "5") def test_registrations(self): version = self.setup.getLastVersionForProfile(self.profile_id)[0] @@ -61,35 +59,36 @@ def test_registrations(self): self.assertEqual(self._how_many_upgrades_to_do(), 1) def test_fix_resource_not_found(self): - title = 'Fix resource not found (GS profile)' + title = "Fix resource not found (GS profile)" step = self._get_upgrade_step(title) self.assertIsNotNone(step) registry = getUtility(IRegistry) bundle = registry.forInterface( IBundleRegistry, - prefix='plone.bundles/masterselectScript', + prefix="plone.bundles/masterselectScript", ) # Simulate preview state. - applyProfile(self.portal, 'plone.formwidget.masterselect:testupgrades') + applyProfile(self.portal, "plone.formwidget.masterselect:testupgrades") self.assertIn( - 'plone.formwidget.masterselect/master-compiled.css', + "plone.formwidget.masterselect/master-compiled.css", self.portal(), ) - self.assertEqual(['masterselect'], bundle.resources) + self.assertEqual(["masterselect"], bundle.resources) self.assertTrue(bundle.compile) self._do_upgrade_step(step) try: from Products.CMFPlone.resources.browser.resource import REQUEST_CACHE_KEY + setattr(self.request, REQUEST_CACHE_KEY, None) except ImportError: pass self.assertNotIn( - 'plone.formwidget.masterselect/master-compiled.css', + "plone.formwidget.masterselect/master-compiled.css", self.portal(), ) self.assertEqual([], bundle.resources) diff --git a/src/plone/formwidget/masterselect/widget.py b/src/plone/formwidget/masterselect/widget.py index 4ea5ec4..7a871dc 100644 --- a/src/plone/formwidget/masterselect/widget.py +++ b/src/plone/formwidget/masterselect/widget.py @@ -31,7 +31,7 @@ vocabulary="jQuery('%(masterID)s').bindMasterSlaveVocabulary(%(json)s);", value="jQuery('%(masterID)s').bindMasterSlaveValue(%(json)s);", attr="jQuery('%(masterID)s').bindMasterSlaveAttr(%(json)s);", - toggle="jQuery('%(masterID)s').bindMasterSlaveToggle(%(json)s);" + toggle="jQuery('%(masterID)s').bindMasterSlaveToggle(%(json)s);", ) JQUERY_ONLOAD = """\ @@ -56,16 +56,16 @@ def boolean_value(value): - return value in (1, '1', 'true', 'True', True) + return value in (1, "1", "true", "True", True) class MasterSelect(object): - """Methods required for widgets - """ + """Methods required for widgets""" def getSlaves(self): - slaves = (getattr(self.field, 'slave_fields', None) - or getattr(self.field.value_type, 'slave_fields', ())) + slaves = getattr(self.field, "slave_fields", None) or getattr( + self.field.value_type, "slave_fields", () + ) for slave in slaves: yield slave.copy() @@ -76,77 +76,71 @@ def renderJS(self): self.request.physicalPathFromURL(self.request.getURL()) ), ) - widgetURL = url + '/++widget++%s/@@masterselect-jsonvalue' % self.__name__ + widgetURL = url + "/++widget++%s/@@masterselect-jsonvalue" % self.__name__ for slave in self.getSlaves(): - if 'slaveID' not in slave: + if "slaveID" not in slave: # Try to get it from widget - widget = self.form.widgets.get(slave['name']) - if widget is not None and getattr(widget, 'id', None) is not None: - slave['slaveID'] = '#' + widget.id + widget = self.form.widgets.get(slave["name"]) + if widget is not None and getattr(widget, "id", None) is not None: + slave["slaveID"] = "#" + widget.id else: # Try our best to create one; won't work for checkboxes, so # better to provide a slaveID in the schema in that case or # sometimes to increase the scope beyond the field - prefix = '-'.join(self.id.split('-')[:-1]) - slave['slaveID'] = '#%s-%s' % (prefix, slave['name']) + prefix = "-".join(self.id.split("-")[:-1]) + slave["slaveID"] = "#%s-%s" % (prefix, slave["name"]) - slave['url'] = widgetURL - slave['masterID'] = slave.get( - 'masterSelector', - '#' + slave.get('masterID', self.id) + slave["url"] = widgetURL + slave["masterID"] = slave.get( + "masterSelector", "#" + slave.get("masterID", self.id) ) - slave['siblings'] = slave.get('siblings', False) - slave['empty_length'] = int(slave.get('empty_length', 0)) - slave.setdefault('control_param', 'master_value') + slave["siblings"] = slave.get("siblings", False) + slave["empty_length"] = int(slave.get("empty_length", 0)) + slave.setdefault("control_param", "master_value") - if 'hide_values' in slave: - values = slave['hide_values'] + if "hide_values" in slave: + values = slave["hide_values"] if not isinstance(values, (tuple, list)): values = [values] if IBool.providedBy(self.field): values = [boolean_value(v) for v in values] - slave['values'] = values + slave["values"] = values - js_template = BINDERS.get(slave.get('action')) or BINDERS['toggle'] + js_template = BINDERS.get(slave.get("action")) or BINDERS["toggle"] # Remove some things from slave we don't need - slave.pop('vocab_method', None) - slave.pop('hide_values', None) - slave.pop('control_param', None) + slave.pop("vocab_method", None) + slave.pop("hide_values", None) + slave.pop("control_param", None) - settings = {'masterID': slave['masterID'], - 'json': json.dumps(slave) - } + settings = {"masterID": slave["masterID"], "json": json.dumps(slave)} yield js_template % settings def getInlineJS(self): """render javascript""" - return JQUERY_ONLOAD % '\n'.join(self.renderJS()) + return JQUERY_ONLOAD % "\n".join(self.renderJS()) @implementer(IMasterSelectWidget) class MasterSelectWidget(select.SelectWidget, MasterSelect): - """Master Select Widget - """ + """Master Select Widget""" - klass = u'form-select masterselect-widget' + klass = "form-select masterselect-widget" @implementer(IMasterSelectBoolWidget) class MasterSelectBoolWidget(checkbox.SingleCheckBoxWidget, MasterSelect): - """MasterSelectBoolWidget - """ + """MasterSelectBoolWidget""" - klass = u'masterselect-widget' + klass = "masterselect-widget" @implementer(IMasterSelectRadioWidget) class MasterSelectRadioWidget(radio.RadioWidget, MasterSelect): - """MasterSelectRadioWidget - """ + """MasterSelectRadioWidget""" - klass = u'masterselect-widget' + klass = "masterselect-widget" @implementer(interfaces.IFieldWidget) @@ -188,8 +182,7 @@ def __init__(self, context, request): # request.environ[DISABLE_TRANSFORM_REQUEST_KEY] = True def createVocabulary(self, value): - """Create a simple vocabulary from provided value, list or tuple - """ + """Create a simple vocabulary from provided value, list or tuple""" terms = [] for token in value: title = token @@ -200,42 +193,43 @@ def createVocabulary(self, value): return SimpleVocabulary(terms) def getVocabulary(self, slave, value, default=None): - kw = {slave['control_param']: value} - vocabulary = slave.get('vocab_method', None) + kw = {slave["control_param"]: value} + vocabulary = slave.get("vocab_method", None) if vocabulary is None: return default return vocabulary(**kw) def __call__(self): self.request.response.setHeader( - 'Content-Type', 'application/json; charset=utf-8') + "Content-Type", "application/json; charset=utf-8" + ) # import pdb; pdb.set_trace() - field = self.request['field'] - slavename = self.request['name'] - value = self.request['value'] + field = self.request["field"] + slavename = self.request["name"] + value = self.request["value"] for slave in self.widget.getSlaves(): # Loop until we find the slave we want - if slave['name'] != slavename: + if slave["name"] != slavename: continue - action = slave.get('action') - if action not in ['vocabulary', 'value', 'attr']: + action = slave.get("action") + if action not in ["vocabulary", "value", "attr"]: continue # --- VALUE ------------------------------------------------------- - if action == 'value': - value = self.getVocabulary(slave, value, '') + if action == "value": + value = self.getVocabulary(slave, value, "") return json.dumps(translate(value, context=self.request)) # --- ATTR- ------------------------------------------------------- - if action == 'attr': + if action == "attr": result = self.getVocabulary(slave, value, None) - if isinstance(result, dict) and 'attr' in result and 'value' in result: + if isinstance(result, dict) and "attr" in result and "value" in result: return json.dumps(result) else: - raise ValueError('Bad attr dictionary for %s.' % slavename) + raise ValueError("Bad attr dictionary for %s." % slavename) # --- VOCABULARY -------------------------------------------------- vocabulary = self.getVocabulary(slave, value) @@ -243,15 +237,16 @@ def __call__(self): if isinstance(vocabulary, (tuple, list)): vocabulary = self.createVocabulary(vocabulary) - widget = self.widget.form.widgets.get(slave['name']) + widget = self.widget.form.widgets.get(slave["name"]) if widget is None: - raise ValueError('Can not find widget: %s' % slave['name']) + raise ValueError("Can not find widget: %s" % slave["name"]) - if (IContextSourceBinder.providedBy(vocabulary) - or IVocabularyTokenized.providedBy(vocabulary)): + if IContextSourceBinder.providedBy( + vocabulary + ) or IVocabularyTokenized.providedBy(vocabulary): widget.field = copy.copy(widget.field) - if hasattr(widget.field, 'value_type'): + if hasattr(widget.field, "value_type"): widget.field.value_type.vocabulary = vocabulary else: widget.field.vocabulary = vocabulary @@ -259,14 +254,14 @@ def __call__(self): widget.updateTerms() widget.update() # widget may define items as a property or as a method - items = widget.items if not callable( - widget.items) else widget.items() + items = widget.items if not callable(widget.items) else widget.items() # translate if possible. content can be a Message, a string, a # unicode for item in items: - item['content'] = translate(safe_unicode( - item['content']), context=self.request) - responseJSON = {'items': items} + item["content"] = translate( + safe_unicode(item["content"]), context=self.request + ) + responseJSON = {"items": items} # disable select box if term length = 'disable_length' # if len(widget.terms) == slave.get('disable_length', None): @@ -274,5 +269,4 @@ def __call__(self): return json.dumps(responseJSON) - raise ValueError('No such master-slave combo: %s %s' % - (field, slavename)) + raise ValueError("No such master-slave combo: %s %s" % (field, slavename))