diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..d054af1
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,56 @@
+# Generated from:
+# https://github.com/plone/meta/tree/main/src/plone/meta/default
+# See the inline comments on how to expand/tweak this configuration file
+#
+# EditorConfig Configuration file, for more details see:
+# http://EditorConfig.org
+# EditorConfig is a convention description, that could be interpreted
+# by multiple editors to enforce common coding conventions for specific
+# file types
+
+# top-most EditorConfig file:
+# Will ignore other EditorConfig files in Home directory or upper tree level.
+root = true
+
+
+[*]
+# Default settings for all files.
+# Unix-style newlines with a newline ending every file
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+# Set default charset
+charset = utf-8
+# Indent style default
+indent_style = space
+# Max Line Length - a hard line wrap, should be disabled
+max_line_length = off
+
+[*.{py,cfg,ini}]
+# 4 space indentation
+indent_size = 4
+
+[*.{yml,zpt,pt,dtml,zcml,html,xml}]
+# 2 space indentation
+indent_size = 2
+
+[*.{json,jsonl,js,jsx,ts,tsx,css,less,scss}]
+# Frontend development
+# 2 space indentation
+indent_size = 2
+max_line_length = 80
+
+[{Makefile,.gitmodules}]
+# Tab indentation (no size specified, but view as 4 spaces)
+indent_style = tab
+indent_size = unset
+tab_width = unset
+
+
+##
+# Add extra configuration options in .meta.toml:
+# [editorconfig]
+# extra_lines = """
+# _your own configuration lines_
+# """
+##
diff --git a/.flake8 b/.flake8
new file mode 100644
index 0000000..3e2d35f
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,22 @@
+# Generated from:
+# https://github.com/plone/meta/tree/main/src/plone/meta/default
+# See the inline comments on how to expand/tweak this configuration file
+[flake8]
+doctests = 1
+ignore =
+ # black takes care of line length
+ E501,
+ # black takes care of where to break lines
+ W503,
+ # black takes care of spaces within slicing (list[:])
+ E203,
+ # black takes care of spaces after commas
+ E231,
+
+##
+# Add extra configuration options in .meta.toml:
+# [flake8]
+# extra_lines = """
+# _your own configuration lines_
+# """
+##
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..e5e4522
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,11 @@
+# Generated from:
+# https://github.com/plone/meta/tree/main/src/plone/meta/default
+# See the inline comments on how to expand/tweak this configuration file
+version: 2
+updates:
+
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ # Check for updates to GitHub Actions every week
+ interval: "weekly"
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
deleted file mode 100644
index 4e11ba7..0000000
--- a/.github/workflows/main.yml
+++ /dev/null
@@ -1,41 +0,0 @@
-name: tests
-
-on:
- push:
- branches: [ main ]
- pull_request:
-
-jobs:
- build:
- strategy:
- matrix:
- config:
- # [Python version, tox env]
- - ["3.7", "plone52-py37"]
- - ["3.8", "plone52-py38"]
- - ["3.9", "plone52-py39"]
- - ["3.7", "plone60-py37"]
- - ["3.8", "plone60-py38"]
- - ["3.9", "plone60-py39"]
- runs-on: ubuntu-latest
- name: ${{ matrix.config[1] }}
- steps:
- - uses: actions/checkout@v2
- - name: Set up Python
- uses: actions/setup-python@v2
- with:
- python-version: ${{ matrix.config[0] }}
- - name: Pip cache
- uses: actions/cache@v2
- with:
- path: ~/.cache/pip
- key: ${{ runner.os }}-pip-${{ matrix.config[0] }}-${{ hashFiles('setup.*', 'tox.ini') }}
- restore-keys: |
- ${{ runner.os }}-pip-${{ matrix.config[0] }}-
- ${{ runner.os }}-pip-
- - name: Install dependencies
- run: |
- python -m pip install --upgrade pip
- pip install tox
- - name: Test
- run: tox -e ${{ matrix.config[1] }}
diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml
new file mode 100644
index 0000000..f9a346f
--- /dev/null
+++ b/.github/workflows/meta.yml
@@ -0,0 +1,65 @@
+# Generated from:
+# https://github.com/plone/meta/tree/main/src/plone/meta/default
+# See the inline comments on how to expand/tweak this configuration file
+name: Meta
+on:
+ push:
+ branches:
+ - master
+ - main
+ pull_request:
+ branches:
+ - master
+ - main
+ workflow_dispatch:
+
+##
+# To set environment variables for all jobs, add in .meta.toml:
+# [github]
+# env = """
+# debug: 1
+# image-name: 'org/image'
+# image-tag: 'latest'
+# """
+##
+
+jobs:
+ qa:
+ uses: plone/meta/.github/workflows/qa.yml@2.x
+ coverage:
+ uses: plone/meta/.github/workflows/coverage.yml@2.x
+ dependencies:
+ uses: plone/meta/.github/workflows/dependencies.yml@2.x
+ release_ready:
+ uses: plone/meta/.github/workflows/release_ready.yml@2.x
+ circular:
+ uses: plone/meta/.github/workflows/circular.yml@2.x
+
+##
+# To modify the list of default jobs being created add in .meta.toml:
+# [github]
+# jobs = [
+# "qa",
+# "coverage",
+# "dependencies",
+# "release_ready",
+# "circular",
+# ]
+##
+
+##
+# To request that some OS level dependencies get installed
+# when running tests/coverage jobs, add in .meta.toml:
+# [github]
+# os_dependencies = "git libxml2 libxslt"
+##
+
+
+##
+# Specify additional jobs in .meta.toml:
+# [github]
+# extra_lines = """
+# another:
+# uses: org/repo/.github/workflows/file.yml@main
+# """
+##
diff --git a/.github/workflows/test-matrix.yml b/.github/workflows/test-matrix.yml
new file mode 100644
index 0000000..05872b5
--- /dev/null
+++ b/.github/workflows/test-matrix.yml
@@ -0,0 +1,72 @@
+# Generated from:
+# https://github.com/plone/meta/tree/main/src/plone/meta/default
+# See the inline comments on how to expand/tweak this configuration file
+name: Tests
+
+on:
+ push:
+
+jobs:
+ build:
+ permissions:
+ contents: read
+ pull-requests: write
+ strategy:
+ # We want to see all failures:
+ fail-fast: false
+ matrix:
+ os:
+ - ["ubuntu", "ubuntu-latest"]
+ config:
+ # [Python version, visual name, tox env]
+ - ["3.13", "6.2 on py3.13", "py313-plone62"]
+ - ["3.10", "6.2 on py3.10", "py310-plone62"]
+
+ runs-on: ${{ matrix.os[1] }}
+ if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
+ name: ${{ matrix.config[1] }}
+ steps:
+ - uses: actions/checkout@v6
+ with:
+ persist-credentials: false
+ - name: Set up Python
+ uses: actions/setup-python@v6
+ with:
+ python-version: ${{ matrix.config[0] }}
+ allow-prereleases: true
+
+##
+# Add extra configuration options in .meta.toml:
+# [github]
+# extra_lines_after_os_dependencies = """
+# _your own configuration lines_
+# """
+##
+ - name: Pip cache
+ uses: actions/cache@v5
+ with:
+ path: ~/.cache/pip
+ key: ${{ runner.os }}-pip-${{ matrix.config[0] }}-${{ hashFiles('setup.*', 'tox.ini') }}
+ restore-keys: |
+ ${{ runner.os }}-pip-${{ matrix.config[0] }}-
+ ${{ runner.os }}-pip-
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install tox
+ - name: Initialize tox
+ # the bash one-liner below does not work on Windows
+ if: contains(matrix.os, 'ubuntu')
+ run: |
+ if [ `tox list --no-desc -f init|wc -l` = 1 ]; then tox -e init;else true; fi
+ - name: Test
+ run: tox -e ${{ matrix.config[2] }}
+
+
+##
+# Add extra configuration options in .meta.toml:
+# [github]
+# extra_lines = """
+# _your own configuration lines_
+# """
+##
diff --git a/.gitignore b/.gitignore
index a44a7ac..a602abe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,37 +1,58 @@
-*.EGG
-*.EGG-INFO
-*.egg
+# Generated from:
+# https://github.com/plone/meta/tree/main/src/plone/meta/default
+# See the inline comments on how to expand/tweak this configuration file
+# python related
*.egg-info
-*.map
-*.mo
*.pyc
*.pyo
-*.js.map
-*.tmp*
-.*.cfg
-.DS_Store
-.idea
-/bin/
-/build/
-/develop-eggs/
-/devsrc/
-/dist/
-/docs/_screenshots/*.png
-/downloads/
-/eggs/
-/extras/
-/fake-eggs/
-/html/
-/include/
-/lib/
-/local/
-/parts/
-/resources/theme/
-/result
-/var/
-components
-node_modules
-!.isort.cfg
-pip-selfcheck.json
-src/*
-.python-version
+
+# translation related
+*.mo
+
+# tools related
+build/
+.coverage
+.*project
+coverage.xml
+dist/
+docs/_build
+__pycache__/
+.tox
+.vscode/
+node_modules/
+forest.dot
+forest.json
+
+# venv / buildout related
+bin/
+develop-eggs/
+eggs/
+.eggs/
+etc/
+.installed.cfg
+include/
+lib/
+lib64
+.mr.developer.cfg
+parts/
+pyvenv.cfg
+var/
+local.cfg
+
+# mxdev
+/instance/
+/.make-sentinels/
+/*-mxdev.txt
+/reports/
+/sources/
+/venv/
+.installed.txt
+
+
+##
+# Add extra configuration options in .meta.toml:
+# [gitignore]
+# extra_lines = """
+# _your own configuration lines_
+# """
+##
diff --git a/.meta.toml b/.meta.toml
new file mode 100644
index 0000000..f462a34
--- /dev/null
+++ b/.meta.toml
@@ -0,0 +1,9 @@
+# Generated from:
+# https://github.com/plone/meta/tree/main/src/plone/meta/default
+# See the inline comments on how to expand/tweak this configuration file
+[meta]
+template = "default"
+commit-id = "2.3.1"
+
+[tox]
+test_matrix = {"6.2" = ["*"]}
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..b3e6370
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,94 @@
+# Generated from:
+# https://github.com/plone/meta/tree/main/src/plone/meta/default
+# See the inline comments on how to expand/tweak this configuration file
+ci:
+ autofix_prs: false
+ autoupdate_schedule: monthly
+
+repos:
+- repo: https://github.com/asottile/pyupgrade
+ rev: v3.21.2
+ hooks:
+ - id: pyupgrade
+ args: [--py38-plus]
+- repo: https://github.com/pycqa/isort
+ rev: 7.0.0
+ hooks:
+ - id: isort
+- repo: https://github.com/psf/black-pre-commit-mirror
+ rev: 25.11.0
+ hooks:
+ - id: black
+- repo: https://github.com/collective/zpretty
+ rev: 3.1.1
+ hooks:
+ - id: zpretty
+
+##
+# Add extra configuration options in .meta.toml:
+# [pre_commit]
+# zpretty_extra_lines = """
+# _your own configuration lines_
+# """
+##
+- repo: https://github.com/PyCQA/flake8
+ rev: 7.3.0
+ hooks:
+ - id: flake8
+
+##
+# Add extra configuration options in .meta.toml:
+# [pre_commit]
+# flake8_extra_lines = """
+# _your own configuration lines_
+# """
+##
+- repo: https://github.com/codespell-project/codespell
+ rev: v2.4.1
+ hooks:
+ - id: codespell
+ additional_dependencies:
+ - tomli
+
+##
+# Add extra configuration options in .meta.toml:
+# [pre_commit]
+# codespell_extra_lines = """
+# _your own configuration lines_
+# """
+##
+- repo: https://github.com/mgedmin/check-manifest
+ rev: "0.51"
+ hooks:
+ - id: check-manifest
+- repo: https://github.com/regebro/pyroma
+ rev: "5.0"
+ hooks:
+ - id: pyroma
+- repo: https://github.com/mgedmin/check-python-versions
+ rev: "0.24.0"
+ hooks:
+ - id: check-python-versions
+ args: ['--only', 'setup.py,pyproject.toml']
+- repo: https://github.com/collective/i18ndude
+ rev: "6.3.0"
+ hooks:
+ - id: i18ndude
+
+
+##
+# Add extra configuration options in .meta.toml:
+# [pre_commit]
+# i18ndude_extra_lines = """
+# _your own configuration lines_
+# """
+##
+
+
+##
+# Add extra configuration options in .meta.toml:
+# [pre_commit]
+# extra_lines = """
+# _your own configuration lines_
+# """
+##
diff --git a/CHANGES.rst b/CHANGES.rst
index 31daeaf..bcf82af 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,11 +1,12 @@
Changelog
=========
-2.0.1 (unreleased)
-------------------
-
-- Nothing changed yet.
+.. You should *NOT* be adding new change log entries to this file.
+ You should create a file in the news directory instead.
+ For helpful instructions, please see:
+ https://github.com/plone/plone.releaser/blob/master/ADD-A-NEWS-ITEM.rst
+.. towncrier release notes start
2.0.0 (2022-07-22)
------------------
@@ -25,7 +26,7 @@ Changelog
1.1.2 (2017-07-03)
------------------
-- Fix issue where draft sync failed because draft might have been withotu aq wrapper
+- Fix issue where draft sync failed because draft might have been without aq wrapper
[datakurre]
1.1.1 (2016-09-09)
diff --git a/MANIFEST.in b/MANIFEST.in
index e8ed5a3..83ef884 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,9 +1,10 @@
include *.rst
recursive-include docs *
-recursive-include plone *
+recursive-include src *
global-exclude *pyc
exclude bootstrap.py
exclude *.cfg .coveragerc .github *.ini
+recursive-exclude news *
diff --git a/README.rst b/README.rst
index 7486e3d..a29f0b4 100644
--- a/README.rst
+++ b/README.rst
@@ -1,14 +1,6 @@
Introduction
============
-.. image:: https://secure.travis-ci.org/plone/plone.app.drafts.png?branch=master
- :alt: Travis CI badge
- :target: http://travis-ci.org/plone/plone.app.drafts
-
-.. image:: https://coveralls.io/repos/plone/plone.app.drafts/badge.png?branch=master
- :alt: Coveralls badge
- :target: https://coveralls.io/r/plone/plone.app.drafts
-
plone.app.drafts implements services for managing auto-saved content drafts in Plone.
This addresses two problems:
diff --git a/buildout.cfg b/buildout.cfg
deleted file mode 100644
index e257ee2..0000000
--- a/buildout.cfg
+++ /dev/null
@@ -1,2 +0,0 @@
-[buildout]
-extends = test-4.x.cfg
diff --git a/plone/app/drafts/browser/__init__.py b/news/.gitkeep
similarity index 100%
rename from plone/app/drafts/browser/__init__.py
rename to news/.gitkeep
diff --git a/news/3928.breaking b/news/3928.breaking
new file mode 100644
index 0000000..4fb7ad4
--- /dev/null
+++ b/news/3928.breaking
@@ -0,0 +1,2 @@
+Replace ``pkg_resources`` namespace with PEP 420 native namespace.
+Support only Plone 6.2 and Python 3.10+.
diff --git a/plone/__init__.py b/plone/__init__.py
deleted file mode 100644
index 03d08ff..0000000
--- a/plone/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-# -*- coding: utf-8 -*-
-__import__("pkg_resources").declare_namespace(__name__)
diff --git a/plone/app/__init__.py b/plone/app/__init__.py
deleted file mode 100644
index 03d08ff..0000000
--- a/plone/app/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-# -*- coding: utf-8 -*-
-__import__("pkg_resources").declare_namespace(__name__)
diff --git a/plone/app/drafts/behaviors.zcml b/plone/app/drafts/behaviors.zcml
deleted file mode 100644
index 4d4dd8a..0000000
--- a/plone/app/drafts/behaviors.zcml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
diff --git a/plone/app/drafts/browser/configure.zcml b/plone/app/drafts/browser/configure.zcml
deleted file mode 100644
index 5ff09e0..0000000
--- a/plone/app/drafts/browser/configure.zcml
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/plone/app/drafts/browser/drafts.pt b/plone/app/drafts/browser/drafts.pt
deleted file mode 100644
index 6500b2c..0000000
--- a/plone/app/drafts/browser/drafts.pt
+++ /dev/null
@@ -1,60 +0,0 @@
-
-
-
-
-
-Drafts
-
-
- Drafts are created when a page is edited and has not yet been saved.
- A draft found here represents either a content item currently being
- edited, an item that has been abandoned, e.g. because of a browser crash.
-
-
-Return to overview
-
-
- No drafts found
-
-
-
-
- Drafts for
- on
-
-
- The following drafts are in progress.
-
-
-
-
-
-
-
-
-
diff --git a/plone/app/drafts/browser/overview.pt b/plone/app/drafts/browser/overview.pt
deleted file mode 100644
index 372424c..0000000
--- a/plone/app/drafts/browser/overview.pt
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
-
-
-Drafts
-
-
- Drafts are created when a page is edited and has not yet been saved.
- A draft found here represents either a content item currently being
- edited, an item that has been abandoned, e.g. because of a browser crash.
-
-
-
- No drafts found
-
-
-
-
- Users
-
-
- Drafts are managed per user. Click on a user id below to find drafts for
- that user.
-
-
-
-
-
-
-
-
-
diff --git a/plone/app/drafts/browser/targets.pt b/plone/app/drafts/browser/targets.pt
deleted file mode 100644
index 6df6665..0000000
--- a/plone/app/drafts/browser/targets.pt
+++ /dev/null
@@ -1,69 +0,0 @@
-
-
-
-
-
-Drafts
-
-
- Drafts are created when a page is edited and has not yet been saved.
- A draft found here represents either a content item currently being
- edited, an item that has been abandoned, e.g. because of a browser crash.
-
-
-Return to overview
-
-
- No drafts found
-
-
-
-
- Drafts for
-
-
- Drafts are organised by the target object. Click on a key below to
- view drafts for this item.
-
-
-
-
-
-
-
-
-
diff --git a/plone/app/drafts/configure.zcml b/plone/app/drafts/configure.zcml
deleted file mode 100644
index 44ac090..0000000
--- a/plone/app/drafts/configure.zcml
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/plone/app/drafts/profiles/default/componentregistry.xml b/plone/app/drafts/profiles/default/componentregistry.xml
deleted file mode 100644
index 557a538..0000000
--- a/plone/app/drafts/profiles/default/componentregistry.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
diff --git a/plone/app/drafts/profiles/default/metadata.xml b/plone/app/drafts/profiles/default/metadata.xml
deleted file mode 100644
index 8be2512..0000000
--- a/plone/app/drafts/profiles/default/metadata.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
- 1
-
-
-
diff --git a/plone/app/drafts/profiles/default/toolset.xml b/plone/app/drafts/profiles/default/toolset.xml
deleted file mode 100644
index 29a1ce6..0000000
--- a/plone/app/drafts/profiles/default/toolset.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..c20adb6
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,167 @@
+# Generated from:
+# https://github.com/plone/meta/tree/main/src/plone/meta/default
+# See the inline comments on how to expand/tweak this configuration file
+[build-system]
+requires = ["setuptools>=68.2,<80", "wheel"]
+
+[tool.towncrier]
+directory = "news/"
+filename = "CHANGES.rst"
+title_format = "{version} ({project_date})"
+underlines = ["-", ""]
+
+[[tool.towncrier.type]]
+directory = "breaking"
+name = "Breaking changes:"
+showcontent = true
+
+[[tool.towncrier.type]]
+directory = "feature"
+name = "New features:"
+showcontent = true
+
+[[tool.towncrier.type]]
+directory = "bugfix"
+name = "Bug fixes:"
+showcontent = true
+
+[[tool.towncrier.type]]
+directory = "internal"
+name = "Internal:"
+showcontent = true
+
+[[tool.towncrier.type]]
+directory = "documentation"
+name = "Documentation:"
+showcontent = true
+
+[[tool.towncrier.type]]
+directory = "tests"
+name = "Tests:"
+showcontent = true
+
+##
+# Add extra configuration options in .meta.toml:
+# [pyproject]
+# towncrier_extra_lines = """
+# extra_configuration
+# """
+##
+
+[tool.isort]
+profile = "plone"
+
+##
+# Add extra configuration options in .meta.toml:
+# [pyproject]
+# isort_extra_lines = """
+# extra_configuration
+# """
+##
+
+[tool.black]
+target-version = ["py38"]
+
+##
+# Add extra configuration options in .meta.toml:
+# [pyproject]
+# black_extra_lines = """
+# extra_configuration
+# """
+##
+
+[tool.codespell]
+ignore-words-list = "discreet,assertin,thet,"
+skip = "*.po,"
+##
+# Add extra configuration options in .meta.toml:
+# [pyproject]
+# codespell_ignores = "foo,bar"
+# codespell_skip = "*.po,*.map,package-lock.json"
+##
+
+[tool.dependencychecker]
+Zope = [
+ # Zope own provided namespaces
+ 'App', 'OFS', 'Products.Five', 'Products.OFSP', 'Products.PageTemplates',
+ 'Products.SiteAccess', 'Shared', 'Testing', 'ZPublisher', 'ZTUtils',
+ 'Zope2', 'webdav', 'zmi',
+ # ExtensionClass own provided namespaces
+ 'ExtensionClass', 'ComputedAttribute', 'MethodObject',
+ # Zope dependencies
+ 'AccessControl', 'Acquisition', 'AuthEncoding', 'beautifulsoup4', 'BTrees',
+ 'cffi', 'Chameleon', 'DateTime', 'DocumentTemplate',
+ 'MultiMapping', 'multipart', 'PasteDeploy', 'Persistence', 'persistent',
+ 'pycparser', 'python-gettext', 'pytz', 'RestrictedPython', 'roman',
+ 'soupsieve', 'transaction', 'waitress', 'WebOb', 'WebTest', 'WSGIProxy2',
+ 'z3c.pt', 'zc.lockfile', 'ZConfig', 'zExceptions', 'ZODB', 'zodbpickle',
+ 'zope.annotation', 'zope.browser', 'zope.browsermenu', 'zope.browserpage',
+ 'zope.browserresource', 'zope.cachedescriptors', 'zope.component',
+ 'zope.configuration', 'zope.container', 'zope.contentprovider',
+ 'zope.contenttype', 'zope.datetime', 'zope.deferredimport',
+ 'zope.deprecation', 'zope.dottedname', 'zope.event', 'zope.exceptions',
+ 'zope.filerepresentation', 'zope.globalrequest', 'zope.hookable',
+ 'zope.i18n', 'zope.i18nmessageid', 'zope.interface', 'zope.lifecycleevent',
+ 'zope.location', 'zope.pagetemplate', 'zope.processlifetime', 'zope.proxy',
+ 'zope.ptresource', 'zope.publisher', 'zope.schema', 'zope.security',
+ 'zope.sequencesort', 'zope.site', 'zope.size', 'zope.structuredtext',
+ 'zope.tal', 'zope.tales', 'zope.testbrowser', 'zope.testing',
+ 'zope.traversing', 'zope.viewlet'
+]
+'Products.CMFCore' = [
+ 'docutils', 'five.localsitemanager', 'Missing', 'Products.BTreeFolder2',
+ 'Products.GenericSetup', 'Products.MailHost', 'Products.PythonScripts',
+ 'Products.StandardCacheManagers', 'Products.ZCatalog', 'Record',
+ 'zope.sendmail', 'Zope'
+]
+'plone.base' = [
+ 'plone.batching', 'plone.registry', 'plone.schema','plone.z3cform',
+ 'Products.CMFCore', 'Products.CMFDynamicViewFTI',
+]
+python-dateutil = ['dateutil']
+pytest-plone = ['pytest', 'zope.pytestlayer', 'plone.testing', 'plone.app.testing']
+
+##
+# Add extra configuration options in .meta.toml:
+# [pyproject]
+# dependencies_ignores = "['zestreleaser.towncrier']"
+# dependencies_mappings = [
+# "gitpython = ['git']",
+# "pygithub = ['github']",
+# ]
+##
+
+[tool.check-manifest]
+ignore = [
+ ".editorconfig",
+ ".flake8",
+ ".meta.toml",
+ ".pre-commit-config.yaml",
+ "dependabot.yml",
+ "mx.ini",
+ "tox.ini",
+
+]
+
+##
+# Add extra configuration options in .meta.toml:
+# [pyproject]
+# check_manifest_ignores = """
+# "*.map.js",
+# "*.pyc",
+# """
+# check_manifest_extra_lines = """
+# ignore-bad-ideas = [
+# "some/test/file/PKG-INFO",
+# ]
+# """
+##
+
+
+##
+# Add extra configuration options in .meta.toml:
+# [pyproject]
+# extra_lines = """
+# _your own configuration lines_
+# """
+##
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index caafa66..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,6 +0,0 @@
-[isort]
-force_alphabetical_sort = True
-force_single_line = True
-lines_after_imports = 2
-line_length = 200
-not_skip = __init__.py
\ No newline at end of file
diff --git a/setup.py b/setup.py
index 646dc56..d001ce7 100644
--- a/setup.py
+++ b/setup.py
@@ -1,11 +1,7 @@
-# -*- coding: utf-8 -*-
-from setuptools import find_packages
from setuptools import setup
-import os
-
-version = "2.0.1.dev0"
+version = "3.0.0.dev0"
setup(
name="plone.app.drafts",
@@ -18,14 +14,13 @@
"Development Status :: 4 - Beta",
"Environment :: Web Environment",
"Framework :: Plone",
- "Framework :: Plone :: 5.2",
- "Framework :: Plone :: 6.0",
+ "Framework :: Plone :: 6.2",
"License :: OSI Approved :: GNU General Public License v2 (GPLv2)",
"Programming Language :: Python",
- "Programming Language :: Python :: 3.7",
- "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",
"Topic :: Internet :: WWW/HTTP",
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
"Topic :: Software Development :: Libraries :: Python Modules",
@@ -35,25 +30,31 @@
author_email="plone-developers@lists.sourceforge.net",
url="http://plone.org",
license="GPL",
- packages=find_packages(exclude=["ez_setup"]),
- namespace_packages=["plone", "plone.app"],
include_package_data=True,
zip_safe=False,
+ python_requires=">=3.10",
install_requires=[
- "setuptools",
- "ZODB3",
- "zope.interface",
- "zope.component",
- "zope.schema",
- "zope.annotation",
"plone.app.uuid",
+ "plone.autoform",
"plone.behavior>=1.1",
- "Zope2",
+ "plone.dexterity",
+ "plone.protect",
+ "plone.uuid",
+ "Products.CMFCore",
+ "Products.GenericSetup",
+ "z3c.form",
+ "zope.annotation",
+ "zope.component",
+ "zope.interface",
+ "zope.schema",
+ "Zope",
],
extras_require={
"test": [
+ "plone.app.contenttypes",
"plone.app.testing",
"plone.app.dexterity",
+ "plone.testing",
],
},
entry_points="""
diff --git a/plone/app/drafts/__init__.py b/src/plone/app/drafts/__init__.py
similarity index 97%
rename from plone/app/drafts/__init__.py
rename to src/plone/app/drafts/__init__.py
index 4616980..d49fdc4 100644
--- a/plone/app/drafts/__init__.py
+++ b/src/plone/app/drafts/__init__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
from plone.app.drafts.interfaces import ICurrentDraftManagement
from plone.app.drafts.interfaces import IDrafting
from plone.app.drafts.lifecycle import syncDraftOnSave
diff --git a/src/plone/app/drafts/behaviors.zcml b/src/plone/app/drafts/behaviors.zcml
new file mode 100644
index 0000000..4948039
--- /dev/null
+++ b/src/plone/app/drafts/behaviors.zcml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/src/plone/app/drafts/browser/__init__.py b/src/plone/app/drafts/browser/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/plone/app/drafts/browser/configure.zcml b/src/plone/app/drafts/browser/configure.zcml
new file mode 100644
index 0000000..a4f0f54
--- /dev/null
+++ b/src/plone/app/drafts/browser/configure.zcml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/plone/app/drafts/browser/drafts.pt b/src/plone/app/drafts/browser/drafts.pt
new file mode 100644
index 0000000..8f49d60
--- /dev/null
+++ b/src/plone/app/drafts/browser/drafts.pt
@@ -0,0 +1,103 @@
+
+
+
+
+
+ Drafts
+
+
+ Drafts are created when a page is edited and has not yet been saved.
+ A draft found here represents either a content item currently being
+ edited, an item that has been abandoned, e.g. because of a browser crash.
+
+
+ Return to overview
+
+
+ No drafts found
+
+
+
+
+ Drafts for
+
+ on
+
+
+
+ The following drafts are in progress.
+
+
+
+
+
+
+
+
+
diff --git a/src/plone/app/drafts/browser/overview.pt b/src/plone/app/drafts/browser/overview.pt
new file mode 100644
index 0000000..cad1060
--- /dev/null
+++ b/src/plone/app/drafts/browser/overview.pt
@@ -0,0 +1,64 @@
+
+
+
+
+
+ Drafts
+
+
+ Drafts are created when a page is edited and has not yet been saved.
+ A draft found here represents either a content item currently being
+ edited, an item that has been abandoned, e.g. because of a browser crash.
+
+
+
+ No drafts found
+
+
+
+
+ Users
+
+
+ Drafts are managed per user. Click on a user id below to find drafts for
+ that user.
+
+
+
+
+
+
+
+
+
diff --git a/src/plone/app/drafts/browser/targets.pt b/src/plone/app/drafts/browser/targets.pt
new file mode 100644
index 0000000..8e84aa2
--- /dev/null
+++ b/src/plone/app/drafts/browser/targets.pt
@@ -0,0 +1,118 @@
+
+
+
+
+
+ Drafts
+
+
+ Drafts are created when a page is edited and has not yet been saved.
+ A draft found here represents either a content item currently being
+ edited, an item that has been abandoned, e.g. because of a browser crash.
+
+
+ Return to overview
+
+
+ No drafts found
+
+
+
+
+ Drafts for
+
+
+
+ Drafts are organised by the target object. Click on a key below to
+ view drafts for this item.
+
+
+
+
+
+
+
+
+
diff --git a/plone/app/drafts/browser/tool.gif b/src/plone/app/drafts/browser/tool.gif
similarity index 100%
rename from plone/app/drafts/browser/tool.gif
rename to src/plone/app/drafts/browser/tool.gif
diff --git a/plone/app/drafts/browser/view.py b/src/plone/app/drafts/browser/view.py
similarity index 98%
rename from plone/app/drafts/browser/view.py
rename to src/plone/app/drafts/browser/view.py
index 79c769b..8a550f3 100644
--- a/plone/app/drafts/browser/view.py
+++ b/src/plone/app/drafts/browser/view.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
from datetime import datetime
from plone.app.uuid.utils import uuidToObject
@@ -9,7 +8,7 @@
from urllib.parse import quote
-class View(object):
+class View:
"""A shared view class that is used for each of the three views. They
each use a different template.
"""
diff --git a/src/plone/app/drafts/configure.zcml b/src/plone/app/drafts/configure.zcml
new file mode 100644
index 0000000..f5abff9
--- /dev/null
+++ b/src/plone/app/drafts/configure.zcml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plone/app/drafts/current.py b/src/plone/app/drafts/current.py
similarity index 98%
rename from plone/app/drafts/current.py
rename to src/plone/app/drafts/current.py
index 02b3e6f..76b0517 100644
--- a/plone/app/drafts/current.py
+++ b/src/plone/app/drafts/current.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
from plone.app.drafts.interfaces import DRAFT_KEY
from plone.app.drafts.interfaces import DRAFT_NAME_KEY
from plone.app.drafts.interfaces import ICurrentDraftManagement
@@ -18,7 +17,7 @@
@adapter(IRequest)
@implementer(ICurrentDraftManagement)
-class DefaultCurrentDraftManagement(object):
+class DefaultCurrentDraftManagement:
def __init__(self, request):
self.request = request
self.annotations = IAnnotations(request)
diff --git a/plone/app/drafts/dexterity.py b/src/plone/app/drafts/dexterity.py
similarity index 88%
rename from plone/app/drafts/dexterity.py
rename to src/plone/app/drafts/dexterity.py
index f37c5bb..13968cb 100644
--- a/plone/app/drafts/dexterity.py
+++ b/src/plone/app/drafts/dexterity.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
from Acquisition import aq_base
from plone.app.drafts.interfaces import ICurrentDraftManagement
from plone.app.drafts.interfaces import IDraftable
@@ -76,10 +75,10 @@ def __init__(self, form, request, context):
if isDraftable(fti):
current = ICurrentDraftManagement(request)
- if current.targetKey != "++add++{0}".format(form.portal_type):
+ if current.targetKey != f"++add++{form.portal_type}":
beginDrafting(context, None)
current.path = "/".join(context.getPhysicalPath())
- current.targetKey = "++add++{0}".format(form.portal_type)
+ current.targetKey = f"++add++{form.portal_type}"
current.save()
else:
current.mark()
@@ -89,12 +88,12 @@ def __init__(self, form, request, context):
context = DraftProxy(current.draft, target.__of__(context))
alsoProvides(request, IAddFormDrafting)
- super(DefaultAddFormFieldWidgets, self).__init__(form, request, context) # noqa
+ super().__init__(form, request, context) # noqa
def update(self):
if IAddFormDrafting.providedBy(self.request):
self.ignoreContext = False
- super(DefaultAddFormFieldWidgets, self).update()
+ super().update()
@adapter(IGroup, IAddFormDrafting, Interface)
@@ -104,13 +103,11 @@ def __init__(self, form, request, context):
draft = getCurrentDraft(request)
target = getattr(draft, "_draftAddFormTarget")
context = DraftProxy(draft, target.__of__(context))
- super(DefaultAddFormGroupFieldWidgets, self).__init__(
- form, request, context
- ) # noqa
+ super().__init__(form, request, context) # noqa
def update(self):
self.ignoreContext = False
- super(DefaultAddFormGroupFieldWidgets, self).update()
+ super().update()
@adapter(DefaultEditForm, IFormLayer, IDexterityContent)
@@ -133,9 +130,7 @@ def __init__(self, form, request, context):
context = DraftProxy(current.draft, context)
alsoProvides(request, IEditFormDrafting)
- super(DefaultEditFormFieldWidgets, self).__init__(
- form, request, context
- ) # noqa
+ super().__init__(form, request, context) # noqa
@adapter(IGroup, IEditFormDrafting, Interface)
@@ -144,9 +139,7 @@ class DefaultEditFormGroupFieldWidgets(FieldWidgetsBase):
def __init__(self, form, request, context):
draft = getCurrentDraft(request)
context = DraftProxy(draft, context)
- super(DefaultEditFormGroupFieldWidgets, self).__init__(
- form, request, context
- ) # noqa
+ super().__init__(form, request, context) # noqa
@adapter(WidgetsView, IDisplayFormDrafting, IDexterityContent)
@@ -163,9 +156,7 @@ def __init__(self, form, request, context):
if current.draft:
context = DraftProxy(current.draft, context)
- super(DefaultDisplayFormFieldWidgets, self).__init__(
- form, request, context
- ) # noqa
+ super().__init__(form, request, context) # noqa
@adapter(IGroup, IDisplayFormDrafting, Interface)
@@ -174,9 +165,7 @@ class DefaultDisplayFormGroupFieldWidgets(FieldWidgetsBase):
def __init__(self, form, request, context):
draft = getCurrentDraft(request)
context = DraftProxy(draft, context)
- super(DefaultDisplayFormGroupFieldWidgets, self).__init__(
- form, request, context
- ) # noqa
+ super().__init__(form, request, context) # noqa
def autosave(event): # noqa
@@ -201,7 +190,7 @@ def autosave(event): # noqa
if target is None:
target = createContent(form.portal_type)
target.id = ""
- IMutableUUID(target).set("++add++{0}".format(form.portal_type))
+ IMutableUUID(target).set(f"++add++{form.portal_type}")
draft._draftAddFormTarget = target
target = target.__of__(context)
@@ -225,9 +214,7 @@ def autosave(event): # noqa
content = DraftProxy(draft, target)
# Drop known non-draftable values
- data = dict(
- [(k, v) for k, v in data.items() if k not in AUTOSAVE_BLACKLIST]
- ) # noqa
+ data = {k: v for k, v in data.items() if k not in AUTOSAVE_BLACKLIST} # noqa
# Values are applied within savepoint to allow revert of any
# unexpected side-effects from setting field values
diff --git a/plone/app/drafts/dexterity.zcml b/src/plone/app/drafts/dexterity.zcml
similarity index 95%
rename from plone/app/drafts/dexterity.zcml
rename to src/plone/app/drafts/dexterity.zcml
index 07a7b0e..de6de94 100644
--- a/plone/app/drafts/dexterity.zcml
+++ b/src/plone/app/drafts/dexterity.zcml
@@ -1,6 +1,7 @@
+ i18n_domain="plone.app.drafts"
+ >
diff --git a/plone/app/drafts/draft.py b/src/plone/app/drafts/draft.py
similarity index 95%
rename from plone/app/drafts/draft.py
rename to src/plone/app/drafts/draft.py
index 3cdb5e9..828d118 100644
--- a/plone/app/drafts/draft.py
+++ b/src/plone/app/drafts/draft.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
from persistent import Persistent
from plone.app.drafts.interfaces import IDraft
from zope.annotation.interfaces import IAttributeAnnotatable
diff --git a/plone/app/drafts/interfaces.py b/src/plone/app/drafts/interfaces.py
similarity index 79%
rename from plone/app/drafts/interfaces.py
rename to src/plone/app/drafts/interfaces.py
index 47d232c..210fb8d 100644
--- a/plone/app/drafts/interfaces.py
+++ b/src/plone/app/drafts/interfaces.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
from zope import schema
from zope.interface import Interface
@@ -17,9 +16,9 @@ class IDraft(Interface):
to it as required.
"""
- _draftUserId = schema.TextLine(title=u"User id")
- _draftTargetKey = schema.TextLine(title=u"Target object key")
- __name__ = schema.TextLine(title=u"Unique draft name")
+ _draftUserId = schema.TextLine(title="User id")
+ _draftTargetKey = schema.TextLine(title="Target object key")
+ __name__ = schema.TextLine(title="Unique draft name")
class IDraftStorage(Interface):
@@ -29,18 +28,18 @@ class IDraftStorage(Interface):
"""
enabled = schema.Bool(
- title=u"Whether drafting is enabled",
+ title="Whether drafting is enabled",
default=True,
)
drafts = schema.Dict(
- title=u"Drafts",
- description=u"Use the methods below to inspect and manipulate this",
- key_type=schema.TextLine(title=u"User id"),
+ title="Drafts",
+ description="Use the methods below to inspect and manipulate this",
+ key_type=schema.TextLine(title="User id"),
value_type=schema.Dict(
- key_type=schema.TextLine(title=u"Draft target key"),
+ key_type=schema.TextLine(title="Draft target key"),
value_type=schema.Dict(
- key_type=schema.TextLine(title=u"Draft name"),
+ key_type=schema.TextLine(title="Draft name"),
value_type=schema.Object(schema=IDraft),
),
),
@@ -70,7 +69,7 @@ def discardDraft(draft):
"""Discard a particular draft."""
def getDrafts(userId, targetKey):
- """Get a list mappping of all drafts under the given userId and
+ """Get a list mapping of all drafts under the given userId and
target key. The returned mapping should not be modified directly.
"""
@@ -128,11 +127,11 @@ class ICurrentDraftManagement(Interface):
The current draft information may be persisted across requests by calling
``save()``. Once saved, it can be later discard by calling ``save()``.
- The deafult implementation stores information in cookies that are set
+ The default implementation stores information in cookies that are set
when the form is entered and cleared when it is saved or cancelled. The
- cookes are set for a path corresponding to the content object. This allows
+ cookies are set for a path corresponding to the content object. This allows
things like AJAX requests in the visual editor to find the same draft
- information, and minimises the risk of a draft for one object being used
+ information, and minimizes the risk of a draft for one object being used
for another.
The default path may be obtained via the ``defaultPath`` property, but
@@ -145,18 +144,18 @@ class ICurrentDraftManagement(Interface):
been set.
"""
- userId = schema.TextLine(title=u"Current user id")
- targetKey = schema.TextLine(title=u"Current target key")
- draftName = schema.TextLine(title=u"Current draft name")
- path = schema.TextLine(title=u"Path prefix in which the data should be retained")
+ userId = schema.TextLine(title="Current user id")
+ targetKey = schema.TextLine(title="Current target key")
+ draftName = schema.TextLine(title="Current draft name")
+ path = schema.TextLine(title="Path prefix in which the data should be retained")
defaultPath = schema.TextLine(
- title=u"Default path prefix for this request", readonly=True
+ title="Default path prefix for this request", readonly=True
)
draft = schema.Object(
- title=u"Current draft",
- description=u"If userId, targetKey and draftName are set, the "
- u"draft will be lazily fetched from the storage",
+ title="Current draft",
+ description="If userId, targetKey and draftName are set, the "
+ "draft will be lazily fetched from the storage",
schema=IDraft,
)
diff --git a/plone/app/drafts/lifecycle.py b/src/plone/app/drafts/lifecycle.py
similarity index 99%
rename from plone/app/drafts/lifecycle.py
rename to src/plone/app/drafts/lifecycle.py
index 230c1fb..038e771 100644
--- a/plone/app/drafts/lifecycle.py
+++ b/src/plone/app/drafts/lifecycle.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
from plone.app.drafts.interfaces import ICurrentDraftManagement
from plone.app.drafts.interfaces import IDraftStorage
from plone.app.drafts.utils import getCurrentDraft
diff --git a/src/plone/app/drafts/profiles/default/componentregistry.xml b/src/plone/app/drafts/profiles/default/componentregistry.xml
new file mode 100644
index 0000000..956962e
--- /dev/null
+++ b/src/plone/app/drafts/profiles/default/componentregistry.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
diff --git a/src/plone/app/drafts/profiles/default/metadata.xml b/src/plone/app/drafts/profiles/default/metadata.xml
new file mode 100644
index 0000000..5380f6f
--- /dev/null
+++ b/src/plone/app/drafts/profiles/default/metadata.xml
@@ -0,0 +1,6 @@
+
+
+ 1
+
+
+
diff --git a/plone/app/drafts/profiles/default/plone.app.drafts.txt b/src/plone/app/drafts/profiles/default/plone.app.drafts.txt
similarity index 100%
rename from plone/app/drafts/profiles/default/plone.app.drafts.txt
rename to src/plone/app/drafts/profiles/default/plone.app.drafts.txt
diff --git a/src/plone/app/drafts/profiles/default/toolset.xml b/src/plone/app/drafts/profiles/default/toolset.xml
new file mode 100644
index 0000000..ab6e123
--- /dev/null
+++ b/src/plone/app/drafts/profiles/default/toolset.xml
@@ -0,0 +1,6 @@
+
+
+
+
diff --git a/plone/app/drafts/proxy.py b/src/plone/app/drafts/proxy.py
similarity index 98%
rename from plone/app/drafts/proxy.py
rename to src/plone/app/drafts/proxy.py
index 81cc43d..acaae14 100644
--- a/plone/app/drafts/proxy.py
+++ b/src/plone/app/drafts/proxy.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
from Acquisition import aq_base
from collections.abc import MutableMapping
from plone.app.drafts.interfaces import IDraftProxy
@@ -56,7 +55,7 @@ def __get__(self, inst, cls=None):
@implementer(IDraftProxy)
-class DraftProxy(object):
+class DraftProxy:
"""A simple proxy object that is initialised with a draft object and the
underlying target. All attribute and annotation writes are performed
against the draft; all reads are performed against the draft unless the
@@ -145,8 +144,7 @@ def __getitem__(self, key):
def __iter__(self):
# adhere to MutableMapping interface
- for val in self.draftAnnotations.values():
- yield val
+ yield from self.draftAnnotations.values()
def __len__(self):
# adhere to MutableMapping interface
diff --git a/plone/app/drafts/storage.py b/src/plone/app/drafts/storage.py
similarity index 94%
rename from plone/app/drafts/storage.py
rename to src/plone/app/drafts/storage.py
index 935f04f..537c7b3 100644
--- a/plone/app/drafts/storage.py
+++ b/src/plone/app/drafts/storage.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
from BTrees.OOBTree import OOBTree
from OFS.SimpleItem import SimpleItem
from plone.app.drafts.draft import Draft
@@ -41,19 +40,19 @@ def createDraft(self, userId, targetKey, factory=None):
draft = factory(userId, targetKey)
if not draft.__name__:
- draft.__name__ = u"draft"
+ draft.__name__ = "draft"
if draft.__name__ in container:
idx = len(container)
while (
- u"{0}-{1}".format(
+ "{}-{}".format(
draft.__name__,
idx,
)
in container
):
idx += 1
- draft.__name__ = u"{0}-{1}".format(
+ draft.__name__ = "{}-{}".format(
draft.__name__,
idx,
)
diff --git a/plone/app/drafts/testing.py b/src/plone/app/drafts/testing.py
similarity index 99%
rename from plone/app/drafts/testing.py
rename to src/plone/app/drafts/testing.py
index c45a335..1d3d873 100644
--- a/plone/app/drafts/testing.py
+++ b/src/plone/app/drafts/testing.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
from plone.app.testing import FunctionalTesting
from plone.app.testing import IntegrationTesting
from plone.app.testing import PLONE_FIXTURE
diff --git a/plone/app/drafts/tests.py b/src/plone/app/drafts/tests.py
similarity index 82%
rename from plone/app/drafts/tests.py
rename to src/plone/app/drafts/tests.py
index 6d745dd..601a04d 100644
--- a/plone/app/drafts/tests.py
+++ b/src/plone/app/drafts/tests.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
from pkg_resources import parse_version
from plone.app.drafts.draft import Draft
from plone.app.drafts.interfaces import DRAFT_NAME_KEY
@@ -78,14 +77,14 @@ def test_createDraft_existing_user_only(self):
def test_createDraft_factory(self):
def factory(userId, targetKey):
- return Draft(name=u"foo")
+ return Draft(name="foo")
draft1 = self.storage.createDraft("user1", "123", factory=factory)
- self.assertEqual(u"foo", draft1.__name__)
+ self.assertEqual("foo", draft1.__name__)
self.assertTrue(draft1.__name__ in self.storage.drafts["user1"]["123"])
draft2 = self.storage.createDraft("user1", "123", factory=factory)
- self.assertEqual(u"foo-1", draft2.__name__)
+ self.assertEqual("foo-1", draft2.__name__)
self.assertTrue(draft2.__name__ in self.storage.drafts["user1"]["123"])
def test_discardDrafts(self):
@@ -145,17 +144,17 @@ def test_discardDraft_keep_user(self):
def test_discardDraft_not_found(self):
self.storage.createDraft("user1", "123")
- draft = Draft("user1", "123", u"bogus")
+ draft = Draft("user1", "123", "bogus")
self.storage.discardDraft(draft)
def test_discardDraft_no_key(self):
self.storage.createDraft("user1", "123")
- draft = Draft("user1", "234", u"draft")
+ draft = Draft("user1", "234", "draft")
self.storage.discardDraft(draft)
def test_discardDraft_no_user(self):
self.storage.createDraft("user1", "123")
- draft = Draft("user2", "123", u"draft")
+ draft = Draft("user2", "123", "draft")
self.storage.discardDraft(draft)
def test_getDrafts(self):
@@ -182,7 +181,7 @@ def test_getDraft_found(self):
def test_getDraft_not_found(self):
self.storage.createDraft("user1", "123")
- self.assertEqual(None, self.storage.getDraft("user1", "123", u"bogus"))
+ self.assertEqual(None, self.storage.getDraft("user1", "123", "bogus"))
def test_getDraft_no_key(self):
draft = self.storage.createDraft("user1", "123")
@@ -211,32 +210,32 @@ def test_attributes(self):
self.folder.invokeFactory("Document", "d1")
target = self.folder["d1"]
- target.title = u"Old title"
+ target.title = "Old title"
draft = Draft()
draft.someAttribute = 1
proxy = DraftProxy(draft, target)
- self.assertEqual(u"Old title", proxy.title)
+ self.assertEqual("Old title", proxy.title)
self.assertEqual(1, proxy.someAttribute)
- proxy.title = u"New title"
+ proxy.title = "New title"
- self.assertEqual(u"New title", proxy.title)
+ self.assertEqual("New title", proxy.title)
def test_attribute_deletion(self):
self.folder.invokeFactory("Document", "d1")
target = self.folder["d1"]
- target.title = u"Old title"
- target.description = u"Old description"
+ target.title = "Old title"
+ target.description = "Old description"
draft = Draft()
draft.someAttribute = 1
- draft.description = u"New description"
+ draft.description = "New description"
proxy = DraftProxy(draft, target)
@@ -245,7 +244,7 @@ def test_attribute_deletion(self):
del proxy.description
self.assertEqual(
- set(["someAttribute", "title", "description"]),
+ {"someAttribute", "title", "description"},
draft._proxyDeleted,
)
@@ -257,8 +256,8 @@ def test_attribute_deletion(self):
self.assertFalse(hasattr(proxy, "title"))
self.assertFalse(hasattr(proxy, "description"))
- self.assertEqual(u"Old title", target.title)
- self.assertEqual(u"Old description", target.description)
+ self.assertEqual("Old title", target.title)
+ self.assertEqual("Old description", target.description)
def test_interfaces(self):
@@ -281,58 +280,58 @@ def test_annotations(self):
target = self.folder["d1"]
targetAnnotations = IAnnotations(target)
- targetAnnotations[u"test.key"] = 123
- targetAnnotations[u"other.key"] = 456
+ targetAnnotations["test.key"] = 123
+ targetAnnotations["other.key"] = 456
draft = Draft()
draftAnnotations = IAnnotations(draft)
- draftAnnotations[u"some.key"] = 234
+ draftAnnotations["some.key"] = 234
proxy = DraftProxy(draft, target)
proxyAnnotations = IAnnotations(proxy)
- self.assertEqual(123, proxyAnnotations[u"test.key"])
- self.assertEqual(234, proxyAnnotations[u"some.key"])
+ self.assertEqual(123, proxyAnnotations["test.key"])
+ self.assertEqual(234, proxyAnnotations["some.key"])
- proxyAnnotations[u"test.key"] = 789
+ proxyAnnotations["test.key"] = 789
- self.assertEqual(789, proxyAnnotations[u"test.key"])
- self.assertEqual(123, targetAnnotations[u"test.key"])
+ self.assertEqual(789, proxyAnnotations["test.key"])
+ self.assertEqual(123, targetAnnotations["test.key"])
# Annotations API
- self.assertEqual(789, proxyAnnotations.get(u"test.key"))
+ self.assertEqual(789, proxyAnnotations.get("test.key"))
keys = proxyAnnotations.keys()
- self.assertTrue(u"test.key" in keys)
- self.assertTrue(u"some.key" in keys)
- self.assertTrue(u"other.key" in keys)
-
- self.assertEqual(789, proxyAnnotations.setdefault(u"test.key", -1))
- self.assertEqual(234, proxyAnnotations.setdefault(u"some.key", -1))
- self.assertEqual(456, proxyAnnotations.setdefault(u"other.key", -1))
- self.assertEqual(-1, proxyAnnotations.setdefault(u"new.key", -1))
-
- del proxyAnnotations[u"test.key"]
- self.assertFalse(u"test.key" in proxyAnnotations)
- self.assertFalse(u"test.key" in draftAnnotations)
- self.assertTrue(u"test.key" in targetAnnotations)
- self.assertTrue(u"test.key" in draft._proxyAnnotationsDeleted)
-
- del proxyAnnotations[u"some.key"]
- self.assertFalse(u"some.key" in proxyAnnotations)
- self.assertFalse(u"some.key" in draftAnnotations)
- self.assertFalse(u"some.key" in targetAnnotations)
- self.assertTrue(u"some.key" in draft._proxyAnnotationsDeleted)
+ self.assertTrue("test.key" in keys)
+ self.assertTrue("some.key" in keys)
+ self.assertTrue("other.key" in keys)
+
+ self.assertEqual(789, proxyAnnotations.setdefault("test.key", -1))
+ self.assertEqual(234, proxyAnnotations.setdefault("some.key", -1))
+ self.assertEqual(456, proxyAnnotations.setdefault("other.key", -1))
+ self.assertEqual(-1, proxyAnnotations.setdefault("new.key", -1))
+
+ del proxyAnnotations["test.key"]
+ self.assertFalse("test.key" in proxyAnnotations)
+ self.assertFalse("test.key" in draftAnnotations)
+ self.assertTrue("test.key" in targetAnnotations)
+ self.assertTrue("test.key" in draft._proxyAnnotationsDeleted)
+
+ del proxyAnnotations["some.key"]
+ self.assertFalse("some.key" in proxyAnnotations)
+ self.assertFalse("some.key" in draftAnnotations)
+ self.assertFalse("some.key" in targetAnnotations)
+ self.assertTrue("some.key" in draft._proxyAnnotationsDeleted)
# this key was never in the proxy/draft
- del proxyAnnotations[u"other.key"]
- self.assertFalse(u"other.key" in proxyAnnotations)
- self.assertFalse(u"other.key" in draftAnnotations)
- self.assertTrue(u"other.key" in targetAnnotations)
- self.assertTrue(u"other.key" in draft._proxyAnnotationsDeleted)
+ del proxyAnnotations["other.key"]
+ self.assertFalse("other.key" in proxyAnnotations)
+ self.assertFalse("other.key" in draftAnnotations)
+ self.assertTrue("other.key" in targetAnnotations)
+ self.assertTrue("other.key" in draft._proxyAnnotationsDeleted)
class TestDraftSyncer(unittest.TestCase):
@@ -340,7 +339,7 @@ class TestDraftSyncer(unittest.TestCase):
layer = DRAFTS_INTEGRATION_TESTING
def test_syncDraft(self):
- class Target(object):
+ class Target:
pass
draft = Draft()
@@ -351,7 +350,7 @@ class Target(object):
@adapter(Draft, Target)
@implementer(IDraftSyncer)
- class Syncer1(object):
+ class Syncer1:
def __init__(self, draft, target):
self.draft = draft
self.target = target
@@ -359,11 +358,11 @@ def __init__(self, draft, target):
def __call__(self):
self.target.a1 = self.draft.a1
- provideAdapter(Syncer1, name=u"s1")
+ provideAdapter(Syncer1, name="s1")
@adapter(Draft, Target)
@implementer(IDraftSyncer)
- class Syncer2(object):
+ class Syncer2:
def __init__(self, draft, target):
self.draft = draft
self.target = target
@@ -371,7 +370,7 @@ def __init__(self, draft, target):
def __call__(self):
self.target.a2 = self.draft.a2
- provideAdapter(Syncer2, name=u"s2")
+ provideAdapter(Syncer2, name="s2")
syncDraft(draft, target)
@@ -392,8 +391,8 @@ def test_userId(self):
current = ICurrentDraftManagement(request)
self.assertEqual(TEST_USER_ID, current.userId)
- current.userId = u"third-user"
- self.assertEqual(u"third-user", current.userId)
+ current.userId = "third-user"
+ self.assertEqual("third-user", current.userId)
def test_targetKey(self):
request = self.request
@@ -401,13 +400,13 @@ def test_targetKey(self):
current = ICurrentDraftManagement(request)
self.assertEqual(None, current.targetKey)
- request.set("plone.app.drafts.targetKey", u"123")
- self.assertEqual(u"123", current.targetKey)
+ request.set("plone.app.drafts.targetKey", "123")
+ self.assertEqual("123", current.targetKey)
- current.targetKey = u"234"
- self.assertEqual(u"234", current.targetKey)
+ current.targetKey = "234"
+ self.assertEqual("234", current.targetKey)
- self.assertEqual(u"123", request.get("plone.app.drafts.targetKey"))
+ self.assertEqual("123", request.get("plone.app.drafts.targetKey"))
def test_draftName(self):
request = self.request
@@ -415,13 +414,13 @@ def test_draftName(self):
current = ICurrentDraftManagement(request)
self.assertEqual(None, current.draftName)
- request.set("plone.app.drafts.draftName", u"draft-1")
- self.assertEqual(u"draft-1", current.draftName)
+ request.set("plone.app.drafts.draftName", "draft-1")
+ self.assertEqual("draft-1", current.draftName)
- current.draftName = u"draft-2"
- self.assertEqual(u"draft-2", current.draftName)
+ current.draftName = "draft-2"
+ self.assertEqual("draft-2", current.draftName)
- self.assertEqual(u"draft-1", request.get("plone.app.drafts.draftName"))
+ self.assertEqual("draft-1", request.get("plone.app.drafts.draftName"))
def test_path(self):
request = self.request
@@ -429,13 +428,13 @@ def test_path(self):
current = ICurrentDraftManagement(request)
self.assertEqual(None, current.path)
- request.set("plone.app.drafts.path", u"/test")
- self.assertEqual(u"/test", current.path)
+ request.set("plone.app.drafts.path", "/test")
+ self.assertEqual("/test", current.path)
- current.path = u"/test/test-1"
- self.assertEqual(u"/test/test-1", current.path)
+ current.path = "/test/test-1"
+ self.assertEqual("/test/test-1", current.path)
- self.assertEqual(u"/test", request.get("plone.app.drafts.path"))
+ self.assertEqual("/test", request.get("plone.app.drafts.path"))
def test_draft(self):
request = self.request
@@ -443,19 +442,19 @@ def test_draft(self):
current = ICurrentDraftManagement(request)
self.assertEqual(None, current.draft)
- current.userId = u"user1"
- current.targetKey = u"123"
- current.draftName = u"draft"
+ current.userId = "user1"
+ current.targetKey = "123"
+ current.draftName = "draft"
self.assertEqual(None, current.draft)
storage = getUtility(IDraftStorage)
- created = storage.createDraft(u"user1", u"123")
+ created = storage.createDraft("user1", "123")
current.draftName = created.__name__
self.assertEqual(created, current.draft)
- newDraft = storage.createDraft(u"user1", u"123")
+ newDraft = storage.createDraft("user1", "123")
current.draft = newDraft
self.assertEqual(newDraft, current.draft)
@@ -484,7 +483,7 @@ def test_mark(self):
current.mark()
self.assertFalse(IDrafting.providedBy(request))
- current.targetKey = u"123"
+ current.targetKey = "123"
current.mark()
self.assertTrue(IDrafting.providedBy(request))
@@ -500,32 +499,32 @@ def test_save(self):
self.assertFalse("plone.app.drafts.userId" in response.cookies)
self.assertFalse("plone.app.drafts.path" in response.cookies)
- current.targetKey = u"123"
+ current.targetKey = "123"
self.assertEqual(True, current.save())
self.assertEqual(
- {"value": "123", "quoted": True, "path": "/"},
+ {"value": "123", "Path": "/"},
response.cookies["plone.app.drafts.targetKey"],
)
self.assertFalse("plone.app.drafts.draftName" in response.cookies)
self.assertFalse("plone.app.drafts.path" in response.cookies)
- current.targetKey = u"123"
- current.draftName = u"draft-1"
+ current.targetKey = "123"
+ current.draftName = "draft-1"
self.assertEqual(True, current.save())
self.assertEqual(
- {"value": "123", "quoted": True, "path": "/"},
+ {"value": "123", "Path": "/"},
response.cookies["plone.app.drafts.targetKey"],
)
self.assertEqual(
- {"value": "draft-1", "quoted": True, "path": "/"},
+ {"value": "draft-1", "Path": "/"},
response.cookies["plone.app.drafts.draftName"],
)
self.assertFalse("plone.app.drafts.path" in response.cookies)
- current.targetKey = u"123"
- current.draftName = u"draft-1"
+ current.targetKey = "123"
+ current.draftName = "draft-1"
current.path = "/test"
# clear data
@@ -535,15 +534,15 @@ def test_save(self):
self.assertEqual(True, current.save())
self.assertEqual(
- {"value": "123", "quoted": True, "path": "/test"},
+ {"value": "123", "Path": "/test"},
response.cookies["plone.app.drafts.targetKey"],
)
self.assertEqual(
- {"value": "draft-1", "quoted": True, "path": "/test"},
+ {"value": "draft-1", "Path": "/test"},
response.cookies["plone.app.drafts.draftName"],
)
self.assertEqual(
- {"value": "/test", "quoted": True, "path": "/test"},
+ {"value": "/test", "Path": "/test"},
response.cookies["plone.app.drafts.path"],
)
@@ -566,10 +565,9 @@ def test_discard(self):
pass
deletedToken = {
- "expires": expires,
- "max_age": 0,
- "path": "/",
- "quoted": True,
+ "Expires": expires,
+ "Max-Age": "0",
+ "Path": "/",
"value": "deleted",
}
@@ -589,7 +587,7 @@ def test_discard(self):
current.path = "/test"
current.discard()
- deletedToken["path"] = "/test"
+ deletedToken["Path"] = "/test"
self.assertEqual(
deletedToken,
@@ -678,9 +676,9 @@ def test_getCurrentDraft_draft_details_set_not_in_storage(self):
request = self.request
management = ICurrentDraftManagement(request)
- management.userId = u"user1"
- management.targetKey = u"123"
- management.draftName = u"bogus"
+ management.userId = "user1"
+ management.targetKey = "123"
+ management.draftName = "bogus"
draft = getCurrentDraft(request)
self.assertEqual(None, draft)
@@ -693,12 +691,12 @@ def test_getCurrentDraft_draft_details_set_not_in_storage_create(self):
request = self.request
management = ICurrentDraftManagement(request)
- management.userId = u"user1"
- management.targetKey = u"123"
- management.draftName = u"bogus"
+ management.userId = "user1"
+ management.targetKey = "123"
+ management.draftName = "bogus"
draft = getCurrentDraft(request, create=True)
- inStorage = getUtility(IDraftStorage).getDraft(u"user1", u"123", draft.__name__)
+ inStorage = getUtility(IDraftStorage).getDraft("user1", "123", draft.__name__)
self.assertEqual(inStorage, draft)
@@ -718,11 +716,11 @@ def test_getCurrentDraft_draft_details_set_not_in_storage_create(self):
def test_getCurrentDraft_draft_details_set_in_storage(self):
request = self.request
- inStorage = getUtility(IDraftStorage).createDraft(u"user1", u"123")
+ inStorage = getUtility(IDraftStorage).createDraft("user1", "123")
management = ICurrentDraftManagement(request)
- management.userId = u"user1"
- management.targetKey = u"123"
+ management.userId = "user1"
+ management.targetKey = "123"
management.draftName = inStorage.__name__
draft = getCurrentDraft(request)
@@ -735,11 +733,11 @@ def test_getCurrentDraft_draft_details_set_in_storage(self):
def test_getCurrentDraft_draft_details_set_in_storage_create(self):
request = self.request
- inStorage = getUtility(IDraftStorage).createDraft(u"user1", u"123")
+ inStorage = getUtility(IDraftStorage).createDraft("user1", "123")
management = ICurrentDraftManagement(request)
- management.userId = u"user1"
- management.targetKey = u"123"
+ management.userId = "user1"
+ management.targetKey = "123"
management.draftName = inStorage.__name__
draft = getCurrentDraft(request, create=True)
@@ -788,9 +786,9 @@ def test_add_to_portal_root_cancel(self):
# We should now have cookies with the drafts information
cookies = browser.cookies.forURL(browser.url)
- self.assertEqual('"/plone"', cookies["plone.app.drafts.path"])
+ self.assertEqual("/plone", cookies["plone.app.drafts.path"])
self.assertEqual(
- '"{0}"'.format(self.get_portal_target_key()),
+ f"{self.get_portal_target_key()}",
cookies["plone.app.drafts.targetKey"],
)
self.assertNotIn(
@@ -828,9 +826,9 @@ def test_add_to_portal_root_save(self):
# We should now have cookies with the drafts information
cookies = browser.cookies.forURL(browser.url)
- self.assertEqual('"/plone"', cookies["plone.app.drafts.path"])
+ self.assertEqual("/plone", cookies["plone.app.drafts.path"])
self.assertEqual(
- '"{0}"'.format(self.get_portal_target_key()),
+ f"{self.get_portal_target_key()}",
cookies["plone.app.drafts.targetKey"],
)
self.assertNotIn(
@@ -847,16 +845,14 @@ def test_add_to_portal_root_save(self):
browser.cookies.create(
DRAFT_NAME_KEY,
- u"draft",
+ "draft",
path="/plone",
)
# We can now fill in the required fields and save. The cookies should
# expire.
- browser.getControl(
- name="form.widgets.IDublinCore.title"
- ).value = u"New Document"
+ browser.getControl(name="form.widgets.IDublinCore.title").value = "New Document"
browser.getControl(name="form.buttons.save").click()
self.assertNotIn(
"plone.app.drafts.targetKey",
@@ -887,11 +883,11 @@ def test_add_to_folder(self):
# We should now have cookies with the drafts information
cookies = browser.cookies.forURL(browser.url)
self.assertEqual(
- '"{0}"'.format(self.folder.absolute_url_path()),
+ f"{self.folder.absolute_url_path()}",
cookies["plone.app.drafts.path"],
)
self.assertEqual(
- '"{0}"'.format(IUUID(self.folder)),
+ f"{IUUID(self.folder)}",
cookies["plone.app.drafts.targetKey"],
)
self.assertNotIn(
@@ -915,7 +911,7 @@ def test_edit(self):
browser.handleErrors = False
self.folder.invokeFactory("MyDocument", "d1")
- self.folder["d1"].title = u"New title"
+ self.folder["d1"].title = "New title"
transaction.commit()
@@ -933,11 +929,11 @@ def test_edit(self):
# We should now have cookies with the drafts information
cookies = browser.cookies.forURL(browser.url)
self.assertEqual(
- '"{0}"'.format(self.folder["d1"].absolute_url_path()),
+ "{}".format(self.folder["d1"].absolute_url_path()),
cookies["plone.app.drafts.path"],
)
self.assertEqual(
- '"{0}"'.format(uuid),
+ f"{uuid}",
cookies["plone.app.drafts.targetKey"],
)
self.assertNotIn(
@@ -965,7 +961,7 @@ def test_edit_existing_draft(self):
browser.handleErrors = False
self.folder.invokeFactory("MyDocument", "d1")
- self.folder["d1"].title = u"New title"
+ self.folder["d1"].title = "New title"
uuid = IUUID(self.folder["d1"])
@@ -987,19 +983,19 @@ def test_edit_existing_draft(self):
# We should now have cookies with the drafts information
cookies = browser.cookies.forURL(browser.url)
self.assertEqual(
- '"{0}"'.format(self.folder["d1"].absolute_url_path()),
+ "{}".format(self.folder["d1"].absolute_url_path()),
cookies["plone.app.drafts.path"],
)
self.assertEqual(
- '"{0}"'.format(uuid),
+ f"{uuid}",
cookies["plone.app.drafts.targetKey"],
)
self.assertEqual(
- '"{0}"'.format(TEST_USER_ID),
+ f"{TEST_USER_ID}",
cookies["plone.app.drafts.userId"],
)
self.assertEqual(
- '"{0}"'.format(draft.__name__),
+ f"{draft.__name__}",
cookies["plone.app.drafts.draftName"],
)
@@ -1027,7 +1023,7 @@ def test_edit_multiple_existing_drafts(self):
browser.handleErrors = False
self.folder.invokeFactory("MyDocument", "d1")
- self.folder["d1"].title = u"New title"
+ self.folder["d1"].title = "New title"
transaction.commit()
@@ -1050,11 +1046,11 @@ def test_edit_multiple_existing_drafts(self):
# We should now have cookies with the drafts information
cookies = browser.cookies.forURL(browser.url)
self.assertEqual(
- '"{0}"'.format(self.folder["d1"].absolute_url_path()),
+ "{}".format(self.folder["d1"].absolute_url_path()),
cookies["plone.app.drafts.path"],
)
self.assertEqual(
- '"{0}"'.format(uuid),
+ f"{uuid}",
cookies["plone.app.drafts.targetKey"],
)
self.assertNotIn(
diff --git a/plone/app/drafts/utils.py b/src/plone/app/drafts/utils.py
similarity index 98%
rename from plone/app/drafts/utils.py
rename to src/plone/app/drafts/utils.py
index db7d4d9..74ba3fc 100644
--- a/plone/app/drafts/utils.py
+++ b/src/plone/app/drafts/utils.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
from AccessControl import getSecurityManager
from plone.app.drafts.interfaces import ICurrentDraftManagement
from plone.app.drafts.interfaces import IDraftStorage
diff --git a/test-5.2.cfg b/test-5.2.cfg
deleted file mode 100644
index d1e31d8..0000000
--- a/test-5.2.cfg
+++ /dev/null
@@ -1,26 +0,0 @@
-[buildout]
-extends =
- https://raw.githubusercontent.com/collective/buildout.plonetest/master/test-5.2.x.cfg
- https://raw.githubusercontent.com/collective/buildout.plonetest/master/qa.cfg
-
-package-name = plone.app.drafts
-package-extras = [test]
-package-min-coverage = 80
-parts+=
- createcoverage
- coverage-sh
- code-analysis
-
-[code-analysis]
-directory = plone
-# E203 Whitespace before ':' (false positives when using black)
-# E231 missing whitespace after ',' (conflicts with black)
-# E501 line too long
-# W503 Line break occurred before a binary operator [outdated]
-flake8-ignore = E203,E231,E501,W503
-
-[versions]
-# plone.app.drafts is pinned in core, so we must unpin it here.
-plone.app.drafts =
-setuptools = 41.2.0
-zc.buildout = 2.13.3
diff --git a/test-6.0.cfg b/test-6.0.cfg
deleted file mode 100644
index 0a924a5..0000000
--- a/test-6.0.cfg
+++ /dev/null
@@ -1,54 +0,0 @@
-[buildout]
-extends =
- https://raw.githubusercontent.com/plone/buildout.coredev/6.0/sources.cfg
- https://raw.githubusercontent.com/plone/buildout.coredev/6.0/checkouts.cfg
- https://raw.githubusercontent.com/plone/buildout.coredev/6.0/versions.cfg
- https://raw.githubusercontent.com/collective/buildout.plonetest/master/qa.cfg
-
-parts =
- instance
- test
- code-analysis
- createcoverage
-
-extensions = mr.developer
-versions = versions
-package-name = plone.app.drafts
-package-extras = [test]
-test-eggs =
-develop = .
-
-[instance]
-recipe = plone.recipe.zope2instance
-user = ${buildout:plone-user}
-wsgi = on
-eggs =
- Plone
- plone.app.upgrade
- ${buildout:package-name}
- ${buildout:eggs}
-deprecation-warnings = on
-environment-vars =
- zope_i18n_compile_mo_files true
-zcml =
- ${buildout:package-name}
-
-[test]
-recipe = zc.recipe.testrunner
-defaults = ['-s', '${buildout:package-name}', '--auto-color', '--auto-progress']
-eggs =
- Plone
- plone.app.upgrade
- ${buildout:package-name} ${buildout:package-extras}
- ${buildout:test-eggs}
-
-[code-analysis]
-directory= ${buildout:directory}/plone/app/tiles
-flake8-ignore = E203,E231,E501,W503
-
-[versions]
-plone.app.drafts =
-setuptools =
-zc.buildout =
-coverage = >=3.7
-plone.app.robotframework = 1.5.0
diff --git a/tox.ini b/tox.ini
index b33a469..ef633eb 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,18 +1,238 @@
+# Generated from:
+# https://github.com/plone/meta/tree/main/src/plone/meta/default
+# See the inline comments on how to expand/tweak this configuration file
[tox]
-minversion = 3.18
+# We need 4.4.0 for constrain_package_deps.
+min_version = 4.4.0
envlist =
- plone52-py{37,38,39}
- plone60-py{37,38,39}
+ lint
+ test
+ py313-plone62
+ py312-plone62
+ py311-plone62
+ py310-plone62
+ dependencies
+
+
+##
+# Add extra configuration options in .meta.toml:
+# - to specify a custom testing combination of Plone and python versions, use `test_matrix`
+# Use ["*"] to use all supported Python versions for this Plone version.
+# - to specify extra custom environments, use `envlist_lines`
+# - to specify extra `tox` top-level options, use `config_lines`
+# [tox]
+# test_matrix = {"6.2" = ["3.13", "3.12"], "6.1" = ["*"]}
+# envlist_lines = """
+# my_other_environment
+# """
+# config_lines = """
+# my_extra_top_level_tox_configuration_lines
+# """
+##
+
+[testenv:init]
+description = Prepare environment
+skip_install = true
+allowlist_externals =
+ echo
+commands =
+ echo "Initial setup complete"
+
+[testenv:format]
+description = automatically reformat code
+skip_install = true
+deps =
+ pre-commit
+commands =
+ pre-commit run -a pyupgrade
+ pre-commit run -a isort
+ pre-commit run -a black
+ pre-commit run -a zpretty
+
+[testenv:lint]
+description = run linters that will help improve the code style
+skip_install = true
+deps =
+ pre-commit
+commands =
+ pre-commit run -a
+
+[testenv:dependencies]
+description = check if the package defines all its dependencies
+skip_install = true
+deps =
+ build
+ z3c.dependencychecker==2.14.3
+commands =
+ python -m build --sdist
+ dependencychecker
+
+[testenv:dependencies-graph]
+description = generate a graph out of the dependencies of the package
+skip_install = false
+allowlist_externals =
+ sh
+deps =
+ pipdeptree==2.5.1
+ graphviz # optional dependency of pipdeptree
+commands =
+ sh -c 'pipdeptree --exclude setuptools,wheel,pipdeptree,zope.interface,zope.component --graph-output svg > dependencies.svg'
+
+
+[test_runner]
+deps = zope.testrunner
+test =
+ zope-testrunner --all --test-path={toxinidir}/src -s plone.app.drafts {posargs}
+coverage =
+ coverage run --branch --source plone.app.drafts {envbindir}/zope-testrunner --quiet --all --test-path={toxinidir}/src -s plone.app.drafts {posargs}
+ coverage report -m --format markdown
+ coverage xml
+ coverage html
+
+[base]
+description = shared configuration for tests and coverage
+use_develop = true
+skip_install = false
+constrain_package_deps = true
+set_env =
+ ROBOT_BROWSER=headlesschrome
+
+##
+# Specify extra test environment variables in .meta.toml:
+# [tox]
+# test_environment_variables = """
+# PIP_EXTRA_INDEX_URL=https://my-pypi.my-server.com/
+# """
+#
+# Set constrain_package_deps .meta.toml:
+# [tox]
+# constrain_package_deps = false
+##
+deps =
+ {[test_runner]deps}
+ plone62: -c https://dist.plone.org/release/6.2-dev/constraints.txt
+
+##
+# Specify additional deps in .meta.toml:
+# [tox]
+# test_deps_additional = """
+# -esources/plonegovbr.portal_base[test]
+# """
+#
+# Specify a custom constraints file in .meta.toml:
+# [tox]
+# constraints_file = "https://my-server.com/constraints.txt"
+##
+extras =
+ test
+
+
+##
+# Add extra configuration options in .meta.toml:
+# [tox]
+# test_extras = """
+# tests
+# widgets
+# """
+#
+# Add extra configuration options in .meta.toml:
+# [tox]
+# testenv_options = """
+# basepython = /usr/bin/python3.8
+# """
+##
+
+[testenv:test]
+description = run the distribution tests
+use_develop = {[base]use_develop}
+skip_install = {[base]skip_install}
+constrain_package_deps = {[base]constrain_package_deps}
+set_env = {[base]set_env}
+deps =
+ {[test_runner]deps}
+ -c https://dist.plone.org/release/6.2-dev/constraints.txt
+
+commands = {[test_runner]test}
+extras = {[base]extras}
+
[testenv]
-# We do not install with pip, but with buildout:
-usedevelop = false
+description = run the distribution tests (generative environments)
+use_develop = {[base]use_develop}
+skip_install = {[base]skip_install}
+constrain_package_deps = {[base]constrain_package_deps}
+set_env = {[base]set_env}
+deps = {[base]deps}
+commands = {[test_runner]test}
+extras = {[base]extras}
+
+
+[testenv:coverage]
+description = get a test coverage report
+use_develop = {[base]use_develop}
+skip_install = {[base]skip_install}
+constrain_package_deps = {[base]constrain_package_deps}
+set_env = {[base]set_env}
+deps =
+ {[test_runner]deps}
+ coverage
+ -c https://dist.plone.org/release/6.2-dev/constraints.txt
+
+commands = {[test_runner]coverage}
+extras = {[base]extras}
+
+
+[testenv:release-check]
+description = ensure that the distribution is ready to release
skip_install = true
deps =
- zc.buildout
-commands_pre =
- plone52: {envbindir}/buildout -Nc {toxinidir}/test-5.2.cfg buildout:directory={envdir} buildout:develop={toxinidir} install test code-analysis
- plone60: {envbindir}/buildout -Nc {toxinidir}/test-6.0.cfg buildout:directory={envdir} buildout:develop={toxinidir} install test code-analysis
+ twine
+ build
+ towncrier
+ -c https://dist.plone.org/release/6.2-dev/constraints.txt
commands =
- {envbindir}/code-analysis {toxinidir}/plone/app/drafts
- {envbindir}/test
+ # fake version to not have to install the package
+ # we build the change log as news entries might break
+ # the README that is displayed on PyPI
+ towncrier build --version=100.0.0 --yes
+ python -m build --sdist
+ twine check dist/*
+
+[testenv:circular]
+description = ensure there are no cyclic dependencies
+use_develop = true
+skip_install = false
+# Here we must always constrain the package deps to what is already installed,
+# otherwise we simply get the latest from PyPI, which may not work.
+constrain_package_deps = true
+set_env =
+
+##
+# Specify extra test environment variables in .meta.toml:
+# [tox]
+# test_environment_variables = """
+# PIP_EXTRA_INDEX_URL=https://my-pypi.my-server.com/
+# """
+##
+allowlist_externals =
+ sh
+deps =
+ pipdeptree
+ pipforester
+ -c https://dist.plone.org/release/6.2-dev/constraints.txt
+commands =
+ # Generate the full dependency tree
+ sh -c 'pipdeptree -j > forest.json'
+ # Generate a DOT graph with the circular dependencies, if any
+ pipforester -i forest.json -o forest.dot --cycles
+ # Report if there are any circular dependencies, i.e. error if there are any
+ pipforester -i forest.json --check-cycles -o /dev/null
+
+
+##
+# Add extra configuration options in .meta.toml:
+# [tox]
+# extra_lines = """
+# _your own configuration lines_
+# """
+##