From f65d0c4e803b98b138ad76d0e014ae479088a2a5 Mon Sep 17 00:00:00 2001 From: Wesley B <62723358+wesleyboar@users.noreply.github.com> Date: Thu, 14 Jul 2022 19:42:46 -0500 Subject: [PATCH] feat(tup-308): working form plugin (aka tup-230) (#498) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(tup-230): working form plugin * docs(tup-230): clarify form submission failure * feat(tup-230): use jquery version that has ajax The django-forms(-maintained) plugin expects jquery with ajax features. Our CMS was using a slim build of jQuery. The size difference is 17KB, which I consider negligible. * feat(tup-230): make spam protection available * feat(tup-230): e-mail template (when e-mail works) * docs(tup-230): tweak comment in settings * fix(tup-230): comments & semantic html in template * fix(tup-230): revert and make some html changes - less changes form original - sometimes the original html was better for styling * feat(tup-230): style form (no design review yet) Also, no button styling yet. * chore(tup-230): rename css file, add pointer file Pointer file mimics what I do for blog CSS, which is also in source. * chore(settings): tup-308, how to set captcha keys Ref: https://github.com/TACC/Core-Portal-Deployments/commit/48e6e9c * noop(settings): tup-308, add new line * fix(settings): tup-308, use new form css from #500 * fix(settings): tup-308, new form templates by #500 Not a straight clone of #500. I added a little CSS and a small document to avoid cloning one template. See: - default.html.md - django.cms.forms.css * noop(settings): tup-308, form settings from #501 * fix(settings): tup-308, css changes from #501 * fix(settings): tup-308, "*" to "(required)" I use a different solution than #501. In #501, markup was changed to "(require)": https://github.com/TACC/Core-CMS/blob/5f63bdf/taccsite_cms/templates/djangocms_forms/form_template/default.html#L27 But here, I do not edit the markup, so no need to clone template: https://github.com/TACC/Core-CMS/blob/8a46a21/taccsite_cms/templates/djangocms_forms/form_template/default.html.md * fix(css): tup-308, small form style issues - bigger gap between single checkbox and input - add gap between multiple checkbox and input - align multiple checkbox and input - simpler, less specific checkbox label selector - remove undesired label margin* * This style was ineffectual—good—in 3.6, but "fixed"—bad—when ported. * fix(css): tup-308, small form style issues - limit which help text gets margin - remove ineffectual, unnecessary margin removal - bigger gap between single checkbox and input - align single checkbox and input - add gap between multiple checkbox and input - align multiple checkbox and input - remove label margin on last of multiple checkboxes/radios - simpler, less specific checkbox label selector - remove undesired label margin* * This style was ineffectual—good—in 3.6, but "fixed"—bad—when ported. * docs(styles): typo fix for KSS comment * fix(form): tup-308 only working export formats 1. Only let users export with supported formats. 2. Update form version to rev. that supports this. Source: https://github.com/avryhof/djangocms-forms/issues/8 * fix(form): delete unnecessary cloned template * fix(form): tup-308, clean up templates - remove unedited cloned template - update commit in a comment * fix(form): tup-308, move form export settings * fix(form): tup-308, remove unused file I forgot to delete this file earlier. * fix(form): tup-308, remove redundant script The removal happens form a merged PR on the plugin: https://github.com/avryhof/djangocms-forms/pull/12 * chore(core-styles): v0.7.0-beta * fix(taccsite_custom): get main relevant PRs merged * docs(form): no commented settings --- poetry.lock | 33 +++-- pyproject.toml | 2 +- taccsite_cms/_settings/form_plugin.py | 116 +++++++++++++++ taccsite_cms/settings.py | 29 +--- .../djangocms_forms/css/djangocms_forms.css | 3 + .../css/src/_imports/components/c-button.css | 7 + .../_imports/components/django.cms.forms.css | 133 ++++++++++++++++++ .../css/src/_imports/elements/form.css | 68 +++++++++ .../css/src/_imports/settings/color.css | 17 ++- .../css/src/_imports/settings/font.css | 1 + taccsite_cms/static/site_cms/css/src/site.css | 4 + .../templates/assets_site_delayed.html | 2 +- .../captcha/widget_v2_checkbox.custom.html | 19 +++ .../captcha/widget_v2_checkbox.custom.md | 7 + .../form_template/default.html.md | 14 ++ taccsite_cms/urls.py | 2 + taccsite_custom | 2 +- 17 files changed, 425 insertions(+), 34 deletions(-) create mode 100644 taccsite_cms/_settings/form_plugin.py create mode 100644 taccsite_cms/static/djangocms_forms/css/djangocms_forms.css create mode 100644 taccsite_cms/static/site_cms/css/src/_imports/components/c-button.css create mode 100644 taccsite_cms/static/site_cms/css/src/_imports/components/django.cms.forms.css create mode 100644 taccsite_cms/static/site_cms/css/src/_imports/elements/form.css create mode 100644 taccsite_cms/templates/captcha/widget_v2_checkbox.custom.html create mode 100644 taccsite_cms/templates/captcha/widget_v2_checkbox.custom.md create mode 100644 taccsite_cms/templates/djangocms_forms/form_template/default.html.md diff --git a/poetry.lock b/poetry.lock index 234f6bb1a..3a6528b17 100644 --- a/poetry.lock +++ b/poetry.lock @@ -307,6 +307,17 @@ python-versions = "*" [package.dependencies] Django = ">=2.1" +[[package]] +name = "django-recaptcha" +version = "3.0.0" +description = "Django recaptcha form field/widget app." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +django = "*" + [[package]] name = "django-sekizai" version = "2.0.0" @@ -526,8 +537,8 @@ django-filer = ">=1.7" djangocms-attributes-field = ">=1" [[package]] -name = "djangocms-forms" -version = "0.2.5" +name = "djangocms-forms-maintained" +version = "202011091152" description = "The easiest and most flexible Django CMS Form builder w/ ReCaptcha v2 support!" category = "main" optional = false @@ -537,6 +548,7 @@ python-versions = "*" django-appconf = "*" django-cms = ">=3.0" django-ipware = "*" +django-recaptcha = "*" hashids = "*" jsonfield = "*" requests = "*" @@ -826,7 +838,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" parso = ">=0.7.0,<0.8.0" [package.extras] -qa = ["flake8 (==3.7.9)"] +qa = ["flake8 (3.7.9)"] testing = ["Django (<3.1)", "colorama", "docopt", "pytest (>=3.9.0,<5.0.0)"] [[package]] @@ -1105,7 +1117,7 @@ idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} urllib3 = ">=1.21.1,<1.27" [package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] [[package]] @@ -1212,7 +1224,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" [package.extras] brotli = ["brotlipy (>=0.6.0)"] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] [[package]] name = "urlobject" @@ -1277,7 +1289,7 @@ testing = ["jaraco.itertools", "func-timeout"] [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "ede4ded5906287ebc533a39066285a4aa70377909a594c51f34fe0c8f72da0a6" +content-hash = "8515ec924f9be13f59a965c2328565289c11c642a3ed1c79a3a3ad16396f91b7" [metadata.files] aldryn-apphooks-config = [ @@ -1378,6 +1390,10 @@ django-polymorphic = [ {file = "django-polymorphic-3.0.0.tar.gz", hash = "sha256:9d886f19f031d26bb1391c055ed9be06fb226a04a4cec1842b372c58873b3caa"}, {file = "django_polymorphic-3.0.0-py2.py3-none-any.whl", hash = "sha256:73b75eb44ea302bd32820f8661e469509d245ce7f7ff09cd2ad149e5c42034ff"}, ] +django-recaptcha = [ + {file = "django-recaptcha-3.0.0.tar.gz", hash = "sha256:253197051288923cae675d7eff91b619e3775311292a5dbaf27a8a55ffebc670"}, + {file = "django_recaptcha-3.0.0-py3-none-any.whl", hash = "sha256:1aed69fd6ac8fd9e99e52665392ae6748f8b6339ace656fad779fe0c6c915a52"}, +] django-sekizai = [ {file = "django-sekizai-2.0.0.tar.gz", hash = "sha256:e829f09b0d6bf01ee5cde05de1fb3faf2fbc5df66dc4dc280fbaac224ca4336f"}, {file = "django_sekizai-2.0.0-py3-none-any.whl", hash = "sha256:5c5e16845d37ce822fc655ce79ec02715191b3d03330b550997bcb842cf24fdf"}, @@ -1446,8 +1462,9 @@ djangocms-file = [ {file = "djangocms-file-3.0.0.tar.gz", hash = "sha256:61f8df1ee136c0202fa65b9fe87024f3ceece66262ddd0cccfffbe979170ffdd"}, {file = "djangocms_file-3.0.0-py3-none-any.whl", hash = "sha256:6918717355488c764941d56813362e7ab497f3a4cd73e71e2d4c0f09bff79687"}, ] -djangocms-forms = [ - {file = "djangocms-forms-0.2.5.tar.gz", hash = "sha256:83719b7bec66f935fc30698d81541d52cf35a45bef27089cd8899fa9d4d19d36"}, +djangocms-forms-maintained = [ + {file = "djangocms-forms-maintained-202011091152.tar.gz", hash = "sha256:d3d45179e4d536ca2e6cd1a62f5c39e130417b2e4163d1f1510e8d78ac2839d7"}, + {file = "djangocms_forms_maintained-202011091152-py2.py3-none-any.whl", hash = "sha256:973d1df55bb8742718b20ec576dc7d153b9264b1abd824d4e1561caf4a615b0a"}, ] djangocms-googlemap = [ {file = "djangocms-googlemap-2.0.0.tar.gz", hash = "sha256:1e63f2e3adb6278b401881476aa3795560bd4a441e3509310044316bf9c9b867"}, diff --git a/pyproject.toml b/pyproject.toml index ef73f889f..cd69dd148 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,7 @@ djangocms-blog = "1.1.1" djangocms-bootstrap4 = "2.0.0" djangocms-column = "1.11.0" djangocms-file = "3.0.0" -djangocms-forms = "0.2.5" +djangocms-forms-maintained = { git = "https://github.com/avryhof/djangocms-forms", rev = "eaa6415efeefc8a3018450d0ca7b864e0e4b9dd2" } djangocms-googlemap = "2.0.0" djangocms-icon = "2.0.0" djangocms-link = "3.0.0" diff --git a/taccsite_cms/_settings/form_plugin.py b/taccsite_cms/_settings/form_plugin.py new file mode 100644 index 000000000..ef8cb1930 --- /dev/null +++ b/taccsite_cms/_settings/form_plugin.py @@ -0,0 +1,116 @@ +"""Configure djangocms_forms and related apps""" + +from django.utils.translation import gettext_lazy as _ + +######################## +# KNOWN ISSUES +######################## + +# Unable to export to Excel nor YAML +# (No success hiding those formats from editor) +# https://github.com/avryhof/djangocms-forms/issues/8 + +######################## +# DJANGO_RECAPTCHA +# https://github.com/avryhof/django-recaptcha/ +######################## + +# To properly avoid client error about using test keys, +# define RECAPTCHA_..._KEY settings on client-facing servers + +######################## +# DJANGOCMS_FORMS +# https://github.com/avryhof/djangocms-forms/ +######################## + +DJANGOCMS_FORMS_USE_HTML5_REQUIRED = True + +DJANGOCMS_FORMS_SPAM_PROTECTIONS = ( + (0, _('None')), + # (1, _('Honeypot')), # if necessary, learn how to use + (2, _('ReCAPTCHA')), +) +DEFAULT_SPAM_PROTECTION = 0 + +# Improve form legibility and conceal unavailable features +# https://github.com/avryhof/djangocms-forms/blob/97d7c21/djangocms_forms/cms_plugins.py#L69-L121 +DJANGOCMS_FORMS_FIELDSETS = ( + (None, {'fields': ('name', 'form_template',),}), + ( + _('Text'), + { + 'description': _( + 'The Title and Description ' + 'will display above the input fields and Submit button.' + ), + 'fields': ('title', 'description','submit_btn_txt',), + }, + ), + ( + None, + { + 'description': _( + 'You can change the message that appears after someone submits your form. By default, this says "Thank you!"' + ), + 'fields': ('post_submit_msg',), + }, + ), + ( + _('Redirect settings'), + { + 'description': _( + 'Whether and how to redirect the form after submission.' + ), + 'fields': ('success_redirect', ('page_redirect', 'external_redirect'), 'redirect_delay',), + } + ), + ( + _('Submission settings'), + { + 'description': 'Whether to save form data.', + 'fields': ( + 'save_data', + 'spam_protection', + ), + }, + ), + ( + _('Submission e-mail (unavailable feature)'), + { + 'classes': ('collapse',), + 'description': 'Choose storage options to capture form data. You can enter an e-mail address to which to e-mail form submissions.', + 'fields': ( + # Submitting form with 'email_to' defined causes server error + # FAQ: We may need to setup some django e-mail server for these + 'email_to', + 'email_from', + 'email_subject', + 'email_uploaded_files', + ), + }, + ), +) + +DJANGOCMS_FORMS_FORMAT_CHOICES = ( + ("csv", _("CSV")), + ("json", _("JSON")), +) + +######################## +# DJANGO +# https://docs.djangoproject.com/en/2.2/ref/settings/#form-renderer +######################## + +# Allow form template override +# https://github.com/torchbox/django-recaptcha/issues/211#issuecomment-675608391 +FORM_RENDERER = 'django.forms.renderers.TemplatesSetting' + +######################## +# DJANGO CMS +######################## + +_INSTALLED_APPS = [ + 'djangocms_forms', # form plugin for editors + 'django.forms', # support form template override + 'captcha', # support recaptcha for djangocms_forms +] diff --git a/taccsite_cms/settings.py b/taccsite_cms/settings.py index 0cf50a5d0..c3a77fabf 100644 --- a/taccsite_cms/settings.py +++ b/taccsite_cms/settings.py @@ -16,6 +16,11 @@ from django.utils.translation import gettext_lazy as _ +from taccsite_cms._settings.form_plugin import * +from taccsite_cms._settings.form_plugin import ( + _INSTALLED_APPS as form_plugin_INSTALLED_APPS +) + SECRET_KEY = 'CHANGE_ME' def gettext(s): return s @@ -93,16 +98,6 @@ def gettext(s): return s GOOGLE_ANALYTICS_PROPERTY_ID = "UA-123ABC@%$&-#" GOOGLE_ANALYTICS_PRELOAD = True -######################## -# CMS FORMS -######################## - -# Create CMS Forms -# SEE: https://pypi.org/project/djangocms-forms/ -# SEE: https://www.google.com/recaptcha/admin/create -DJANGOCMS_FORMS_RECAPTCHA_PUBLIC_KEY = "" -DJANGOCMS_FORMS_RECAPTCHA_SECRET_KEY = "" - ######################## # ELASTICSEARCH ######################## @@ -352,6 +347,8 @@ def gettext(s): return s 'aldryn_apphooks_config', # search index & django CMS Blog 'test_without_migrations', # run tests faster +] + form_plugin_INSTALLED_APPS + [ + # core TACC CMS # HELP: If this were top of list, would TACC/Core-CMS/pull/169 fix break? 'taccsite_cms', @@ -461,18 +458,6 @@ def get_subdirs_as_module_names(path): DJANGOCMS_AUDIO_ALLOWED_EXTENSIONS = ['mp3', 'ogg', 'wav'] -# Djangocms Forms Settings. -# SEE: https://github.com/mishbahr/djangocms-forms#configuration -DJANGOCMS_FORMS_PLUGIN_MODULE = ('Generic') -DJANGOCMS_FORMS_PLUGIN_NAME = ('Form') - -DJANGOCMS_FORMS_TEMPLATES = ( - ('djangocms_forms/form_template/default.html', ('Default')), -) -DJANGOCMS_FORMS_USE_HTML5_REQUIRED = False - -DJANGOCMS_FORMS_REDIRECT_DELAY = 1 - # Elasticsearch Indexing HAYSTACK_ROUTERS = ['aldryn_search.router.LanguageRouter', ] HAYSTACK_SIGNAL_PROCESSOR = 'taccsite_cms.signal_processor.RealtimeSignalProcessor' diff --git a/taccsite_cms/static/djangocms_forms/css/djangocms_forms.css b/taccsite_cms/static/djangocms_forms/css/djangocms_forms.css new file mode 100644 index 000000000..73a636ac0 --- /dev/null +++ b/taccsite_cms/static/djangocms_forms/css/djangocms_forms.css @@ -0,0 +1,3 @@ +/* SEE: ../../site_cms/css/src/app.djangocms_forms.css */ +/* FAQ: We uses future syntax that must be processed to work in the present; + and the process does not support outputting files to this directory */ diff --git a/taccsite_cms/static/site_cms/css/src/_imports/components/c-button.css b/taccsite_cms/static/site_cms/css/src/_imports/components/c-button.css new file mode 100644 index 000000000..f33d150ff --- /dev/null +++ b/taccsite_cms/static/site_cms/css/src/_imports/components/c-button.css @@ -0,0 +1,7 @@ +@import url("@tacc/core-styles/src/lib/_imports/components/c-button.css"); + +.c-button { + --max-width: unset; + + font-size: 1.6rem; +} diff --git a/taccsite_cms/static/site_cms/css/src/_imports/components/django.cms.forms.css b/taccsite_cms/static/site_cms/css/src/_imports/components/django.cms.forms.css new file mode 100644 index 000000000..49160991d --- /dev/null +++ b/taccsite_cms/static/site_cms/css/src/_imports/components/django.cms.forms.css @@ -0,0 +1,133 @@ +/* +Django CMS Form Components + +These styles are only for forms created by the Django CMS Form plugin. + +Reference: + +- [Django CMS Form Default Template](https://github.com/avryhof/djangocms-forms/blob/master/djangocms_forms/templates/djangocms_forms/form_template/default.html) +- [TACC's Django CMS Form Default Template](https://github.com/TACC/Core-CMS/blob/master/taccsite_cms/templates/djangocms_forms/form_template/default.html) + +Styleguide Components.DjangoCMS.Forms.App +*/ +@import url("@tacc/core-styles/src/lib/_imports/components/c-button.css"); + + + +/* Content */ + +.description { + margin-block: 25px; +} + +.help-text { + font-size: var(--global-font-size--small); + font-style: italic; +} +:not(ul) + .help-text { + margin-top: 0.3em; /* mimic Bootstrap _reboot.css label { margin-bottom } */ +} + + + +/* Field */ + +.field-wrapper { + margin-bottom: 2rem; /* mimic
`margin-bottom` */ +} + +.field-wrapper:not(.checkboxinput) { + display: flex; + flex-direction: column; + align-items: start; /* i.e. prevent stretch */ +} +.field-wrapper:not(.checkboxinput) > .field-errors { + order: 1; /* i.e. move to end */ +} + +.field-wrapper.checkboxinput { + display: inline-grid; + + column-gap: 0.5em; + align-items: center; + grid-template-columns: min-content auto; /* shrink input, let label extend */ + grid-template-areas: + 'error _____' + 'input label' + 'notes notes'; +} +.field-wrapper.checkboxinput .field-errors { grid-area: error; } +.field-wrapper.checkboxinput input { grid-area: input; } +.field-wrapper.checkboxinput label { grid-area: label; } +.field-wrapper.checkboxinput .help-text { grid-area: notes; } +.field-wrapper.checkboxinput label { + margin-bottom: 0; /* overwrite forms.css label */ +} + +ul.radioselect label, +ul.checkboxselectmultiple label { + display: flex; + gap: 0.5em; + align-items: center; +} +ul.radioselect li:last-child label, +ul.checkboxselectmultiple li:last-child label { + margin-bottom: 0; /* overwrite forms.css label */ +} + +.asterisk { + margin-left: 0.5em; + color: var(--global-color-danger--dark); +} +/* To replace markup text "*" with new text "(required)" */ +.asterisk { + width: 0; + display: inline-block; + visibility: hidden; + line-height: 0; +} +.asterisk::after { + content: '(required)'; + visibility: visible; + line-height: initial; +} + + + +/* Errors */ + +.field-errors { + font-size: var(--global-font-size--small); +} +.field-errors ul { + margin-top: 1rem; /* mimic ul margin-bottom */ + margin-bottom: 0; /* overwrite ul margin-bottom */ +} + + + +/* Lists */ + +ul.radioselect, +ul.checkboxselectmultiple { + list-style: none; + + padding-left: 0; +} + + + +/* Layout */ + +.button-wrapper { + margin-top: 35px; +} + + + +/* Button */ + +.button-wrapper button { + @extend .c-button; + @extend .c-button--primary; +} diff --git a/taccsite_cms/static/site_cms/css/src/_imports/elements/form.css b/taccsite_cms/static/site_cms/css/src/_imports/elements/form.css new file mode 100644 index 000000000..290311c84 --- /dev/null +++ b/taccsite_cms/static/site_cms/css/src/_imports/elements/form.css @@ -0,0 +1,68 @@ +/* +Forms + +Elements that create forms which the user can fill out and submit to the Web site or application. + +``` +