diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1a195301..2fc28a9d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,40 +1,43 @@ -name: tests +name: Tests on: push: - branches: [ master ] + branches: master pull_request: - branches: [ master, 3.x ] + branches: master workflow_dispatch: + jobs: build: + runs-on: ubuntu-latest strategy: fail-fast: false matrix: - config: - # [Python version, tox env] - - ["3.8", "plone60-py38"] - - ["3.9", "plone60-py39"] - - ["3.10", "plone60-py310"] - runs-on: ubuntu-latest - name: ${{ matrix.config[1] }} + python: + - "3.8" + - "3.9" + - "3.10" + plone: + - "6.0-dev" + 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] }} + - uses: actions/checkout@v2 + + - name: Setup Plone ${{ matrix.plone }} with Python ${{ matrix.python }} + id: setup + uses: plone/setup-plone@v1.0.0 + with: + python-version: ${{ matrix.python }} + plone-version: ${{ matrix.plone }} + + - name: Install package + run: | + make install + + - name: Run Lint + run: | + make lint + + - name: Run tests + run: | + make test-ignore-warnings diff --git a/.gitignore b/.gitignore index a771a74e..2ca49476 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,11 @@ var/ pyvenv.cfg /venv/ /.vscode -local.cfg \ No newline at end of file +local.cfg + +.*.cfg +*-mxdev.txt +/.make-sentinels/ +sources/ +.installed.txt +stamp-yarn diff --git a/CHANGES.rst b/CHANGES.rst index 343e46a0..5a06867b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -14,6 +14,9 @@ Changelog - Schemaeditor UI: close modals and reload fields(sets) when saving. [petschki] +- Fix bug which did not render correctly GroupForm widgets (see #370) + [petschki] + 4.1.0 (2022-08-10) ------------------ diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..2b7805a9 --- /dev/null +++ b/Makefile @@ -0,0 +1,313 @@ +# Makefile to configure and run Plone instance + +############################################################################## +# SETUP MAKE + +## Defensive settings for make: https://tech.davis-hansson.com/p/make/ +SHELL:=bash +.ONESHELL: +# for Makefile debugging purposes add -x to the .SHELLFLAGS +.SHELLFLAGS:=-eu -o pipefail -O inherit_errexit -c +.SILENT: +.DELETE_ON_ERROR: +MAKEFLAGS+=--warn-undefined-variables +MAKEFLAGS+=--no-builtin-rules + +# Colors +# OK=Green, warn=yellow, error=red +ifeq ($(TERM),) +# no colors if not in terminal + MARK_COLOR= + OK_COLOR= + WARN_COLOR= + ERROR_COLOR= + NO_COLOR= +else + MARK_COLOR=`tput setaf 6` + OK_COLOR=`tput setaf 2` + WARN_COLOR=`tput setaf 3` + ERROR_COLOR=`tput setaf 1` + NO_COLOR=`tput sgr0` +endif + +############################################################################## +# SETTINGS AND VARIABLE +# adjust to your project needs +PROJECT_NAME=collective.easyform +IMAGE_NAME=${PROJECT_NAME} +CONSTRAINTS=constraints.txt +PIP_REQUIREMENTS_IN_FILE=requirements.txt +ADDONBASE=./ +ADDONFOLDER=${ADDONBASE}src/ +# it is possible to define an alternative YAML file for the the instance.set default i +INSTANCE_YAML?=instance.yaml +INSTANCE_FOLDER?=instance + +PIP_PARAMS= --pre + +############################################################################## +# targets and prerequisites +# target has to be one file, otherwise step gets executes for each file separate +PREPARE_PREREQUISITES=${PIP_REQUIREMENTS_IN_FILE} ${CONSTRAINTS} mx.ini ${ADDONBASE}setup.cfg +PREPARE_TARGET=requirements-mxdev.txt +INSTALL_PREREQUSISTES=${PREPARE_TARGET} +INSTALL_TARGET=.installed.txt +INSTANCE_PREREQUISITES=${INSTALL_TARGET} ${INSTANCE_YAML} +INSTANCE_TARGET=${INSTANCE_FOLDER}/etc/zope.ini ${INSTANCE_FOLDER}/etc/zope.conf ${INSTANCE_FOLDER}/etc/site.zcml +TEST_PREREQUISITES=${INSTALL_TARGET} +RUN_PREREQUISITES=${INSTANCE_TARGET} + +############################################################################## +# CONVINIENCE + +# install and run +.PHONY: all # full install, test and run +all:style test run + +# Add the following 'help' target to your Makefile +# And add help text after each target name starting with '\#\#' +.PHONY: help +help: ## This help message + @echo "${OK_COLOR}This is the Makefile for ${WARN_COLOR}${PROJECT_NAME}${NO_COLOR}" + @echo + @echo "${WARN_COLOR}Additional parameters:${NO_COLOR}" + @echo "${MARK_COLOR}PYTHON${NO_COLOR}: Python interpreter to be used (default: python3)" + @echo "${MARK_COLOR}VENV${NO_COLOR}: [on|off] wether to create a Python virtual environment or not (default: on)"s + @echo "${MARK_COLOR}VENV_FOLDER${NO_COLOR}: location of the virtual environment (default: ./venv)" + @echo + @echo "${WARN_COLOR}Targets:${NO_COLOR}" + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +############################################################################## +# targets and prerequisites +# target has to be one file, otherwise step gets executes for each file separate +PREPARE_PREREQUISITES=${PIP_REQUIREMENTS_IN_FILE} ${CONSTRAINTS} mx.ini ${ADDONBASE}setup.cfg +PREPARE_TARGET=requirements-mxdev.txt +INSTALL_PREREQUSISTES=${PREPARE_TARGET} +INSTALL_TARGET=.installed.txt +INSTANCE_PREREQUISITES=${INSTALL_TARGET} ${INSTANCE_YAML} +INSTANCE_TARGET=${INSTANCE_FOLDER}/etc/zope.ini ${INSTANCE_FOLDER}/etc/zope.conf ${INSTANCE_FOLDER}/etc/site.zcml +TEST_PREREQUISITES=${INSTALL_TARGET} +RUN_PREREQUISITES=${INSTANCE_TARGET} + +############################################################################## +# BASE + +SENTINELFOLDER=.make-sentinels/ +SENTINEL=${SENTINELFOLDER}ABOUT.txt +${SENTINEL}: + @mkdir -p ${SENTINELFOLDER} + @echo "Sentinels for the Makefile process." > ${SENTINEL} + +# PYTHON, VENV, PIP +# venv and pybin +PYTHON?=python3 +VENV?=on +ifeq ("${VENV}", "on") + VENV_FOLDER?=./venv + PYBIN=${VENV_FOLDER}/bin/ +else + VENV_FOLDER?= + ifneq ("${VENV_FOLDER}", "") + PYBIN=${VENV_FOLDER}/bin/ + PYTHON=${PYBIN}python + else + PYBIN= + endif +endif + +# installed? +ifeq (, $(shell which $(PYTHON) )) + $(error "PYTHON=$(PYTHON) not found in $(PATH)") +endif + +# version ok? +PYTHON_VERSION_MIN=3.7 +PYTHON_VERSION_OK=$(shell $(PYTHON) -c 'import sys; print(int(sys.version_info[0:2] >= tuple(map(int, "$(PYTHON_VERSION_MIN)".split(".")))))' ) + +ifeq ($(PYTHON_VERSION_OK),0) + $(error "Need python $(PYTHON_VERSION) >= $(PYTHON_VERSION_MIN)") +endif + +VENV_SENTINEL=${SENTINELFOLDER}venv.sentinel +${VENV_SENTINEL}: ${SENTINEL} +ifeq ("${VENV}", "on") + @echo "$(OK_COLOR)Setup Python Virtual Environment under '${VENV_FOLDER}' $(NO_COLOR)" + @${PYTHON} -m venv ${VENV_FOLDER} +else + @echo "$(OK_COLOR)Use current local or global Python: `which ${PYTHON}` $(NO_COLOR)" +endif + @touch ${VENV_SENTINEL} + +PIP_SENTINEL=${SENTINELFOLDER}pip.sentinel +${PIP_SENTINEL}: ${VENV_SENTINEL} ${CONSTRAINTS} ${SENTINEL} + @echo "$(OK_COLOR)Install pip$(NO_COLOR)" + @${PYBIN}pip install -U "pip>=22.0.2" wheel setuptools + @touch ${PIP_SENTINEL} + +############################################################################## +# MXDEV + +MXDEV_SENTINEL=${SENTINELFOLDER}pip-mxdev.sentinel +${MXDEV_SENTINEL}: ${PIP_SENTINEL} + @echo "$(OK_COLOR)Install mxdev$(NO_COLOR)" + @${PYBIN}pip install "mxdev==2.1.0" "libvcs==0.11.1" + @touch ${MXDEV_SENTINEL} + +.PHONY: prepare +prepare: ${PREPARE_TARGET} ## prepare soures and dependencies + +${PREPARE_PREREQUISITES}: + @touch $@ + +${PREPARE_TARGET}: ${MXDEV_SENTINEL} ${PREPARE_PREREQUISITES} + @echo "$(OK_COLOR)Prepare sources and dependencies$(NO_COLOR)" + @${PYBIN}mxdev -c mx.ini + +.PHONY: install +install: ${INSTALL_TARGET} ## pip install all dependencies and scripts + +${INSTALL_TARGET}: ${PREPARE_TARGET} + @echo "$(OK_COLOR)Install dependencies and scripts$(NO_COLOR)" + @${PYBIN}pip install -r ${PREPARE_TARGET} ${PIP_PARAMS} + @${PYBIN}pip freeze >${INSTALL_TARGET} + +############################################################################## +# INSTANCE + +COOKIECUTTER_SENTINEL=${SENTINELFOLDER}pip-cookiecutter.sentinel +${COOKIECUTTER_SENTINEL}: + @echo "$(OK_COLOR)Install cookiecutter$(NO_COLOR)" + @${PYBIN}pip install "cookiecutter>=2.1.1" + @touch ${COOKIECUTTER_SENTINEL} + +${INSTANCE_YAML}: + @touch ${INSTANCE_YAML} + +.PHONY: instance +instance: ${INSTANCE_TARGET} ## create configuration for an zope (plone) instance + +${INSTANCE_TARGET}: ${INSTANCE_PREREQUISITES} ${COOKIECUTTER_SENTINEL} ${INSTANCE_YAML} + @echo "$(OK_COLOR)Create Plone/Zope configuration from ${INSTANCE_YAML} at ${INSTANCE_FOLDER}$(NO_COLOR)" + @${PYBIN}cookiecutter -f --no-input --config-file ${INSTANCE_YAML} https://github.com/plone/cookiecutter-zope-instance + + +############################################################################## +# TESTING + +TESTRUNNER_SENTINEL=${SENTINELFOLDER}pip-testrunner.sentinel +${TESTRUNNER_SENTINEL}: ${PIP_SENTINEL} + @echo "$(OK_COLOR)Install zope.testrunner$(NO_COLOR)" + @${PYBIN}pip install zope.testrunner + @touch ${TESTRUNNER_SENTINEL} + +.PHONY: test +test: ${TEST_PREREQUISITES} ${TESTRUNNER_SENTINEL} ## run tests + @echo "$(OK_COLOR)Run addon tests$(NO_COLOR)" + @${PYBIN}zope-testrunner --auto-color --auto-progress --test-path=${ADDONFOLDER} + +.PHONY: test-ignore-warnings +test-ignore-warnings: ${TEST_PREREQUISITES} ${TESTRUNNER_SENTINEL} ## run tests (hide warnings) + @echo "$(OK_COLOR)Run addon tests$(NO_COLOR)" + @PYTHONWARNINGS=ignore ${PYBIN}zope-testrunner --auto-color --auto-progress --test-path=${ADDONFOLDER} + +############################################################################## +# CODE FORMATTING + +BLACK_SENTINEL=${SENTINELFOLDER}pip-black.sentinel +${BLACK_SENTINEL}: ${PREPARE_TARGET} + @echo "$(OK_COLOR)Install black$(NO_COLOR)" + @${PYBIN}pip install black + @touch ${BLACK_SENTINEL} + +ISORT_SENTINEL=${SENTINELFOLDER}pip-isort.sentinel +${ISORT_SENTINEL}: ${PREPARE_TARGET} + @echo "$(OK_COLOR)Install isort$(NO_COLOR)" + @${PYBIN}pip install isort + @touch ${ISORT_SENTINEL} + +ZPRETTY_SENTINEL=${SENTINELFOLDER}pip-zpretty.sentinel +${ZPRETTY_SENTINEL}: ${PREPARE_TARGET} + @echo "$(OK_COLOR)Install zpretty$(NO_COLOR)" + @${PYBIN}pip install "zpretty>=2.2.0" + @touch ${ZPRETTY_SENTINEL} + +.PHONY: apply-style-black +apply-style-black: ${BLACK_SENTINEL} ## apply/format code style black (to Python files) + @echo "$(OK_COLOR)Apply style black rules to code in ${ADDONFOLDER}/*$(NO_COLOR)" + @${PYBIN}black ${ADDONFOLDER} + +.PHONY: apply-style-isort +apply-style-isort: ${ISORT_SENTINEL} ## apply/format code style isort (sorted imports in Python files) + @echo "$(OK_COLOR)Apply style isort rules to code in ${ADDONFOLDER}/*$(NO_COLOR)" + @${PYBIN}isort ${ADDONFOLDER} + +.PHONY: apply-style-zpretty +apply-style-zpretty: ${ZPRETTY_SENTINEL} ## apply/format code style zpretty (to XML/ZCML files) + @echo "$(OK_COLOR)Apply style zpretty rules to code in ${ADDONFOLDER}/*$(NO_COLOR)" + @find ${ADDONFOLDER} -name '*.zcml' -exec ${PYBIN}zpretty -iz {} + + @find ${ADDONFOLDER} -name "*.xml"|grep -v locales|xargs ${PYBIN}zpretty -ix + +.PHONY: style ## apply code styles black, isort and zpretty +style: apply-style-black apply-style-isort + +.PHONY: format ## alias for "style" +FORMATTING: style + +.PHONY: lint-black +lint-black: ${BLACK_SENTINEL} ## lint code-style black (to Python files) + @echo "$(OK_COLOR)Lint black rules to code in ${ADDONFOLDER}/*$(NO_COLOR)" + @${PYBIN}black --check ${ADDONFOLDER} + +.PHONY: lint-isort +lint-isort: ${ISORT_SENTINEL} ## lint code-style isort (sorted imports in Python files) + @echo "$(OK_COLOR)Lint style isort rules to code in ${ADDONFOLDER}/*$(NO_COLOR)" + @${PYBIN}isort --check-only ${ADDONFOLDER} + +.PHONY: lint-zpretty +lint-zpretty: ${ZPRETTY_SENTINEL} ## lint code-style zpretty (to XML/ZCML files) + @echo "$(OK_COLOR)Lint style zpretty rules to code in ${ADDONFOLDER}/*$(NO_COLOR)" + @find ${ADDONFOLDER} -name '*.zcml' -exec ${PYBIN}zpretty --check -z {} + + @find ${ADDONFOLDER} -name '*.xml'|grep -v locales|xargs ${PYBIN}zpretty --check -x + +.PHONY: lint ## lint all: check if complies with code-styles black, isort and zpretty +lint: lint-black lint-isort + +############################################################################## +# RUN + +.PHONY: run +run: ${RUN_PREREQUISITES} ## run/start Plone + @echo "$(OK_COLOR)Run Plone$(NO_COLOR)" + @${PYBIN}runwsgi -v instance/etc/zope.ini + +############################################################################## +# CLEAN +.PHONY: clean-venv +clean-venv: ## remove Python virtual environment +ifeq ("${VENV}", "on") + @echo "$(OK_COLOR)Remove Virtualenv.$(NO_COLOR)" + rm -rf ${VENV_FOLDER} ${SENTINELFOLDER}/pip*.sentinel ${VENV_SENTINEL} +else: + @echo "$(OK_WARN)No self-created Python virtualenv at '${VENV_FOLDER}'! Nothing to do.$(NO_COLOR)" +endif + +.PHONY: clean-pyc +clean-pyc: ## remove Python file artifacts + @echo "$(OK_COLOR)Remove Python file artifacts (like byte-code) of code in current directory.$(NO_COLOR)" + find . -name '*.pyc' -exec rm -f {} + + find . -name '*.pyo' -exec rm -f {} + + find . -name '*~' -exec rm -f {} + + find . -name '__pycache__' -exec rm -fr {} + + +.PHONY: clean-make +clean-make: ## remove make artifact @echo "$(OK_COLOR)Remove Plone/Zope configuration (keeps data) and sentinel files.$(NO_COLOR)" + rm -rf ${INSTALL_PREREQUSISTES} ${INSTANCE_TARGET} ${SENTINELFOLDER} constraints-mxdev.txt + +.PHONY: clean-instance +clean-instance: ## remove instance configuration (keeps data) + @echo "$(OK_COLOR)Remove Plone/Zope configuration (keeps data) and sentinel files.$(NO_COLOR)" + rm -f ${INSTANCE_TARGET} + +.PHONY: clean +clean: clean-venv clean-pyc clean-make clean-instance ## clean all (except local database and pip installed packages) diff --git a/base.cfg b/base.cfg deleted file mode 100644 index 0c5e9fd3..00000000 --- a/base.cfg +++ /dev/null @@ -1,57 +0,0 @@ -[buildout] -index = https://pypi.org/simple/ -extends = - https://raw.githubusercontent.com/collective/buildout.plonetest/master/qa.cfg - -show-picked-versions = true -extensions = - mr.developer - -parts = - instance - omelette - releaser - test - -develop = . -package-name = collective.easyform -package-extras = [test,downloadxlsx] -test-eggs = - -[instance] -recipe = plone.recipe.zope2instance -user = admin:admin -http-address = 8080 -environment-vars = - zope_i18n_compile_mo_files true -eggs = - Plone - Pillow - collective.easyform - Products.PDBDebugMode - pdbpp - -[omelette] -recipe = collective.recipe.omelette -eggs = ${instance:eggs} - -[releaser] -recipe = zc.recipe.egg -eggs = zest.releaser[recommended] - -[code-analysis] -pre-commit-hook = False -flake8 = True -flake8-ignore = E501,C901,W503 - -[versions] -# Don't use a released version of collective.exportimport -collective.easyform = -plone.formwidget.hcaptcha = 1.0.1 -plone.formwidget.recaptcha = 2.3.0 -# For Buildout related packages, it is easiest to keep them at the same version for all environments. -# Keep these empty will ensure it uses what got installed by requirements.txt -setuptools = -wheel = -zc.buildout = -pip = diff --git a/bootstrap-py3.sh b/bootstrap-py3.sh deleted file mode 100755 index 0e252d0a..00000000 --- a/bootstrap-py3.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -# see https://community.plone.org/t/not-using-bootstrap-py-as-default/620 -# rm -r ./lib ./include ./local ./bin -python3 -m venv venv --clear -./venv/bin/pip install -r requirements.txt -./venv/bin/buildout bootstrap diff --git a/bootstrap.sh b/bootstrap.sh deleted file mode 100755 index 488359c6..00000000 --- a/bootstrap.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -# see https://community.plone.org/t/not-using-bootstrap-py-as-default/620 -rm -r ./lib ./include ./local ./bin -virtualenv --clear . -./bin/pip install -r requirements.txt -./bin/buildout diff --git a/buildout.cfg b/buildout.cfg deleted file mode 100644 index 03b20238..00000000 --- a/buildout.cfg +++ /dev/null @@ -1,48 +0,0 @@ -[buildout] -extends = tests-6.0.x.cfg - -parts += - releaser - i18ndude - vscode - omelette - -[instance] -eggs += - ${buildout:package-name} ${buildout:package-extras} - Products.PrintingMailHost - - -[omelette] -recipe = collective.recipe.omelette -eggs = ${test:eggs} - - -[releaser] -recipe = zc.recipe.egg -eggs = zest.releaser - - -[i18ndude] -recipe = zc.recipe.egg -eggs = i18ndude - - -[sphinxbuilder] -recipe = collective.recipe.sphinxbuilder -source = ${buildout:directory}/docs -eggs = - ${instance:eggs} - sphinxcontrib-robotdoc - sphinxcontrib-httpdomain - -[versions] -# Don't use a released version of collective.easyform -collective.easyform = - -[vscode] -recipe = collective.recipe.vscode -eggs = ${test:eggs} -flake8-enabled = false -black-enabled = true -generate-envfile = true diff --git a/constraints.txt b/constraints.txt new file mode 100644 index 00000000..c0e8f2f6 --- /dev/null +++ b/constraints.txt @@ -0,0 +1 @@ +-c https://dist.plone.org/release/6.0-dev/constraints.txt diff --git a/mx.ini b/mx.ini new file mode 100644 index 00000000..f6fee0b1 --- /dev/null +++ b/mx.ini @@ -0,0 +1,23 @@ +[settings] +# This is a mxdev configuration file +# +# available options are documented at +# https://pypi.org/proejct/mxdev/ +# +# read also README_MAKE.md in this folder +# +requirements-in = requirements.txt +requirements-out = requirements-mxdev.txt + +# ignore own dev-package from existing constraints +# because it is pinned in the plone-release +ignores = + collective.easyform + +version-overrides = + +default-install-mode = direct + +# variables +; github = git+ssh://git@github.com +github = git+https://github.com diff --git a/requirements.txt b/requirements.txt index a336f8ca..002e6dba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,6 @@ -zc.buildout==3.0.0rc3 \ No newline at end of file +-c constraints.txt +-e .[test] --install-option="--pre" + +# WSGI: A system for configuration of WSGI web components in declarative .ini format. +Paste +pdbpp diff --git a/setup.cfg b/setup.cfg index 296cfd51..ab0088d6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,6 +4,16 @@ force_alphabetical_sort = True force_single_line = True lines_after_imports = 2 +[flake8] +ignore = + W503, + C812, + E501, + C815 +exclude = bootstrap.py,docs,*.egg.,omelette,bin,parts,eggs,var +max-complexity = 15 + + [zest.releaser] create-wheel = yes diff --git a/setup.py b/setup.py index 3a30fcc6..ad2cd47d 100644 --- a/setup.py +++ b/setup.py @@ -46,6 +46,7 @@ "plone.schema", "plone.schemaeditor>=4.0.0b1", "plone.supermodel", + "plone.restapi", "Products.CMFPlone>=6.0.0b1", "Products.validation", "setuptools", diff --git a/src/collective/easyform/actions.py b/src/collective/easyform/actions.py index 25b9cbeb..55b9a8ee 100644 --- a/src/collective/easyform/actions.py +++ b/src/collective/easyform/actions.py @@ -25,8 +25,8 @@ from csv import writer as csvwriter from datetime import date from datetime import datetime -from DateTime import DateTime from datetime import timedelta +from DateTime import DateTime from decimal import Decimal from email import encoders from email.header import Header @@ -61,6 +61,7 @@ from zope.schema import Bool from zope.schema import getFieldsInOrder from zope.security.interfaces import IPermission + import six @@ -70,7 +71,7 @@ @implementer(IActionFactory) class ActionFactory(object): - title = u"" + title = "" def __init__(self, fieldcls, title, permission, *args, **kw): self.fieldcls = fieldcls @@ -223,9 +224,9 @@ def get_owner_info(self, context): toemail = self.get_portal_email_address(context) if not toemail: raise ValueError( - u"Unable to mail form input because no recipient address has " - u"been specified. Please check the recipient settings of the " - u"EasyForm Mailer within the current form folder." + "Unable to mail form input because no recipient address has " + "been specified. Please check the recipient settings of the " + "EasyForm Mailer within the current form folder." ) return (fullname, toemail) @@ -273,7 +274,7 @@ def get_addresses(self, fields, request, context, from_addr=None, to_addr=None): def get_subject(self, fields, request, context): """Return subject.""" # get subject header - nosubject = u"(no subject)" # TODO: translate + nosubject = "(no subject)" # TODO: translate subject = None if hasattr(self, "subjectOverride") and self.subjectOverride: # subject has a TALES override @@ -460,7 +461,7 @@ def get_attachments(self, fields, request): filename, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "utf-8", - output + output, ) ) @@ -575,7 +576,7 @@ def getScript(self, context): script._validateProxy = lambda i=None: None # Force proxy role - if role != u"none": + if role != "none": script.manage_proxy((role,)) if six.PY2 and isinstance(body, six.text_type): @@ -698,9 +699,9 @@ def get_data(row, i): if six.PY2 and isinstance(data, six.text_type): return data.encode("utf-8") if isinstance(data, (list, tuple, set)): - data = '|'.join(data) + data = "|".join(data) if six.PY2: - return data.encode('utf-8') + return data.encode("utf-8") return data return [get_data(row, i) for i in names] @@ -901,17 +902,17 @@ def onSuccess(self, fields, request): MailerAction = ActionFactory( Mailer, - _(u"label_mailer_action", default=u"Mailer"), + _("label_mailer_action", default="Mailer"), "collective.easyform.AddMailers", ) CustomScriptAction = ActionFactory( CustomScript, - _(u"label_customscript_action", default=u"Custom Script"), + _("label_customscript_action", default="Custom Script"), "collective.easyform.AddCustomScripts", ) SaveDataAction = ActionFactory( SaveData, - _(u"label_savedata_action", default=u"Save Data"), + _("label_savedata_action", default="Save Data"), "collective.easyform.AddDataSavers", ) diff --git a/src/collective/easyform/api.py b/src/collective/easyform/api.py index e2733927..2b219dd3 100644 --- a/src/collective/easyform/api.py +++ b/src/collective/easyform/api.py @@ -20,7 +20,7 @@ import six -CONTEXT_KEY = u"context" +CONTEXT_KEY = "context" # regular expression for dollar-sign variable replacement. # we want to find ${identifier} patterns dollarRE = compile(r"\$\{(.+?)\}") @@ -244,7 +244,7 @@ def cleanup(value): """ if isinstance(value, six.string_types): value = safe_unicode(value).strip() - value = value.replace(u",", u"\n").replace(u";", u"\n") + value = value.replace(",", "\n").replace(";", "\n") value = [s for s in value.splitlines()] if isinstance(value, (list, tuple)): diff --git a/src/collective/easyform/browser/actions.py b/src/collective/easyform/browser/actions.py index 5d058d2b..45272a36 100644 --- a/src/collective/easyform/browser/actions.py +++ b/src/collective/easyform/browser/actions.py @@ -134,7 +134,7 @@ def get_schema(self): return get_schema(get_context(self.field)) def description(self): - return _(u"${items} input(s) saved", mapping={"items": self.field.itemsSaved()}) + return _("${items} input(s) saved", mapping={"items": self.field.itemsSaved()}) @property def update_schema(self): @@ -168,11 +168,11 @@ def remove(self, id_and_item): (id, item) = id_and_item self.field.delDataRow(id) - @button.buttonAndHandler(PMF(u"Download"), name="download") + @button.buttonAndHandler(PMF("Download"), name="download") def handleDownload(self, action): pass - @button.buttonAndHandler(_(u"Clear all"), name="clearall") + @button.buttonAndHandler(_("Clear all"), name="clearall") def handleClearAll(self, action): self.field.clearSavedFormInput() @@ -199,7 +199,7 @@ def __call__(self): self.context.field.download(self.request.response, delimiter=delimiter) else: self.context.field.download(self.request.response) - return u"" + return "" return super(SavedDataFormWrapper, self).__call__() @@ -252,10 +252,10 @@ class EasyFormActionsListing(SchemaListing): @memoize def _field_factory(self, field): - field_identifier = u"{0}.{1}".format(field.__module__, field.__class__.__name__) + field_identifier = "{0}.{1}".format(field.__module__, field.__class__.__name__) return queryUtility(IActionFactory, name=field_identifier) - @button.buttonAndHandler(PMF(u"Save")) + @button.buttonAndHandler(PMF("Save")) def handleSaveDefaults(self, action): data, errors = self.extractData() if errors: @@ -316,7 +316,7 @@ def additionalSchemata(self): ) return [v for k, v in adapters] - @button.buttonAndHandler(PMF(u"Save"), name="save") + @button.buttonAndHandler(PMF("Save"), name="save") def handleSave(self, action): data, errors = self.extractData() if errors: @@ -333,7 +333,7 @@ def handleSave(self, action): notify(SchemaModifiedEvent(self.context.aq_parent)) self.redirectToParent() - @button.buttonAndHandler(PMF(u"Cancel"), name="cancel") + @button.buttonAndHandler(PMF("Cancel"), name="cancel") def handleCancel(self, action): self.redirectToParent() @@ -353,11 +353,11 @@ def __init__(self, context, request): @lazy_property def label(self): return _( - u"Edit Action '${fieldname}'", mapping={"fieldname": self.field.__name__} + "Edit Action '${fieldname}'", mapping={"fieldname": self.field.__name__} ) -but = button.Button("modeleditor", title=_(u"Edit XML Actions Model")) +but = button.Button("modeleditor", title=_("Edit XML Actions Model")) EasyFormActionsListing.buttons += button.Buttons(but) handler = button.Handler(but, EasyFormActionsListing.handleModelEdit) EasyFormActionsListing.handlers.addHandler(but, handler) @@ -366,7 +366,7 @@ def label(self): class ModelEditorView(ModelEditorView): """Editor view""" - title = _(u"Edit XML Actions Model") + title = _("Edit XML Actions Model") def modelSource(self): return self.context.aq_parent.actions_model diff --git a/src/collective/easyform/browser/controlpanel.py b/src/collective/easyform/browser/controlpanel.py index 5ccb90a4..5075efba 100644 --- a/src/collective/easyform/browser/controlpanel.py +++ b/src/collective/easyform/browser/controlpanel.py @@ -17,8 +17,8 @@ def getContent(self): class IEasyFormControlPanel(Interface): allowedFields = schema.List( - title=_(u"Allowed Fields"), - description=_(u"These Fields are available for your forms."), + title=_("Allowed Fields"), + description=_("These Fields are available for your forms."), value_type=schema.Choice( required=False, vocabulary="easyform.SchemaEditorFields", @@ -27,18 +27,18 @@ class IEasyFormControlPanel(Interface): ) csv_delimiter = schema.TextLine( - title=_(u"CSV delimiter"), + title=_("CSV delimiter"), max_length=1, - description=_(u"Set the default delimiter for CSV download."), + description=_("Set the default delimiter for CSV download."), required=True, - default=u",", + default=",", ) class EasyFormControlPanelForm(RegistryEditForm): schema = IEasyFormControlPanel schema_prefix = "easyform" - label = _(u"easyform Settings") + label = _("easyform Settings") def updateFields(self): super(EasyFormControlPanelForm, self).updateFields() diff --git a/src/collective/easyform/browser/exportimport.py b/src/collective/easyform/browser/exportimport.py index bb0ec018..89bcc1c2 100644 --- a/src/collective/easyform/browser/exportimport.py +++ b/src/collective/easyform/browser/exportimport.py @@ -43,7 +43,7 @@ class EasyFormImportForm(form.Form): ignoreContext = True ignoreReadonly = True - @button.buttonAndHandler(_(u"import"), name="import") + @button.buttonAndHandler(_("import"), name="import") def handleImport(self, action): data, errors = self.extractData() if errors: @@ -53,7 +53,7 @@ def handleImport(self, action): ctx = TarballImportContext(self.context, data["upload"]) IFilesystemImporter(self.context).import_(ctx, "structure", True) - self.status = _(u"Form imported.") + self.status = _("Form imported.") IStatusMessage(self.request).addStatusMessage(self.status, type="info") url = getMultiAdapter((self.context, self.request), name="absolute_url")() diff --git a/src/collective/easyform/browser/fields.py b/src/collective/easyform/browser/fields.py index 89c75182..76a45a92 100644 --- a/src/collective/easyform/browser/fields.py +++ b/src/collective/easyform/browser/fields.py @@ -17,19 +17,20 @@ from plone.schemaeditor.browser.schema.traversal import SchemaContext from plone.supermodel.parser import SupermodelParseError from Products.CMFPlone.utils import safe_bytes +from Products.CMFPlone.utils import safe_unicode from Products.Five import BrowserView from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile +from Products.statusmessages.interfaces import IStatusMessage from z3c.form import button from zope.cachedescriptors.property import Lazy as lazy_property from zope.component import getAdapters from zope.component import queryMultiAdapter from zope.interface import implementer from ZPublisher.BaseRequest import DefaultPublishTraverse -from Products.statusmessages.interfaces import IStatusMessage -from Products.CMFPlone.utils import safe_unicode import html + NAMESPACE = "{http://namespaces.plone.org/supermodel/schema}" try: @@ -95,7 +96,7 @@ def handleModelEdit(self, action): self.request.response.redirect("@@modeleditor") @button.buttonAndHandler( - _(u'Save'), + _("Save"), ) def handleSave(self, action): super(FieldsSchemaListing, self).handleSaveDefaults(self, action) @@ -103,7 +104,7 @@ def handleSave(self, action): if HAVE_RESOURCE_EDITOR: - but = button.Button("modeleditor", title=_(u"Edit XML Fields Model")) + but = button.Button("modeleditor", title=_("Edit XML Fields Model")) FieldsSchemaListing.buttons += button.Buttons(but) handler = button.Handler(but, FieldsSchemaListing.handleModelEdit) FieldsSchemaListing.handlers.addHandler(but, handler) @@ -142,13 +143,13 @@ class ModelEditorView(BrowserView): template = ViewPageTemplateFile("modeleditor.pt") - title = _(u"Edit XML Fields Model") + title = _("Edit XML Fields Model") def modelSource(self): return self.context.aq_parent.fields_model def authorized(self, context, request): - authenticator = queryMultiAdapter((context, request), name=u"authenticator") + authenticator = queryMultiAdapter((context, request), name="authenticator") return authenticator and authenticator.verify() def save(self, source): @@ -184,7 +185,7 @@ def __call__(self): # a little more sanity checking, look at first two element levels if root.tag != NAMESPACE + "model": IStatusMessage(self.request).addStatusMessage( - _(u"Error: root tag must be 'model'"), + _("Error: root tag must be 'model'"), "error", ) return super().__call__() @@ -192,7 +193,7 @@ def __call__(self): for element in root.getchildren(): if element.tag != NAMESPACE + "schema": IStatusMessage(self.request).addStatusMessage( - _(u"Error: all model elements must be 'schema'"), + _("Error: all model elements must be 'schema'"), "error", ) return super().__call__() @@ -200,11 +201,11 @@ def __call__(self): # can supermodel parse it? # This is mainly good for catching bad dotted names. try: - plone.supermodel.loadString(source, policy=u"dexterity") + plone.supermodel.loadString(source, policy="dexterity") except SupermodelParseError as e: message = e.args[0].replace('\n File ""', "") IStatusMessage(self.request).addStatusMessage( - u"SuperModelParseError: {0}".format(html.escape(message)), + "SuperModelParseError: {0}".format(html.escape(message)), "error", ) return super().__call__() diff --git a/src/collective/easyform/browser/likert.py b/src/collective/easyform/browser/likert.py index a62be85d..ae2bf125 100644 --- a/src/collective/easyform/browser/likert.py +++ b/src/collective/easyform/browser/likert.py @@ -1,23 +1,24 @@ __docformat__ = "reStructuredText" -import zope.component -import zope.interface -import zope.schema.interfaces -from zope.pagetemplate.interfaces import IPageTemplate +from collective.easyform.interfaces import ILikertWidget from plone.memoize.view import memoize - from z3c.form import interfaces -from z3c.form.widget import Widget, FieldWidget from z3c.form.browser import widget +from z3c.form.widget import FieldWidget +from z3c.form.widget import Widget +from zope.pagetemplate.interfaces import IPageTemplate + +import zope.component +import zope.interface +import zope.schema.interfaces -from collective.easyform.interfaces import ILikertWidget @zope.interface.implementer_only(ILikertWidget) class LikertWidget(widget.HTMLTextInputWidget, Widget): """Input type text widget implementation.""" - klass = u'likert-widget' - css = u'text' - value = u'' + klass = "likert-widget" + css = "text" + value = "" def update(self): super(LikertWidget, self).update() @@ -27,22 +28,21 @@ def extract(self, default=interfaces.NO_VALUE): """See z3c.form.interfaces.IWidget.""" answers = [] if self.field.questions is None: - return '' + return "" for index, question in enumerate(self.field.questions): question_answer = self.extract_question_answer(index, default) if question_answer is not None: answers.append(question_answer) - return u', '.join(answers) + return ", ".join(answers) def extract_question_answer(self, index, default): """See z3c.form.interfaces.IWidget.""" - name = '%s.%i' % (self.name, index) - if (name not in self.request and - '%s-empty-marker' % name in self.request): + name = "%s.%i" % (self.name, index) + if name not in self.request and "%s-empty-marker" % name in self.request: return None value = self.request.get(name, default) if value != default: - return '%i: %s' % (index + 1, value) + return "%i: %s" % (index + 1, value) else: return None diff --git a/src/collective/easyform/browser/view.py b/src/collective/easyform/browser/view.py index c6cdfb4d..f740c00d 100644 --- a/src/collective/easyform/browser/view.py +++ b/src/collective/easyform/browser/view.py @@ -184,7 +184,7 @@ def setErrorsMessage(self, errors): self.status = self.formErrorsMessage @button.buttonAndHandler( - PMF(u"Submit"), name="submit", condition=lambda form: not form.thanksPage + PMF("Submit"), name="submit", condition=lambda form: not form.thanksPage ) def handleSubmit(self, action): unsorted_data, errors = self.extractData() @@ -217,7 +217,7 @@ def handleSubmit(self, action): self.request.response.write(safe_encode(thanksPage)) @button.buttonAndHandler( - _(u"Reset"), name="reset", condition=lambda form: form.context.useCancelButton + _("Reset"), name="reset", condition=lambda form: form.context.useCancelButton ) def handleReset(self, action): self.request.response.redirect(self.nextURL()) @@ -300,12 +300,14 @@ def updateActions(self): if "reset" in self.actions: self.actions["reset"].title = self.context.resetLabel - def updateWidgets(self): - super(EasyFormForm, self).updateWidgets() + def markWidgets(self): for w in self.widgets.values(): if not IEasyFormWidget.providedBy(w): - # add marker for custom widget renderer alsoProvides(w, IEasyFormWidget) + for g in self.groups: + for w in g.widgets.values(): + if not IEasyFormWidget.providedBy(w): + alsoProvides(w, IEasyFormWidget) def formMaybeForceSSL(self): """Redirect to an https:// URL if the 'force SSL' option is on. @@ -328,6 +330,7 @@ def update(self): """Update form - see interfaces.IForm""" self.formMaybeForceSSL() super(EasyFormForm, self).update() + self.markWidgets() self.template = self.form_template if self.request.method != "POST" or self.context.thanksPageOverride: # go with all but default thank you page rendering @@ -393,7 +396,7 @@ def header_injection(self): def css_class(self): css_class = None if self.form_instance.thanksPage: - css_class = u"easyform-thankspage" + css_class = "easyform-thankspage" return css_class @@ -436,7 +439,7 @@ def __call__(self, value, size=1048576, allowed_types=None, forbidden_types=None return _( "msg_file_too_big", mapping={"size": size}, - default=u"File is bigger than allowed size of ${size} bytes!", + default="File is bigger than allowed size of ${size} bytes!", ) ftype = splitext(value.filename)[-1] # remove leading dot '.' from file extension @@ -445,13 +448,13 @@ def __call__(self, value, size=1048576, allowed_types=None, forbidden_types=None return _( "msg_file_not_allowed", mapping={"ftype": ftype.upper()}, - default=u'File type "${ftype}" is not allowed!', + default='File type "${ftype}" is not allowed!', ) if forbidden_types and ftype in forbidden_types: return _( "msg_file_not_allowed", mapping={"ftype": ftype.upper()}, - default=u'File type "${ftype}" is not allowed!', + default='File type "${ftype}" is not allowed!', ) return False diff --git a/src/collective/easyform/browser/widgets.py b/src/collective/easyform/browser/widgets.py index 222ca96d..25d266ff 100644 --- a/src/collective/easyform/browser/widgets.py +++ b/src/collective/easyform/browser/widgets.py @@ -23,9 +23,9 @@ class LabelWidget(widget.HTMLFormElement, Widget): """Textarea widget implementation.""" - klass = u"label-widget" - css = u"label" - value = u"" + klass = "label-widget" + css = "label" + value = "" def update(self): super(LabelWidget, self).update() @@ -43,9 +43,9 @@ def LabelFieldWidget(field, request): class RichLabelWidget(widget.HTMLFormElement, Widget): """Textarea widget implementation.""" - klass = u"rich-label-widget" - css = u"richlabel" - value = u"" + klass = "rich-label-widget" + css = "richlabel" + value = "" def update(self): super(RichLabelWidget, self).update() @@ -70,7 +70,7 @@ class RichLabelRenderWidget(ViewMixinForTemplates, BrowserView): # overriding plone.app.z3cform widget.pt: @implementer(IRenderWidget) class RenderWidget(ViewMixinForTemplates, BrowserView): - index = ViewPageTemplateFile('widget.pt') + index = ViewPageTemplateFile("widget.pt") @adapter(IRenderWidget, Interface) @@ -89,6 +89,7 @@ def __call__(self): return "" return depends_on + @adapter(IRenderWidget, Interface) @implementer(IBrowserView) class WidgetCssClassView(object): diff --git a/src/collective/easyform/config.py b/src/collective/easyform/config.py index 2a9af2cf..86ef7e90 100644 --- a/src/collective/easyform/config.py +++ b/src/collective/easyform/config.py @@ -47,7 +47,7 @@ FORM_ERROR_MARKER = "FORM_ERROR_MARKER" -DEFAULT_SCRIPT = u""" +DEFAULT_SCRIPT = """ ## Python Script ##bind container=container ##bind context=context diff --git a/src/collective/easyform/exportimport.py b/src/collective/easyform/exportimport.py index be75bfdf..7d6a6a56 100644 --- a/src/collective/easyform/exportimport.py +++ b/src/collective/easyform/exportimport.py @@ -71,7 +71,13 @@ class EasyFormFieldMetadataHandler(object): def read(self, fieldNode, schema, field): name = field.__name__ - for i in ["TDefault", "TEnabled", "TValidator", "depends_on", "css_class",]: + for i in [ + "TDefault", + "TEnabled", + "TValidator", + "depends_on", + "css_class", + ]: value = fieldNode.get(ns(i, self.namespace)) if value: data = schema.queryTaggedValue(i, {}) @@ -98,7 +104,13 @@ def read(self, fieldNode, schema, field): def write(self, fieldNode, schema, field): name = field.__name__ - for i in ["TDefault", "TEnabled", "TValidator", "depends_on", "css_class",]: + for i in [ + "TDefault", + "TEnabled", + "TValidator", + "depends_on", + "css_class", + ]: value = schema.queryTaggedValue(i, {}).get(name, None) if value: fieldNode.set(ns(i, self.namespace), value) diff --git a/src/collective/easyform/fields.py b/src/collective/easyform/fields.py index 19011df0..71150aa4 100644 --- a/src/collective/easyform/fields.py +++ b/src/collective/easyform/fields.py @@ -4,11 +4,11 @@ from collective.easyform.interfaces import IEasyForm from collective.easyform.interfaces import IEasyFormForm from collective.easyform.interfaces import IFieldExtender -from collective.easyform.interfaces import ILabel from collective.easyform.interfaces import IHCaptcha +from collective.easyform.interfaces import ILabel +from collective.easyform.interfaces import ILikert from collective.easyform.interfaces import INorobotCaptcha from collective.easyform.interfaces import IReCaptcha -from collective.easyform.interfaces import ILikert from collective.easyform.interfaces import IRichLabel from collective.easyform.validators import IFieldValidator from plone.schemaeditor.fields import FieldFactory @@ -29,7 +29,7 @@ from zope.schema.interfaces import IField -def superAdapter(specific_interface, adapter, objects, name=u""): +def superAdapter(specific_interface, adapter, objects, name=""): """Find the next most specific adapter. This is called by a FieldExtenderValidator or FieldExtenderDefault instance. @@ -199,16 +199,16 @@ def fromUnicode(self, str): class RichLabel(Label): """A Rich Label field""" - rich_label = u"" + rich_label = "" - def __init__(self, rich_label=u"", **kw): + def __init__(self, rich_label="", **kw): self.rich_label = rich_label super(RichLabel, self).__init__(**kw) -LabelFactory = FieldFactory(Label, _(u"label_label_field", default=u"Label")) +LabelFactory = FieldFactory(Label, _("label_label_field", default="Label")) RichLabelFactory = FieldFactory( - RichLabel, _(u"label_richlabel_field", default=u"Rich Label") + RichLabel, _("label_richlabel_field", default="Rich Label") ) LabelHandler = BaseHandler(Label) @@ -221,18 +221,17 @@ class ReCaptcha(TextLine): ReCaptchaFactory = FieldFactory( - ReCaptcha, _(u"label_recaptcha_field", default=u"ReCaptcha") + ReCaptcha, _("label_recaptcha_field", default="ReCaptcha") ) ReCaptchaHandler = BaseHandler(ReCaptcha) + @implementer(IHCaptcha) class HCaptcha(TextLine): """A HCaptcha field""" -HCaptchaFactory = FieldFactory( - HCaptcha, _(u"label_hcaptcha_field", default=u"HCaptcha") -) +HCaptchaFactory = FieldFactory(HCaptcha, _("label_hcaptcha_field", default="HCaptcha")) HCaptchaHandler = BaseHandler(HCaptcha) @@ -242,7 +241,7 @@ class NorobotCaptcha(TextLine): NorobotFactory = FieldFactory( - NorobotCaptcha, _(u"label_norobot_field", default=u"NorobotCaptcha") + NorobotCaptcha, _("label_norobot_field", default="NorobotCaptcha") ) NorobotCaptchaHandler = BaseHandler(NorobotCaptcha) @@ -252,12 +251,12 @@ class Likert(TextLine): """A Likert field""" def __init__(self, **kwargs): - self.answers = kwargs.get('answers', None) - if 'answers' in kwargs: - del kwargs['answers'] - self.questions = kwargs.get('questions', None) - if 'questions' in kwargs: - del kwargs['questions'] + self.answers = kwargs.get("answers", None) + if "answers" in kwargs: + del kwargs["answers"] + self.questions = kwargs.get("questions", None) + if "questions" in kwargs: + del kwargs["questions"] Field.__init__(self, **kwargs) def _validate(self, value): @@ -266,23 +265,20 @@ def _validate(self, value): def parse(self, value): result = dict() - lines = value.split(',') + lines = value.split(",") for line in lines: if not line: continue - id, answer = line.split(':') + id, answer = line.split(":") answer = answer.strip() if answer not in self.answers: - raise ValueError('Invalid answer value.') + raise ValueError("Invalid answer value.") index = int(id) if index < 1 or index > len(self.questions): - raise ValueError('Invalid question index.') + raise ValueError("Invalid question index.") result[index] = answer return result -LikertFactory = FieldFactory( - Likert, _(u"label_likert_field", default=u"Likert") -) +LikertFactory = FieldFactory(Likert, _("label_likert_field", default="Likert")) LikertHandler = BaseHandler(Likert) - diff --git a/src/collective/easyform/interfaces/__init__.py b/src/collective/easyform/interfaces/__init__.py index c0fa3c52..29f7488b 100644 --- a/src/collective/easyform/interfaces/__init__.py +++ b/src/collective/easyform/interfaces/__init__.py @@ -19,13 +19,13 @@ from .fields import IEasyFormWidget # noqa from .fields import IFieldExtender # noqa from .fields import IFieldValidator # noqa +from .fields import IHCaptcha # noqa from .fields import ILabel # noqa from .fields import ILabelWidget # noqa -from .fields import INorobotCaptcha # noqa -from .fields import IReCaptcha # noqa from .fields import ILikert # noqa from .fields import ILikertWidget # noqa -from .fields import IHCaptcha # noqa +from .fields import INorobotCaptcha # noqa +from .fields import IReCaptcha # noqa from .fields import IRichLabel # noqa from .fields import IRichLabelWidget # noqa from .layer import IEasyFormLayer # noqa diff --git a/src/collective/easyform/interfaces/actions.py b/src/collective/easyform/interfaces/actions.py index eb1501ca..b49948ad 100644 --- a/src/collective/easyform/interfaces/actions.py +++ b/src/collective/easyform/interfaces/actions.py @@ -31,24 +31,24 @@ class IEasyFormActionsEditorExtender(IFieldEditorExtender): def isValidFieldName(value): if not ID_RE.match(value): raise zope.interface.Invalid( - __(u"Please use only letters, numbers and " u"the following characters: _.") + __("Please use only letters, numbers and " "the following characters: _.") ) return True class INewAction(Schema): - title = zope.schema.TextLine(title=__(u"Title"), required=True) + title = zope.schema.TextLine(title=__("Title"), required=True) __name__ = zope.schema.ASCIILine( - title=__(u"Short Name"), - description=__(u"Used for programmatic access to the field."), + title=__("Short Name"), + description=__("Used for programmatic access to the field."), required=True, constraint=isValidFieldName, ) factory = zope.schema.Choice( - title=_(u"Action type"), vocabulary="EasyFormActions", required=True + title=_("Action type"), vocabulary="EasyFormActions", required=True ) @zope.interface.invariant @@ -59,14 +59,14 @@ def checkTitleTypes(data): and data.factory.fieldcls is not zope.schema.TextLine ): raise zope.interface.Invalid( - __(u"The 'title' field must be a Text line (string) " u"field.") + __("The 'title' field must be a Text line (string) " "field.") ) class IActionFactory(zope.schema.interfaces.IField): """A component that instantiates a action when called.""" - title = zope.schema.TextLine(title=__(u"Title")) + title = zope.schema.TextLine(title=__("Title")) class IEasyFormActionsContext(ISchemaContext): @@ -74,22 +74,22 @@ class IEasyFormActionsContext(ISchemaContext): class IActionExtender(Schema): - fieldset(u"overrides", label=_("Overrides"), fields=["execCondition"]) + fieldset("overrides", label=_("Overrides"), fields=["execCondition"]) directives.read_permission(execCondition=MODIFY_PORTAL_CONTENT) directives.write_permission(execCondition=config.EDIT_TALES_PERMISSION) execCondition = zope.schema.TextLine( - title=_(u"label_execcondition_text", default=u"Execution Condition"), + title=_("label_execcondition_text", default="Execution Condition"), description=_( - u"help_execcondition_text", - default=u"A TALES expression that will be evaluated to determine " - u"whether or not to execute this action. Leave empty if " - u"unneeded, and the action will be executed. Your " - u"expression should evaluate as a boolean; return True " - u"if you wish the action to execute. PLEASE NOTE: errors " - u"in the evaluation of this expression will cause an " - u"error on form display.", + "help_execcondition_text", + default="A TALES expression that will be evaluated to determine " + "whether or not to execute this action. Leave empty if " + "unneeded, and the action will be executed. Your " + "expression should evaluate as a boolean; return True " + "if you wish the action to execute. PLEASE NOTE: errors " + "in the evaluation of this expression will cause an " + "error on form display.", ), - default=u"", + default="", constraint=isTALES, required=False, ) diff --git a/src/collective/easyform/interfaces/customscript.py b/src/collective/easyform/interfaces/customscript.py index 922dd75e..245d196a 100644 --- a/src/collective/easyform/interfaces/customscript.py +++ b/src/collective/easyform/interfaces/customscript.py @@ -17,20 +17,20 @@ class ICustomScript(IAction): directives.read_permission(ProxyRole=MODIFY_PORTAL_CONTENT) directives.write_permission(ProxyRole=config.EDIT_PYTHON_PERMISSION) ProxyRole = zope.schema.Choice( - title=_(u"label_script_proxy", default=u"Proxy role"), + title=_("label_script_proxy", default="Proxy role"), description=_( - u"help_script_proxy", default=u"Role under which to run the script." + "help_script_proxy", default="Role under which to run the script." ), - default=u"none", + default="none", required=True, vocabulary="easyform.ProxyRoleChoices", ) directives.read_permission(ScriptBody=MODIFY_PORTAL_CONTENT) directives.write_permission(ScriptBody=config.EDIT_PYTHON_PERMISSION) ScriptBody = zope.schema.Text( - title=_(u"label_script_body", default=u"Script body"), - description=_(u"help_script_body", default=u"Write your script here."), + title=_("label_script_body", default="Script body"), + description=_("help_script_body", default="Write your script here."), default=config.DEFAULT_SCRIPT, required=False, - missing_value=u"", + missing_value="", ) diff --git a/src/collective/easyform/interfaces/easyform.py b/src/collective/easyform/interfaces/easyform.py index 815bef4f..40580fae 100644 --- a/src/collective/easyform/interfaces/easyform.py +++ b/src/collective/easyform/interfaces/easyform.py @@ -23,7 +23,7 @@ @provider(zope.schema.interfaces.IContextAwareDefaultFactory) def default_submitLabel(context): return translate( - _(u"default_submitLabel", u"Submit"), + _("default_submitLabel", "Submit"), target_language=api.portal.get_current_language(), ) @@ -31,7 +31,7 @@ def default_submitLabel(context): @provider(zope.schema.interfaces.IContextAwareDefaultFactory) def default_resetLabel(context): return translate( - _(u"default_resetLabel", u"Reset"), + _("default_resetLabel", "Reset"), target_language=api.portal.get_current_language(), ) @@ -39,7 +39,7 @@ def default_resetLabel(context): @provider(zope.schema.interfaces.IContextAwareDefaultFactory) def default_thankstitle(context): return translate( - _(u"default_thankstitle", u"Thank You"), + _("default_thankstitle", "Thank You"), target_language=api.portal.get_current_language(), ) @@ -47,7 +47,7 @@ def default_thankstitle(context): @provider(zope.schema.interfaces.IContextAwareDefaultFactory) def default_thanksdescription(context): return translate( - _(u"default_thanksdescription", u"Thanks for your input."), + _("default_thanksdescription", "Thanks for your input."), target_language=api.portal.get_current_language(), ) @@ -87,33 +87,33 @@ class IEasyForm(Schema): directives.omitted("fields_model", "actions_model") fields_model = zope.schema.Text( - title=_(u"Fields Model"), defaultFactory=default_fields + title=_("Fields Model"), defaultFactory=default_fields ) actions_model = zope.schema.Text( - title=_(u"Actions Model"), defaultFactory=default_actions + title=_("Actions Model"), defaultFactory=default_actions ) # DEFAULT formPrologue = RichText( - title=_(u"label_prologue_text", default=u"Form Prologue"), + title=_("label_prologue_text", default="Form Prologue"), description=_( - u"help_prologue_text", - default=u"This text will be displayed above the form fields.", + "help_prologue_text", + default="This text will be displayed above the form fields.", ), required=False, ) formEpilogue = RichText( - title=_(u"label_epilogue_text", default=u"Form Epilogue"), + title=_("label_epilogue_text", default="Form Epilogue"), description=_( - u"help_epilogue_text", - default=u"The text will be displayed after the form fields.", + "help_epilogue_text", + default="The text will be displayed after the form fields.", ), required=False, ) # THANKYOU fieldset( - u"thankyou", + "thankyou", label=_("Thanks Page"), fields=[ "thankstitle", @@ -127,73 +127,73 @@ class IEasyForm(Schema): order=10, ) thankstitle = zope.schema.TextLine( - title=_(u"label_thankstitle", default=u"Thanks title"), + title=_("label_thankstitle", default="Thanks title"), defaultFactory=default_thankstitle, required=True, ) thanksdescription = zope.schema.Text( - title=_(u"label_thanksdescription", default=u"Thanks summary"), - description=_(u"help_thanksdescription", default=u"Used in thanks page."), + title=_("label_thanksdescription", default="Thanks summary"), + description=_("help_thanksdescription", default="Used in thanks page."), defaultFactory=default_thanksdescription, required=False, - missing_value=u"", + missing_value="", ) showAll = zope.schema.Bool( - title=_(u"label_showallfields_text", default=u"Show All Fields"), + title=_("label_showallfields_text", default="Show All Fields"), description=_( - u"help_showallfields_text", - default=u"" - u"Check this to display input for all fields " - u"(except label and file fields). If you check " - u"this, the choices in the pick box below " - u"will be ignored.", + "help_showallfields_text", + default="" + "Check this to display input for all fields " + "(except label and file fields). If you check " + "this, the choices in the pick box below " + "will be ignored.", ), default=True, required=False, ) showFields = zope.schema.List( - title=_(u"label_showfields_text", default=u"Show Responses"), + title=_("label_showfields_text", default="Show Responses"), description=_( - u"help_showfields_text", - default=u"Pick the fields whose inputs you'd like to display on " - u"the success page.", + "help_showfields_text", + default="Pick the fields whose inputs you'd like to display on " + "the success page.", ), unique=True, required=False, value_type=zope.schema.Choice(vocabulary="easyform.Fields"), # noqa ) includeEmpties = zope.schema.Bool( - title=_(u"label_includeEmpties_text", default=u"Include Empties"), + title=_("label_includeEmpties_text", default="Include Empties"), description=_( - u"help_includeEmpties_text", - default=u"" - u"Check this to display field titles " - u"for fields that received no input. Uncheck " - u"to leave fields with no input off the list.", + "help_includeEmpties_text", + default="" + "Check this to display field titles " + "for fields that received no input. Uncheck " + "to leave fields with no input off the list.", ), default=True, required=False, ) thanksPrologue = RichText( - title=_(u"label_thanksprologue_text", default=u"Thanks Prologue"), + title=_("label_thanksprologue_text", default="Thanks Prologue"), description=_( - u"help_thanksprologue_text", - default=u"This text will be displayed above the selected field " u"inputs.", + "help_thanksprologue_text", + default="This text will be displayed above the selected field " "inputs.", ), required=False, ) thanksEpilogue = RichText( - title=_(u"label_thanksepilogue_text", default=u"Thanks Epilogue"), + title=_("label_thanksepilogue_text", default="Thanks Epilogue"), description=_( - u"help_thanksepilogue_text", - default=u"The text will be displayed after the field inputs.", + "help_thanksepilogue_text", + default="The text will be displayed after the field inputs.", ), required=False, ) # ADVANCED fieldset( - u"advanced", + "advanced", label=_("Advanced"), fields=[ "submitLabel", @@ -210,89 +210,90 @@ class IEasyForm(Schema): order=20, ) submitLabel = zope.schema.TextLine( - title=_(u"label_submitlabel_text", default=u"Submit Button Label"), - description=_(u"help_submitlabel_text", default=u""), + title=_("label_submitlabel_text", default="Submit Button Label"), + description=_("help_submitlabel_text", default=""), defaultFactory=default_submitLabel, required=False, ) useCancelButton = zope.schema.Bool( - title=_(u"label_showcancel_text", default=u"Show Reset Button"), - description=_(u"help_showcancel_text", default=u""), + title=_("label_showcancel_text", default="Show Reset Button"), + description=_("help_showcancel_text", default=""), default=False, required=False, ) resetLabel = zope.schema.TextLine( - title=_(u"label_reset_button", default=u"Reset Button Label"), - description=_(u"help_reset_button", default=u""), + title=_("label_reset_button", default="Reset Button Label"), + description=_("help_reset_button", default=""), defaultFactory=default_resetLabel, required=False, ) nameAttribute = zope.schema.TextLine( - title=_(u"label_name_attribute", default=u"Name attribute"), + title=_("label_name_attribute", default="Name attribute"), description=_( - u"help_name_attribute", - default=u"optional, sets the name attribute on the form container. " \ - u"can be used for form analytics"), + "help_name_attribute", + default="optional, sets the name attribute on the form container. " + "can be used for form analytics", + ), required=False, ) directives.write_permission(form_tabbing=config.EDIT_ADVANCED_PERMISSION) form_tabbing = zope.schema.Bool( - title=_(u"label_form_tabbing", default=u"Turn fieldsets to tabs"), - description=_(u"help_form_tabbing", default=u""), + title=_("label_form_tabbing", default="Turn fieldsets to tabs"), + description=_("help_form_tabbing", default=""), default=True, required=False, ) directives.write_permission(default_fieldset_label=config.EDIT_ADVANCED_PERMISSION) default_fieldset_label = zope.schema.TextLine( title=_( - u"label_default_fieldset_label_text", - default=u"Custom Default Fieldset Label", + "label_default_fieldset_label_text", + default="Custom Default Fieldset Label", ), description=_( - u"help_default_fieldset_label_text", - default=u"This field allows you to change default fieldset label.", + "help_default_fieldset_label_text", + default="This field allows you to change default fieldset label.", ), required=False, - default=u"", + default="", ) directives.write_permission(method=config.EDIT_TECHNICAL_PERMISSION) method = zope.schema.Choice( - title=_(u"label_method", default=u"Form method"), - description=_(u"help_method", default=u""), - default=u"post", + title=_("label_method", default="Form method"), + description=_("help_method", default=""), + default="post", required=False, vocabulary="easyform.FormMethods", ) directives.write_permission(unload_protection=config.EDIT_TECHNICAL_PERMISSION) unload_protection = zope.schema.Bool( - title=_(u"label_unload_protection", default=u"Unload protection"), - description=_(u"help_unload_protection", default=u""), + title=_("label_unload_protection", default="Unload protection"), + description=_("help_unload_protection", default=""), default=True, required=False, ) directives.write_permission(CSRFProtection=config.EDIT_TECHNICAL_PERMISSION) CSRFProtection = zope.schema.Bool( - title=_(u"label_csrf", default=u"CSRF Protection"), + title=_("label_csrf", default="CSRF Protection"), description=_( - u"help_csrf", - default=u"Check this to employ Cross-Site " - u"Request Forgery protection. Note that only HTTP Post " - u"actions will be allowed.", + "help_csrf", + default="Check this to employ Cross-Site " + "Request Forgery protection. Note that only HTTP Post " + "actions will be allowed.", ), default=True, required=False, ) directives.write_permission(forceSSL=config.EDIT_TECHNICAL_PERMISSION) forceSSL = zope.schema.Bool( - title=_(u"label_force_ssl", default=u"Force SSL connection"), + title=_("label_force_ssl", default="Force SSL connection"), description=_( - u"help_force_ssl", - default=u"Check this to make the form redirect to an SSL-enabled " - u"version of itself (https://) if accessed via a non-SSL " - u"URL (http://). In order to function properly, " - u"this requires a web server that has been configured to " - u"handle the HTTPS protocol on port 443 and forward it to " - u"Zope.", + "help_force_ssl", + default="Check this to make the form redirect to an SSL-enabled " + "version of itself (https://) if accessed via a non-SSL " + "URL (http://). In order to function properly, " + "this requires a web server that has been configured to " + "handle the HTTPS protocol on port 443 and forward it to " + "Zope.", ), default=False, required=False, @@ -300,7 +301,7 @@ class IEasyForm(Schema): # OVERRIDES fieldset( - u"overrides", + "overrides", label=_("Overrides"), fields=[ "thanksPageOverrideAction", @@ -316,141 +317,139 @@ class IEasyForm(Schema): directives.write_permission(thanksPageOverrideAction=config.EDIT_TALES_PERMISSION) thanksPageOverrideAction = zope.schema.Choice( title=_( - u"label_thankspageoverrideaction_text", - default=u"Custom Success Action Type", + "label_thankspageoverrideaction_text", + default="Custom Success Action Type", ), description=_( - u"help_thankspageoverrideaction_text", - default=u"Use this field in place of a thanks-page designation " - u"to determine final action after calling " - u"your action adapter (if you have one). You would " - u"usually use this for a custom success template or " - u"script. Leave empty if unneeded. Otherwise, specify as " - u"you would a CMFFormController action type and argument, " - u"complete with type of action to execute " - u'(e.g., "redirect_to" or "traverse_to") ' - u"and a TALES expression. For example, " - u'"Redirect to" and "string:thanks-page" would redirect ' - u'to "thanks-page".', + "help_thankspageoverrideaction_text", + default="Use this field in place of a thanks-page designation " + "to determine final action after calling " + "your action adapter (if you have one). You would " + "usually use this for a custom success template or " + "script. Leave empty if unneeded. Otherwise, specify as " + "you would a CMFFormController action type and argument, " + "complete with type of action to execute " + '(e.g., "redirect_to" or "traverse_to") ' + "and a TALES expression. For example, " + '"Redirect to" and "string:thanks-page" would redirect ' + 'to "thanks-page".', ), - default=u"redirect_to", + default="redirect_to", required=False, vocabulary="easyform.CustomActions", ) directives.write_permission(thanksPageOverride=config.EDIT_TALES_PERMISSION) thanksPageOverride = zope.schema.TextLine( - title=_(u"label_thankspageoverride_text", default=u"Custom Success Action"), + title=_("label_thankspageoverride_text", default="Custom Success Action"), description=_( - u"help_thankspageoverride_text", - default=u"Use this field in place of a thanks-page designation " - u"to determine final action after calling your action " - u"adapter (if you have one). You would usually use " - u"this for a custom success template or script. " - u"Leave empty if unneeded. Otherwise, specify as you " - u"would a CMFFormController action type and argument, " - u"complete with type of action to execute " - u'(e.g., "redirect_to" or "traverse_to") ' - u"and a TALES expression. For example, " - u'"Redirect to" and "string:thanks-page" would redirect ' - u'to "thanks-page".', + "help_thankspageoverride_text", + default="Use this field in place of a thanks-page designation " + "to determine final action after calling your action " + "adapter (if you have one). You would usually use " + "this for a custom success template or script. " + "Leave empty if unneeded. Otherwise, specify as you " + "would a CMFFormController action type and argument, " + "complete with type of action to execute " + '(e.g., "redirect_to" or "traverse_to") ' + "and a TALES expression. For example, " + '"Redirect to" and "string:thanks-page" would redirect ' + 'to "thanks-page".', ), - default=u"", + default="", constraint=isTALES, required=False, ) directives.write_permission(formActionOverride=config.EDIT_TALES_PERMISSION) formActionOverride = zope.schema.TextLine( - title=_(u"label_formactionoverride_text", default=u"Custom Form Action"), + title=_("label_formactionoverride_text", default="Custom Form Action"), description=_( - u"help_formactionoverride_text", - default=u"Use this field to override the form action attribute. " - u"Specify a URL to which the form will post. " - u"This will bypass form validation, success action " - u"adapter and thanks page.", + "help_formactionoverride_text", + default="Use this field to override the form action attribute. " + "Specify a URL to which the form will post. " + "This will bypass form validation, success action " + "adapter and thanks page.", ), - default=u"", + default="", required=False, constraint=isTALES, ) directives.write_permission(onDisplayOverride=config.EDIT_TALES_PERMISSION) onDisplayOverride = zope.schema.TextLine( - title=_(u"label_OnDisplayOverride_text", default=u"Form Setup Script"), + title=_("label_OnDisplayOverride_text", default="Form Setup Script"), description=_( - u"help_OnDisplayOverride_text", - default=u"A TALES expression that will be called when the form is " - u"displayed. Leave empty if unneeded. The most common " - u"use of this field is to call a python script that " - u"sets defaults for multiple fields by pre-populating " - u"request.form. " - u"Any value returned by the expression is ignored. " - u"PLEASE NOTE: errors in the evaluation of this " - u"expression will cause an error on form display.", + "help_OnDisplayOverride_text", + default="A TALES expression that will be called when the form is " + "displayed. Leave empty if unneeded. The most common " + "use of this field is to call a python script that " + "sets defaults for multiple fields by pre-populating " + "request.form. " + "Any value returned by the expression is ignored. " + "PLEASE NOTE: errors in the evaluation of this " + "expression will cause an error on form display.", ), constraint=isTALES, required=False, - default=u"", + default="", ) directives.write_permission(afterValidationOverride=config.EDIT_TALES_PERMISSION) afterValidationOverride = zope.schema.TextLine( title=_( - u"label_AfterValidationOverride_text", default=u"After Validation Script" + "label_AfterValidationOverride_text", default="After Validation Script" ), description=_( - u"help_AfterValidationOverride_text", - default=u"A TALES expression that will be called after the form is" - u"successfully validated, but before calling an action " - u"adapter (if any) or displaying a thanks page." - u"Form input will be in the request.form dictionary." - u"Leave empty if unneeded. The most " - u"common use of this field is to call a python script" - u"to clean up form input or to script an alternative " - u"action. Any value returned by the expression is ignored." - u"PLEASE NOTE: errors in the evaluation of this " - u"expression willcause an error on form display.", + "help_AfterValidationOverride_text", + default="A TALES expression that will be called after the form is" + "successfully validated, but before calling an action " + "adapter (if any) or displaying a thanks page." + "Form input will be in the request.form dictionary." + "Leave empty if unneeded. The most " + "common use of this field is to call a python script" + "to clean up form input or to script an alternative " + "action. Any value returned by the expression is ignored." + "PLEASE NOTE: errors in the evaluation of this " + "expression willcause an error on form display.", ), constraint=isTALES, required=False, - default=u"", + default="", ) directives.write_permission(headerInjection=config.EDIT_TALES_PERMISSION) headerInjection = zope.schema.TextLine( - title=_(u"label_headerInjection_text", default=u"Header Injection"), + title=_("label_headerInjection_text", default="Header Injection"), description=_( - u"help_headerInjection_text", - default=u"This override field allows you to insert content into " - u"the xhtml head. The typical use is to add custom CSS " - u"or JavaScript. Specify a TALES expression returning a " - u"string. The string will be inserted with no " - u"interpretation. PLEASE NOTE: errors in the evaluation " - u"of this expression will cause an error on form display.", + "help_headerInjection_text", + default="This override field allows you to insert content into " + "the xhtml head. The typical use is to add custom CSS " + "or JavaScript. Specify a TALES expression returning a " + "string. The string will be inserted with no " + "interpretation. PLEASE NOTE: errors in the evaluation " + "of this expression will cause an error on form display.", ), constraint=isTALES, required=False, - default=u"", + default="", ) directives.write_permission(submitLabelOverride=config.EDIT_TALES_PERMISSION) submitLabelOverride = zope.schema.TextLine( - title=_( - u"label_submitlabeloverride_text", default=u"Custom Submit Button Label" - ), + title=_("label_submitlabeloverride_text", default="Custom Submit Button Label"), description=_( - u"help_submitlabeloverride_text", - default=u"This override field allows you to change submit button " - u"label. The typical use is to set label with request " - u"parameters. Specify a TALES expression returning a " - u"string. PLEASE NOTE: errors in the evaluation of this " - u"expression will cause an error on form display.", + "help_submitlabeloverride_text", + default="This override field allows you to change submit button " + "label. The typical use is to set label with request " + "parameters. Specify a TALES expression returning a " + "string. PLEASE NOTE: errors in the evaluation of this " + "expression will cause an error on form display.", ), constraint=isTALES, required=False, - default=u"", + default="", ) class IEasyFormImportFormSchema(Interface): """Schema for easyform import form.""" - upload = zope.schema.Bytes(title=PMF(u"Upload"), required=True) + upload = zope.schema.Bytes(title=PMF("Upload"), required=True) class IEasyFormThanksPage(Interface): diff --git a/src/collective/easyform/interfaces/fields.py b/src/collective/easyform/interfaces/fields.py index 558793b3..e50f59be 100644 --- a/src/collective/easyform/interfaces/fields.py +++ b/src/collective/easyform/interfaces/fields.py @@ -11,7 +11,6 @@ from plone.schemaeditor.schema import ITextLinesField from plone.supermodel.directives import fieldset from plone.supermodel.model import Schema -import z3c.form from z3c.form.interfaces import IFieldWidget from zope import schema from zope.component import getGlobalSiteManager @@ -21,6 +20,7 @@ from zope.schema.vocabulary import SimpleVocabulary import six +import z3c.form import z3c.form.interfaces import zope.interface import zope.schema.interfaces @@ -49,7 +49,7 @@ def widgetsFactory(context): ) ] for adapter in set(adapters): - name = u"{0}.{1}".format(adapter.__module__, adapter.__name__) + name = "{0}.{1}".format(adapter.__module__, adapter.__name__) terms.append(WidgetVocabulary.createTerm(name, str(name), adapter.__name__)) return WidgetVocabulary(terms) @@ -60,14 +60,20 @@ class IEasyFormFieldsEditorExtender(IFieldEditorExtender): class IFieldExtender(Schema): fieldset( - u"advanced", + "advanced", label=_("Advanced"), - fields=["field_widget", "validators", "THidden", "depends_on", "css_class",], + fields=[ + "field_widget", + "validators", + "THidden", + "depends_on", + "css_class", + ], ) directives.write_permission(field_widget=config.EDIT_ADVANCED_PERMISSION) field_widget = zope.schema.Choice( - title=_(u"label_field_widget", default=u"Field Widget"), - description=_(u"help_field_widget", default=u""), + title=_("label_field_widget", default="Field Widget"), + description=_("help_field_widget", default=""), required=False, source=widgetsFactory, ) @@ -75,8 +81,8 @@ class IFieldExtender(Schema): validators = zope.schema.List( title=_("Validators"), description=_( - u"help_userfield_validators", - default=u"Select the validators to use on this field", + "help_userfield_validators", + default="Select the validators to use on this field", ), unique=True, required=False, @@ -84,99 +90,99 @@ class IFieldExtender(Schema): ) directives.write_permission(THidden=config.EDIT_TALES_PERMISSION) THidden = zope.schema.Bool( - title=_(u"label_hidden", default=u"Hidden"), - description=_(u"help_hidden", default=u"Field is hidden"), + title=_("label_hidden", default="Hidden"), + description=_("help_hidden", default="Field is hidden"), required=False, default=False, ) directives.write_permission(depends_on=config.EDIT_TALES_PERMISSION) depends_on = zope.schema.TextLine( title=_( - u'Field depends on', + "Field depends on", ), description=_( - u'This is using the pat-depends from patternslib, all options are supported. Please read the pat-depends documentations for options.', + 'This is using the pat-depends from patternslib, all options are supported. Please read the pat-depends documentations for options.', ), - default=u'', + default="", required=False, ) directives.write_permission(css_class=config.EDIT_TALES_PERMISSION) css_class = zope.schema.TextLine( title=_( - u'CSS Class', + "CSS Class", ), description=_( - u'Define additional CSS class for this field here. This allowes for formating individual fields via CSS.', + "Define additional CSS class for this field here. This allowes for formating individual fields via CSS.", ), - default=u'', + default="", required=False, constraint=cssClassConstraint, ) fieldset( - u"overrides", + "overrides", label=_("Overrides"), fields=["TDefault", "TEnabled", "TValidator", "serverSide"], ) directives.write_permission(TDefault=config.EDIT_TALES_PERMISSION) TDefault = zope.schema.TextLine( - title=_(u"label_tdefault_text", default=u"Default Expression"), + title=_("label_tdefault_text", default="Default Expression"), description=_( - u"help_tdefault_text", - default=u"A TALES expression that will be evaluated when the form" - u"is displayed to get the field default value. Leave " - u"empty if unneeded. Your expression should evaluate as a " - u"string. PLEASE NOTE: errors in the evaluation of this " - u"expression will cause an error on form display.", + "help_tdefault_text", + default="A TALES expression that will be evaluated when the form" + "is displayed to get the field default value. Leave " + "empty if unneeded. Your expression should evaluate as a " + "string. PLEASE NOTE: errors in the evaluation of this " + "expression will cause an error on form display.", ), - default=u"", + default="", constraint=isTALES, required=False, ) directives.write_permission(TEnabled=config.EDIT_TALES_PERMISSION) TEnabled = zope.schema.TextLine( - title=_(u"label_tenabled_text", default=u"Enabling Expression"), + title=_("label_tenabled_text", default="Enabling Expression"), description=_( - u"help_tenabled_text", - default=u"A TALES expression that will be evaluated when the form " - u"is displayed to determine whether or not the field is " - u"enabled. Your expression should evaluate as True if " - u"the field should be included in the form, False if it " - u"should be omitted. Leave this expression field empty " - u"if unneeded: the field will be included. PLEASE NOTE: " - u"errors in the evaluation of this expression will cause " - u"an error on form display.", + "help_tenabled_text", + default="A TALES expression that will be evaluated when the form " + "is displayed to determine whether or not the field is " + "enabled. Your expression should evaluate as True if " + "the field should be included in the form, False if it " + "should be omitted. Leave this expression field empty " + "if unneeded: the field will be included. PLEASE NOTE: " + "errors in the evaluation of this expression will cause " + "an error on form display.", ), - default=u"", + default="", constraint=isTALES, required=False, ) directives.write_permission(TValidator=config.EDIT_TALES_PERMISSION) TValidator = zope.schema.TextLine( - title=_(u"label_tvalidator_text", default=u"Custom Validator"), + title=_("label_tvalidator_text", default="Custom Validator"), description=_( - u"help_tvalidator_text", - default=u"A TALES expression that will be evaluated when the form " - u"is validated. Validate against 'value', which will " - u"contain the field input. Return False if valid; if not " - u"valid return a string error message. E.G., " - u"\"python: test(value=='eggs', False, 'input must be " - u'eggs\')" will require "eggs" for input. ' - u"PLEASE NOTE: errors in the evaluation of this " - u"expression will cause an error on form display.", + "help_tvalidator_text", + default="A TALES expression that will be evaluated when the form " + "is validated. Validate against 'value', which will " + "contain the field input. Return False if valid; if not " + "valid return a string error message. E.G., " + "\"python: test(value=='eggs', False, 'input must be " + 'eggs\')" will require "eggs" for input. ' + "PLEASE NOTE: errors in the evaluation of this " + "expression will cause an error on form display.", ), - default=u"", + default="", constraint=isTALES, required=False, ) directives.write_permission(serverSide=config.EDIT_TALES_PERMISSION) serverSide = zope.schema.Bool( - title=_(u"label_server_side_text", default=u"Server-Side Variable"), + title=_("label_server_side_text", default="Server-Side Variable"), description=_( - u"description_server_side_text", - default=u"" - u"Mark this field as a value to be injected into the " - u"request form for use by action adapters and is not " - u"modifiable by or exposed to the client.", + "description_server_side_text", + default="" + "Mark this field as a value to be injected into the " + "request form for use by action adapters and is not " + "modifiable by or exposed to the client.", ), default=False, required=False, @@ -198,7 +204,7 @@ class ILabel(zope.schema.interfaces.IField): class IRichLabel(ILabel): """Rich Label Field.""" - rich_label = RichText(title=_(u"Rich Label"), default=u"", missing_value=u"") + rich_label = RichText(title=_("Rich Label"), default="", missing_value="") class IEasyFormWidget(z3c.form.interfaces.IWidget): @@ -228,21 +234,24 @@ class INorobotCaptcha(zope.schema.interfaces.ITextLine): class ILikert(zope.schema.interfaces.IField): questions = zope.schema.List( - title=_(u'Possible questions'), - description=_(u'Enter allowed choices one per line.'), - required=zope.schema.interfaces.IChoice['vocabulary'].required, - default=zope.schema.interfaces.IChoice['vocabulary'].default, - value_type=zope.schema.TextLine()) + title=_("Possible questions"), + description=_("Enter allowed choices one per line."), + required=zope.schema.interfaces.IChoice["vocabulary"].required, + default=zope.schema.interfaces.IChoice["vocabulary"].default, + value_type=zope.schema.TextLine(), + ) zope.interface.alsoProvides(questions, ITextLinesField) answers = zope.schema.List( - title=_(u'Possible answers'), - description=_(u'Enter allowed choices one per line.'), - required=zope.schema.interfaces.IChoice['vocabulary'].required, - default=zope.schema.interfaces.IChoice['vocabulary'].default, - value_type=zope.schema.TextLine()) + title=_("Possible answers"), + description=_("Enter allowed choices one per line."), + required=zope.schema.interfaces.IChoice["vocabulary"].required, + default=zope.schema.interfaces.IChoice["vocabulary"].default, + value_type=zope.schema.TextLine(), + ) zope.interface.alsoProvides(questions, ITextLinesField) + class ILikertWidget(IEasyFormWidget): """Likert widget.""" diff --git a/src/collective/easyform/interfaces/mailer.py b/src/collective/easyform/interfaces/mailer.py index 16e09125..3dcc8271 100644 --- a/src/collective/easyform/interfaces/mailer.py +++ b/src/collective/easyform/interfaces/mailer.py @@ -7,12 +7,12 @@ from plone import api from plone.app.textfield import RichText from plone.autoform import directives +from plone.autoform.interfaces import OMITTED_KEY from plone.schema import Email from plone.supermodel.directives import fieldset from Products.CMFPlone.utils import safe_unicode from z3c.form.browser.checkbox import CheckBoxFieldWidget from z3c.form.browser.textarea import TextAreaWidget -from plone.autoform.interfaces import OMITTED_KEY from zope.globalrequest import getRequest from zope.i18n import translate from zope.interface import provider @@ -29,7 +29,7 @@ @provider(IContextAwareDefaultFactory) def default_mail_subject(context): - return translate(_(u"Form Submission"), context=getRequest()) + return translate(_("Form Submission"), context=getRequest()) def default_mail_body(): @@ -56,15 +56,13 @@ class IMailer(IAction): directives.write_permission(recipient_name=config.EDIT_ADDRESSING_PERMISSION) directives.read_permission(recipient_name=MODIFY_PORTAL_CONTENT) recipient_name = zope.schema.TextLine( - title=_( - u"label_formmailer_recipient_fullname", default=u"Recipient's full name" - ), + title=_("label_formmailer_recipient_fullname", default="Recipient's full name"), description=_( - u"help_formmailer_recipient_fullname", - default=u"The full name of the recipient of the mailed form.", + "help_formmailer_recipient_fullname", + default="The full name of the recipient of the mailed form.", ), - default=u"", - missing_value=u"", + default="", + missing_value="", required=False, ) @@ -72,30 +70,30 @@ class IMailer(IAction): directives.read_permission(recipient_email=MODIFY_PORTAL_CONTENT) recipient_email = Email( title=_( - u"label_formmailer_recipient_email", default=u"Recipient's e-mail address" + "label_formmailer_recipient_email", default="Recipient's e-mail address" ), description=_( - u"help_formmailer_recipient_email", - default=u"The recipients e-mail address.", + "help_formmailer_recipient_email", + default="The recipients e-mail address.", ), required=False, ) fieldset( - u"addressing", + "addressing", label=_("Addressing"), fields=["to_field", "cc_recipients", "bcc_recipients", "replyto_field"], ) directives.write_permission(to_field=config.EDIT_ADDRESSING_PERMISSION) directives.read_permission(to_field=MODIFY_PORTAL_CONTENT) to_field = zope.schema.Choice( - title=_(u"label_formmailer_to_extract", default=u"Extract Recipient From"), + title=_("label_formmailer_to_extract", default="Extract Recipient From"), description=_( - u"help_formmailer_to_extract", - default=u"Choose a form field from which you wish to extract " - u"input for the To header. If you choose anything other " - u'than "None", this will override the "Recipient\'s " ' - u"e-mail address setting above. Be very cautious about " - u"allowing unguarded user input for this purpose.", + "help_formmailer_to_extract", + default="Choose a form field from which you wish to extract " + "input for the To header. If you choose anything other " + 'than "None", this will override the "Recipient\'s " ' + "e-mail address setting above. Be very cautious about " + "allowing unguarded user input for this purpose.", ), required=False, vocabulary="easyform.Fields", @@ -104,45 +102,45 @@ class IMailer(IAction): directives.write_permission(cc_recipients=config.EDIT_ADDRESSING_PERMISSION) directives.read_permission(cc_recipients=MODIFY_PORTAL_CONTENT) cc_recipients = zope.schema.Text( - title=_(u"label_formmailer_cc_recipients", default=u"CC Recipients"), + title=_("label_formmailer_cc_recipients", default="CC Recipients"), description=_( - u"help_formmailer_cc_recipients", - default=u"E-mail addresses which receive a carbon copy.", + "help_formmailer_cc_recipients", + default="E-mail addresses which receive a carbon copy.", ), - default=u"", - missing_value=u"", + default="", + missing_value="", required=False, ) directives.write_permission(bcc_recipients=config.EDIT_ADDRESSING_PERMISSION) directives.read_permission(bcc_recipients=MODIFY_PORTAL_CONTENT) bcc_recipients = zope.schema.Text( - title=_(u"label_formmailer_bcc_recipients", default=u"BCC Recipients"), + title=_("label_formmailer_bcc_recipients", default="BCC Recipients"), description=_( - u"help_formmailer_bcc_recipients", - default=u"E-mail addresses which receive a blind carbon copy.", + "help_formmailer_bcc_recipients", + default="E-mail addresses which receive a blind carbon copy.", ), - default=u"", - missing_value=u"", + default="", + missing_value="", required=False, ) directives.write_permission(replyto_field=config.EDIT_TECHNICAL_PERMISSION) directives.read_permission(replyto_field=MODIFY_PORTAL_CONTENT) replyto_field = zope.schema.Choice( - title=_(u"label_formmailer_replyto_extract", default=u"Extract Reply-To From"), + title=_("label_formmailer_replyto_extract", default="Extract Reply-To From"), description=_( - u"help_formmailer_replyto_extract", - default=u"Choose a form field from which you wish to extract " - u"input for the Reply-To header. NOTE: You should " - u"activate e-mail address verification for the " - u"designated field.", + "help_formmailer_replyto_extract", + default="Choose a form field from which you wish to extract " + "input for the Reply-To header. NOTE: You should " + "activate e-mail address verification for the " + "designated field.", ), required=False, vocabulary="easyform.Fields", ) fieldset( - u"message", + "message", label=PMF("Message"), fields=[ "msg_subject", @@ -161,28 +159,28 @@ class IMailer(IAction): ) directives.read_permission(msg_subject=MODIFY_PORTAL_CONTENT) msg_subject = zope.schema.TextLine( - title=_(u"label_formmailer_subject", default=u"Subject"), + title=_("label_formmailer_subject", default="Subject"), description=_( - u"help_formmailer_subject", - default=u"" - u"Subject line of message. This is used if you " - u"do not specify a subject field or if the field " - u"is empty.", + "help_formmailer_subject", + default="" + "Subject line of message. This is used if you " + "do not specify a subject field or if the field " + "is empty.", ), defaultFactory=default_mail_subject, - missing_value=u"", + missing_value="", required=False, ) directives.write_permission(subject_field=config.EDIT_ADVANCED_PERMISSION) directives.read_permission(subject_field=MODIFY_PORTAL_CONTENT) subject_field = zope.schema.Choice( - title=_(u"label_formmailer_subject_extract", default=u"Extract Subject From"), + title=_("label_formmailer_subject_extract", default="Extract Subject From"), description=_( - u"help_formmailer_subject_extract", - default=u"" - u"Choose a form field from which you wish to extract " - u"input for the mail subject line.", + "help_formmailer_subject_extract", + default="" + "Choose a form field from which you wish to extract " + "input for the mail subject line.", ), required=False, vocabulary="easyform.Fields", @@ -191,13 +189,13 @@ class IMailer(IAction): directives.widget("body_pre", TextAreaWidget) body_pre = RichText( - title=_(u"label_formmailer_body_pre", default=u"Body (prepended)"), + title=_("label_formmailer_body_pre", default="Body (prepended)"), description=_( - u"help_formmailer_body_pre", - default=u"Text prepended to fields listed in mail-body", + "help_formmailer_body_pre", + default="Text prepended to fields listed in mail-body", ), - default=u"", - missing_value=u"", + default="", + missing_value="", default_mime_type="text/x-web-intelligent", allowed_mime_types=("text/x-web-intelligent",), output_mime_type="text/x-html-safe", @@ -206,13 +204,13 @@ class IMailer(IAction): directives.read_permission(body_post=MODIFY_PORTAL_CONTENT) directives.widget("body_post", TextAreaWidget) body_post = RichText( - title=_(u"label_formmailer_body_post", default=u"Body (appended)"), + title=_("label_formmailer_body_post", default="Body (appended)"), description=_( - u"help_formmailer_body_post", - default=u"Text appended to fields listed in mail-body", + "help_formmailer_body_post", + default="Text appended to fields listed in mail-body", ), - default=u"", - missing_value=u"", + default="", + missing_value="", default_mime_type="text/x-web-intelligent", allowed_mime_types=("text/x-web-intelligent",), output_mime_type="text/x-html-safe", @@ -221,14 +219,14 @@ class IMailer(IAction): directives.read_permission(body_footer=MODIFY_PORTAL_CONTENT) directives.widget("body_footer", TextAreaWidget) body_footer = RichText( - title=_(u"label_formmailer_body_footer", default=u"Body (signature)"), + title=_("label_formmailer_body_footer", default="Body (signature)"), description=_( - u"help_formmailer_body_footer", - default=u"Text used as the footer at " - u"bottom, delimited from the body by a dashed line.", + "help_formmailer_body_footer", + default="Text used as the footer at " + "bottom, delimited from the body by a dashed line.", ), - default=u"", - missing_value=u"", + default="", + missing_value="", default_mime_type="text/x-web-intelligent", allowed_mime_types=("text/x-web-intelligent",), output_mime_type="text/x-html-safe", @@ -237,14 +235,14 @@ class IMailer(IAction): directives.read_permission(showAll=MODIFY_PORTAL_CONTENT) showAll = zope.schema.Bool( - title=_(u"label_mailallfields_text", default=u"Include All Fields"), + title=_("label_mailallfields_text", default="Include All Fields"), description=_( - u"help_mailallfields_text", - default=u"" - u"Check this to include input for all fields " - u"(except label and file fields). If you check " - u"this, the choices in the pick box below " - u"will be ignored.", + "help_mailallfields_text", + default="" + "Check this to include input for all fields " + "(except label and file fields). If you check " + "this, the choices in the pick box below " + "will be ignored.", ), default=True, required=False, @@ -252,11 +250,11 @@ class IMailer(IAction): directives.read_permission(showFields=MODIFY_PORTAL_CONTENT) showFields = zope.schema.List( - title=_(u"label_mailfields_text", default=u"Show Responses"), + title=_("label_mailfields_text", default="Show Responses"), description=_( - u"help_mailfields_text", - default=u"Pick the fields whose inputs you'd like to include in " - u"the e-mail.", + "help_mailfields_text", + default="Pick the fields whose inputs you'd like to include in " + "the e-mail.", ), unique=True, required=False, @@ -265,13 +263,13 @@ class IMailer(IAction): directives.read_permission(includeEmpties=MODIFY_PORTAL_CONTENT) includeEmpties = zope.schema.Bool( - title=_(u"label_mailEmpties_text", default=u"Include Empties"), + title=_("label_mailEmpties_text", default="Include Empties"), description=_( - u"help_mailEmpties_text", - default=u"" - u"Check this to include titles " - u"for fields that received no input. Uncheck " - u"to leave fields with no input out of the e-mail.", + "help_mailEmpties_text", + default="" + "Check this to include titles " + "for fields that received no input. Uncheck " + "to leave fields with no input out of the e-mail.", ), default=True, required=False, @@ -279,13 +277,13 @@ class IMailer(IAction): directives.read_permission(sendCSV=MODIFY_PORTAL_CONTENT) sendCSV = zope.schema.Bool( - title=_(u"label_sendCSV_text", default=u"Send CSV data attachment"), + title=_("label_sendCSV_text", default="Send CSV data attachment"), description=_( - u"help_sendCSV_text", - default=u"" - u"Check this to send a CSV file " - u"attachment containing the values " - u"filled out in the form.", + "help_sendCSV_text", + default="" + "Check this to send a CSV file " + "attachment containing the values " + "filled out in the form.", ), default=False, required=False, @@ -293,12 +291,15 @@ class IMailer(IAction): directives.read_permission(sendWithHeader=MODIFY_PORTAL_CONTENT) sendWithHeader = zope.schema.Bool( - title=_(u"label_sendWithHeader_text", default=u"Include header in attached CSV/XLSX data"), + title=_( + "label_sendWithHeader_text", + default="Include header in attached CSV/XLSX data", + ), description=_( - u"help_sendWithHeader_text", - default=u"" - u"Check this to include the CSV/XLSX header " - u"in file attachments.", + "help_sendWithHeader_text", + default="" + "Check this to include the CSV/XLSX header " + "in file attachments.", ), default=False, required=False, @@ -306,102 +307,99 @@ class IMailer(IAction): directives.read_permission(sendXLSX=MODIFY_PORTAL_CONTENT) sendXLSX = zope.schema.Bool( - title=_(u"label_sendXLSX_text", default=u"Send XLSX data attachment"), + title=_("label_sendXLSX_text", default="Send XLSX data attachment"), description=_( - u"help_sendXLSX_text", - default=u"" - u"Check this to send a XLSX file " - u"attachment containing the values " - u"filled out in the form.", + "help_sendXLSX_text", + default="" + "Check this to send a XLSX file " + "attachment containing the values " + "filled out in the form.", ), default=False, required=False, ) - directives.read_permission(sendXML=MODIFY_PORTAL_CONTENT) sendXML = zope.schema.Bool( - title=_(u"label_sendXML_text", default=u"Send XML data attachment"), + title=_("label_sendXML_text", default="Send XML data attachment"), description=_( - u"help_sendXML_text", - default=u"" - u"Check this to send an XML file " - u"attachment containing the values " - u"filled out in the form.", + "help_sendXML_text", + default="" + "Check this to send an XML file " + "attachment containing the values " + "filled out in the form.", ), default=False, required=False, ) - fieldset(u"template", label=PMF("Template"), fields=["body_pt", "body_type"]) + fieldset("template", label=PMF("Template"), fields=["body_pt", "body_type"]) directives.write_permission(body_pt=config.EDIT_TALES_PERMISSION) directives.read_permission(body_pt=MODIFY_PORTAL_CONTENT) body_pt = zope.schema.Text( - title=_(u"label_formmailer_body_pt", default=u"Mail-Body Template"), + title=_("label_formmailer_body_pt", default="Mail-Body Template"), description=_( - u"help_formmailer_body_pt", - default=u"This is a Zope Page Template used for rendering of " - u"the mail-body. You don't need to modify it, but if you " - u"know TAL (Zope's Template Attribute Language) have " - u"the full power to customize your outgoing mails.", + "help_formmailer_body_pt", + default="This is a Zope Page Template used for rendering of " + "the mail-body. You don't need to modify it, but if you " + "know TAL (Zope's Template Attribute Language) have " + "the full power to customize your outgoing mails.", ), defaultFactory=default_mail_body, - missing_value=u"", + missing_value="", ) directives.write_permission(body_type=config.EDIT_ADVANCED_PERMISSION) directives.read_permission(body_type=MODIFY_PORTAL_CONTENT) body_type = zope.schema.Choice( - title=_(u"label_formmailer_body_type", default=u"Mail Format"), + title=_("label_formmailer_body_type", default="Mail Format"), description=_( - u"help_formmailer_body_type", - default=u"Set the mime-type of the mail-body. Change this " - u"setting only if you know exactly what you are doing. " - u"Leave it blank for default behaviour.", + "help_formmailer_body_type", + default="Set the mime-type of the mail-body. Change this " + "setting only if you know exactly what you are doing. " + "Leave it blank for default behaviour.", ), - default=u"html", + default="html", vocabulary="easyform.MimeList", ) fieldset( - u"headers", label=_("Headers"), fields=["xinfo_headers", "additional_headers"] + "headers", label=_("Headers"), fields=["xinfo_headers", "additional_headers"] ) directives.widget(xinfo_headers=CheckBoxFieldWidget) directives.write_permission(xinfo_headers=config.EDIT_TECHNICAL_PERMISSION) directives.read_permission(xinfo_headers=MODIFY_PORTAL_CONTENT) xinfo_headers = zope.schema.List( - title=_(u"label_xinfo_headers_text", default=u"HTTP Headers"), + title=_("label_xinfo_headers_text", default="HTTP Headers"), description=_( - u"help_xinfo_headers_text", - default=u"" - u"Pick any items from the HTTP headers that " - u"you'd like to insert as X- headers in the message.", + "help_xinfo_headers_text", + default="" + "Pick any items from the HTTP headers that " + "you'd like to insert as X- headers in the message.", ), unique=True, required=False, - default=[u"HTTP_X_FORWARDED_FOR", u"REMOTE_ADDR", u"PATH_INFO"], - missing_value=[u"HTTP_X_FORWARDED_FOR", u"REMOTE_ADDR", u"PATH_INFO"], + default=["HTTP_X_FORWARDED_FOR", "REMOTE_ADDR", "PATH_INFO"], + missing_value=["HTTP_X_FORWARDED_FOR", "REMOTE_ADDR", "PATH_INFO"], value_type=zope.schema.Choice(vocabulary="easyform.XinfoHeaders"), ) directives.write_permission(additional_headers=config.EDIT_TECHNICAL_PERMISSION) directives.read_permission(additional_headers=MODIFY_PORTAL_CONTENT) additional_headers = zope.schema.List( - title=_(u"label_formmailer_additional_headers", default=u"Additional Headers"), + title=_("label_formmailer_additional_headers", default="Additional Headers"), description=_( - u"help_formmailer_additional_headers", - default=u"Additional e-mail-header lines. Only use " - u"RFC822-compliant headers.", + "help_formmailer_additional_headers", + default="Additional e-mail-header lines. Only use " + "RFC822-compliant headers.", ), unique=True, required=False, value_type=zope.schema.TextLine( - title=_( - u"extra_header", default=u"${name} Header", mapping={u"name": u"HTTP"} - ) + title=_("extra_header", default="${name} Header", mapping={"name": "HTTP"}) ), ) fieldset( - u"overrides", + "overrides", label=_("Overrides"), fields=[ "subjectOverride", @@ -415,100 +413,100 @@ class IMailer(IAction): directives.write_permission(subjectOverride=config.EDIT_TALES_PERMISSION) directives.read_permission(subjectOverride=MODIFY_PORTAL_CONTENT) subjectOverride = zope.schema.TextLine( - title=_(u"label_subject_override_text", default=u"Subject Expression"), + title=_("label_subject_override_text", default="Subject Expression"), description=_( - u"help_subject_override_text", - default=u"A TALES expression that will be evaluated to override " - u"any value otherwise entered for the e-mail subject " - u"header. Leave empty if unneeded. Your expression " - u"should evaluate as a string. PLEASE NOTE: errors in " - u"the evaluation of this expression will cause an error " - u"on form display.", + "help_subject_override_text", + default="A TALES expression that will be evaluated to override " + "any value otherwise entered for the e-mail subject " + "header. Leave empty if unneeded. Your expression " + "should evaluate as a string. PLEASE NOTE: errors in " + "the evaluation of this expression will cause an error " + "on form display.", ), required=False, - default=u"", - missing_value=u"", + default="", + missing_value="", constraint=isTALES, ) directives.write_permission(senderOverride=config.EDIT_TALES_PERMISSION) directives.read_permission(senderOverride=MODIFY_PORTAL_CONTENT) senderOverride = zope.schema.TextLine( - title=_(u"label_sender_override_text", default=u"Sender Expression"), + title=_("label_sender_override_text", default="Sender Expression"), description=_( - u"help_sender_override_text", - default=u"A TALES expression that will be evaluated to override " - u'the "From" header. Leave empty if unneeded. ' - u"Your expression should evaluate as a string. " - u"Example: python:fields['replyto'] " - u"PLEASE NOTE: errors in the evaluation of this " - u"expression will cause an error on form display.", + "help_sender_override_text", + default="A TALES expression that will be evaluated to override " + 'the "From" header. Leave empty if unneeded. ' + "Your expression should evaluate as a string. " + "Example: python:fields['replyto'] " + "PLEASE NOTE: errors in the evaluation of this " + "expression will cause an error on form display.", ), required=False, - default=u"", - missing_value=u"", + default="", + missing_value="", constraint=isTALES, ) directives.write_permission(recipientOverride=config.EDIT_TALES_PERMISSION) directives.read_permission(recipientOverride=MODIFY_PORTAL_CONTENT) recipientOverride = zope.schema.TextLine( - title=_(u"label_recipient_override_text", default=u"Recipient Expression"), + title=_("label_recipient_override_text", default="Recipient Expression"), description=_( - u"help_recipient_override_text", - default=u"A TALES expression that will be evaluated to override " - u"any value otherwise entered for the recipient " - u"e-mail address. You are strongly cautioned against using" - u"unvalidated data from the request for this purpose. " - u"Leave empty if unneeded. Your expression should " - u"evaluate as a string. PLEASE NOTE: errors in the " - u"evaluation of this expression will cause " - u"an error on form display.", + "help_recipient_override_text", + default="A TALES expression that will be evaluated to override " + "any value otherwise entered for the recipient " + "e-mail address. You are strongly cautioned against using" + "unvalidated data from the request for this purpose. " + "Leave empty if unneeded. Your expression should " + "evaluate as a string. PLEASE NOTE: errors in the " + "evaluation of this expression will cause " + "an error on form display.", ), required=False, - default=u"", - missing_value=u"", + default="", + missing_value="", constraint=isTALES, ) directives.write_permission(ccOverride=config.EDIT_TALES_PERMISSION) directives.read_permission(ccOverride=MODIFY_PORTAL_CONTENT) ccOverride = zope.schema.TextLine( - title=_(u"label_cc_override_text", default=u"CC Expression"), + title=_("label_cc_override_text", default="CC Expression"), description=_( - u"help_cc_override_text", - default=u"A TALES expression that will be evaluated to override " - u"any value otherwise entered for the CC list. You are " - u"strongly cautioned against using unvalidated data from " - u"the request for this purpose. Leave empty if unneeded. " - u"Your expression should evaluate as a sequence of " - u"strings. PLEASE NOTE: errors in the evaluation of this " - u"expression will cause an error on form display.", + "help_cc_override_text", + default="A TALES expression that will be evaluated to override " + "any value otherwise entered for the CC list. You are " + "strongly cautioned against using unvalidated data from " + "the request for this purpose. Leave empty if unneeded. " + "Your expression should evaluate as a sequence of " + "strings. PLEASE NOTE: errors in the evaluation of this " + "expression will cause an error on form display.", ), required=False, - default=u"", - missing_value=u"", + default="", + missing_value="", constraint=isTALES, ) directives.write_permission(bccOverride=config.EDIT_TALES_PERMISSION) directives.read_permission(bccOverride=MODIFY_PORTAL_CONTENT) bccOverride = zope.schema.TextLine( - title=_(u"label_bcc_override_text", default=u"BCC Expression"), + title=_("label_bcc_override_text", default="BCC Expression"), description=_( - u"help_bcc_override_text", - default=u"A TALES expression that will be evaluated to override " - u"any value otherwise entered for the BCC list. " - u"You are strongly cautioned against using " - u"unvalidated data from the request for this purpose. " - u"Leave empty if unneeded. Your expression should " - u"evaluate as a sequence of strings. PLEASE NOTE: errors " - u"in the evaluation of this expression will cause " - u"an error on form display.", + "help_bcc_override_text", + default="A TALES expression that will be evaluated to override " + "any value otherwise entered for the BCC list. " + "You are strongly cautioned against using " + "unvalidated data from the request for this purpose. " + "Leave empty if unneeded. Your expression should " + "evaluate as a sequence of strings. PLEASE NOTE: errors " + "in the evaluation of this expression will cause " + "an error on form display.", ), required=False, - default=u"", - missing_value=u"", + default="", + missing_value="", constraint=isTALES, ) @@ -516,6 +514,5 @@ class IMailer(IAction): # extend list of omitted fields if XLSX extra is not available if not HAS_XLSX_SUPPORT: omitted_fields = IMailer.queryTaggedValue(OMITTED_KEY, [])[:] - omitted_fields.append((zope.interface.Interface, 'sendXLSX', 'true')) + omitted_fields.append((zope.interface.Interface, "sendXLSX", "true")) IMailer.setTaggedValue(OMITTED_KEY, omitted_fields) - diff --git a/src/collective/easyform/interfaces/savedata.py b/src/collective/easyform/interfaces/savedata.py index 20084978..b280b212 100644 --- a/src/collective/easyform/interfaces/savedata.py +++ b/src/collective/easyform/interfaces/savedata.py @@ -16,37 +16,37 @@ class ISavedDataFormWrapper(IFormWrapper): class IExtraData(Interface): dt = zope.schema.TextLine( - title=_(u"Posting Date/Time"), required=False, default=u"", missing_value=u"" + title=_("Posting Date/Time"), required=False, default="", missing_value="" ) HTTP_X_FORWARDED_FOR = zope.schema.TextLine( title=_( - u"extra_header", - default=u"${name} Header", - mapping={u"name": u"HTTP_X_FORWARDED_FOR"}, + "extra_header", + default="${name} Header", + mapping={"name": "HTTP_X_FORWARDED_FOR"}, ), required=False, - default=u"", - missing_value=u"", + default="", + missing_value="", ) REMOTE_ADDR = zope.schema.TextLine( title=_( - u"extra_header", - default=u"${name} Header", - mapping={u"name": u"REMOTE_ADDR"}, + "extra_header", + default="${name} Header", + mapping={"name": "REMOTE_ADDR"}, ), required=False, - default=u"", - missing_value=u"", + default="", + missing_value="", ) HTTP_USER_AGENT = zope.schema.TextLine( title=_( - u"extra_header", - default=u"${name} Header", - mapping={u"name": u"HTTP_USER_AGENT"}, + "extra_header", + default="${name} Header", + mapping={"name": "HTTP_USER_AGENT"}, ), required=False, - default=u"", - missing_value=u"", + default="", + missing_value="", ) @@ -56,11 +56,11 @@ class ISaveData(IAction): return it in csv- or tab-delimited format.""" showFields = zope.schema.List( - title=_(u"label_savefields_text", default=u"Saved Fields"), + title=_("label_savefields_text", default="Saved Fields"), description=_( - u"help_savefields_text", - default=u"Pick the fields whose inputs you'd like to include in " - u"the saved data. If empty, all fields will be saved.", + "help_savefields_text", + default="Pick the fields whose inputs you'd like to include in " + "the saved data. If empty, all fields will be saved.", ), unique=True, required=False, @@ -68,25 +68,25 @@ class ISaveData(IAction): ) directives.widget(ExtraData=CheckBoxFieldWidget) ExtraData = zope.schema.List( - title=_(u"label_savedataextra_text", default="Extra Data"), + title=_("label_savedataextra_text", default="Extra Data"), description=_( - u"help_savedataextra_text", - default=u"Pick any extra data you'd like saved with the form " u"input.", + "help_savedataextra_text", + default="Pick any extra data you'd like saved with the form " "input.", ), unique=True, value_type=zope.schema.Choice(vocabulary="easyform.ExtraDataDL"), ) DownloadFormat = zope.schema.Choice( - title=_(u"label_downloadformat_text", default=u"Download Format"), - default=u"csv", + title=_("label_downloadformat_text", default="Download Format"), + default="csv", vocabulary="easyform.FormatDL", ) UseColumnNames = zope.schema.Bool( - title=_(u"label_usecolumnnames_text", default=u"Include Column Names"), + title=_("label_usecolumnnames_text", default="Include Column Names"), description=_( - u"help_usecolumnnames_text", - default=u"Do you wish to have column names on the first line of " - u"downloaded input?", + "help_usecolumnnames_text", + default="Do you wish to have column names on the first line of " + "downloaded input?", ), default=True, required=False, diff --git a/src/collective/easyform/interfaces/validators.py b/src/collective/easyform/interfaces/validators.py index 6633f916..7ded454a 100644 --- a/src/collective/easyform/interfaces/validators.py +++ b/src/collective/easyform/interfaces/validators.py @@ -7,7 +7,7 @@ class InvalidTALESError(zope.schema.ValidationError): - __doc__ = u"Please enter a valid TALES expression." + __doc__ = "Please enter a valid TALES expression." def isTALES(value): @@ -20,7 +20,7 @@ def isTALES(value): class InvalidCSSClassNameError(zope.schema.ValidationError): - __doc__ = u"Please enter valid CSS class names." + __doc__ = "Please enter valid CSS class names." def cssClassConstraint(value): @@ -29,7 +29,6 @@ def cssClassConstraint(value): return True parts = value.strip().split(" ") for part in parts: - if not re.match(r'^[A-Za-z]*[A-Za-z\-\_0-9]*[\w][\s]?$', part): + if not re.match(r"^[A-Za-z]*[A-Za-z\-\_0-9]*[\w][\s]?$", part): raise InvalidCSSClassNameError return True - diff --git a/src/collective/easyform/migration/fields.py b/src/collective/easyform/migration/fields.py index d5e5c779..e931f06f 100644 --- a/src/collective/easyform/migration/fields.py +++ b/src/collective/easyform/migration/fields.py @@ -20,36 +20,36 @@ def append_field(schema, type_, name, properties): - field = etree.SubElement(schema, u"field") - field.set(u"name", name) - field.set(u"type", type_) + field = etree.SubElement(schema, "field") + field.set("name", name) + field.set("type", type_) return field def append_label_field(schema, type_, name, properties): field = append_field(schema, type_, name, properties) - append_node(field, u"required", u"False") + append_node(field, "required", "False") return field def append_date_field(schema, type_, name, properties): if properties.get("fgShowHM", False): - return append_field(schema, u"zope.schema.Datetime", name, properties) + return append_field(schema, "zope.schema.Datetime", name, properties) else: - return append_field(schema, u"zope.schema.Date", name, properties) + return append_field(schema, "zope.schema.Date", name, properties) def append_fieldset(schema, type_, name, properties): - fieldset = etree.SubElement(schema, u"fieldset") - fieldset.set(u"name", name) + fieldset = etree.SubElement(schema, "fieldset") + fieldset.set("name", name) return fieldset def set_attribute(field, name, value): - if u":" in name: + if ":" in name: ns, attr = name.split(":") ns = NAMESPACES.get(ns, ns) - field.set(u"{{{}}}{}".format(ns, attr), value) + field.set("{{{}}}{}".format(ns, attr), value) else: field.set(name, value) @@ -61,7 +61,7 @@ def append_node(field, name, value): name = "{{{}}}{}".format(ns, name) node = etree.SubElement(field, name) if isinstance(value, (list, tuple)): - value = u" ".join(value) + value = " ".join(value) node.text = value return node @@ -77,12 +77,12 @@ def append_list_node(field, name, value): def append_required_node(field, name, value): - if value == u"False": + if value == "False": append_node(field, name, value) def append_maxlength_node(field, name, value): - if value != u"0": + if value != "0": append_node(field, name, value) @@ -93,7 +93,7 @@ def append_vocab_node(field, name, value): else: node = field - append_list_node(node, u"values", value) + append_list_node(node, "values", value) def append_default_node(field, name, value): @@ -129,12 +129,12 @@ def append_or_set_title(field, name, value): def convert_tales_expressions(value): - if value == u"here/memberEmail": - return u"python:member and member.getProperty('email', '') or ''" - elif value == u"here/memberFullName": - return u"python:member and member.getProperty('fullname', '') or ''" - elif value == u"here/memberId": - return u"python:member and member.id or ''" + if value == "here/memberEmail": + return "python:member and member.getProperty('email', '') or ''" + elif value == "here/memberFullName": + return "python:member and member.getProperty('fullname', '') or ''" + elif value == "here/memberId": + return "python:member and member.id or ''" return value diff --git a/src/collective/easyform/migration/pfg.py b/src/collective/easyform/migration/pfg.py index 3b348032..6cd4627e 100644 --- a/src/collective/easyform/migration/pfg.py +++ b/src/collective/easyform/migration/pfg.py @@ -68,18 +68,18 @@ def migrate(self, unittest=0): class IMigratePloneFormGenFormSchema(model.Schema): dry_run = schema.Bool( - title=u"Dry run", + title="Dry run", required=True, default=False, ) class MigratePloneFormGenForm(AutoExtensibleForm, Form): - label = u"Migrate PloneFormGen Forms" + label = "Migrate PloneFormGen Forms" ignoreContext = True schema = IMigratePloneFormGenFormSchema - @buttonAndHandler(u"Migrate") + @buttonAndHandler("Migrate") def handle_migrate(self, action): data, errors = self.extractData() if len(errors) > 0: @@ -96,9 +96,9 @@ def handle_migrate(self, action): self.migration_done = True if data.get("dry_run", False): transaction.abort() - logger.info(u"PloneFormGen migration finished (dry run)") + logger.info("PloneFormGen migration finished (dry run)") else: - logger.info(u"PloneFormGen migration finished") + logger.info("PloneFormGen migration finished") def migrate(self): alsoProvides(self.request, IDisableCSRFProtection) diff --git a/src/collective/easyform/serializer.py b/src/collective/easyform/serializer.py index 80e2da5b..0e807483 100644 --- a/src/collective/easyform/serializer.py +++ b/src/collective/easyform/serializer.py @@ -1,32 +1,30 @@ -import datetime -from datetime import date, datetime -import imp -import pdb +from collective.easyform.api import get_actions +from collective.easyform.api import get_schema +from collective.easyform.interfaces import IEasyForm +from collective.easyform.interfaces import ISaveData +from datetime import date +from datetime import datetime from dateutil import parser -import json -import logging - -from zope.component import adapter -from zope.interface import implementer -from zope.interface import Interface -from zope.schema import getFieldsInOrder -from zope.schema.interfaces import ISet, IDate, IDatetime - -from plone.restapi.serializer.dxcontent import SerializeToJson as DXContentToJson +from plone.app.textfield.interfaces import IRichText +from plone.app.textfield.value import RichTextValue +from plone.restapi.deserializer import json_body from plone.restapi.deserializer.dxcontent import ( DeserializeFromJson as DXContentFromJson, ) -from plone.restapi.deserializer import json_body -from plone.restapi.interfaces import ISerializeToJson from plone.restapi.interfaces import IDeserializeFromJson -from plone.app.textfield.value import RichTextValue -from plone.app.textfield.interfaces import IRichText - -from collective.easyform.api import get_actions -from collective.easyform.api import get_schema -from collective.easyform.interfaces import IEasyForm -from collective.easyform.interfaces import ISaveData +from plone.restapi.interfaces import ISerializeToJson +from plone.restapi.serializer.dxcontent import SerializeToJson as DXContentToJson from Products.CMFPlone.utils import safe_unicode +from zope.component import adapter +from zope.interface import implementer +from zope.interface import Interface +from zope.schema import getFieldsInOrder +from zope.schema.interfaces import IDate +from zope.schema.interfaces import IDatetime +from zope.schema.interfaces import ISet + +import json +import logging logger = logging.getLogger("collective.easyform.migration") @@ -87,7 +85,7 @@ def convertBeforeSerialize(value): elif isinstance(value, set): return list(value) elif isinstance(value, RichTextValue): - return safe_unicode(value.raw) #raw_encoded + return safe_unicode(value.raw) # raw_encoded else: return value diff --git a/src/collective/easyform/tests/testCustomScript.py b/src/collective/easyform/tests/testCustomScript.py index 57104165..586fb99a 100644 --- a/src/collective/easyform/tests/testCustomScript.py +++ b/src/collective/easyform/tests/testCustomScript.py @@ -149,13 +149,13 @@ def afterSetUp(self): self.folder.invokeFactory("EasyForm", "ff1") self.ff1 = getattr(self.folder, "ff1") self.ff1.CSRFProtection = False - self.request["form.widgets.title"] = u"Test field" - self.request["form.widgets.__name__"] = u"test_field" - self.request["form.widgets.description"] = u"foobar" + self.request["form.widgets.title"] = "Test field" + self.request["form.widgets.__name__"] = "test_field" + self.request["form.widgets.description"] = "foobar" self.request["form.widgets.factory"] = ["label_textline_field"] self.request["form.widgets.required"] = ["selected"] self.request["form.widgets.fieldset_id"] = "0" - self.request["form.buttons.add"] = u"Add" + self.request["form.buttons.add"] = "Add" view = self.ff1.restrictedTraverse("fields/@@add-field") view.update() form = view.form_instance @@ -165,11 +165,11 @@ def afterSetUp(self): def createScript(self): """Creates FormCustomScript object""" # 1. Create custom script adapter in the form folder - self.request["form.widgets.title"] = u"Adapter" - self.request["form.widgets.__name__"] = u"adapter" - self.request["form.widgets.description"] = u"" + self.request["form.widgets.title"] = "Adapter" + self.request["form.widgets.__name__"] = "adapter" + self.request["form.widgets.description"] = "" self.request["form.widgets.factory"] = ["Custom Script"] - self.request["form.buttons.add"] = u"Add" + self.request["form.buttons.add"] = "Add" view = self.ff1.restrictedTraverse("actions/@@add-action") view.update() form = view.form_instance @@ -309,7 +309,7 @@ def testSecurity(self): self.assertTrue(throwed, "Bypassed security, baaad!") - adapter.ProxyRole = u"Manager" + adapter.ProxyRole = "Manager" throwed = False try: adapter.onSuccess({}, FakeRequest()) @@ -323,12 +323,12 @@ def testSecurity(self): def testSetProxyRole(self): """Exercise setProxyRole""" self.createScript() - self.request["form.widgets.title"] = u"Adapter" - self.request["form.widgets.description"] = u"" - self.request["form.widgets.ProxyRole"] = [u"Manager"] + self.request["form.widgets.title"] = "Adapter" + self.request["form.widgets.description"] = "" + self.request["form.widgets.ProxyRole"] = ["Manager"] self.request["form.widgets.ScriptBody"] = six.text_type(proxied_script) - self.request["form.widgets.IActionExtender.execCondition"] = u"" - self.request["form.buttons.save"] = u"Save" + self.request["form.widgets.IActionExtender.execCondition"] = "" + self.request["form.buttons.save"] = "Save" view = self.ff1.restrictedTraverse("actions") view = view.publishTraverse(view.request, "adapter") view = view.publishTraverse(view.request, "adapter") @@ -336,12 +336,12 @@ def testSetProxyRole(self): form = view.form_instance data, errors = form.extractData() self.assertEqual(len(errors), 0) - self.request["form.widgets.title"] = u"Adapter" - self.request["form.widgets.description"] = u"" - self.request["form.widgets.ProxyRole"] = [u"none"] + self.request["form.widgets.title"] = "Adapter" + self.request["form.widgets.description"] = "" + self.request["form.widgets.ProxyRole"] = ["none"] self.request["form.widgets.ScriptBody"] = six.text_type(proxied_script) - self.request["form.widgets.IActionExtender.execCondition"] = u"" - self.request["form.buttons.save"] = u"Save" + self.request["form.widgets.IActionExtender.execCondition"] = "" + self.request["form.buttons.save"] = "Save" view = self.ff1.restrictedTraverse("actions") view = view.publishTraverse(view.request, "adapter") view = view.publishTraverse(view.request, "adapter") @@ -349,12 +349,12 @@ def testSetProxyRole(self): form = view.form_instance data, errors = form.extractData() self.assertEqual(len(errors), 0) - self.request["form.widgets.title"] = u"Adapter" - self.request["form.widgets.description"] = u"" - self.request["form.widgets.ProxyRole"] = [u"bogus"] + self.request["form.widgets.title"] = "Adapter" + self.request["form.widgets.description"] = "" + self.request["form.widgets.ProxyRole"] = ["bogus"] self.request["form.widgets.ScriptBody"] = six.text_type(proxied_script) - self.request["form.widgets.IActionExtender.execCondition"] = u"" - self.request["form.buttons.save"] = u"Save" + self.request["form.widgets.IActionExtender.execCondition"] = "" + self.request["form.buttons.save"] = "Save" view = self.ff1.restrictedTraverse("actions") view = view.publishTraverse(view.request, "adapter") view = view.publishTraverse(view.request, "adapter") @@ -362,7 +362,7 @@ def testSetProxyRole(self): form = view.form_instance data, errors = form.extractData() self.assertEqual(len(errors), 1) - self.assertEqual(errors[0].message, u"Required input is missing.") + self.assertEqual(errors[0].message, "Required input is missing.") def testProxyRole(self): """Test seeing how setting proxy role affects unauthorized diff --git a/src/collective/easyform/tests/testDocTests.py b/src/collective/easyform/tests/testDocTests.py index 421aab03..c11b4f54 100644 --- a/src/collective/easyform/tests/testDocTests.py +++ b/src/collective/easyform/tests/testDocTests.py @@ -44,10 +44,10 @@ def get_browser(layer, auth=True): browser = Browser(layer["app"]) browser.handleErrors = False if auth: - browser.open('http://nohost/plone/login_form') - browser.getControl('Login Name').value = SITE_OWNER_NAME - browser.getControl('Password').value = SITE_OWNER_PASSWORD - browser.getControl('Log in').click() + browser.open("http://nohost/plone/login_form") + browser.getControl("Login Name").value = SITE_OWNER_NAME + browser.getControl("Password").value = SITE_OWNER_PASSWORD + browser.getControl("Log in").click() return browser diff --git a/src/collective/easyform/tests/testEmbedding.py b/src/collective/easyform/tests/testEmbedding.py index e6c50a65..de9a73e1 100644 --- a/src/collective/easyform/tests/testEmbedding.py +++ b/src/collective/easyform/tests/testEmbedding.py @@ -48,10 +48,10 @@ def afterSetUp(self): base.EasyFormTestCase.afterSetUp(self) self.folder.invokeFactory("EasyForm", "ff1") self.ff1 = getattr(self.folder, "ff1") - self.ff1.title = u"ff1" + self.ff1.title = "ff1" self.ff1.CSRFProtection = False # no csrf protection actions = get_actions(self.ff1) - actions["mailer"].recipient_email = u"mdummy@address.com" + actions["mailer"].recipient_email = "mdummy@address.com" set_actions(self.ff1, actions) self.mailhost = self.folder.MailHost self.mailhost._send = self.dummy_send @@ -77,7 +77,7 @@ def test_embedded_form_renders(self): def test_embedded_form_validates(self): # fake an incomplete form submission - self.LoadRequestForm(**{"mypfg.buttons.submit": u"Submit"}) + self.LoadRequestForm(**{"mypfg.buttons.submit": "Submit"}) # render the form view = self.ff1.restrictedTraverse("@@embedded") @@ -90,7 +90,7 @@ def test_embedded_form_validates(self): def test_doesnt_process_submission_of_other_form(self): # fake submission of a *different* form (note mismatch of form # submission marker with prefix) - self.LoadRequestForm(**{"form.buttons.submit": u"Submit"}) + self.LoadRequestForm(**{"form.buttons.submit": "Submit"}) # let's preset a faux controller_state (as if from the other form) # to make sure it doesn't throw things off @@ -125,10 +125,10 @@ def test_render_thank_you_on_success(self): self.LoadRequestForm( **{ - "form.widgets.topic": u"monkeys", - "form.widgets.comments": u"I am not a walnut.", - "form.widgets.replyto": u"foobar@example.com", - "form.buttons.submit": u"Submit", + "form.widgets.topic": "monkeys", + "form.widgets.comments": "I am not a walnut.", + "form.widgets.replyto": "foobar@example.com", + "form.buttons.submit": "Submit", } ) # should raise a retry exception triggering a new publish attempt diff --git a/src/collective/easyform/tests/testLikert.py b/src/collective/easyform/tests/testLikert.py index 210e3e91..ef38a999 100644 --- a/src/collective/easyform/tests/testLikert.py +++ b/src/collective/easyform/tests/testLikert.py @@ -1,13 +1,13 @@ -import unittest -import six - from collective.easyform.tests.base import EasyFormTestCase +import six +import unittest -class LikertFieldTests(unittest.TestCase): +class LikertFieldTests(unittest.TestCase): def _getTargetClass(self): from collective.easyform.fields import Likert + return Likert def _makeOne(self, *args, **kw): @@ -20,80 +20,88 @@ def test_ctor_defaults(self): def test_validate_not_required(self): field = self._makeOne(required=False) field.validate(None) - field.validate(u'') + field.validate("") def test_validate_with_answers(self): - field = self._makeOne(required=False, questions=[u'Question 1'], answers=[u'Agree', u'Disagree']) + field = self._makeOne( + required=False, questions=["Question 1"], answers=["Agree", "Disagree"] + ) field.validate(None) - field.validate(u'') - field.validate(u'1: Agree') - self.assertRaises(ValueError, field.validate, u'1:agree') - self.assertRaises(ValueError, field.validate, u'-1:agree') - self.assertRaises(ValueError, field.validate, u'Agree') + field.validate("") + field.validate("1: Agree") + self.assertRaises(ValueError, field.validate, "1:agree") + self.assertRaises(ValueError, field.validate, "-1:agree") + self.assertRaises(ValueError, field.validate, "Agree") def test_validate_with_more_answers(self): - field = self._makeOne(required=False, questions=[u'Question 1', u'Question 2'], answers=[u'Agree', u'Disagree']) + field = self._makeOne( + required=False, + questions=["Question 1", "Question 2"], + answers=["Agree", "Disagree"], + ) field.validate(None) - field.validate(u'') - field.validate(u'1: Agree') - field.validate(u'2: Agree') - field.validate(u'1: Disagree, 2: Agree') - self.assertRaises(ValueError, field.validate, u'1:agree') - self.assertRaises(ValueError, field.validate, u'-1:agree') - self.assertRaises(ValueError, field.validate, u'Agree') + field.validate("") + field.validate("1: Agree") + field.validate("2: Agree") + field.validate("1: Disagree, 2: Agree") + self.assertRaises(ValueError, field.validate, "1:agree") + self.assertRaises(ValueError, field.validate, "-1:agree") + self.assertRaises(ValueError, field.validate, "Agree") def test_parse(self): - field = self._makeOne(required=False, questions=[u'Question 1', u'Question 2'], answers=[u'Agree', u'Disagree']) + field = self._makeOne( + required=False, + questions=["Question 1", "Question 2"], + answers=["Agree", "Disagree"], + ) field.validate(None) - self.assertEquals(dict(), field.parse(u'')) - self.assertEquals({1: u'Agree'}, field.parse(u'1: Agree')) - self.assertEquals({2: u'Agree'}, field.parse(u'2: Agree')) + self.assertEquals(dict(), field.parse("")) + self.assertEquals({1: "Agree"}, field.parse("1: Agree")) + self.assertEquals({2: "Agree"}, field.parse("2: Agree")) self.assertEquals( - {1: u'Disagree', 2: u'Agree'}, - field.parse(u'1: Disagree, 2: Agree') + {1: "Disagree", 2: "Agree"}, field.parse("1: Disagree, 2: Agree") ) class LikerWidgetTests(EasyFormTestCase): - def test_dummy(self): self.folder.invokeFactory("EasyForm", "ff1") ff1 = getattr(self.folder, "ff1") - self.assertEqual(ff1.portal_type, u'EasyForm') + self.assertEqual(ff1.portal_type, "EasyForm") - from zope.interface import Interface - from zope.schema import getFieldsInOrder - from collective.easyform.api import set_fields - from collective.easyform.api import set_actions - from collective.easyform.api import get_schema from collective.easyform.actions import SaveData + from collective.easyform.api import get_schema + from collective.easyform.api import set_actions + from collective.easyform.api import set_fields from collective.easyform.fields import Likert + from zope.interface import Interface + from zope.schema import getFieldsInOrder class IWithLikert(Interface): - likert = Likert(questions=[u"Q1", u"Q2"], answers=[u"Agree", u"Disagree"]) + likert = Likert(questions=["Q1", "Q2"], answers=["Agree", "Disagree"]) set_fields(ff1, IWithLikert) schema = get_schema(ff1) fields = getFieldsInOrder(schema) self.assertEqual(len(fields), 1) - self.assertEqual(fields[0][0], 'likert') + self.assertEqual(fields[0][0], "likert") self.assertTrue(isinstance(fields[0][1], Likert)) # check that LikertWidget is used # and that questions and answers are rendered in a table - view = ff1.restrictedTraverse('view') + view = ff1.restrictedTraverse("view") rendered = view() - self.assertTrue(u"likert-widget" in rendered) - self.assertTrue(u"Q1" in rendered) - self.assertTrue(u"Q2" in rendered) - self.assertTrue(u"Agree" in rendered) - self.assertTrue(u"Disagree" in rendered) + self.assertTrue("likert-widget" in rendered) + self.assertTrue("Q1" in rendered) + self.assertTrue("Q2" in rendered) + self.assertTrue("Agree" in rendered) + self.assertTrue("Disagree" in rendered) class IWithSaver(Interface): saver = SaveData(showFields=[]) - IWithSaver.setTaggedValue('context', ff1) + IWithSaver.setTaggedValue("context", ff1) set_actions(ff1, IWithSaver) # submit data to test value extraction from widget @@ -103,7 +111,7 @@ class IWithSaver(Interface): "form.widgets.likert.0": "Agree", "form.widgets.likert.1": "Disagree", } - view.request.method = 'POST' + view.request.method = "POST" rendered = view() - self.assertTrue(u"Thank You" in rendered) - self.assertTrue(u"1: Agree, 2: Disagree" in rendered) + self.assertTrue("Thank You" in rendered) + self.assertTrue("1: Agree, 2: Disagree" in rendered) diff --git a/src/collective/easyform/tests/testMailer.py b/src/collective/easyform/tests/testMailer.py index 940cff66..3dc13d81 100644 --- a/src/collective/easyform/tests/testMailer.py +++ b/src/collective/easyform/tests/testMailer.py @@ -34,6 +34,7 @@ try: from openpyxl import load_workbook + HAS_OPENPYXL = True except ImportError: HAS_OPENPYXL = False @@ -115,6 +116,7 @@ """ + class TestFunctions(base.EasyFormTestCase): """Test mailer action""" @@ -136,7 +138,7 @@ def afterSetUp(self): self.mailhost = self.folder.MailHost self.mailhost._send = self.dummy_send actions = get_actions(self.ff1) - actions["mailer"].recipient_email = u"mdummy@address.com" + actions["mailer"].recipient_email = "mdummy@address.com" set_actions(self.ff1, actions) def LoadRequestForm(self, **kwargs): @@ -164,7 +166,11 @@ def test_Mailer_Basic(self): mailer = get_actions(self.ff1)["mailer"] - data = {"topic": "test subject", "replyto": "foo@spam.com", "comments": "test comments"} + data = { + "topic": "test subject", + "replyto": "foo@spam.com", + "comments": "test comments", + } request = self.LoadRequestForm(**data) mailer.onSuccess(data, request) @@ -179,7 +185,11 @@ def test_MailerAdditionalHeaders(self): mailer = get_actions(self.ff1)["mailer"] - data = {"topic": "test subject", "replyto": "foo@spam.com", "comments": "test comments"} + data = { + "topic": "test subject", + "replyto": "foo@spam.com", + "comments": "test comments", + } request = self.LoadRequestForm(**data) mailer.additional_headers = ["Generator: Plone", "Token: abc "] @@ -203,7 +213,11 @@ def test_MailerLongSubject(self): mailer = get_actions(self.ff1)["mailer"] - data = {"topic": long_subject, "replyto": "foo@spam.com", "comments": "test comments"} + data = { + "topic": long_subject, + "replyto": "foo@spam.com", + "comments": "test comments", + } request = self.LoadRequestForm(**data) mailer.onSuccess(data, request) @@ -292,7 +306,7 @@ def test_TemplateReplacement(self): def test_UTF8Subject(self): """Test mailer with uft-8 encoded subject line""" - utf8_subject = u"Effacer les entrées sauvegardées" + utf8_subject = "Effacer les entrées sauvegardées" data = dict( topic=utf8_subject, replyto="test@test.org", comments="test comments" ) @@ -310,7 +324,7 @@ def test_UTF8Subject(self): def test_UnicodeSubject(self): """Test mailer with Unicode encoded subject line""" - utf8_subject = u"Effacer les entrées sauvegardées" + utf8_subject = "Effacer les entrées sauvegardées" unicode_subject = utf8_subject data = dict( topic=unicode_subject, replyto="test@test.org", comments="test comments" @@ -328,7 +342,7 @@ def test_UnicodeSubject(self): def test_Utf8ListSubject(self): """Test mailer with Unicode encoded subject line""" - utf8_subject_list = [u"Effacer les entrées", u"sauvegardées"] + utf8_subject_list = ["Effacer les entrées", "sauvegardées"] data = dict( topic=utf8_subject_list, replyto="test@test.org", comments="test comments" ) @@ -350,7 +364,11 @@ def test_MailerOverrides(self): mailer.subjectOverride = "python: '{0} and {1}'.format('eggs', 'spam')" mailer.senderOverride = "string: spam@eggs.com" mailer.recipientOverride = "string: eggs@spam.com" - data = {"topic": "test subject", "replyto": "foo@spam.com", "comments": "test comments"} + data = { + "topic": "test subject", + "replyto": "foo@spam.com", + "comments": "test comments", + } request = self.LoadRequestForm(**data) mailer.onSuccess(data, request) @@ -362,7 +380,11 @@ def test_MailerOverridesWithFieldValues(self): mailer = get_actions(self.ff1)["mailer"] mailer.subjectOverride = "fields/topic" mailer.recipientOverride = "fields/replyto" - data = {"topic": "eggs and spam", "replyto": "test@test.ts", "comments": "test comments"} + data = { + "topic": "eggs and spam", + "replyto": "test@test.ts", + "comments": "test comments", + } request = self.LoadRequestForm(**data) mailer.onSuccess(data, request) @@ -375,7 +397,11 @@ def testMultiRecipientOverrideByString(self): mailer = get_actions(self.ff1)["mailer"] mailer.recipientOverride = "string: eggs@spam.com, spam@spam.com" - data = {"topic": "test subject", "replyto": "foo@spam.com", "comments": "cool stuff"} + data = { + "topic": "test subject", + "replyto": "foo@spam.com", + "comments": "cool stuff", + } request = self.LoadRequestForm(**data) mailer.onSuccess(data, request) @@ -387,7 +413,11 @@ def testMultiRecipientOverrideByTuple(self): mailer = get_actions(self.ff1)["mailer"] mailer.recipientOverride = "python: ('eggs@spam.com', 'spam.spam.com')" - data = {"topic": "test subject", "replyto": "foo@spam.com", "comments": "cool stuff"} + data = { + "topic": "test subject", + "replyto": "foo@spam.com", + "comments": "cool stuff", + } request = self.LoadRequestForm(**data) mailer.onSuccess(data, request) @@ -400,7 +430,11 @@ def testRecipientFromRequest(self): mailer.to_field = "replyto" mailer.replyto_field = None - fields = {"topic": "test subject", "replyto": "eggs@spamandeggs.com", "comments": "cool stuff"} + fields = { + "topic": "test subject", + "replyto": "eggs@spamandeggs.com", + "comments": "cool stuff", + } request = self.LoadRequestForm(**fields) mailer.onSuccess(fields, request) @@ -574,7 +608,7 @@ def testNoRecipient(self): for easyforms. It will not take the default site's recipient. """ mailer = get_actions(self.ff1)["mailer"] - mailer.recipient_email = u"" + mailer.recipient_email = "" mailer.to_field = None mailer.replyto_field = None fields = dict( @@ -613,7 +647,7 @@ def test_MailerXMLAttachments(self): replyto="test@test.org", topic="test subject", richtext="Raw", - comments=u"test comments😀", + comments="test comments😀", datetime="2019-04-01T00:00:00", date="2019-04-02", delta=datetime.timedelta(1), @@ -630,7 +664,7 @@ def test_MailerXMLAttachments(self): attachments = mailer.get_attachments(fields, request) self.assertEqual(1, len(attachments)) self.assertIn( - u"Content-Type: application/xml\nMIME-Version: 1.0\nContent-Transfer-Encoding: base64\nContent-Disposition: attachment", + "Content-Type: application/xml\nMIME-Version: 1.0\nContent-Transfer-Encoding: base64\nContent-Disposition: attachment", mailer.get_mail_text(fields, request, context), ) name, mime, enc, xml = attachments[0] @@ -669,7 +703,7 @@ def test_MailerCSVAttachments(self): replyto="test@test.org", topic="test subject", richtext="Raw", - comments=u"test comments😀", + comments="test comments😀", datetime="2019-04-01T00:00:00", date="2019-04-02", delta=datetime.timedelta(1), @@ -687,7 +721,7 @@ def test_MailerCSVAttachments(self): attachments = mailer.get_attachments(fields, request) self.assertEqual(1, len(attachments)) self.assertIn( - u"Content-Type: application/csv\nMIME-Version: 1.0\nContent-Transfer-Encoding: base64\nContent-Disposition: attachment", + "Content-Type: application/csv\nMIME-Version: 1.0\nContent-Transfer-Encoding: base64\nContent-Disposition: attachment", mailer.get_mail_text(fields, request, context), ) name, mime, enc, csv = attachments[0] @@ -715,7 +749,7 @@ def test_MailerCSVAttachments(self): @unittest.skipUnless(HAS_OPENPYXL, "Requires openpyxl") def test_MailerXLSXAttachments(self): - """ Test mailer with dummy_send """ + """Test mailer with dummy_send""" mailer = get_actions(self.ff1)["mailer"] mailer.sendXML = False mailer.sendCSV = False @@ -727,7 +761,7 @@ def test_MailerXLSXAttachments(self): replyto="test@test.org", topic="test subject", richtext="Raw", - comments=u"test comments😀", + comments="test comments😀", datetime="2019-04-01T00:00:00", date="2019-04-02", delta=datetime.timedelta(1), @@ -745,7 +779,7 @@ def test_MailerXLSXAttachments(self): attachments = mailer.get_attachments(fields, request) self.assertEqual(1, len(attachments)) self.assertIn( - u"Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\nMIME-Version: 1.0\nContent-Transfer-Encoding: base64\nContent-Disposition: attachment", + "Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\nMIME-Version: 1.0\nContent-Transfer-Encoding: base64\nContent-Disposition: attachment", mailer.get_mail_text(fields, request, context), ) name, mime, enc, xlsx = attachments[0] @@ -753,7 +787,10 @@ def test_MailerXLSXAttachments(self): wb.active ws = wb.active - row = [cell.value and cell.value.encode('utf-8') or b'' for cell in list(ws.rows)[0]] + row = [ + cell.value and cell.value.encode("utf-8") or b"" + for cell in list(ws.rows)[0] + ] output = [ b"test@test.org", diff --git a/src/collective/easyform/tests/testMisc.py b/src/collective/easyform/tests/testMisc.py index 930b599f..6764775d 100644 --- a/src/collective/easyform/tests/testMisc.py +++ b/src/collective/easyform/tests/testMisc.py @@ -91,7 +91,7 @@ def test_is_sub_easyform_root(self): class TestCustomTemplates(base.EasyFormTestCase): - no_schema = u""" + no_schema = """ diff --git a/src/collective/easyform/tests/testSavedDataSerializer.py b/src/collective/easyform/tests/testSavedDataSerializer.py index c38af6fa..f4a213e2 100644 --- a/src/collective/easyform/tests/testSavedDataSerializer.py +++ b/src/collective/easyform/tests/testSavedDataSerializer.py @@ -1,15 +1,21 @@ # -*- coding: utf-8 -*- -from datetime import datetime, date -from plone.app.textfield import RichText, RichTextValue -from .testSaver import BaseSaveData, FakeRequest -from collective.easyform.api import get_actions, set_fields -from plone.restapi.interfaces import ISerializeToJson +from .testSaver import BaseSaveData +from .testSaver import FakeRequest +from collective.easyform.api import get_actions +from collective.easyform.api import set_fields +from datetime import date +from datetime import datetime +from plone import schema +from plone.app.textfield import RichText +from plone.app.textfield import RichTextValue from plone.restapi.interfaces import IDeserializeFromJson +from plone.restapi.interfaces import ISerializeToJson +from plone.supermodel import model from zope.component import getMultiAdapter -import json from zope.interface import Interface -from plone.supermodel import model -from plone import schema + +import json + class ISpecialConverters(model.Schema, Interface): @@ -17,10 +23,9 @@ class ISpecialConverters(model.Schema, Interface): date = schema.Date() set = schema.Set() rich = RichText() - -class SavedDataSerializerTestCase(BaseSaveData): +class SavedDataSerializerTestCase(BaseSaveData): def testDefaultFormDataSerialization(self): self.request = self.layer["request"] self.createSaver() @@ -57,7 +62,7 @@ def testSpecialConverterFormDataSerialization(self): datetime=datetime(1999, 12, 25, 19, 0, 0), date=date(2004, 11, 30), set=set(["please", "kill", "me"]), - rich=RichTextValue(raw=u"
testing is fùn says Michaël
"), + rich=RichTextValue(raw="
testing is fùn says Michaël
"), ) saver.onSuccess(request.form, request) @@ -70,7 +75,9 @@ def testSpecialConverterFormDataSerialization(self): self.assertIn("please", json.dumps(obj)) self.assertIn("kill", json.dumps(obj)) self.assertIn("me", json.dumps(obj)) - self.assertIn(u"
testing is f\\u00f9n says Micha\\u00ebl
", json.dumps(obj)) + self.assertIn( + "
testing is f\\u00f9n says Micha\\u00ebl
", json.dumps(obj) + ) def serialize(self, obj=None): if obj is None: @@ -80,81 +87,85 @@ def serialize(self, obj=None): class SavedDataDeserializerTestCase(BaseSaveData): - def testDefaultFormDataDeserialization(self): - BODY = ( '{"actions_model": "\\n \\n \\n ' - 'Saver\\n \\n \\n",' - '"savedDataStorage": ' - '{"saver": {"1658237759974": {"topic": "test subject", "replyto": ' - '"test@test.org", "comments": "test comments", "id": 1658237759974}}}}') + BODY = ( + '{"actions_model": "\\n \\n \\n ' + 'Saver\\n \\n \\n",' + '"savedDataStorage": ' + '{"saver": {"1658237759974": {"topic": "test subject", "replyto": ' + '"test@test.org", "comments": "test comments", "id": 1658237759974}}}}' + ) self.deserialize(body=BODY, context=self.ff1) saver = get_actions(self.ff1)["saver"] data = saver.getSavedFormInput()[0] self.assertIn("topic", data) - self.assertEqual("test subject", data['topic']) + self.assertEqual("test subject", data["topic"]) self.assertIn("replyto", data) - self.assertEqual("test@test.org", data['replyto']) + self.assertEqual("test@test.org", data["replyto"]) self.assertIn("comments", data) - self.assertEqual("test comments", data['comments']) + self.assertEqual("test comments", data["comments"]) def testSpecialConvertersFormDataDeserialization(self): - BODY = ( '{"fields_model": "\\n \\n \\n \\n \\n \\n \\n", ' - '"actions_model": "\\n \\n \\n ' - 'Saver\\n \\n \\n",' - '"savedDataStorage": ' - '{"saver": {"1658240583228": {"datetime": "1999-12-25T19:00:00", "date": ' - '"2004-11-30", "set": ["please", "me", "kill"], "rich": "
testing is ' - 'f\\u00f9n says Micha\\u00ebl
", "id": 1658240583228}}}}') + BODY = ( + '{"fields_model": "\\n \\n \\n \\n \\n \\n \\n", ' + '"actions_model": "\\n \\n \\n ' + 'Saver\\n \\n \\n",' + '"savedDataStorage": ' + '{"saver": {"1658240583228": {"datetime": "1999-12-25T19:00:00", "date": ' + '"2004-11-30", "set": ["please", "me", "kill"], "rich": "
testing is ' + 'f\\u00f9n says Micha\\u00ebl
", "id": 1658240583228}}}}' + ) self.deserialize(body=BODY, context=self.ff1) saver = get_actions(self.ff1)["saver"] data = saver.getSavedFormInput()[0] self.assertIn("datetime", data) - self.assertEqual(datetime(1999, 12, 25, 19, 0, 0), data['datetime']) + self.assertEqual(datetime(1999, 12, 25, 19, 0, 0), data["datetime"]) self.assertIn("date", data) - self.assertEqual(datetime(2004, 11, 30), data['date']) + self.assertEqual(datetime(2004, 11, 30), data["date"]) self.assertIn("set", data) - setdata = list(data['set']) + setdata = list(data["set"]) setdata.sort() self.assertEqual(["kill", "me", "please"], setdata) self.assertIn("rich", data) - self.assertEqual(u"
testing is fùn says Michaël
", data['rich'].raw) + self.assertEqual( + "
testing is fùn says Michaël
", data["rich"].raw + ) - def deserialize(self, body="{}", validate_all=False, context=None, create=False): self.request = self.layer["request"] self.request["BODY"] = body deserializer = getMultiAdapter((context, self.request), IDeserializeFromJson) - return deserializer(validate_all=validate_all, create=create) \ No newline at end of file + return deserializer(validate_all=validate_all, create=create) diff --git a/src/collective/easyform/tests/testSaver.py b/src/collective/easyform/tests/testSaver.py index 2b9058a2..cff67958 100644 --- a/src/collective/easyform/tests/testSaver.py +++ b/src/collective/easyform/tests/testSaver.py @@ -30,8 +30,10 @@ import transaction import unittest + try: from openpyxl import load_workbook + HAS_OPENPYXL = True except ImportError: HAS_OPENPYXL = False @@ -65,11 +67,11 @@ def setUp(self): def createSaver(self): """Creates FormCustomScript object""" # 1. Create custom script adapter in the form folder - self.portal.REQUEST["form.widgets.title"] = u"Saver" - self.portal.REQUEST["form.widgets.__name__"] = u"saver" - self.portal.REQUEST["form.widgets.description"] = u"" + self.portal.REQUEST["form.widgets.title"] = "Saver" + self.portal.REQUEST["form.widgets.__name__"] = "saver" + self.portal.REQUEST["form.widgets.description"] = "" self.portal.REQUEST["form.widgets.factory"] = ["Save Data"] - self.portal.REQUEST["form.buttons.add"] = u"Add" + self.portal.REQUEST["form.buttons.add"] = "Add" view = self.ff1.restrictedTraverse("actions/@@add-action") view.update() form = view.form_instance @@ -82,14 +84,13 @@ def createSaver(self): class SaveDataTestCase(BaseSaveData): - def testSavedDataView(self): """test saved data view""" self.createSaver() view = self.ff1.restrictedTraverse("saveddata") - self.assertEqual(list(view.items()), [("saver", u"Saver")]) + self.assertEqual(list(view.items()), [("saver", "Saver")]) # as the owner, TEST_USER_ID can still see the saved data setRoles(self.portal, TEST_USER_ID, []) @@ -305,7 +306,9 @@ def testSaverDownloadTSV(self): def testSaverDownloadXLSX(self): """test save data""" - with open(join(dirname(__file__), "fixtures", "fieldset_multiple_choice.xml")) as f: + with open( + join(dirname(__file__), "fixtures", "fieldset_multiple_choice.xml") + ) as f: self.ff1.fields_model = f.read() self.createSaver() @@ -320,7 +323,7 @@ def testSaverDownloadXLSX(self): topic="test subject", replyto="test@test.org", comments="test comments", - multiplechoice=["Red", "Blue"] + multiplechoice=["Red", "Blue"], ) saver.onSuccess(request.form, request) @@ -657,11 +660,11 @@ def setUp(self): def createSaver(self): """Creates FormCustomScript object""" # 1. Create custom script adapter in the form folder - self.portal.REQUEST["form.widgets.title"] = u"Saver" - self.portal.REQUEST["form.widgets.__name__"] = u"saver" - self.portal.REQUEST["form.widgets.description"] = u"" + self.portal.REQUEST["form.widgets.title"] = "Saver" + self.portal.REQUEST["form.widgets.__name__"] = "saver" + self.portal.REQUEST["form.widgets.description"] = "" self.portal.REQUEST["form.widgets.factory"] = ["Save Data"] - self.portal.REQUEST["form.buttons.add"] = u"Add" + self.portal.REQUEST["form.buttons.add"] = "Add" view = self.ff1.restrictedTraverse("actions/@@add-action") view.update() commit() @@ -723,17 +726,13 @@ def test_download_saveddata_csv_delimiter_required(self): self.assertTrue("CSV delimiter is required." in self.browser.contents) def test_download_saveddata_csv_delimiter_from_form(self): - self.browser.open( - self.portal_url + "/test-folder/ff1/@@actions/saver/@@data" - ) + self.browser.open(self.portal_url + "/test-folder/ff1/@@actions/saver/@@data") self.assertTrue("Saved Data" in self.browser.contents) self.browser.getControl(name="form.buttons.download").click() self.assertEqual( self.browser.contents, "Your E-Mail Address,Subject,Comments\r\n" ) - self.browser.open( - self.portal_url + "/test-folder/ff1/@@actions/saver/@@data" - ) + self.browser.open(self.portal_url + "/test-folder/ff1/@@actions/saver/@@data") self.assertTrue("Saved Data" in self.browser.contents) input = self.browser.getControl("CSV delimiter") input.value = ";" @@ -744,10 +743,8 @@ def test_download_saveddata_csv_delimiter_from_form(self): # this test depends on internals of zope.testbrowser controls def test_download_saveddata_suggests_csv_delimiter_defines_maxlength(self): - self.browser.open( - self.portal_url + "/test-folder/ff1/@@actions/saver/@@data" - ) + self.browser.open(self.portal_url + "/test-folder/ff1/@@actions/saver/@@data") self.assertTrue("Saved Data" in self.browser.contents) input = self.browser.getControl("CSV delimiter") self.assertTrue(input._elem.has_attr("maxlength")) - self.assertEqual(input._elem.get("maxlength"), u"1") + self.assertEqual(input._elem.get("maxlength"), "1") diff --git a/src/collective/easyform/tests/testValidators.py b/src/collective/easyform/tests/testValidators.py index 17f5d188..56083430 100644 --- a/src/collective/easyform/tests/testValidators.py +++ b/src/collective/easyform/tests/testValidators.py @@ -3,6 +3,7 @@ from StringIO import StringIO # for Python 2 except ImportError: from io import StringIO # for Python 3 + from collective.easyform import validators from collective.easyform.api import get_schema from collective.easyform.api import set_fields @@ -13,7 +14,6 @@ from os.path import dirname from os.path import join from plone import api - from plone.namedfile.file import NamedFile from plone.namedfile.interfaces import INamed from plone.registry.interfaces import IRegistry @@ -21,22 +21,25 @@ from Products.validation import validation from z3c.form.interfaces import IFormLayer from zope.component import getUtility -from zope.interface.interfaces import ComponentLookupError from zope.i18n import translate from zope.interface import classImplements +from zope.interface.interfaces import ComponentLookupError from ZPublisher.BaseRequest import BaseRequest from ZPublisher.HTTPRequest import FileUpload import unittest + try: from plone.formwidget.hcaptcha.interfaces import IHCaptchaSettings + HAS_HCAPTCHA = True except ImportError: HAS_HCAPTCHA = False try: from plone.formwidget.recaptcha.interfaces import IReCaptchaSettings + HAS_RECAPTCHA = True except ImportError: HAS_RECAPTCHA = False @@ -44,9 +47,9 @@ IFieldValidator = validators.IFieldValidator FORM_DATA = { - "topic": u"test subject", - "replyto": u"test@test.org", - "comments": u"test comments", + "topic": "test subject", + "replyto": "test@test.org", + "comments": "test comments", } @@ -172,8 +175,8 @@ def test_validator_translation(self): self.assertEqual(len(errors), 1) expected_error_message = ( - u"Validierung fehlgeschlagen (isInternationalPhoneNumber): " - u"'testcomments' Ist keine g\xfcltige internationale Telefonnummer" + "Validierung fehlgeschlagen (isInternationalPhoneNumber): " + "'testcomments' Ist keine g\xfcltige internationale Telefonnummer" ) self.assertEqual(errors[0].createMessage(), expected_error_message) @@ -388,20 +391,20 @@ def test_filiesize_smallsize_validation(self): def test_filiesize_bigsize_validation(self): self.assertEqual( translate(self.validate_view(DummyFile(1000000000))), - u"File is bigger than allowed size of 1048576 bytes!", + "File is bigger than allowed size of 1048576 bytes!", ) def test_filiesize_bigsize_custom_validation(self): self.assertEqual( translate(self.validate_view(DummyFile(1025), 1024)), - u"File is bigger than allowed size of 1024 bytes!", + "File is bigger than allowed size of 1024 bytes!", ) def test_forbidden_type_validation_fail(self): validation = self.validate_view( DummyFile(filename="foo.ZIP"), forbidden_types=("zip",) ) - self.assertEqual(translate(validation), u'File type "ZIP" is not allowed!') + self.assertEqual(translate(validation), 'File type "ZIP" is not allowed!') def test_forbidden_type_validation_pass(self): validation = self.validate_view( @@ -413,7 +416,7 @@ def test_allowed_type_validation_fail(self): validation = self.validate_view( DummyFile(filename="foo.ZIP"), allowed_types=("txt",) ) - self.assertEqual(translate(validation), u'File type "ZIP" is not allowed!') + self.assertEqual(translate(validation), 'File type "ZIP" is not allowed!') def test_allowed_type_validation_pass(self): validation = self.validate_view( @@ -425,7 +428,7 @@ def test_allowed_type_no_ext(self): validation = self.validate_view( DummyFile(filename="foo"), allowed_types=("txt",) ) - self.assertEqual(translate(validation), u'File type "" is not allowed!') + self.assertEqual(translate(validation), 'File type "" is not allowed!') @unittest.skipUnless(HAS_RECAPTCHA, "Requires plone.formwidget.recaptcha") @@ -441,10 +444,9 @@ def afterSetUp(self): # Put some dummy values for recaptcha registry = getUtility(IRegistry) - proxy = registry.forInterface(IReCaptchaSettings) - proxy.public_key = u"foo" - proxy.private_key = u"bar" + proxy.public_key = "foo" + proxy.private_key = "bar" def test_no_answer(self): data = {"verification": ""} @@ -473,8 +475,8 @@ class TestFieldsetRecaptchaValidator(TestSingleRecaptchaValidator): class TestSingleHcaptchaValidator(LoadFixtureBase): """Can't test captcha passes but we can test it fails - Copy/paste test from Recaptcha, same api & add'on - structure + Copy/paste test from Recaptcha, same api & add'on + structure """ schema_fixture = "hcaptcha.xml" @@ -485,15 +487,15 @@ def afterSetUp(self): # Put some dummy values for recaptcha registry = getUtility(IRegistry) proxy = registry.forInterface(IHCaptchaSettings) - proxy.public_key = u"foo" - proxy.private_key = u"bar" + proxy.public_key = "foo" + proxy.private_key = "bar" def test_no_answer(self): data = {"verification": ""} request = self.LoadRequestForm(**data) request.method = "POST" form = EasyFormForm(self.ff1, request)() - self.assertIn("
", form) + self.assertIn('
', form) self.assertNotIn("Thanks for your input.", form) def test_wrong(self): @@ -501,7 +503,7 @@ def test_wrong(self): request = self.LoadRequestForm(**data) request.method = "POST" form = EasyFormForm(self.ff1, request)() - self.assertIn("
", form) + self.assertIn('
', form) self.assertNotIn("Thanks for your input.", form) diff --git a/src/collective/easyform/upgrades/__init__.py b/src/collective/easyform/upgrades/__init__.py index b67b7ef1..4e442f8d 100644 --- a/src/collective/easyform/upgrades/__init__.py +++ b/src/collective/easyform/upgrades/__init__.py @@ -59,15 +59,13 @@ def fix_savedata_persistence_issues(context): from persistent.mapping import PersistentMapping catalog = api.portal.get_tool("portal_catalog") - forms = catalog.unrestrictedSearchResults(portal_type='EasyForm') + forms = catalog.unrestrictedSearchResults(portal_type="EasyForm") for item in forms: form = item.getObject() - if hasattr(form, '_inputStorage'): + if hasattr(form, "_inputStorage"): # Convert to persistent mapping form._inputStorage = PersistentMapping(form._inputStorage) - logger.info( - 'Fixed storage of {}'.format('/'.join(form.getPhysicalPath())) - ) + logger.info("Fixed storage of {}".format("/".join(form.getPhysicalPath()))) def change_saveddata_action_permission(context): @@ -78,7 +76,7 @@ def change_saveddata_action_permission(context): action = category.get("saveddata") if action is None: return - action._setPropValue('permissions', ("collective.easyform: Download Saved Input",)) + action._setPropValue("permissions", ("collective.easyform: Download Saved Input",)) def remove_migrate_all_forms_record(context): diff --git a/src/collective/easyform/validators.py b/src/collective/easyform/validators.py index 8b7722f6..b0ae2ea2 100644 --- a/src/collective/easyform/validators.py +++ b/src/collective/easyform/validators.py @@ -31,7 +31,7 @@ def isCommaSeparatedEmails(value, **kwargs): for v in value.split(","): if not reg_tool.isValidEmail(v.strip()): return _( - u"Must be a valid list of email addresses " u"(separated by commas)." + "Must be a valid list of email addresses " "(separated by commas)." ) @@ -40,12 +40,12 @@ def isChecked(value, **kwargs): (isinstance(value, bool) and value) or (isinstance(value, six.string_types) and value == "1") ): - return _(u"Must be checked.") + return _("Must be checked.") def isUnchecked(value, **kwargs): if not isChecked(value): - return _(u"Must be unchecked.") + return _("Must be unchecked.") def isNotLinkSpam(value, **kwargs): diff --git a/src/collective/easyform/vocabularies.py b/src/collective/easyform/vocabularies.py index 2e1068dd..654c2068 100644 --- a/src/collective/easyform/vocabularies.py +++ b/src/collective/easyform/vocabularies.py @@ -27,47 +27,47 @@ def _make_vocabulary(items): @provider(IVocabularyFactory) def CustomActionsVocabularyFactory(context): - items = [(_(u"Traverse to"), u"traverse_to"), (_(u"Redirect to"), u"redirect_to")] + items = [(_("Traverse to"), "traverse_to"), (_("Redirect to"), "redirect_to")] return _make_vocabulary(items) @provider(IVocabularyFactory) def MimeListVocabularyFactory(context): - items = [(u"HTML", u"html"), (PMF(u"Text"), u"plain")] + items = [("HTML", "html"), (PMF("Text"), "plain")] return _make_vocabulary(items) @provider(IVocabularyFactory) def FormMethodsVocabularyFactory(context): - return SimpleVocabulary.fromValues((u"post", u"get")) + return SimpleVocabulary.fromValues(("post", "get")) @provider(IVocabularyFactory) def XinfoHeadersVocabularyFactory(context): return SimpleVocabulary.fromValues( ( - u"HTTP_X_FORWARDED_FOR", - u"REMOTE_ADDR", - u"PATH_INFO", - u"HTTP_USER_AGENT", - u"HTTP_REFERER", + "HTTP_X_FORWARDED_FOR", + "REMOTE_ADDR", + "PATH_INFO", + "HTTP_USER_AGENT", + "HTTP_REFERER", ) ) @provider(IVocabularyFactory) def ProxyRoleChoicesVocabularyFactory(context): - items = [(u"No proxy role", u"none"), (u"Manager", u"Manager")] + items = [("No proxy role", "none"), ("Manager", "Manager")] return _make_vocabulary(items) @provider(IVocabularyFactory) def ExtraDataDLVocabularyFactory(context): items = [ - (_(u"vocabulary_postingdt_text", default=u"Posting Date/Time"), u"dt"), - (u"HTTP_X_FORWARDED_FOR", u"HTTP_X_FORWARDED_FOR"), - (u"REMOTE_ADDR", u"REMOTE_ADDR"), - (u"HTTP_USER_AGENT", u"HTTP_USER_AGENT"), + (_("vocabulary_postingdt_text", default="Posting Date/Time"), "dt"), + ("HTTP_X_FORWARDED_FOR", "HTTP_X_FORWARDED_FOR"), + ("REMOTE_ADDR", "REMOTE_ADDR"), + ("HTTP_USER_AGENT", "HTTP_USER_AGENT"), ] return _make_vocabulary(items) @@ -75,13 +75,13 @@ def ExtraDataDLVocabularyFactory(context): @provider(IVocabularyFactory) def FormatDLVocabularyFactory(context): items = [ - (_(u"vocabulary_tsv_text", default=u"Tab-Separated Values"), u"tsv"), - (_(u"vocabulary_csv_text", default=u"Comma-Separated Values"), u"csv"), + (_("vocabulary_tsv_text", default="Tab-Separated Values"), "tsv"), + (_("vocabulary_csv_text", default="Comma-Separated Values"), "csv"), ] if HAS_XLSX_SUPPORT: items.append( - (_(u"vocabulary_xlsx_text", default=u"XLSX"), u"xlsx"), + (_("vocabulary_xlsx_text", default="XLSX"), "xlsx"), ) return _make_vocabulary(items) diff --git a/tests-6.0.x.cfg b/tests-6.0.x.cfg deleted file mode 100644 index 3d9a6274..00000000 --- a/tests-6.0.x.cfg +++ /dev/null @@ -1,4 +0,0 @@ -[buildout] -extends = - https://raw.githubusercontent.com/collective/buildout.plonetest/master/test-6.0.x.cfg - base.cfg diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 5c1e490d..00000000 --- a/tox.ini +++ /dev/null @@ -1,15 +0,0 @@ -[tox] -minversion = 3.18 -envlist = - plone60-py{38,39,310} - -[testenv] -# We do not install with pip, but with buildout: -usedevelop = false -skip_install = true -deps = - -r requirements.txt -commands_pre = - plone60: {envbindir}/buildout -Nc {toxinidir}/tests-6.0.x.cfg buildout:directory={envdir} buildout:develop={toxinidir} install test -commands = - {envbindir}/test