From ff71c8b77f0584b94431811dadb3dd8d5285bef6 Mon Sep 17 00:00:00 2001 From: Dan Palmer Date: Mon, 27 Aug 2018 13:30:40 +0100 Subject: [PATCH 001/185] Fix argument requirements on Django 2.1 --- feincms/module/medialibrary/fields.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/feincms/module/medialibrary/fields.py b/feincms/module/medialibrary/fields.py index 71d8073d9..d0cddffe0 100644 --- a/feincms/module/medialibrary/fields.py +++ b/feincms/module/medialibrary/fields.py @@ -83,9 +83,9 @@ class AdminFileWithPreviewWidget(AdminFileWidget): Simple AdminFileWidget, but detects if the file is an image and tries to render a small thumbnail besides the input field. """ - def render(self, name, value, attrs=None): + def render(self, name, value, *args, attrs=None, **kwargs): r = super(AdminFileWithPreviewWidget, self).render( - name, value, attrs=attrs) + name, value, *args, attrs=attrs, **kwargs) if value and getattr(value, 'instance', None): image = admin_thumbnail(value.instance) From 7bef8959cd4d1228cec3640d9f4c9ed61daf1033 Mon Sep 17 00:00:00 2001 From: Dan Palmer Date: Mon, 27 Aug 2018 13:46:53 +0100 Subject: [PATCH 002/185] Fix for Python 2 --- feincms/module/medialibrary/fields.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/feincms/module/medialibrary/fields.py b/feincms/module/medialibrary/fields.py index d0cddffe0..95046060b 100644 --- a/feincms/module/medialibrary/fields.py +++ b/feincms/module/medialibrary/fields.py @@ -83,9 +83,9 @@ class AdminFileWithPreviewWidget(AdminFileWidget): Simple AdminFileWidget, but detects if the file is an image and tries to render a small thumbnail besides the input field. """ - def render(self, name, value, *args, attrs=None, **kwargs): + def render(self, name, value, attrs=None, *args, **kwargs): r = super(AdminFileWithPreviewWidget, self).render( - name, value, *args, attrs=attrs, **kwargs) + name, value, attrs=attrs, *args, **kwargs) if value and getattr(value, 'instance', None): image = admin_thumbnail(value.instance) From 8e74ceb6b64294a6b96084fe7317e006fe9e8094 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Mon, 27 Aug 2018 15:08:54 +0200 Subject: [PATCH 003/185] Make the .add-row selector less specific Refs #673. --- feincms/static/feincms/item_editor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feincms/static/feincms/item_editor.js b/feincms/static/feincms/item_editor.js index 91fc5706c..570898a1f 100644 --- a/feincms/static/feincms/item_editor.js +++ b/feincms/static/feincms/item_editor.js @@ -158,7 +158,7 @@ if (!Array.prototype.indexOf) { // Use Django's built-in inline spawing mechanism (Django 1.2+) // must use django.jQuery since the bound function lives there: django.jQuery('#'+modvar+'_set-group').find( - 'div.add-row > a').triggerHandler('click'); + '.add-row a').triggerHandler('click'); var new_form_count = parseInt($('#id_'+modvar+'_set-TOTAL_FORMS').val(), 10); if(new_form_count > old_form_count){ return $('#'+modvar+'_set-'+(new_form_count-1)); From 5008e4eb3bb4762c0d6c2af57bd1ff7765edfd02 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Mon, 27 Aug 2018 15:11:36 +0200 Subject: [PATCH 004/185] Fix #673: Make content inlines work with Django 2.1 --- feincms/admin/item_editor.py | 7 +++-- .../admin/feincms/content_inline.html | 23 ++++++-------- .../admin/feincms/content_inline_dj20.html | 31 +++++++++++++++++++ 3 files changed, 45 insertions(+), 16 deletions(-) create mode 100644 feincms/templates/admin/feincms/content_inline_dj20.html diff --git a/feincms/admin/item_editor.py b/feincms/admin/item_editor.py index 3666599b5..051265f6e 100644 --- a/feincms/admin/item_editor.py +++ b/feincms/admin/item_editor.py @@ -8,7 +8,7 @@ import logging import warnings -from django import forms +from django import VERSION, forms from django.contrib.admin.options import InlineModelAdmin from django.contrib.admin.utils import unquote from django.contrib.auth import get_permission_codename @@ -46,7 +46,10 @@ class FeinCMSInline(InlineModelAdmin): form = ItemEditorForm extra = 0 fk_name = 'parent' - template = 'admin/feincms/content_inline.html' + if VERSION < (2, 1): + template = 'admin/feincms/content_inline_dj20.html' + else: + template = 'admin/feincms/content_inline.html' # ------------------------------------------------------------------------ diff --git a/feincms/templates/admin/feincms/content_inline.html b/feincms/templates/admin/feincms/content_inline.html index 9dd72347c..10b5839cb 100644 --- a/feincms/templates/admin/feincms/content_inline.html +++ b/feincms/templates/admin/feincms/content_inline.html @@ -1,11 +1,15 @@ -{% load i18n admin_urls admin_static feincms_admin_tags %} -
+{% load i18n admin_urls static feincms_admin_tags %} +
+

{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}

{{ inline_admin_formset.formset.management_form }} {{ inline_admin_formset.formset.non_form_errors }} -{% for inline_admin_form in inline_admin_formset %}
-

{{ inline_admin_formset.opts.verbose_name|capfirst }}: {% if inline_admin_form.original %}{{ inline_admin_form.original }}{% if inline_admin_form.model_admin.show_change_link and inline_admin_form.model_admin.has_registered_model %} {% trans "Change" %}{% endif %} +{% for inline_admin_form in inline_admin_formset %}
+

{{ inline_admin_formset.opts.verbose_name|capfirst }}: {% if inline_admin_form.original %}{{ inline_admin_form.original }}{% if inline_admin_form.model_admin.show_change_link and inline_admin_form.model_admin.has_registered_model %} {% if inline_admin_formset.has_change_permission %}{% trans "Change" %}{% else %}{% trans "View" %}{% endif %}{% endif %} {% else %}#{{ forloop.counter }}{% endif %} {% if inline_admin_form.show_url %}{% trans "View on site" %}{% endif %} {% if inline_admin_formset.formset.can_delete and inline_admin_form.original %}{{ inline_admin_form.deletion_field.field }} {{ inline_admin_form.deletion_field.label_tag }}{% endif %} @@ -18,14 +22,5 @@

{{ inline_admin_formset.opts.verbose_name|capfirst }}: {% endfor %} +

- - diff --git a/feincms/templates/admin/feincms/content_inline_dj20.html b/feincms/templates/admin/feincms/content_inline_dj20.html new file mode 100644 index 000000000..9dd72347c --- /dev/null +++ b/feincms/templates/admin/feincms/content_inline_dj20.html @@ -0,0 +1,31 @@ +{% load i18n admin_urls admin_static feincms_admin_tags %} +
+

{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}

+{{ inline_admin_formset.formset.management_form }} +{{ inline_admin_formset.formset.non_form_errors }} + +{% for inline_admin_form in inline_admin_formset %}
+

{{ inline_admin_formset.opts.verbose_name|capfirst }}: {% if inline_admin_form.original %}{{ inline_admin_form.original }}{% if inline_admin_form.model_admin.show_change_link and inline_admin_form.model_admin.has_registered_model %} {% trans "Change" %}{% endif %} +{% else %}#{{ forloop.counter }}{% endif %} + {% if inline_admin_form.show_url %}{% trans "View on site" %}{% endif %} + {% if inline_admin_formset.formset.can_delete and inline_admin_form.original %}{{ inline_admin_form.deletion_field.field }} {{ inline_admin_form.deletion_field.label_tag }}{% endif %} +

+ {% if inline_admin_form.form.non_field_errors %}{{ inline_admin_form.form.non_field_errors }}{% endif %} + {% for fieldset in inline_admin_form %} + {% post_process_fieldsets fieldset %} + {% include "admin/includes/fieldset.html" %} + {% endfor %} + {% if inline_admin_form.needs_explicit_pk_field %}{{ inline_admin_form.pk_field.field }}{% endif %} + {{ inline_admin_form.fk_field.field }} +
{% endfor %} +
+ + From 78ec29ad50740ef14f265663c7090a960f7c2e4f Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Mon, 27 Aug 2018 15:20:41 +0200 Subject: [PATCH 005/185] Mention the fix in the CHANGELOG --- CHANGELOG.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index cd9db6621..fbdf903ec 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -10,6 +10,8 @@ Change log ``FEINCMS_THUMBNAIL_CACHE_TIMEOUT`` instead of the hardcoded value of seven days. - Reverted the deprecation of navigation extension autodiscovery. +- Fixed the item editor JavaScript and HTML to work with Django 2.1's + updated inlines. `v1.14.0`_ (2018-08-16) From 0ef94d6100b4c0a2782401467940f5d4671a0497 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Mon, 27 Aug 2018 15:21:45 +0200 Subject: [PATCH 006/185] Django 2.2's admin app starts checking INSTALLED_APPS it seems --- tests/testapp/settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/testapp/settings.py b/tests/testapp/settings.py index a04015c44..560875262 100644 --- a/tests/testapp/settings.py +++ b/tests/testapp/settings.py @@ -16,6 +16,7 @@ 'django.contrib.auth', 'django.contrib.admin', 'django.contrib.contenttypes', + 'django.contrib.messages', 'django.contrib.sessions', 'django.contrib.sitemaps', 'django.contrib.sites', From d8caa2f9c7a9ffb79d3bbfc88e27e26ab0b31cad Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Mon, 27 Aug 2018 15:28:35 +0200 Subject: [PATCH 007/185] FeinCMS v1.14.2 --- feincms/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feincms/__init__.py b/feincms/__init__.py index 78d50b052..b8bee14f7 100644 --- a/feincms/__init__.py +++ b/feincms/__init__.py @@ -1,6 +1,6 @@ from __future__ import absolute_import, unicode_literals -VERSION = (1, 14, 1) +VERSION = (1, 14, 2) __version__ = '.'.join(map(str, VERSION)) From e915e2c86905482ce13e7fb835f9c765e64164b1 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Tue, 23 Oct 2018 10:24:33 +0200 Subject: [PATCH 008/185] Easy tox test runner --- .gitignore | 4 ++++ tests/cov.sh | 3 --- tox.ini | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) delete mode 100755 tests/cov.sh create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index 45f25c901..5dd000736 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,7 @@ tests/test.zip /tests/.coverage /tests/htmlcov venv +.tox +.coverage +htmlcov +test.zip diff --git a/tests/cov.sh b/tests/cov.sh deleted file mode 100755 index c577a1f65..000000000 --- a/tests/cov.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -venv/bin/coverage run --branch --include="*feincms/feincms*" ./manage.py test testapp -venv/bin/coverage html diff --git a/tox.ini b/tox.ini new file mode 100644 index 000000000..3f96dd417 --- /dev/null +++ b/tox.ini @@ -0,0 +1,39 @@ +[testenv] +basepython = python3 + +[testenv:style] +deps = + flake8 +changedir = {toxinidir} +commands = + flake8 . +skip_install = true + +# [testenv:docs] +# deps = +# Sphinx +# sphinx-rtd-theme +# Django +# django-ckeditor +# django-content-editor +# django-tree-queries +# django-imagefield +# django-versatileimagefield +# html-sanitizer +# requests +# changedir = docs +# commands = make html +# skip_install = true +# whitelist_externals = make + +[testenv:tests] +deps = + Django + django-mptt + Pillow + coverage +changedir = {toxinidir} +skip_install = true +commands = + coverage run tests/manage.py test -v 2 {posargs:testapp} + coverage html From accd7a8246410a797e11ce3f1d0d964ec48cfcb1 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Tue, 23 Oct 2018 10:27:41 +0200 Subject: [PATCH 009/185] Evaluate callables passed to only_language --- CHANGELOG.rst | 2 ++ feincms/translations.py | 4 +++- tests/testapp/tests/test_page.py | 6 ++++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index fbdf903ec..57ecdc338 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -12,6 +12,8 @@ Change log - Reverted the deprecation of navigation extension autodiscovery. - Fixed the item editor JavaScript and HTML to work with Django 2.1's updated inlines. +- Fixed ``TranslatedObjectManager.only_language`` to evaluate callables + before filtering. `v1.14.0`_ (2018-08-16) diff --git a/feincms/translations.py b/feincms/translations.py index 8d860c3fe..eaab23da9 100644 --- a/feincms/translations.py +++ b/feincms/translations.py @@ -169,7 +169,9 @@ def only_language(self, language=short_language_code): Uses the currently active language by default. """ - return self.filter(translations__language_code=language) + return self.filter(translations__language_code=( + language() if callable(language) else language + )) @python_2_unicode_compatible diff --git a/tests/testapp/tests/test_page.py b/tests/testapp/tests/test_page.py index e4d342879..a9c48e0e8 100644 --- a/tests/testapp/tests/test_page.py +++ b/tests/testapp/tests/test_page.py @@ -468,8 +468,10 @@ def test_10_mediafile_and_imagecontent(self): self.assertEqual(MediaFile.objects.only_language('en').count(), 0) self.assertEqual( MediaFile.objects.only_language( - '%s-ha' % short_language_code()).count(), - 1) + lambda: '%s-ha' % short_language_code() + ).count(), + 1, + ) self.assertTrue( '%s-ha' % short_language_code() in mf.available_translations) From f270fb1707cf363e6c71171868cb791441d5e9b6 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Tue, 23 Oct 2018 10:29:22 +0200 Subject: [PATCH 010/185] FeinCMS v1.14.3 --- feincms/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feincms/__init__.py b/feincms/__init__.py index b8bee14f7..64452ceb7 100644 --- a/feincms/__init__.py +++ b/feincms/__init__.py @@ -1,6 +1,6 @@ from __future__ import absolute_import, unicode_literals -VERSION = (1, 14, 2) +VERSION = (1, 14, 3) __version__ = '.'.join(map(str, VERSION)) From 81f690f608311ddabe0baa703b6237db936c25b3 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Fri, 21 Dec 2018 10:11:15 +0100 Subject: [PATCH 011/185] This is still the official location --- feincms/views/decorators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feincms/views/decorators.py b/feincms/views/decorators.py index 365761807..2fab30127 100644 --- a/feincms/views/decorators.py +++ b/feincms/views/decorators.py @@ -6,5 +6,5 @@ from feincms.apps import * warnings.warn( - 'Import ApplicationContent and friends from feincms.apps.', + 'Import ApplicationContent and friends from feincms.content.application.models', DeprecationWarning, stacklevel=2) From 458950f9503a1ac38a441f32bc65552d26dd110f Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Fri, 21 Dec 2018 10:23:48 +0100 Subject: [PATCH 012/185] Allow rendering plugin templates in the context of their parent template --- CHANGELOG.rst | 4 ++++ feincms/models.py | 2 +- feincms/templatetags/feincms_tags.py | 20 ++++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 57ecdc338..0bca657ab 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -14,6 +14,10 @@ Change log updated inlines. - Fixed ``TranslatedObjectManager.only_language`` to evaluate callables before filtering. +- Changed the ``render`` protocol of content types to allow returning a + tuple of ``(ct_template, ct_context)`` which works the same way as + `feincms3's template renderers + `__. `v1.14.0`_ (2018-08-16) diff --git a/feincms/models.py b/feincms/models.py index c99c295cb..0e3d49b2c 100644 --- a/feincms/models.py +++ b/feincms/models.py @@ -2,7 +2,7 @@ This is the core of FeinCMS All models defined here are abstract, which means no tables are created in -the feincms\_ namespace. +the feincms namespace. """ from __future__ import absolute_import, unicode_literals diff --git a/feincms/templatetags/feincms_tags.py b/feincms/templatetags/feincms_tags.py index 3b71011bf..a0de9da09 100644 --- a/feincms/templatetags/feincms_tags.py +++ b/feincms/templatetags/feincms_tags.py @@ -9,6 +9,7 @@ import django from django import template from django.conf import settings +from django.template.engine import Engine from django.utils.safestring import mark_safe from feincms.utils import get_singleton, get_singleton_url @@ -37,6 +38,25 @@ def _render_content(content, **kwargs): r = content.render(**kwargs) + if isinstance(r, (list, tuple)): + # Modeled after feincms3's TemplatePluginRenderer + context = kwargs["context"] + plugin_template, plugin_context = r + + if not hasattr(plugin_template, "render"): # Quacks like a template? + try: + engine = context.template.engine + except AttributeError: + engine = Engine.get_default() + + if isinstance(plugin_template, (list, tuple)): + plugin_template = engine.select_template(plugin_template) + else: + plugin_template = engine.get_template(plugin_template) + + with context.push(plugin_context): + return plugin_template.render(context) + if request is not None: level = getattr(request, 'feincms_render_level', 1) setattr(request, 'feincms_render_level', max(level - 1, 0)) From 15a3ac899da948041436dd456d586008197c12a7 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Fri, 21 Dec 2018 10:28:22 +0100 Subject: [PATCH 013/185] Late import for Django 1.7 compatibility --- feincms/templatetags/feincms_tags.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/feincms/templatetags/feincms_tags.py b/feincms/templatetags/feincms_tags.py index a0de9da09..1450d7ace 100644 --- a/feincms/templatetags/feincms_tags.py +++ b/feincms/templatetags/feincms_tags.py @@ -9,7 +9,6 @@ import django from django import template from django.conf import settings -from django.template.engine import Engine from django.utils.safestring import mark_safe from feincms.utils import get_singleton, get_singleton_url @@ -47,6 +46,11 @@ def _render_content(content, **kwargs): try: engine = context.template.engine except AttributeError: + # This fails hard in Django 1.7 (ImportError). So what. This + # just means that this particular feature isn't available + # there. + from django.template.engine import Engine + engine = Engine.get_default() if isinstance(plugin_template, (list, tuple)): From 435e462d85d1f6ba3f598e97b2110b9f29a17d83 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Fri, 21 Dec 2018 10:31:35 +0100 Subject: [PATCH 014/185] Add migrations so that test models are created _after_ e.g. sites.Site --- setup.cfg | 2 +- tests/testapp/migrations/0001_initial.py | 85 ++++++++++++++++++++++++ tests/testapp/migrations/__init__.py | 0 3 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 tests/testapp/migrations/0001_initial.py create mode 100644 tests/testapp/migrations/__init__.py diff --git a/setup.cfg b/setup.cfg index e0f9c91ef..cc4bd75fa 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [flake8] -exclude=venv,.tox,build,docs +exclude=venv,.tox,build,docs,migrations [wheel] universal = 1 diff --git a/tests/testapp/migrations/0001_initial.py b/tests/testapp/migrations/0001_initial.py new file mode 100644 index 000000000..a851c06a8 --- /dev/null +++ b/tests/testapp/migrations/0001_initial.py @@ -0,0 +1,85 @@ +# Generated by Django 2.1.4 on 2018-12-21 09:29 + +from django.db import migrations, models +import django.db.models.deletion +import feincms.extensions.base + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Category', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=20)), + ('slug', models.SlugField()), + ('lft', models.PositiveIntegerField(db_index=True, editable=False)), + ('rght', models.PositiveIntegerField(db_index=True, editable=False)), + ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)), + ('level', models.PositiveIntegerField(db_index=True, editable=False)), + ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='testapp.Category')), + ], + options={ + 'verbose_name': 'category', + 'verbose_name_plural': 'categories', + 'ordering': ['tree_id', 'lft'], + }, + ), + migrations.CreateModel( + name='CustomContentType', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('region', models.CharField(max_length=255)), + ('ordering', models.IntegerField(default=0, verbose_name='ordering')), + ], + options={ + 'verbose_name': 'custom content type', + 'verbose_name_plural': 'custom content types', + 'db_table': 'testapp_mymodel_customcontenttype', + 'ordering': ['ordering'], + 'permissions': [], + 'abstract': False, + }, + ), + migrations.CreateModel( + name='ExampleCMSBase', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ], + options={ + 'abstract': False, + }, + bases=(models.Model, feincms.extensions.base.ExtensionsMixin), + ), + migrations.CreateModel( + name='ExampleCMSBase2', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ], + options={ + 'abstract': False, + }, + bases=(models.Model, feincms.extensions.base.ExtensionsMixin), + ), + migrations.CreateModel( + name='MyModel', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ], + options={ + 'abstract': False, + }, + bases=(models.Model, feincms.extensions.base.ExtensionsMixin), + ), + migrations.AddField( + model_name='customcontenttype', + name='parent', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='customcontenttype_set', to='testapp.MyModel'), + ), + ] diff --git a/tests/testapp/migrations/__init__.py b/tests/testapp/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb From 93cac884ed88cfc618df5cbe8937b8868275395a Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Fri, 21 Dec 2018 10:54:47 +0100 Subject: [PATCH 015/185] FeinCMS v1.15.0 --- CHANGELOG.rst | 7 ++++++- feincms/__init__.py | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0bca657ab..ebcfd15bc 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,10 @@ Change log `Next version`_ ~~~~~~~~~~~~~~~ + +`v1.15.0`_ (2018-12-21) +~~~~~~~~~~~~~~~~~~~~~~~ + - Actually made use of the timeout specified as ``FEINCMS_THUMBNAIL_CACHE_TIMEOUT`` instead of the hardcoded value of seven days. @@ -52,4 +56,5 @@ Change log .. _v1.14.0: https://github.com/feincms/feincms/compare/v1.13.0...v1.14.0 -.. _Next version: https://github.com/feincms/feincms/compare/v1.14.0...master +.. _v1.15.0: https://github.com/feincms/feincms/compare/v1.14.0...v1.15.0 +.. _Next version: https://github.com/feincms/feincms/compare/v1.15.0...master diff --git a/feincms/__init__.py b/feincms/__init__.py index 64452ceb7..c78c98e30 100644 --- a/feincms/__init__.py +++ b/feincms/__init__.py @@ -1,6 +1,6 @@ from __future__ import absolute_import, unicode_literals -VERSION = (1, 14, 3) +VERSION = (1, 15, 0) __version__ = '.'.join(map(str, VERSION)) From 93662596c0d4ba7c8f278d602283bdf848b1f3b1 Mon Sep 17 00:00:00 2001 From: "Martin J. Laubach" Date: Wed, 16 Jan 2019 11:16:45 +0100 Subject: [PATCH 016/185] Make the rebuild_mptt management command work with Django 1.11+ --- feincms/management/commands/rebuild_mptt.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/feincms/management/commands/rebuild_mptt.py b/feincms/management/commands/rebuild_mptt.py index 5e2b2c2cb..b99291d02 100644 --- a/feincms/management/commands/rebuild_mptt.py +++ b/feincms/management/commands/rebuild_mptt.py @@ -10,16 +10,22 @@ from __future__ import absolute_import, unicode_literals -from django.core.management.base import NoArgsCommand +try: + from django.core.management.base import NoArgsCommand as BaseCommand +except ImportError: + from django.core.management.base import BaseCommand from feincms.module.page.models import Page -class Command(NoArgsCommand): +class Command(BaseCommand): help = ( "Run this manually to rebuild your mptt pointers. Only use in" " emergencies.") def handle_noargs(self, **options): + self.handle(**options) + + def handle(self, **options): self.stdout.write("Rebuilding MPTT pointers for Page") Page._tree_manager.rebuild() From a3d3d4b49b9a46c9eb093f79b65a113aa8e4f9ae Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Fri, 1 Feb 2019 18:19:57 +0100 Subject: [PATCH 017/185] Reformat everything using black --- CHANGELOG.rst | 2 + feincms/__init__.py | 19 +- feincms/_internal.py | 28 +- feincms/admin/__init__.py | 10 +- feincms/admin/filters.py | 66 +- feincms/admin/item_editor.py | 139 +- feincms/admin/tree_editor.py | 295 ++-- feincms/content/application/models.py | 219 +-- feincms/content/contactform/__init__.py | 6 +- feincms/content/contactform/models.py | 52 +- feincms/content/file/models.py | 21 +- feincms/content/filer/models.py | 61 +- feincms/content/image/models.py | 51 +- feincms/content/medialibrary/models.py | 6 +- feincms/content/raw/models.py | 6 +- feincms/content/richtext/models.py | 21 +- feincms/content/section/models.py | 75 +- feincms/content/template/models.py | 20 +- feincms/content/video/models.py | 53 +- feincms/context_processors.py | 4 +- feincms/contrib/fields.py | 11 +- feincms/contrib/preview/urls.py | 3 +- feincms/contrib/preview/views.py | 8 +- feincms/contrib/richtext.py | 16 +- feincms/contrib/tagging.py | 59 +- feincms/default_settings.py | 98 +- feincms/extensions/__init__.py | 13 +- feincms/extensions/base.py | 45 +- feincms/extensions/changedate.py | 23 +- feincms/extensions/ct_tracker.py | 54 +- feincms/extensions/datepublisher.py | 75 +- feincms/extensions/featured.py | 13 +- feincms/extensions/seo.py | 43 +- feincms/extensions/translations.py | 125 +- .../commands/medialibrary_orphans.py | 4 +- .../commands/medialibrary_to_filer.py | 28 +- feincms/management/commands/rebuild_mptt.py | 6 +- feincms/models.py | 317 ++-- feincms/module/extensions/changedate.py | 8 +- feincms/module/extensions/ct_tracker.py | 8 +- feincms/module/extensions/datepublisher.py | 8 +- feincms/module/extensions/featured.py | 8 +- feincms/module/extensions/seo.py | 8 +- feincms/module/extensions/translations.py | 8 +- feincms/module/medialibrary/__init__.py | 2 +- feincms/module/medialibrary/contents.py | 33 +- feincms/module/medialibrary/fields.py | 50 +- feincms/module/medialibrary/forms.py | 43 +- feincms/module/medialibrary/modeladmins.py | 154 +- feincms/module/medialibrary/models.py | 154 +- feincms/module/medialibrary/thumbnail.py | 9 +- feincms/module/medialibrary/zip.py | 99 +- feincms/module/mixins.py | 38 +- feincms/module/page/admin.py | 2 +- feincms/module/page/extensions/excerpt.py | 17 +- feincms/module/page/extensions/navigation.py | 56 +- .../page/extensions/navigationgroups.py | 21 +- .../module/page/extensions/relatedpages.py | 24 +- feincms/module/page/extensions/sites.py | 18 +- feincms/module/page/extensions/symlinks.py | 29 +- feincms/module/page/extensions/titles.py | 48 +- feincms/module/page/forms.py | 155 +- feincms/module/page/modeladmins.py | 173 +- feincms/module/page/models.py | 164 +- feincms/module/page/processors.py | 54 +- feincms/module/page/sitemap.py | 35 +- feincms/shortcuts.py | 2 +- .../templatetags/applicationcontent_tags.py | 41 +- feincms/templatetags/feincms_admin_tags.py | 35 +- feincms/templatetags/feincms_page_tags.py | 142 +- feincms/templatetags/feincms_tags.py | 32 +- feincms/templatetags/feincms_thumbnail.py | 108 +- feincms/templatetags/fragment_tags.py | 25 +- feincms/translations.py | 96 +- feincms/urls.py | 4 +- feincms/utils/__init__.py | 51 +- feincms/utils/queryset_transform.py | 15 +- feincms/utils/templatetags.py | 31 +- feincms/views/__init__.py | 27 +- feincms/views/decorators.py | 6 +- setup.cfg | 5 +- setup.py | 70 +- tests/manage.py | 3 +- tests/testapp/applicationcontent_urls.py | 45 +- tests/testapp/context_processors.py | 2 +- tests/testapp/migrations/0001_initial.py | 130 +- tests/testapp/models.py | 106 +- tests/testapp/navigation_extensions.py | 9 +- tests/testapp/settings.py | 99 +- tests/testapp/tests/test_cms.py | 55 +- tests/testapp/tests/test_extensions.py | 70 +- tests/testapp/tests/test_page.py | 1480 ++++++++--------- tests/testapp/tests/test_stuff.py | 42 +- tests/testapp/tests/utils.py | 2 + tests/testapp/urls.py | 21 +- tox.ini | 2 + 96 files changed, 3296 insertions(+), 2981 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ebcfd15bc..5a54bc50d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,8 @@ Change log `Next version`_ ~~~~~~~~~~~~~~~ +- Reformatted everything using black. + `v1.15.0`_ (2018-12-21) ~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/feincms/__init__.py b/feincms/__init__.py index c78c98e30..308b8f1cd 100644 --- a/feincms/__init__.py +++ b/feincms/__init__.py @@ -1,7 +1,7 @@ from __future__ import absolute_import, unicode_literals VERSION = (1, 15, 0) -__version__ = '.'.join(map(str, VERSION)) +__version__ = ".".join(map(str, VERSION)) class LazySettings(object): @@ -10,7 +10,7 @@ def _load_settings(self): from django.conf import settings as django_settings for key in dir(default_settings): - if not key.startswith('FEINCMS_'): + if not key.startswith("FEINCMS_"): continue value = getattr(default_settings, key) @@ -46,6 +46,7 @@ def ensure_completely_loaded(force=False): return True from django.apps import apps + if not apps.ready: return @@ -61,13 +62,17 @@ def ensure_completely_loaded(force=False): import django from distutils.version import LooseVersion - if LooseVersion(django.get_version()) < LooseVersion('1.8'): + if LooseVersion(django.get_version()) < LooseVersion("1.8"): for model in apps.get_models(): for cache_name in ( - '_field_cache', '_field_name_cache', '_m2m_cache', - '_related_objects_cache', '_related_many_to_many_cache', - '_name_map'): + "_field_cache", + "_field_name_cache", + "_m2m_cache", + "_related_objects_cache", + "_related_many_to_many_cache", + "_name_map", + ): try: delattr(model._meta, cache_name) except AttributeError: @@ -85,7 +90,7 @@ def ensure_completely_loaded(force=False): # get_models cache again here. If we don't do this, Django 1.5 chokes # on a model validation error (Django 1.4 doesn't exhibit this # problem). See Issue #323 on github. - if hasattr(apps, 'cache'): + if hasattr(apps, "cache"): apps.cache.get_models.cache_clear() if apps.ready: diff --git a/feincms/_internal.py b/feincms/_internal.py index 134046da1..44a669748 100644 --- a/feincms/_internal.py +++ b/feincms/_internal.py @@ -11,9 +11,7 @@ from django.template.loader import render_to_string -__all__ = ( - 'monkeypatch_method', 'monkeypatch_property', -) +__all__ = ("monkeypatch_method", "monkeypatch_property") def monkeypatch_method(cls): @@ -28,6 +26,7 @@ def (self, [...]): def decorator(func): setattr(cls, func.__name__, func) return func + return decorator @@ -43,24 +42,23 @@ def (self, [...]): def decorator(func): setattr(cls, func.__name__, property(func)) return func + return decorator -if LooseVersion(get_version()) < LooseVersion('1.10'): +if LooseVersion(get_version()) < LooseVersion("1.10"): + def ct_render_to_string(template, ctx, **kwargs): from django.template import RequestContext - context_instance = kwargs.get('context') - if context_instance is None and kwargs.get('request'): - context_instance = RequestContext(kwargs['request']) + context_instance = kwargs.get("context") + if context_instance is None and kwargs.get("request"): + context_instance = RequestContext(kwargs["request"]) + + return render_to_string(template, ctx, context_instance=context_instance) + - return render_to_string( - template, - ctx, - context_instance=context_instance) else: + def ct_render_to_string(template, ctx, **kwargs): - return render_to_string( - template, - ctx, - request=kwargs.get('request')) + return render_to_string(template, ctx, request=kwargs.get("request")) diff --git a/feincms/admin/__init__.py b/feincms/admin/__init__.py index 0a2edfe1f..9008a9982 100644 --- a/feincms/admin/__init__.py +++ b/feincms/admin/__init__.py @@ -5,10 +5,12 @@ FieldListFilter.register( - lambda f: getattr(f, 'parent_filter', False), + lambda f: getattr(f, "parent_filter", False), ParentFieldListFilter, - take_priority=True) + take_priority=True, +) FieldListFilter.register( - lambda f: getattr(f, 'category_filter', False), + lambda f: getattr(f, "category_filter", False), CategoryFieldListFilter, - take_priority=True) + take_priority=True, +) diff --git a/feincms/admin/filters.py b/feincms/admin/filters.py index cb3187ded..75a1a73c2 100644 --- a/feincms/admin/filters.py +++ b/feincms/admin/filters.py @@ -26,38 +26,44 @@ class ParentFieldListFilter(ChoicesFieldListFilter): my_model_field.page_parent_filter = True """ - def __init__(self, f, request, params, model, model_admin, - field_path=None): + def __init__(self, f, request, params, model, model_admin, field_path=None): super(ParentFieldListFilter, self).__init__( - f, request, params, model, model_admin, field_path) + f, request, params, model, model_admin, field_path + ) - parent_ids = model.objects.exclude(parent=None).values_list( - "parent__id", flat=True).order_by("parent__id").distinct() + parent_ids = ( + model.objects.exclude(parent=None) + .values_list("parent__id", flat=True) + .order_by("parent__id") + .distinct() + ) parents = model.objects.filter(pk__in=parent_ids).values_list( - "pk", "title", "level") - self.lookup_choices = [( - pk, - "%s%s" % ( - "  " * level, - shorten_string(title, max_length=25)), - ) for pk, title, level in parents] + "pk", "title", "level" + ) + self.lookup_choices = [ + ( + pk, + "%s%s" % ("  " * level, shorten_string(title, max_length=25)), + ) + for pk, title, level in parents + ] def choices(self, cl): yield { - 'selected': self.lookup_val is None, - 'query_string': cl.get_query_string({}, [self.lookup_kwarg]), - 'display': _('All') + "selected": self.lookup_val is None, + "query_string": cl.get_query_string({}, [self.lookup_kwarg]), + "display": _("All"), } for pk, title in self.lookup_choices: yield { - 'selected': pk == int(self.lookup_val or '0'), - 'query_string': cl.get_query_string({self.lookup_kwarg: pk}), - 'display': mark_safe(smart_text(title)) + "selected": pk == int(self.lookup_val or "0"), + "query_string": cl.get_query_string({self.lookup_kwarg: pk}), + "display": mark_safe(smart_text(title)), } def title(self): - return _('Parent') + return _("Parent") class CategoryFieldListFilter(ChoicesFieldListFilter): @@ -67,10 +73,10 @@ class CategoryFieldListFilter(ChoicesFieldListFilter): my_model_field.category_filter = True """ - def __init__(self, f, request, params, model, model_admin, - field_path=None): + def __init__(self, f, request, params, model, model_admin, field_path=None): super(CategoryFieldListFilter, self).__init__( - f, request, params, model, model_admin, field_path) + f, request, params, model, model_admin, field_path + ) # Restrict results to categories which are actually in use: if DJANGO_VERSION < (1, 8): @@ -85,7 +91,7 @@ def __init__(self, f, request, params, model, model_admin, self.lookup_choices = sorted( [ - (i.pk, '%s (%s)' % (i, i._related_count)) + (i.pk, "%s (%s)" % (i, i._related_count)) for i in related_model.objects.annotate( _related_count=Count(related_name) ).exclude(_related_count=0) @@ -95,17 +101,17 @@ def __init__(self, f, request, params, model, model_admin, def choices(self, cl): yield { - 'selected': self.lookup_val is None, - 'query_string': cl.get_query_string({}, [self.lookup_kwarg]), - 'display': _('All') + "selected": self.lookup_val is None, + "query_string": cl.get_query_string({}, [self.lookup_kwarg]), + "display": _("All"), } for pk, title in self.lookup_choices: yield { - 'selected': pk == int(self.lookup_val or '0'), - 'query_string': cl.get_query_string({self.lookup_kwarg: pk}), - 'display': mark_safe(smart_text(title)) + "selected": pk == int(self.lookup_val or "0"), + "query_string": cl.get_query_string({self.lookup_kwarg: pk}), + "display": mark_safe(smart_text(title)), } def title(self): - return _('Category') + return _("Category") diff --git a/feincms/admin/item_editor.py b/feincms/admin/item_editor.py index 051265f6e..9e9fc696b 100644 --- a/feincms/admin/item_editor.py +++ b/feincms/admin/item_editor.py @@ -20,8 +20,8 @@ # ------------------------------------------------------------------------ -FEINCMS_CONTENT_FIELDSET_NAME = 'FEINCMS_CONTENT' -FEINCMS_CONTENT_FIELDSET = (FEINCMS_CONTENT_FIELDSET_NAME, {'fields': ()}) +FEINCMS_CONTENT_FIELDSET_NAME = "FEINCMS_CONTENT" +FEINCMS_CONTENT_FIELDSET = (FEINCMS_CONTENT_FIELDSET_NAME, {"fields": ()}) logger = logging.getLogger(__name__) @@ -45,11 +45,11 @@ class FeinCMSInline(InlineModelAdmin): form = ItemEditorForm extra = 0 - fk_name = 'parent' + fk_name = "parent" if VERSION < (2, 1): - template = 'admin/feincms/content_inline_dj20.html' + template = "admin/feincms/content_inline_dj20.html" else: - template = 'admin/feincms/content_inline.html' + template = "admin/feincms/content_inline.html" # ------------------------------------------------------------------------ @@ -70,7 +70,8 @@ def __init__(self, model, admin_site): def get_inline_instances(self, request, *args, **kwargs): inline_instances = super(ItemEditor, self).get_inline_instances( - request, *args, **kwargs) + request, *args, **kwargs + ) self.append_feincms_inlines(inline_instances, request) return inline_instances @@ -83,9 +84,12 @@ def append_feincms_inlines(self, inline_instances, request): inline_instances.append(inline_instance) def can_add_content(self, request, content_type): - perm = '.'.join(( - content_type._meta.app_label, - get_permission_codename('add', content_type._meta))) + perm = ".".join( + ( + content_type._meta.app_label, + get_permission_codename("add", content_type._meta), + ) + ) return request.user.has_perm(perm) def get_feincms_inlines(self, model, request): @@ -97,27 +101,26 @@ def get_feincms_inlines(self, model, request): if not self.can_add_content(request, content_type): continue - attrs = { - '__module__': model.__module__, - 'model': content_type, - } + attrs = {"__module__": model.__module__, "model": content_type} - if hasattr(content_type, 'feincms_item_editor_inline'): + if hasattr(content_type, "feincms_item_editor_inline"): inline = content_type.feincms_item_editor_inline - attrs['form'] = inline.form + attrs["form"] = inline.form - if hasattr(content_type, 'feincms_item_editor_form'): + if hasattr(content_type, "feincms_item_editor_form"): warnings.warn( - 'feincms_item_editor_form on %s is ignored because ' - 'feincms_item_editor_inline is set too' % content_type, - RuntimeWarning) + "feincms_item_editor_form on %s is ignored because " + "feincms_item_editor_inline is set too" % content_type, + RuntimeWarning, + ) else: inline = FeinCMSInline - attrs['form'] = getattr( - content_type, 'feincms_item_editor_form', inline.form) + attrs["form"] = getattr( + content_type, "feincms_item_editor_form", inline.form + ) - name = '%sFeinCMSInline' % content_type.__name__ + name = "%sFeinCMSInline" % content_type.__name__ # TODO: We generate a new class every time. Is that really wanted? inline_class = type(str(name), (inline,), attrs) inlines.append(inline_class) @@ -129,21 +132,19 @@ def get_content_type_map(self, request): for content_type in self.model._feincms_content_types: if self.model == content_type._feincms_content_class: content_name = content_type._meta.verbose_name - content_types.append( - (content_name, content_type.__name__.lower())) + content_types.append((content_name, content_type.__name__.lower())) return content_types def get_extra_context(self, request): """ Return extra context parameters for add/change views. """ extra_context = { - 'request': request, - 'model': self.model, - 'available_templates': getattr( - self.model, '_feincms_templates', ()), - 'has_parent_attribute': hasattr(self.model, 'parent'), - 'content_types': self.get_content_type_map(request), - 'FEINCMS_CONTENT_FIELDSET_NAME': FEINCMS_CONTENT_FIELDSET_NAME, + "request": request, + "model": self.model, + "available_templates": getattr(self.model, "_feincms_templates", ()), + "has_parent_attribute": hasattr(self.model, "parent"), + "content_types": self.get_content_type_map(request), + "FEINCMS_CONTENT_FIELDSET_NAME": FEINCMS_CONTENT_FIELDSET_NAME, } for processor in self.model.feincms_item_editor_context_processors: @@ -154,9 +155,7 @@ def get_extra_context(self, request): def add_view(self, request, **kwargs): if not self.has_add_permission(request): logger.warning( - "Denied adding %s to \"%s\" (no add permission)", - self.model, - request.user + 'Denied adding %s to "%s" (no add permission)', self.model, request.user ) raise Http404 @@ -164,64 +163,60 @@ def add_view(self, request, **kwargs): # insert dummy object as 'original' so template code can grab defaults # for template, etc. - context['original'] = self.model() + context["original"] = self.model() # If there are errors in the form, we need to preserve the object's # template as it was set when the user attempted to save it, so that # the same regions appear on screen. - if request.method == 'POST' and \ - hasattr(self.model, '_feincms_templates'): - context['original'].template_key = request.POST['template_key'] + if request.method == "POST" and hasattr(self.model, "_feincms_templates"): + context["original"].template_key = request.POST["template_key"] context.update(self.get_extra_context(request)) - context.update(kwargs.get('extra_context', {})) - kwargs['extra_context'] = context + context.update(kwargs.get("extra_context", {})) + kwargs["extra_context"] = context return super(ItemEditor, self).add_view(request, **kwargs) def render_change_form(self, request, context, **kwargs): - if kwargs.get('add'): - if request.method == 'GET' and 'adminform' in context: - if 'template_key' in context['adminform'].form.initial: - context['original'].template_key = ( - context['adminform'].form.initial['template_key']) + if kwargs.get("add"): + if request.method == "GET" and "adminform" in context: + if "template_key" in context["adminform"].form.initial: + context["original"].template_key = context[ + "adminform" + ].form.initial["template_key"] # ensure that initially-selected template in form is also # used to render the initial regions in the item editor - return super( - ItemEditor, self).render_change_form(request, context, **kwargs) + return super(ItemEditor, self).render_change_form(request, context, **kwargs) def change_view(self, request, object_id, **kwargs): obj = self.get_object(request, unquote(object_id)) if not self.has_change_permission(request, obj): logger.warning( - "Denied editing %s to \"%s\" (no edit permission)", + 'Denied editing %s to "%s" (no edit permission)', self.model, - request.user + request.user, ) raise Http404 context = {} context.update(self.get_extra_context(request)) - context.update(kwargs.get('extra_context', {})) - kwargs['extra_context'] = context - return super(ItemEditor, self).change_view( - request, object_id, **kwargs) + context.update(kwargs.get("extra_context", {})) + kwargs["extra_context"] = context + return super(ItemEditor, self).change_view(request, object_id, **kwargs) def save_related(self, request, form, formsets, change): - super(ItemEditor, self).save_related( - request, form, formsets, change) + super(ItemEditor, self).save_related(request, form, formsets, change) itemeditor_post_save_related.send( - sender=form.instance.__class__, - instance=form.instance, - created=not change) + sender=form.instance.__class__, instance=form.instance, created=not change + ) @property def change_form_template(self): opts = self.model._meta return [ - 'admin/feincms/%s/%s/item_editor.html' % ( - opts.app_label, opts.object_name.lower()), - 'admin/feincms/%s/item_editor.html' % opts.app_label, - 'admin/feincms/item_editor.html', + "admin/feincms/%s/%s/item_editor.html" + % (opts.app_label, opts.object_name.lower()), + "admin/feincms/%s/item_editor.html" % opts.app_label, + "admin/feincms/item_editor.html", ] def get_fieldsets(self, request, obj=None): @@ -230,9 +225,7 @@ def get_fieldsets(self, request, obj=None): Is it reasonable to assume this should always be included? """ - fieldsets = copy.deepcopy( - super(ItemEditor, self).get_fieldsets(request, obj) - ) + fieldsets = copy.deepcopy(super(ItemEditor, self).get_fieldsets(request, obj)) names = [f[0] for f in fieldsets] if FEINCMS_CONTENT_FIELDSET_NAME not in names: @@ -247,16 +240,20 @@ def get_fieldsets(self, request, obj=None): recover_form_template = "admin/feincms/recover_form.html" # For Reversion < v2.0.0 - def render_revision_form(self, request, obj, version, context, - revert=False, recover=False): + def render_revision_form( + self, request, obj, version, context, revert=False, recover=False + ): context.update(self.get_extra_context(request)) return super(ItemEditor, self).render_revision_form( - request, obj, version, context, revert, recover) + request, obj, version, context, revert, recover + ) # For Reversion >= v2.0.0 - def _reversion_revisionform_view(self, request, version, template_name, - extra_context=None): + def _reversion_revisionform_view( + self, request, version, template_name, extra_context=None + ): context = extra_context or {} context.update(self.get_extra_context(request)) return super(ItemEditor, self)._reversion_revisionform_view( - request, version, template_name, context) + request, version, template_name, context + ) diff --git a/feincms/admin/tree_editor.py b/feincms/admin/tree_editor.py index bff48fa40..2eecb1484 100644 --- a/feincms/admin/tree_editor.py +++ b/feincms/admin/tree_editor.py @@ -14,8 +14,12 @@ from django.contrib.staticfiles.templatetags.staticfiles import static from django.db.models import Q from django.http import ( - HttpResponse, HttpResponseBadRequest, - HttpResponseForbidden, HttpResponseNotFound, HttpResponseServerError) + HttpResponse, + HttpResponseBadRequest, + HttpResponseForbidden, + HttpResponseNotFound, + HttpResponseServerError, +) from django.utils.html import escape from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _, ugettext @@ -38,15 +42,14 @@ def django_boolean_icon(field_val, alt_text=None, title=None): """ # Origin: contrib/admin/templatetags/admin_list.py - BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'} + BOOLEAN_MAPPING = {True: "yes", False: "no", None: "unknown"} alt_text = alt_text or BOOLEAN_MAPPING[field_val] if title is not None: title = 'title="%s" ' % title else: - title = '' - icon_url = static('feincms/img/icon-%s.gif' % BOOLEAN_MAPPING[field_val]) - return mark_safe( - '%s' % (icon_url, alt_text, title)) + title = "" + icon_url = static("feincms/img/icon-%s.gif" % BOOLEAN_MAPPING[field_val]) + return mark_safe('%s' % (icon_url, alt_text, title)) def _build_tree_structure(queryset): @@ -64,23 +67,16 @@ def _build_tree_structure(queryset): all_nodes = {} mptt_opts = queryset.model._mptt_meta - items = queryset.order_by( - mptt_opts.tree_id_attr, - mptt_opts.left_attr, - ).values_list( - "pk", - "%s_id" % mptt_opts.parent_attr, + items = queryset.order_by(mptt_opts.tree_id_attr, mptt_opts.left_attr).values_list( + "pk", "%s_id" % mptt_opts.parent_attr ) for p_id, parent_id in items: - all_nodes.setdefault( - str(parent_id) if parent_id else 0, - [], - ).append(p_id) + all_nodes.setdefault(str(parent_id) if parent_id else 0, []).append(p_id) return all_nodes # ------------------------------------------------------------------------ -def ajax_editable_boolean_cell(item, attr, text='', override=None): +def ajax_editable_boolean_cell(item, attr, text="", override=None): """ Generate a html snippet for showing a boolean value on the admin page. Item is an object, attr is the attribute name we should display. Text @@ -95,7 +91,7 @@ def ajax_editable_boolean_cell(item, attr, text='', override=None): (useful for "disabled and you can't change it" situations). """ if text: - text = ' (%s)' % text + text = " (%s)" % text if override is not None: a = [django_boolean_icon(override, text), text] @@ -103,15 +99,13 @@ def ajax_editable_boolean_cell(item, attr, text='', override=None): value = getattr(item, attr) a = [ '' % ( - item.pk, - attr, - 'checked="checked"' if value else '', - )] + ' data-inplace-attribute="%s" %s>' + % (item.pk, attr, 'checked="checked"' if value else "") + ] a.insert(0, '
' % (attr, item.pk)) - a.append('
') - return mark_safe(''.join(a)) + a.append("
") + return mark_safe("".join(a)) # ------------------------------------------------------------------------ @@ -127,8 +121,10 @@ class MyTreeEditor(TreeEditor): active_toggle = ajax_editable_boolean('active', _('is active')) """ + def _fn(self, item): return ajax_editable_boolean_cell(item, attr) + _fn.short_description = short_description _fn.editable_boolean_field = attr return _fn @@ -147,8 +143,11 @@ def __init__(self, request, *args, **kwargs): def get_queryset(self, *args, **kwargs): mptt_opts = self.model._mptt_meta - qs = super(ChangeList, self).get_queryset(*args, **kwargs).\ - order_by(mptt_opts.tree_id_attr, mptt_opts.left_attr) + qs = ( + super(ChangeList, self) + .get_queryset(*args, **kwargs) + .order_by(mptt_opts.tree_id_attr, mptt_opts.left_attr) + ) # Force has_filters, so that the expand/collapse in sidebar is visible self.has_filters = True return qs @@ -157,14 +156,15 @@ def get_results(self, request): mptt_opts = self.model._mptt_meta if settings.FEINCMS_TREE_EDITOR_INCLUDE_ANCESTORS: clauses = [ - Q(**{ - mptt_opts.tree_id_attr: tree_id, - mptt_opts.left_attr + '__lte': lft, - mptt_opts.right_attr + '__gte': rght, - }) for lft, rght, tree_id in self.queryset.values_list( - mptt_opts.left_attr, - mptt_opts.right_attr, - mptt_opts.tree_id_attr, + Q( + **{ + mptt_opts.tree_id_attr: tree_id, + mptt_opts.left_attr + "__lte": lft, + mptt_opts.right_attr + "__gte": rght, + } + ) + for lft, rght, tree_id in self.queryset.values_list( + mptt_opts.left_attr, mptt_opts.right_attr, mptt_opts.tree_id_attr ) ] # We could optimise a bit here by explicitely filtering out @@ -177,7 +177,7 @@ def get_results(self, request): # clauses if the initial query set is unfiltered. This # is good. self.queryset |= self.model._default_manager.filter( - reduce(lambda p, q: p | q, clauses), + reduce(lambda p, q: p | q, clauses) ) super(ChangeList, self).get_results(request) @@ -186,11 +186,13 @@ def get_results(self, request): # which is not passed in later stages in the tree editor for item in self.result_list: item.feincms_changeable = self.model_admin.has_change_permission( - request, item) + request, item + ) item.feincms_addable = ( - item.feincms_changeable and - self.model_admin.has_add_permission(request, item)) + item.feincms_changeable + and self.model_admin.has_add_permission(request, item) + ) # ------------------------------------------------------------------------ @@ -215,29 +217,32 @@ def __init__(self, *args, **kwargs): self.list_display = list(self.list_display) - if 'indented_short_title' not in self.list_display: - if self.list_display[0] == 'action_checkbox': - self.list_display[1] = 'indented_short_title' + if "indented_short_title" not in self.list_display: + if self.list_display[0] == "action_checkbox": + self.list_display[1] = "indented_short_title" else: - self.list_display[0] = 'indented_short_title' - self.list_display_links = ('indented_short_title',) + self.list_display[0] = "indented_short_title" + self.list_display_links = ("indented_short_title",) opts = self.model._meta self.change_list_template = [ - 'admin/feincms/%s/%s/tree_editor.html' % ( - opts.app_label, opts.object_name.lower()), - 'admin/feincms/%s/tree_editor.html' % opts.app_label, - 'admin/feincms/tree_editor.html', + "admin/feincms/%s/%s/tree_editor.html" + % (opts.app_label, opts.object_name.lower()), + "admin/feincms/%s/tree_editor.html" % opts.app_label, + "admin/feincms/tree_editor.html", ] - self.object_change_permission =\ - opts.app_label + '.' + get_permission_codename('change', opts) - self.object_add_permission =\ - opts.app_label + '.' + get_permission_codename('add', opts) - self.object_delete_permission =\ - opts.app_label + '.' + get_permission_codename('delete', opts) + self.object_change_permission = ( + opts.app_label + "." + get_permission_codename("change", opts) + ) + self.object_add_permission = ( + opts.app_label + "." + get_permission_codename("add", opts) + ) + self.object_delete_permission = ( + opts.app_label + "." + get_permission_codename("delete", opts) + ) def changeable(self, item): - return getattr(item, 'feincms_changeable', True) + return getattr(item, "feincms_changeable", True) def indented_short_title(self, item): """ @@ -245,7 +250,7 @@ def indented_short_title(self, item): the object's depth in the hierarchy. """ mptt_opts = item._mptt_meta - r = '' + r = "" try: url = item.get_absolute_url() except (AttributeError,): @@ -254,31 +259,35 @@ def indented_short_title(self, item): if url: r = ( '') % (url, item.pk) + ' value="%s" id="_refkey_%d" />' + ) % (url, item.pk) - changeable_class = '' + changeable_class = "" if not self.changeable(item): - changeable_class = ' tree-item-not-editable' - tree_root_class = '' + changeable_class = " tree-item-not-editable" + tree_root_class = "" if not item.parent_id: - tree_root_class = ' tree-root' + tree_root_class = " tree-root" r += ( '  ') % ( + ' style="width: %dpx;">  ' + ) % ( item.pk, changeable_class, tree_root_class, - 14 + getattr(item, mptt_opts.level_attr) * 18) + 14 + getattr(item, mptt_opts.level_attr) * 18, + ) -# r += '' - if hasattr(item, 'short_title') and callable(item.short_title): + # r += '' + if hasattr(item, "short_title") and callable(item.short_title): r += escape(item.short_title()) else: - r += escape('%s' % item) -# r += '' + r += escape("%s" % item) + # r += '' return mark_safe(r) - indented_short_title.short_description = _('title') + + indented_short_title.short_description = _("title") def _collect_editable_booleans(self): """ @@ -286,7 +295,7 @@ def _collect_editable_booleans(self): want the user to be able to edit arbitrary fields by crafting an AJAX request by hand. """ - if hasattr(self, '_ajax_editable_booleans'): + if hasattr(self, "_ajax_editable_booleans"): return self._ajax_editable_booleans = {} @@ -299,14 +308,17 @@ def _collect_editable_booleans(self): except (AttributeError, TypeError): continue - attr = getattr(item, 'editable_boolean_field', None) + attr = getattr(item, "editable_boolean_field", None) if attr: - if hasattr(item, 'editable_boolean_result'): + if hasattr(item, "editable_boolean_result"): result_func = item.editable_boolean_result else: + def _fn(attr): return lambda self, instance: [ - ajax_editable_boolean_cell(instance, attr)] + ajax_editable_boolean_cell(instance, attr) + ] + result_func = _fn(attr) self._ajax_editable_booleans[attr] = result_func @@ -315,17 +327,22 @@ def _toggle_boolean(self, request): Handle an AJAX toggle_boolean request """ try: - item_id = int(request.POST.get('item_id', None)) - attr = str(request.POST.get('attr', None)) + item_id = int(request.POST.get("item_id", None)) + attr = str(request.POST.get("attr", None)) except Exception: return HttpResponseBadRequest("Malformed request") if not request.user.is_staff: logger.warning( - "Denied AJAX request by non-staff \"%s\" to toggle boolean" - " %s for object #%s", request.user, attr, item_id) + 'Denied AJAX request by non-staff "%s" to toggle boolean' + " %s for object #%s", + request.user, + attr, + item_id, + ) return HttpResponseForbidden( - _("You do not have permission to modify this object")) + _("You do not have permission to modify this object") + ) self._collect_editable_booleans() @@ -339,15 +356,24 @@ def _toggle_boolean(self, request): if not self.has_change_permission(request, obj=obj): logger.warning( - "Denied AJAX request by \"%s\" to toggle boolean %s for" - " object %s", request.user, attr, item_id) + 'Denied AJAX request by "%s" to toggle boolean %s for' " object %s", + request.user, + attr, + item_id, + ) return HttpResponseForbidden( - _("You do not have permission to modify this object")) + _("You do not have permission to modify this object") + ) new_state = not getattr(obj, attr) logger.info( - "Toggle %s on #%d %s to %s by \"%s\"", - attr, obj.pk, obj, "on" if new_state else "off", request.user) + 'Toggle %s on #%d %s to %s by "%s"', + attr, + obj.pk, + obj, + "on" if new_state else "off", + request.user, + ) try: before_data = self._ajax_editable_booleans[attr](self, obj) @@ -359,17 +385,16 @@ def _toggle_boolean(self, request): data = self._ajax_editable_booleans[attr](self, obj) except Exception: - logger.exception( - "Unhandled exception while toggling %s on %s", attr, obj) - return HttpResponseServerError( - "Unable to toggle %s on %s" % (attr, obj)) + logger.exception("Unhandled exception while toggling %s on %s", attr, obj) + return HttpResponseServerError("Unable to toggle %s on %s" % (attr, obj)) # Weed out unchanged cells to keep the updates small. This assumes # that the order a possible get_descendents() returns does not change # before and after toggling this attribute. Unlikely, but still... return HttpResponse( json.dumps([b for a, b in zip(before_data, data) if a != b]), - content_type="application/json") + content_type="application/json", + ) def get_changelist(self, request, **kwargs): return ChangeList @@ -380,30 +405,36 @@ def changelist_view(self, request, extra_context=None, *args, **kwargs): change list/actions page. """ - if 'actions_column' not in self.list_display: - self.list_display.append('actions_column') + if "actions_column" not in self.list_display: + self.list_display.append("actions_column") # handle common AJAX requests if request.is_ajax(): - cmd = request.POST.get('__cmd') - if cmd == 'toggle_boolean': + cmd = request.POST.get("__cmd") + if cmd == "toggle_boolean": return self._toggle_boolean(request) - elif cmd == 'move_node': + elif cmd == "move_node": return self._move_node(request) - return HttpResponseBadRequest('Oops. AJAX request not understood.') + return HttpResponseBadRequest("Oops. AJAX request not understood.") extra_context = extra_context or {} - extra_context['tree_structure'] = mark_safe( - json.dumps(_build_tree_structure(self.get_queryset(request)))) - extra_context['node_levels'] = mark_safe(json.dumps( - dict(self.get_queryset(request).order_by().values_list( - 'pk', self.model._mptt_meta.level_attr - )) - )) + extra_context["tree_structure"] = mark_safe( + json.dumps(_build_tree_structure(self.get_queryset(request))) + ) + extra_context["node_levels"] = mark_safe( + json.dumps( + dict( + self.get_queryset(request) + .order_by() + .values_list("pk", self.model._mptt_meta.level_attr) + ) + ) + ) return super(TreeEditor, self).changelist_view( - request, extra_context, *args, **kwargs) + request, extra_context, *args, **kwargs + ) def has_add_permission(self, request, obj=None): """ @@ -429,8 +460,7 @@ def has_change_permission(self, request, obj=None): else: r = request.user.has_perm(perm) - return r and super(TreeEditor, self).has_change_permission( - request, obj) + return r and super(TreeEditor, self).has_change_permission(request, obj) def has_delete_permission(self, request, obj=None): """ @@ -443,30 +473,29 @@ def has_delete_permission(self, request, obj=None): else: r = request.user.has_perm(perm) - return r and super(TreeEditor, self).has_delete_permission( - request, obj) + return r and super(TreeEditor, self).has_delete_permission(request, obj) def _move_node(self, request): - if hasattr(self.model.objects, 'move_node'): + if hasattr(self.model.objects, "move_node"): tree_manager = self.model.objects else: tree_manager = self.model._tree_manager queryset = self.get_queryset(request) - cut_item = queryset.get(pk=request.POST.get('cut_item')) - pasted_on = queryset.get(pk=request.POST.get('pasted_on')) - position = request.POST.get('position') + cut_item = queryset.get(pk=request.POST.get("cut_item")) + pasted_on = queryset.get(pk=request.POST.get("pasted_on")) + position = request.POST.get("position") if not self.has_change_permission(request, cut_item): - self.message_user(request, _('No permission')) - return HttpResponse('FAIL') + self.message_user(request, _("No permission")) + return HttpResponse("FAIL") - if position in ('last-child', 'left', 'right'): + if position in ("last-child", "left", "right"): try: tree_manager.move_node(cut_item, pasted_on, position) except InvalidMove as e: - self.message_user(request, '%s' % e) - return HttpResponse('FAIL') + self.message_user(request, "%s" % e) + return HttpResponse("FAIL") # Ensure that model save methods have been run (required to # update Page._cached_url values, might also be helpful for other @@ -475,12 +504,12 @@ def _move_node(self, request): item.save() self.message_user( - request, - ugettext('%s has been moved to a new position.') % cut_item) - return HttpResponse('OK') + request, ugettext("%s has been moved to a new position.") % cut_item + ) + return HttpResponse("OK") - self.message_user(request, _('Did not understand moving instruction.')) - return HttpResponse('FAIL') + self.message_user(request, _("Did not understand moving instruction.")) + return HttpResponse("FAIL") def _actions_column(self, instance): if self.changeable(instance): @@ -488,8 +517,9 @@ def _actions_column(self, instance): return [] def actions_column(self, instance): - return mark_safe(' '.join(self._actions_column(instance))) - actions_column.short_description = _('actions') + return mark_safe(" ".join(self._actions_column(instance))) + + actions_column.short_description = _("actions") def delete_selected_tree(self, modeladmin, request, queryset): """ @@ -498,7 +528,7 @@ def delete_selected_tree(self, modeladmin, request, queryset): trigger the post_delete hooks.) """ # If this is True, the confirmation page has been displayed - if request.POST.get('post'): + if request.POST.get("post"): n = 0 # TODO: The disable_mptt_updates / rebuild is a work around # for what seems to be a mptt problem when deleting items @@ -512,13 +542,15 @@ def delete_selected_tree(self, modeladmin, request, queryset): self.log_deletion(request, obj, obj_display) else: logger.warning( - "Denied delete request by \"%s\" for object #%s", - request.user, obj.id) + 'Denied delete request by "%s" for object #%s', + request.user, + obj.id, + ) if n > 0: queryset.model.objects.rebuild() self.message_user( - request, - _("Successfully deleted %(count)d items.") % {"count": n}) + request, _("Successfully deleted %(count)d items.") % {"count": n} + ) # Return None to display the change list page again return None else: @@ -527,9 +559,10 @@ def delete_selected_tree(self, modeladmin, request, queryset): def get_actions(self, request): actions = super(TreeEditor, self).get_actions(request) - if 'delete_selected' in actions: - actions['delete_selected'] = ( + if "delete_selected" in actions: + actions["delete_selected"] = ( self.delete_selected_tree, - 'delete_selected', - _("Delete selected %(verbose_name_plural)s")) + "delete_selected", + _("Delete selected %(verbose_name_plural)s"), + ) return actions diff --git a/feincms/content/application/models.py b/feincms/content/application/models.py index d872b1731..1de3c2571 100644 --- a/feincms/content/application/models.py +++ b/feincms/content/application/models.py @@ -15,15 +15,24 @@ from django.utils.http import http_date from django.utils.safestring import mark_safe from django.utils.translation import get_language, ugettext_lazy as _ + try: from django.urls import ( - NoReverseMatch, reverse, get_script_prefix, set_script_prefix, - Resolver404, resolve, + NoReverseMatch, + reverse, + get_script_prefix, + set_script_prefix, + Resolver404, + resolve, ) except ImportError: from django.core.urlresolvers import ( - NoReverseMatch, reverse, get_script_prefix, set_script_prefix, - Resolver404, resolve, + NoReverseMatch, + reverse, + get_script_prefix, + set_script_prefix, + Resolver404, + resolve, ) from feincms.admin.item_editor import ItemEditorForm @@ -36,9 +45,13 @@ __all__ = ( - 'ApplicationContent', - 'app_reverse', 'app_reverse_lazy', 'permalink', - 'UnpackTemplateResponse', 'standalone', 'unpack', + "ApplicationContent", + "app_reverse", + "app_reverse_lazy", + "permalink", + "UnpackTemplateResponse", + "standalone", + "unpack", ) @@ -47,6 +60,7 @@ class UnpackTemplateResponse(TemplateResponse): Completely the same as marking applicationcontent-contained views with the ``feincms.views.decorators.unpack`` decorator. """ + _feincms_unpack = True @@ -62,6 +76,7 @@ def inner(request, *args, **kwargs): if isinstance(response, HttpResponse): response.standalone = True return response + return wraps(view_func)(inner) @@ -76,19 +91,20 @@ def inner(request, *args, **kwargs): if isinstance(response, TemplateResponse): response._feincms_unpack = True return response + return wraps(view_func)(inner) def cycle_app_reverse_cache(*args, **kwargs): warnings.warn( - 'cycle_app_reverse_cache does nothing and will be removed in' - ' a future version of FeinCMS.', - DeprecationWarning, stacklevel=2, + "cycle_app_reverse_cache does nothing and will be removed in" + " a future version of FeinCMS.", + DeprecationWarning, + stacklevel=2, ) -def app_reverse(viewname, urlconf=None, args=None, kwargs=None, - *vargs, **vkwargs): +def app_reverse(viewname, urlconf=None, args=None, kwargs=None, *vargs, **vkwargs): """ Reverse URLs from application contents @@ -109,9 +125,9 @@ def app_reverse(viewname, urlconf=None, args=None, kwargs=None, # First parameter might be a request instead of an urlconf path, so # we'll try to be helpful and extract the current urlconf from it - extra_context = getattr(urlconf, '_feincms_extra_context', {}) - appconfig = extra_context.get('app_config', {}) - urlconf = appconfig.get('urlconf_path', urlconf) + extra_context = getattr(urlconf, "_feincms_extra_context", {}) + appconfig = extra_context.get("app_config", {}) + urlconf = appconfig.get("urlconf_path", urlconf) appcontent_class = ApplicationContent._feincms_content_models[0] cache_key = appcontent_class.app_reverse_cache_key(urlconf) @@ -124,10 +140,10 @@ def app_reverse(viewname, urlconf=None, args=None, kwargs=None, if urlconf in appcontent_class.ALL_APPS_CONFIG: # We have an overridden URLconf app_config = appcontent_class.ALL_APPS_CONFIG[urlconf] - urlconf = app_config['config'].get('urls', urlconf) + urlconf = app_config["config"].get("urls", urlconf) prefix = content.parent.get_absolute_url() - prefix += '/' if prefix[-1] != '/' else '' + prefix += "/" if prefix[-1] != "/" else "" url_prefix = (urlconf, prefix) cache.set(cache_key, url_prefix, timeout=APP_REVERSE_CACHE_TIMEOUT) @@ -139,11 +155,8 @@ def app_reverse(viewname, urlconf=None, args=None, kwargs=None, try: set_script_prefix(url_prefix[1]) return reverse( - viewname, - url_prefix[0], - args=args, - kwargs=kwargs, - *vargs, **vkwargs) + viewname, url_prefix[0], args=args, kwargs=kwargs, *vargs, **vkwargs + ) finally: set_script_prefix(prefix) @@ -167,8 +180,10 @@ class MyModel(models.Model): def get_absolute_url(self): return ('myapp.urls', 'model_detail', (), {'slug': self.slug}) """ + def inner(*args, **kwargs): return app_reverse(*func(*args, **kwargs)) + return wraps(func)(inner) @@ -182,8 +197,8 @@ class ApplicationContent(models.Model): class Meta: abstract = True - verbose_name = _('application content') - verbose_name_plural = _('application contents') + verbose_name = _("application content") + verbose_name_plural = _("application contents") @classmethod def initialize_type(cls, APPLICATIONS): @@ -192,7 +207,8 @@ def initialize_type(cls, APPLICATIONS): raise ValueError( "APPLICATIONS must be provided with tuples containing at" " least two parameters (urls, name) and an optional extra" - " config dict") + " config dict" + ) urls, name = i[0:2] @@ -202,20 +218,20 @@ def initialize_type(cls, APPLICATIONS): if not isinstance(app_conf, dict): raise ValueError( "The third parameter of an APPLICATIONS entry must be" - " a dict or the name of one!") + " a dict or the name of one!" + ) else: app_conf = {} - cls.ALL_APPS_CONFIG[urls] = { - "urls": urls, - "name": name, - "config": app_conf - } + cls.ALL_APPS_CONFIG[urls] = {"urls": urls, "name": name, "config": app_conf} cls.add_to_class( - 'urlconf_path', - models.CharField(_('application'), max_length=100, choices=[ - (c['urls'], c['name']) for c in cls.ALL_APPS_CONFIG.values()]) + "urlconf_path", + models.CharField( + _("application"), + max_length=100, + choices=[(c["urls"], c["name"]) for c in cls.ALL_APPS_CONFIG.values()], + ), ) class ApplicationContentItemEditorForm(ItemEditorForm): @@ -223,8 +239,7 @@ class ApplicationContentItemEditorForm(ItemEditorForm): custom_fields = {} def __init__(self, *args, **kwargs): - super(ApplicationContentItemEditorForm, self).__init__( - *args, **kwargs) + super(ApplicationContentItemEditorForm, self).__init__(*args, **kwargs) instance = kwargs.get("instance", None) @@ -233,20 +248,20 @@ def __init__(self, *args, **kwargs): # TODO use urlconf_path from POST if set # urlconf_path = request.POST.get('...urlconf_path', # instance.urlconf_path) - self.app_config = cls.ALL_APPS_CONFIG[ - instance.urlconf_path]['config'] + self.app_config = cls.ALL_APPS_CONFIG[instance.urlconf_path][ + "config" + ] except KeyError: self.app_config = {} self.custom_fields = {} - admin_fields = self.app_config.get('admin_fields', {}) + admin_fields = self.app_config.get("admin_fields", {}) if isinstance(admin_fields, dict): self.custom_fields.update(admin_fields) else: get_fields = get_object(admin_fields) - self.custom_fields.update( - get_fields(self, *args, **kwargs)) + self.custom_fields.update(get_fields(self, *args, **kwargs)) params = self.instance.parameters for k, v in self.custom_fields.items(): @@ -262,11 +277,14 @@ def save(self, commit=True, *args, **kwargs): # custom fields before calling save(commit=True) m = super(ApplicationContentItemEditorForm, self).save( - commit=False, *args, **kwargs) + commit=False, *args, **kwargs + ) m.parameters = dict( (k, self.cleaned_data[k]) - for k in self.custom_fields if k in self.cleaned_data) + for k in self.custom_fields + if k in self.cleaned_data + ) if commit: m.save(**kwargs) @@ -279,8 +297,9 @@ def save(self, commit=True, *args, **kwargs): def __init__(self, *args, **kwargs): super(ApplicationContent, self).__init__(*args, **kwargs) - self.app_config = self.ALL_APPS_CONFIG.get( - self.urlconf_path, {}).get('config', {}) + self.app_config = self.ALL_APPS_CONFIG.get(self.urlconf_path, {}).get( + "config", {} + ) def process(self, request, **kw): page_url = self.parent.get_absolute_url() @@ -290,21 +309,20 @@ def process(self, request, **kw): if "path_mapper" in self.app_config: path_mapper = get_object(self.app_config["path_mapper"]) path, page_url = path_mapper( - request.path, - page_url, - appcontent_parameters=self.parameters + request.path, page_url, appcontent_parameters=self.parameters ) else: - path = request._feincms_extra_context['extra_path'] + path = request._feincms_extra_context["extra_path"] # Resolve the module holding the application urls. - urlconf_path = self.app_config.get('urls', self.urlconf_path) + urlconf_path = self.app_config.get("urls", self.urlconf_path) try: fn, args, kwargs = resolve(path, urlconf_path) except (ValueError, Resolver404): - raise Resolver404(str('Not found (resolving %r in %r failed)') % ( - path, urlconf_path)) + raise Resolver404( + str("Not found (resolving %r in %r failed)") % (path, urlconf_path) + ) # Variables from the ApplicationContent parameters are added to request # so we can expose them to our templates via the appcontent_parameters @@ -312,19 +330,14 @@ def process(self, request, **kw): request._feincms_extra_context.update(self.parameters) # Save the application configuration for reuse elsewhere - request._feincms_extra_context.update({ - 'app_config': dict( - self.app_config, - urlconf_path=self.urlconf_path, - ), - }) + request._feincms_extra_context.update( + {"app_config": dict(self.app_config, urlconf_path=self.urlconf_path)} + ) view_wrapper = self.app_config.get("view_wrapper", None) if view_wrapper: fn = partial( - get_object(view_wrapper), - view=fn, - appcontent_parameters=self.parameters + get_object(view_wrapper), view=fn, appcontent_parameters=self.parameters ) output = fn(request, *args, **kwargs) @@ -334,33 +347,32 @@ def process(self, request, **kw): return output elif output.status_code == 200: - if self.unpack(request, output) and 'view' in kw: + if self.unpack(request, output) and "view" in kw: # Handling of @unpack and UnpackTemplateResponse - kw['view'].template_name = output.template_name - kw['view'].request._feincms_extra_context.update( - output.context_data) + kw["view"].template_name = output.template_name + kw["view"].request._feincms_extra_context.update( + output.context_data + ) else: # If the response supports deferred rendering, render the # response right now. We do not handle template response # middleware. - if hasattr(output, 'render') and callable(output.render): + if hasattr(output, "render") and callable(output.render): output.render() - self.rendered_result = mark_safe( - output.content.decode('utf-8')) + self.rendered_result = mark_safe(output.content.decode("utf-8")) self.rendered_headers = {} # Copy relevant headers for later perusal - for h in ('Cache-Control', 'Last-Modified', 'Expires'): + for h in ("Cache-Control", "Last-Modified", "Expires"): if h in output: - self.rendered_headers.setdefault( - h, []).append(output[h]) + self.rendered_headers.setdefault(h, []).append(output[h]) - elif isinstance(output, tuple) and 'view' in kw: - kw['view'].template_name = output[0] - kw['view'].request._feincms_extra_context.update(output[1]) + elif isinstance(output, tuple) and "view" in kw: + kw["view"].template_name = output[0] + kw["view"].request._feincms_extra_context.update(output[1]) else: self.rendered_result = mark_safe(output) @@ -368,25 +380,26 @@ def process(self, request, **kw): return True # successful def send_directly(self, request, response): - mimetype = response.get('Content-Type', 'text/plain') - if ';' in mimetype: - mimetype = mimetype.split(';')[0] + mimetype = response.get("Content-Type", "text/plain") + if ";" in mimetype: + mimetype = mimetype.split(";")[0] mimetype = mimetype.strip() return ( - response.status_code != 200 or - request.is_ajax() or - getattr(response, 'standalone', False) or - mimetype not in ('text/html', 'text/plain')) + response.status_code != 200 + or request.is_ajax() + or getattr(response, "standalone", False) + or mimetype not in ("text/html", "text/plain") + ) def unpack(self, request, response): - return getattr(response, '_feincms_unpack', False) + return getattr(response, "_feincms_unpack", False) def render(self, **kwargs): - return getattr(self, 'rendered_result', '') + return getattr(self, "rendered_result", "") def finalize(self, request, response): - headers = getattr(self, 'rendered_headers', None) + headers = getattr(self, "rendered_headers", None) if headers: self._update_response_headers(request, response, headers) @@ -399,29 +412,29 @@ def _update_response_headers(self, request, response, headers): # Ideally, for the Cache-Control header, we'd want to do some # intelligent combining, but that's hard. Let's just collect and unique # them and let the client worry about that. - cc_headers = set(('must-revalidate',)) - for x in (cc.split(",") for cc in headers.get('Cache-Control', ())): + cc_headers = set(("must-revalidate",)) + for x in (cc.split(",") for cc in headers.get("Cache-Control", ())): cc_headers |= set((s.strip() for s in x)) if len(cc_headers): - response['Cache-Control'] = ", ".join(cc_headers) - else: # Default value - response['Cache-Control'] = 'no-cache, must-revalidate' + response["Cache-Control"] = ", ".join(cc_headers) + else: # Default value + response["Cache-Control"] = "no-cache, must-revalidate" # Check all Last-Modified headers, choose the latest one - lm_list = [parsedate(x) for x in headers.get('Last-Modified', ())] + lm_list = [parsedate(x) for x in headers.get("Last-Modified", ())] if len(lm_list) > 0: - response['Last-Modified'] = http_date(mktime(max(lm_list))) + response["Last-Modified"] = http_date(mktime(max(lm_list))) # Check all Expires headers, choose the earliest one - lm_list = [parsedate(x) for x in headers.get('Expires', ())] + lm_list = [parsedate(x) for x in headers.get("Expires", ())] if len(lm_list) > 0: - response['Expires'] = http_date(mktime(min(lm_list))) + response["Expires"] = http_date(mktime(min(lm_list))) @classmethod def app_reverse_cache_key(self, urlconf_path, **kwargs): - return 'FEINCMS:%s:APPCONTENT:%s:%s' % ( - getattr(settings, 'SITE_ID', 0), + return "FEINCMS:%s:APPCONTENT:%s:%s" % ( + getattr(settings, "SITE_ID", 0), get_language(), urlconf_path, ) @@ -433,17 +446,21 @@ def closest_match(cls, urlconf_path): except AttributeError: page_class = cls.parent.field.rel.to - contents = cls.objects.filter( - parent__in=page_class.objects.active(), - urlconf_path=urlconf_path, - ).order_by('pk').select_related('parent') + contents = ( + cls.objects.filter( + parent__in=page_class.objects.active(), urlconf_path=urlconf_path + ) + .order_by("pk") + .select_related("parent") + ) if len(contents) > 1: try: current = short_language_code(get_language()) return [ - content for content in contents if - short_language_code(content.parent.language) == current + content + for content in contents + if short_language_code(content.parent.language) == current ][0] except (AttributeError, IndexError): diff --git a/feincms/content/contactform/__init__.py b/feincms/content/contactform/__init__.py index f76f4292e..c25396137 100644 --- a/feincms/content/contactform/__init__.py +++ b/feincms/content/contactform/__init__.py @@ -4,5 +4,7 @@ import warnings warnings.warn( - 'The contactform content has been deprecated. Use form-designer instead.', - DeprecationWarning, stacklevel=2) + "The contactform content has been deprecated. Use form-designer instead.", + DeprecationWarning, + stacklevel=2, +) diff --git a/feincms/content/contactform/models.py b/feincms/content/contactform/models.py index cfda97377..9bf97a793 100644 --- a/feincms/content/contactform/models.py +++ b/feincms/content/contactform/models.py @@ -18,13 +18,11 @@ class ContactForm(forms.Form): - name = forms.CharField(label=_('name')) - email = forms.EmailField(label=_('email')) - subject = forms.CharField(label=_('subject')) + name = forms.CharField(label=_("name")) + email = forms.EmailField(label=_("email")) + subject = forms.CharField(label=_("subject")) - content = forms.CharField( - widget=forms.Textarea, required=False, - label=_('content')) + content = forms.CharField(widget=forms.Textarea, required=False, label=_("content")) class ContactFormContent(models.Model): @@ -35,8 +33,8 @@ class ContactFormContent(models.Model): class Meta: abstract = True - verbose_name = _('contact form') - verbose_name_plural = _('contact forms') + verbose_name = _("contact form") + verbose_name_plural = _("contact forms") @classmethod def initialize_type(cls, form=None): @@ -44,42 +42,40 @@ def initialize_type(cls, form=None): cls.form = form def process(self, request, **kwargs): - if request.GET.get('_cf_thanks'): + if request.GET.get("_cf_thanks"): self.rendered_output = ct_render_to_string( - 'content/contactform/thanks.html', - {'content': self}, - request=request) + "content/contactform/thanks.html", {"content": self}, request=request + ) return - if request.method == 'POST': + if request.method == "POST": form = self.form(request.POST) if form.is_valid(): send_mail( - form.cleaned_data['subject'], - render_to_string('content/contactform/email.txt', { - 'data': form.cleaned_data, - }), - form.cleaned_data['email'], + form.cleaned_data["subject"], + render_to_string( + "content/contactform/email.txt", {"data": form.cleaned_data} + ), + form.cleaned_data["email"], [self.email], fail_silently=True, ) - return HttpResponseRedirect('?_cf_thanks=1') + return HttpResponseRedirect("?_cf_thanks=1") else: - initial = {'subject': self.subject} + initial = {"subject": self.subject} if request.user.is_authenticated(): - initial['email'] = request.user.email - initial['name'] = request.user.get_full_name() + initial["email"] = request.user.email + initial["name"] = request.user.get_full_name() form = self.form(initial=initial) self.rendered_output = ct_render_to_string( - 'content/contactform/form.html', { - 'content': self, - 'form': form, - }, - request=request) + "content/contactform/form.html", + {"content": self, "form": form}, + request=request, + ) def render(self, **kwargs): - return getattr(self, 'rendered_output', '') + return getattr(self, "rendered_output", "") diff --git a/feincms/content/file/models.py b/feincms/content/file/models.py index fda078a9f..acfab79ec 100644 --- a/feincms/content/file/models.py +++ b/feincms/content/file/models.py @@ -20,21 +20,20 @@ class FileContent(models.Model): title = models.CharField(max_length=200) file = models.FileField( - _('file'), max_length=255, - upload_to=os.path.join(settings.FEINCMS_UPLOAD_PREFIX, 'filecontent')) + _("file"), + max_length=255, + upload_to=os.path.join(settings.FEINCMS_UPLOAD_PREFIX, "filecontent"), + ) class Meta: abstract = True - verbose_name = _('file') - verbose_name_plural = _('files') + verbose_name = _("file") + verbose_name_plural = _("files") def render(self, **kwargs): return ct_render_to_string( - [ - 'content/file/%s.html' % self.region, - 'content/file/default.html', - ], - {'content': self}, - request=kwargs.get('request'), - context=kwargs.get('context'), + ["content/file/%s.html" % self.region, "content/file/default.html"], + {"content": self}, + request=kwargs.get("request"), + context=kwargs.get("context"), ) diff --git a/feincms/content/filer/models.py b/feincms/content/filer/models.py index f728f0fa1..351b3e628 100644 --- a/feincms/content/filer/models.py +++ b/feincms/content/filer/models.py @@ -17,17 +17,20 @@ else: __all__ = ( - 'MediaFileContentInline', 'ContentWithFilerFile', - 'FilerFileContent', 'FilerImageContent', + "MediaFileContentInline", + "ContentWithFilerFile", + "FilerFileContent", + "FilerImageContent", ) class MediaFileContentInline(FeinCMSInline): - radio_fields = {'type': admin.VERTICAL} + radio_fields = {"type": admin.VERTICAL} class ContentWithFilerFile(models.Model): """ File content """ + feincms_item_editor_inline = MediaFileContentInline class Meta: @@ -36,25 +39,25 @@ class Meta: def render(self, **kwargs): return ct_render_to_string( [ - 'content/filer/%s_%s.html' % (self.file_type, self.type), - 'content/filer/%s.html' % self.type, - 'content/filer/%s.html' % self.file_type, - 'content/filer/default.html', + "content/filer/%s_%s.html" % (self.file_type, self.type), + "content/filer/%s.html" % self.type, + "content/filer/%s.html" % self.file_type, + "content/filer/default.html", ], - {'content': self}, - request=kwargs.get('request'), - context=kwargs.get('context'), + {"content": self}, + request=kwargs.get("request"), + context=kwargs.get("context"), ) class FilerFileContent(ContentWithFilerFile): - mediafile = FilerFileField(verbose_name=_('file'), related_name='+') - file_type = 'file' - type = 'download' + mediafile = FilerFileField(verbose_name=_("file"), related_name="+") + file_type = "file" + type = "download" class Meta: abstract = True - verbose_name = _('file') - verbose_name_plural = _('files') + verbose_name = _("file") + verbose_name_plural = _("files") class FilerImageContent(ContentWithFilerFile): """ @@ -83,36 +86,28 @@ class FilerImageContent(ContentWithFilerFile): must_always_publish_copyright, date_taken, file, id, is_public, url """ - mediafile = FilerImageField(verbose_name=_('image'), related_name='+') - caption = models.CharField( - _('caption'), - max_length=1000, - blank=True, - ) - url = models.CharField( - _('URL'), - max_length=1000, - blank=True, - ) + mediafile = FilerImageField(verbose_name=_("image"), related_name="+") + caption = models.CharField(_("caption"), max_length=1000, blank=True) + url = models.CharField(_("URL"), max_length=1000, blank=True) - file_type = 'image' + file_type = "image" class Meta: abstract = True - verbose_name = _('image') - verbose_name_plural = _('images') + verbose_name = _("image") + verbose_name_plural = _("images") @classmethod def initialize_type(cls, TYPE_CHOICES=None): if TYPE_CHOICES is None: raise ImproperlyConfigured( - 'You have to set TYPE_CHOICES when' - ' creating a %s' % cls.__name__) + "You have to set TYPE_CHOICES when" " creating a %s" % cls.__name__ + ) cls.add_to_class( - 'type', + "type", models.CharField( - _('type'), + _("type"), max_length=20, choices=TYPE_CHOICES, default=TYPE_CHOICES[0][0], diff --git a/feincms/content/image/models.py b/feincms/content/image/models.py index 170236a92..8ddec6c37 100644 --- a/feincms/content/image/models.py +++ b/feincms/content/image/models.py @@ -43,54 +43,59 @@ class ImageContent(models.Model): """ image = models.ImageField( - _('image'), max_length=255, - upload_to=os.path.join(settings.FEINCMS_UPLOAD_PREFIX, 'imagecontent')) + _("image"), + max_length=255, + upload_to=os.path.join(settings.FEINCMS_UPLOAD_PREFIX, "imagecontent"), + ) alt_text = models.CharField( - _('alternate text'), max_length=255, blank=True, - help_text=_('Description of image')) - caption = models.CharField(_('caption'), max_length=255, blank=True) + _("alternate text"), + max_length=255, + blank=True, + help_text=_("Description of image"), + ) + caption = models.CharField(_("caption"), max_length=255, blank=True) class Meta: abstract = True - verbose_name = _('image') - verbose_name_plural = _('images') + verbose_name = _("image") + verbose_name_plural = _("images") def render(self, **kwargs): - templates = ['content/image/default.html'] - if hasattr(self, 'position'): - templates.insert(0, 'content/image/%s.html' % self.position) + templates = ["content/image/default.html"] + if hasattr(self, "position"): + templates.insert(0, "content/image/%s.html" % self.position) return ct_render_to_string( templates, - {'content': self}, - request=kwargs.get('request'), - context=kwargs.get('context'), + {"content": self}, + request=kwargs.get("request"), + context=kwargs.get("context"), ) def get_image(self): - type, separator, size = getattr(self, 'format', '').partition(':') + type, separator, size = getattr(self, "format", "").partition(":") if not size: return self.image - thumbnailer = { - 'cropscale': feincms_thumbnail.CropscaleThumbnailer, - }.get(type, feincms_thumbnail.Thumbnailer) + thumbnailer = {"cropscale": feincms_thumbnail.CropscaleThumbnailer}.get( + type, feincms_thumbnail.Thumbnailer + ) return thumbnailer(self.image, size) @classmethod def initialize_type(cls, POSITION_CHOICES=None, FORMAT_CHOICES=None): if POSITION_CHOICES: models.CharField( - _('position'), + _("position"), max_length=10, choices=POSITION_CHOICES, - default=POSITION_CHOICES[0][0] - ).contribute_to_class(cls, 'position') + default=POSITION_CHOICES[0][0], + ).contribute_to_class(cls, "position") if FORMAT_CHOICES: models.CharField( - _('format'), + _("format"), max_length=64, choices=FORMAT_CHOICES, - default=FORMAT_CHOICES[0][0] - ).contribute_to_class(cls, 'format') + default=FORMAT_CHOICES[0][0], + ).contribute_to_class(cls, "format") diff --git a/feincms/content/medialibrary/models.py b/feincms/content/medialibrary/models.py index 9e8d2e30d..bc7d2f474 100644 --- a/feincms/content/medialibrary/models.py +++ b/feincms/content/medialibrary/models.py @@ -6,5 +6,7 @@ from feincms.module.medialibrary.contents import MediaFileContent warnings.warn( - 'Import MediaFileContent from feincms.module.medialibrary.contents.', - DeprecationWarning, stacklevel=2) + "Import MediaFileContent from feincms.module.medialibrary.contents.", + DeprecationWarning, + stacklevel=2, +) diff --git a/feincms/content/raw/models.py b/feincms/content/raw/models.py index 487c5a241..283d6c151 100644 --- a/feincms/content/raw/models.py +++ b/feincms/content/raw/models.py @@ -13,12 +13,12 @@ class RawContent(models.Model): snippets too. """ - text = models.TextField(_('content'), blank=True) + text = models.TextField(_("content"), blank=True) class Meta: abstract = True - verbose_name = _('raw content') - verbose_name_plural = _('raw contents') + verbose_name = _("raw content") + verbose_name_plural = _("raw contents") def render(self, **kwargs): return mark_safe(self.text) diff --git a/feincms/content/richtext/models.py b/feincms/content/richtext/models.py index 8ad5a1e9c..a893eb1b4 100644 --- a/feincms/content/richtext/models.py +++ b/feincms/content/richtext/models.py @@ -24,26 +24,21 @@ class RichTextContent(models.Model): feincms_item_editor_context_processors = ( lambda x: settings.FEINCMS_RICHTEXT_INIT_CONTEXT, ) - feincms_item_editor_includes = { - 'head': [settings.FEINCMS_RICHTEXT_INIT_TEMPLATE], - } + feincms_item_editor_includes = {"head": [settings.FEINCMS_RICHTEXT_INIT_TEMPLATE]} class Meta: abstract = True - verbose_name = _('rich text') - verbose_name_plural = _('rich texts') + verbose_name = _("rich text") + verbose_name_plural = _("rich texts") def render(self, **kwargs): return ct_render_to_string( - 'content/richtext/default.html', - {'content': self}, - request=kwargs.get('request'), - context=kwargs.get('context'), + "content/richtext/default.html", + {"content": self}, + request=kwargs.get("request"), + context=kwargs.get("context"), ) @classmethod def initialize_type(cls, cleanse=None): - cls.add_to_class( - 'text', - RichTextField(_('text'), blank=True, cleanse=cleanse), - ) + cls.add_to_class("text", RichTextField(_("text"), blank=True, cleanse=cleanse)) diff --git a/feincms/content/section/models.py b/feincms/content/section/models.py index 44dbdf571..4af9a8b3a 100644 --- a/feincms/content/section/models.py +++ b/feincms/content/section/models.py @@ -15,8 +15,8 @@ class SectionContentInline(FeinCMSInline): - raw_id_fields = ('mediafile',) - radio_fields = {'type': admin.VERTICAL} + raw_id_fields = ("mediafile",) + radio_fields = {"type": admin.VERTICAL} class SectionContent(models.Model): @@ -28,39 +28,46 @@ class SectionContent(models.Model): feincms_item_editor_context_processors = ( lambda x: settings.FEINCMS_RICHTEXT_INIT_CONTEXT, ) - feincms_item_editor_includes = { - 'head': [settings.FEINCMS_RICHTEXT_INIT_TEMPLATE], - } + feincms_item_editor_includes = {"head": [settings.FEINCMS_RICHTEXT_INIT_TEMPLATE]} - title = models.CharField(_('title'), max_length=200, blank=True) - richtext = RichTextField(_('text'), blank=True) + title = models.CharField(_("title"), max_length=200, blank=True) + richtext = RichTextField(_("text"), blank=True) mediafile = MediaFileForeignKey( - MediaFile, on_delete=models.CASCADE, - verbose_name=_('media file'), - related_name='+', blank=True, null=True) + MediaFile, + on_delete=models.CASCADE, + verbose_name=_("media file"), + related_name="+", + blank=True, + null=True, + ) class Meta: abstract = True - verbose_name = _('section') - verbose_name_plural = _('sections') + verbose_name = _("section") + verbose_name_plural = _("sections") @classmethod def initialize_type(cls, TYPE_CHOICES=None, cleanse=None): - if 'feincms.module.medialibrary' not in django_settings.INSTALLED_APPS: + if "feincms.module.medialibrary" not in django_settings.INSTALLED_APPS: raise ImproperlyConfigured( - 'You have to add \'feincms.module.medialibrary\' to your' - ' INSTALLED_APPS before creating a %s' % cls.__name__) + "You have to add 'feincms.module.medialibrary' to your" + " INSTALLED_APPS before creating a %s" % cls.__name__ + ) if TYPE_CHOICES is None: raise ImproperlyConfigured( - 'You need to set TYPE_CHOICES when creating a' - ' %s' % cls.__name__) - - cls.add_to_class('type', models.CharField( - _('type'), - max_length=10, choices=TYPE_CHOICES, - default=TYPE_CHOICES[0][0] - )) + "You need to set TYPE_CHOICES when creating a" " %s" % cls.__name__ + ) + + cls.add_to_class( + "type", + models.CharField( + _("type"), + max_length=10, + choices=TYPE_CHOICES, + default=TYPE_CHOICES[0][0], + ), + ) if cleanse: cls.cleanse = cleanse @@ -68,29 +75,28 @@ def initialize_type(cls, TYPE_CHOICES=None, cleanse=None): @classmethod def get_queryset(cls, filter_args): # Explicitly add nullable FK mediafile to minimize the DB query count - return cls.objects.select_related('parent', 'mediafile').filter( - filter_args) + return cls.objects.select_related("parent", "mediafile").filter(filter_args) def render(self, **kwargs): if self.mediafile: mediafile_type = self.mediafile.type else: - mediafile_type = 'nomedia' + mediafile_type = "nomedia" return ct_render_to_string( [ - 'content/section/%s_%s.html' % (mediafile_type, self.type), - 'content/section/%s.html' % mediafile_type, - 'content/section/%s.html' % self.type, - 'content/section/default.html', + "content/section/%s_%s.html" % (mediafile_type, self.type), + "content/section/%s.html" % mediafile_type, + "content/section/%s.html" % self.type, + "content/section/default.html", ], - {'content': self}, - request=kwargs.get('request'), - context=kwargs.get('context'), + {"content": self}, + request=kwargs.get("request"), + context=kwargs.get("context"), ) def save(self, *args, **kwargs): - if getattr(self, 'cleanse', None): + if getattr(self, "cleanse", None): try: # Passes the rich text content as first argument because # the passed callable has been converted into a bound method @@ -101,4 +107,5 @@ def save(self, *args, **kwargs): self.richtext = self.cleanse.im_func(self.richtext) super(SectionContent, self).save(*args, **kwargs) + save.alters_data = True diff --git a/feincms/content/template/models.py b/feincms/content/template/models.py index 26ec26e44..ad203b3d9 100644 --- a/feincms/content/template/models.py +++ b/feincms/content/template/models.py @@ -19,23 +19,23 @@ class TemplateContent(models.Model): ('base.html', 'makes no sense'), ]) """ + class Meta: abstract = True - verbose_name = _('template content') - verbose_name_plural = _('template contents') + verbose_name = _("template content") + verbose_name_plural = _("template contents") @classmethod def initialize_type(cls, TEMPLATES): - cls.add_to_class('template', models.CharField( - _('template'), - max_length=100, - choices=TEMPLATES, - )) + cls.add_to_class( + "template", + models.CharField(_("template"), max_length=100, choices=TEMPLATES), + ) def render(self, **kwargs): return ct_render_to_string( self.template, - {'content': self}, - request=kwargs.get('request'), - context=kwargs.get('context'), + {"content": self}, + request=kwargs.get("request"), + context=kwargs.get("context"), ) diff --git a/feincms/content/video/models.py b/feincms/content/video/models.py index 32a4cae81..1e73cf83a 100644 --- a/feincms/content/video/models.py +++ b/feincms/content/video/models.py @@ -20,38 +20,43 @@ class VideoContent(models.Model): """ PORTALS = ( - ('youtube', re.compile(r'youtube'), lambda url: { - 'v': re.search(r'([?&]v=|./././)([^#&]+)', url).group(2), - }), - ('vimeo', re.compile(r'vimeo'), lambda url: { - 'id': re.search(r'/(\d+)', url).group(1), - }), - ('sf', re.compile(r'sf\.tv'), lambda url: { - 'id': re.search(r'/([a-z0-9\-]+)', url).group(1), - }), + ( + "youtube", + re.compile(r"youtube"), + lambda url: {"v": re.search(r"([?&]v=|./././)([^#&]+)", url).group(2)}, + ), + ( + "vimeo", + re.compile(r"vimeo"), + lambda url: {"id": re.search(r"/(\d+)", url).group(1)}, + ), + ( + "sf", + re.compile(r"sf\.tv"), + lambda url: {"id": re.search(r"/([a-z0-9\-]+)", url).group(1)}, + ), ) video = models.URLField( - _('video link'), + _("video link"), help_text=_( - 'This should be a link to a youtube or vimeo video,' - ' i.e.: http://www.youtube.com/watch?v=zmj1rpzDRZ0')) + "This should be a link to a youtube or vimeo video," + " i.e.: http://www.youtube.com/watch?v=zmj1rpzDRZ0" + ), + ) class Meta: abstract = True - verbose_name = _('video') - verbose_name_plural = _('videos') + verbose_name = _("video") + verbose_name_plural = _("videos") def get_context_dict(self): "Extend this if you need more variables passed to template" - return {'content': self, 'portal': 'unknown'} + return {"content": self, "portal": "unknown"} - def get_templates(self, portal='unknown'): + def get_templates(self, portal="unknown"): "Extend/override this if you want to modify the templates used" - return [ - 'content/video/%s.html' % portal, - 'content/video/unknown.html', - ] + return ["content/video/%s.html" % portal, "content/video/unknown.html"] def ctx_for_video(self, vurl): "Get a context dict for a given video URL" @@ -60,7 +65,7 @@ def ctx_for_video(self, vurl): if match.search(vurl): try: ctx.update(context_fn(vurl)) - ctx['portal'] = portal + ctx["portal"] = portal break except AttributeError: continue @@ -69,8 +74,8 @@ def ctx_for_video(self, vurl): def render(self, **kwargs): ctx = self.ctx_for_video(self.video) return ct_render_to_string( - self.get_templates(ctx['portal']), + self.get_templates(ctx["portal"]), ctx, - request=kwargs.get('request'), - context=kwargs.get('context'), + request=kwargs.get("request"), + context=kwargs.get("context"), ) diff --git a/feincms/context_processors.py b/feincms/context_processors.py index 3f5c9b1a7..30bf7de70 100644 --- a/feincms/context_processors.py +++ b/feincms/context_processors.py @@ -7,8 +7,6 @@ def add_page_if_missing(request): """ try: - return { - 'feincms_page': Page.objects.for_request(request, best_match=True), - } + return {"feincms_page": Page.objects.for_request(request, best_match=True)} except Page.DoesNotExist: return {} diff --git a/feincms/contrib/fields.py b/feincms/contrib/fields.py index 37af3bdec..bf8b8ed3b 100644 --- a/feincms/contrib/fields.py +++ b/feincms/contrib/fields.py @@ -32,11 +32,10 @@ def clean(self, value, *args, **kwargs): return super(JSONFormField, self).clean(value, *args, **kwargs) -if LooseVersion(get_version()) > LooseVersion('1.8'): +if LooseVersion(get_version()) > LooseVersion("1.8"): workaround_class = models.TextField else: - workaround_class = six.with_metaclass( - models.SubfieldBase, models.TextField) + workaround_class = six.with_metaclass(models.SubfieldBase, models.TextField) class JSONField(workaround_class): @@ -54,8 +53,7 @@ def to_python(self, value): if isinstance(value, dict): return value - elif (isinstance(value, six.string_types) or - isinstance(value, six.binary_type)): + elif isinstance(value, six.string_types) or isinstance(value, six.binary_type): # Avoid asking the JSON decoder to handle empty values: if not value: return {} @@ -64,7 +62,8 @@ def to_python(self, value): return json.loads(value) except ValueError: logging.getLogger("feincms.contrib.fields").exception( - "Unable to deserialize store JSONField data: %s", value) + "Unable to deserialize store JSONField data: %s", value + ) return {} else: assert value is None diff --git a/feincms/contrib/preview/urls.py b/feincms/contrib/preview/urls.py index 1f4e324f0..95facb066 100644 --- a/feincms/contrib/preview/urls.py +++ b/feincms/contrib/preview/urls.py @@ -4,6 +4,5 @@ urlpatterns = [ - url(r'^(.*)/_preview/(\d+)/$', PreviewHandler.as_view(), - name='feincms_preview'), + url(r"^(.*)/_preview/(\d+)/$", PreviewHandler.as_view(), name="feincms_preview") ] diff --git a/feincms/contrib/preview/views.py b/feincms/contrib/preview/views.py index e1c37586c..38ba1d6d1 100644 --- a/feincms/contrib/preview/views.py +++ b/feincms/contrib/preview/views.py @@ -29,9 +29,7 @@ def get_object(self): def handler(self, request, *args, **kwargs): if not request.user.is_staff: - raise Http404('Not found (not allowed)') - response = super(PreviewHandler, self).handler( - request, *args, **kwargs) - response['Cache-Control'] =\ - 'no-cache, must-revalidate, no-store, private' + raise Http404("Not found (not allowed)") + response = super(PreviewHandler, self).handler(request, *args, **kwargs) + response["Cache-Control"] = "no-cache, must-revalidate, no-store, private" return response diff --git a/feincms/contrib/richtext.py b/feincms/contrib/richtext.py index 3b9985e55..4e027ea80 100644 --- a/feincms/contrib/richtext.py +++ b/feincms/contrib/richtext.py @@ -6,11 +6,11 @@ class RichTextFormField(forms.fields.CharField): def __init__(self, *args, **kwargs): - self.cleanse = kwargs.pop('cleanse', None) + self.cleanse = kwargs.pop("cleanse", None) super(RichTextFormField, self).__init__(*args, **kwargs) - css_class = self.widget.attrs.get('class', '') - css_class += ' item-richtext' - self.widget.attrs['class'] = css_class + css_class = self.widget.attrs.get("class", "") + css_class += " item-richtext" + self.widget.attrs["class"] = css_class def clean(self, value): value = super(RichTextFormField, self).clean(value) @@ -24,12 +24,12 @@ class RichTextField(models.TextField): Drop-in replacement for Django's ``models.TextField`` which allows editing rich text instead of plain text in the item editor. """ + def __init__(self, *args, **kwargs): - self.cleanse = kwargs.pop('cleanse', None) + self.cleanse = kwargs.pop("cleanse", None) super(RichTextField, self).__init__(*args, **kwargs) def formfield(self, form_class=RichTextFormField, **kwargs): return super(RichTextField, self).formfield( - form_class=form_class, - cleanse=self.cleanse, - **kwargs) + form_class=form_class, cleanse=self.cleanse, **kwargs + ) diff --git a/feincms/contrib/tagging.py b/feincms/contrib/tagging.py index 6520f069c..181f4eed9 100644 --- a/feincms/contrib/tagging.py +++ b/feincms/contrib/tagging.py @@ -19,6 +19,7 @@ from tagging.fields import TagField from tagging.models import Tag from tagging.utils import parse_tag_input + try: from tagging.registry import AlreadyRegistered except ImportError: @@ -27,12 +28,13 @@ # ------------------------------------------------------------------------ def taglist_to_string(taglist): - retval = '' + retval = "" if len(taglist) >= 1: taglist.sort() - retval = ','.join(taglist) + retval = ",".join(taglist) return retval + # ------------------------------------------------------------------------ # The following is lifted from: # http://code.google.com/p/django-tagging/issues/detail?id=189 @@ -57,19 +59,23 @@ def clean(self, value): if VERSION >= (1, 10): + class Tag_formatvalue_mixin(object): def format_value(self, value): - value = parse_tag_input(value or '') + value = parse_tag_input(value or "") return super(Tag_formatvalue_mixin, self).format_value(value) + + else: # _format_value is a private method previous to Django 1.10, # do the job in render() instead to avoid fiddling with # anybody's privates class Tag_formatvalue_mixin(object): def render(self, name, value, attrs=None, *args, **kwargs): - value = parse_tag_input(value or '') + value = parse_tag_input(value or "") return super(Tag_formatvalue_mixin, self).render( - name, value, attrs, *args, **kwargs) + name, value, attrs, *args, **kwargs + ) class fv_FilteredSelectMultiple(Tag_formatvalue_mixin, FilteredSelectMultiple): @@ -87,17 +93,13 @@ def __init__(self, filter_horizontal=False, *args, **kwargs): def formfield(self, **defaults): if self.filter_horizontal: - widget = fv_FilteredSelectMultiple( - self.verbose_name, is_stacked=False) + widget = fv_FilteredSelectMultiple(self.verbose_name, is_stacked=False) else: widget = fv_SelectMultiple() - defaults['widget'] = widget - choices = [( - six.text_type(t), - six.text_type(t)) for t in Tag.objects.all()] - return TagSelectFormField( - choices=choices, required=not self.blank, **defaults) + defaults["widget"] = widget + choices = [(six.text_type(t), six.text_type(t)) for t in Tag.objects.all()] + return TagSelectFormField(choices=choices, required=not self.blank, **defaults) # ------------------------------------------------------------------------ @@ -111,9 +113,15 @@ def pre_save_handler(sender, instance, **kwargs): # ------------------------------------------------------------------------ -def tag_model(cls, admin_cls=None, field_name='tags', sort_tags=False, - select_field=False, auto_add_admin_field=True, - admin_list_display=True): +def tag_model( + cls, + admin_cls=None, + field_name="tags", + sort_tags=False, + select_field=False, + auto_add_admin_field=True, + admin_list_display=True, +): """ tag_model accepts a number of named parameters: @@ -136,14 +144,17 @@ def tag_model(cls, admin_cls=None, field_name='tags', sort_tags=False, except ImportError: from tagging import register as tagging_register - cls.add_to_class(field_name, ( - TagSelectField if select_field else TagField - )(field_name.capitalize(), blank=True)) + cls.add_to_class( + field_name, + (TagSelectField if select_field else TagField)( + field_name.capitalize(), blank=True + ), + ) # use another name for the tag descriptor # See http://code.google.com/p/django-tagging/issues/detail?id=95 for the # reason why try: - tagging_register(cls, tag_descriptor_attr='tagging_' + field_name) + tagging_register(cls, tag_descriptor_attr="tagging_" + field_name) except AlreadyRegistered: return @@ -152,13 +163,11 @@ def tag_model(cls, admin_cls=None, field_name='tags', sort_tags=False, admin_cls.list_display.append(field_name) admin_cls.list_filter.append(field_name) - if auto_add_admin_field and hasattr( - admin_cls, 'add_extension_options'): - admin_cls.add_extension_options(_('Tagging'), { - 'fields': (field_name,) - }) + if auto_add_admin_field and hasattr(admin_cls, "add_extension_options"): + admin_cls.add_extension_options(_("Tagging"), {"fields": (field_name,)}) if sort_tags: pre_save.connect(pre_save_handler, sender=cls) + # ------------------------------------------------------------------------ diff --git a/feincms/default_settings.py b/feincms/default_settings.py index 3cbf95f2c..53c4a41ef 100644 --- a/feincms/default_settings.py +++ b/feincms/default_settings.py @@ -14,42 +14,41 @@ # e.g. 'uploads' if you would prefer /uploads/imagecontent/test.jpg # to /imagecontent/test.jpg. -FEINCMS_UPLOAD_PREFIX = getattr( - settings, - 'FEINCMS_UPLOAD_PREFIX', - '') +FEINCMS_UPLOAD_PREFIX = getattr(settings, "FEINCMS_UPLOAD_PREFIX", "") # ------------------------------------------------------------------------ # Settings for MediaLibrary #: Local path to newly uploaded media files FEINCMS_MEDIALIBRARY_UPLOAD_TO = getattr( - settings, - 'FEINCMS_MEDIALIBRARY_UPLOAD_TO', - 'medialibrary/%Y/%m/') + settings, "FEINCMS_MEDIALIBRARY_UPLOAD_TO", "medialibrary/%Y/%m/" +) #: Thumbnail function for suitable mediafiles. Only receives the media file #: and should return a thumbnail URL (or nothing). FEINCMS_MEDIALIBRARY_THUMBNAIL = getattr( settings, - 'FEINCMS_MEDIALIBRARY_THUMBNAIL', - 'feincms.module.medialibrary.thumbnail.default_admin_thumbnail') + "FEINCMS_MEDIALIBRARY_THUMBNAIL", + "feincms.module.medialibrary.thumbnail.default_admin_thumbnail", +) # ------------------------------------------------------------------------ # Settings for RichText FEINCMS_RICHTEXT_INIT_TEMPLATE = getattr( settings, - 'FEINCMS_RICHTEXT_INIT_TEMPLATE', - 'admin/content/richtext/init_tinymce4.html') + "FEINCMS_RICHTEXT_INIT_TEMPLATE", + "admin/content/richtext/init_tinymce4.html", +) FEINCMS_RICHTEXT_INIT_CONTEXT = getattr( settings, - 'FEINCMS_RICHTEXT_INIT_CONTEXT', { - 'TINYMCE_JS_URL': '//tinymce.cachefly.net/4.2/tinymce.min.js', - 'TINYMCE_DOMAIN': None, - 'TINYMCE_CONTENT_CSS_URL': None, - 'TINYMCE_LINK_LIST_URL': None - } + "FEINCMS_RICHTEXT_INIT_CONTEXT", + { + "TINYMCE_JS_URL": "//tinymce.cachefly.net/4.2/tinymce.min.js", + "TINYMCE_DOMAIN": None, + "TINYMCE_CONTENT_CSS_URL": None, + "TINYMCE_LINK_LIST_URL": None, + }, ) # ------------------------------------------------------------------------ @@ -57,38 +56,29 @@ #: Include ancestors in filtered tree editor lists FEINCMS_TREE_EDITOR_INCLUDE_ANCESTORS = getattr( - settings, - 'FEINCMS_TREE_EDITOR_INCLUDE_ANCESTORS', - False) + settings, "FEINCMS_TREE_EDITOR_INCLUDE_ANCESTORS", False +) #: Enable checking of object level permissions. Note that if this option is #: enabled, you must plug in an authentication backend that actually does #: implement object level permissions or no page will be editable. FEINCMS_TREE_EDITOR_OBJECT_PERMISSIONS = getattr( - settings, - 'FEINCMS_TREE_EDITOR_OBJECT_PERMISSIONS', - False) + settings, "FEINCMS_TREE_EDITOR_OBJECT_PERMISSIONS", False +) #: When enabled, the page module is automatically registered with Django's #: default admin site (this is activated by default). -FEINCMS_USE_PAGE_ADMIN = getattr( - settings, - 'FEINCMS_USE_PAGE_ADMIN', - True) +FEINCMS_USE_PAGE_ADMIN = getattr(settings, "FEINCMS_USE_PAGE_ADMIN", True) #: app_label.model_name as per apps.get_model. #: defaults to page.Page FEINCMS_DEFAULT_PAGE_MODEL = getattr( - settings, - 'FEINCMS_DEFAULT_PAGE_MODEL', - 'page.Page') + settings, "FEINCMS_DEFAULT_PAGE_MODEL", "page.Page" +) # ------------------------------------------------------------------------ #: Allow random gunk after a valid page? -FEINCMS_ALLOW_EXTRA_PATH = getattr( - settings, - 'FEINCMS_ALLOW_EXTRA_PATH', - False) +FEINCMS_ALLOW_EXTRA_PATH = getattr(settings, "FEINCMS_ALLOW_EXTRA_PATH", False) # ------------------------------------------------------------------------ #: How to switch languages. @@ -96,10 +86,7 @@ #: and overwrites whatever was set before. #: * ``'EXPLICIT'``: The language set has priority, may only be overridden #: by explicitely a language with ``?set_language=xx``. -FEINCMS_TRANSLATION_POLICY = getattr( - settings, - 'FEINCMS_TRANSLATION_POLICY', - 'STANDARD') +FEINCMS_TRANSLATION_POLICY = getattr(settings, "FEINCMS_TRANSLATION_POLICY", "STANDARD") # ------------------------------------------------------------------------ #: Makes the page handling mechanism try to find a cms page with that @@ -107,60 +94,45 @@ #: customised cms-styled error pages. Do not go overboard, this should #: be as simple and as error resistant as possible, so refrain from #: deeply nested error pages or advanced content types. -FEINCMS_CMS_404_PAGE = getattr( - settings, - 'FEINCMS_CMS_404_PAGE', - None) +FEINCMS_CMS_404_PAGE = getattr(settings, "FEINCMS_CMS_404_PAGE", None) # ------------------------------------------------------------------------ #: When uploading files to the media library, replacing an existing entry, #: try to save the new file under the old file name in order to keep the #: media file path (and thus the media url) constant. #: Experimental, this might not work with all storage backends. -FEINCMS_MEDIAFILE_OVERWRITE = getattr( - settings, - 'FEINCMS_MEDIAFILE_OVERWRITE', - False) +FEINCMS_MEDIAFILE_OVERWRITE = getattr(settings, "FEINCMS_MEDIAFILE_OVERWRITE", False) # ------------------------------------------------------------------------ #: Prefix for thumbnails. Set this to something non-empty to separate thumbs #: from uploads. The value should end with a slash, but this is not enforced. -FEINCMS_THUMBNAIL_DIR = getattr( - settings, - 'FEINCMS_THUMBNAIL_DIR', - '_thumbs/') +FEINCMS_THUMBNAIL_DIR = getattr(settings, "FEINCMS_THUMBNAIL_DIR", "_thumbs/") # ------------------------------------------------------------------------ #: feincms_thumbnail template filter library cache timeout. The default is to #: not cache anything for backwards compatibility. FEINCMS_THUMBNAIL_CACHE_TIMEOUT = getattr( - settings, - 'FEINCMS_THUMBNAIL_CACHE_TIMEOUT', - 0) + settings, "FEINCMS_THUMBNAIL_CACHE_TIMEOUT", 0 +) # ------------------------------------------------------------------------ #: Prevent changing template within admin for pages which have been #: allocated a Template with singleton=True -- template field will become #: read-only for singleton pages. FEINCMS_SINGLETON_TEMPLATE_CHANGE_ALLOWED = getattr( - settings, - 'FEINCMS_SINGLETON_TEMPLATE_CHANGE_ALLOWED', - False) + settings, "FEINCMS_SINGLETON_TEMPLATE_CHANGE_ALLOWED", False +) #: Prevent admin page deletion for pages which have been allocated a #: Template with singleton=True FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED = getattr( - settings, - 'FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED', - False) + settings, "FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED", False +) # ------------------------------------------------------------------------ #: Filter languages available for front end users to this set. This allows #: to have languages not yet ready for prime time while being able to access #: those pages in the admin backend. -FEINCMS_FRONTEND_LANGUAGES = getattr( - settings, - 'FEINCMS_FRONTEND_LANGUAGES', - None) +FEINCMS_FRONTEND_LANGUAGES = getattr(settings, "FEINCMS_FRONTEND_LANGUAGES", None) # ------------------------------------------------------------------------ diff --git a/feincms/extensions/__init__.py b/feincms/extensions/__init__.py index 853f27091..ad37b177f 100644 --- a/feincms/extensions/__init__.py +++ b/feincms/extensions/__init__.py @@ -1,10 +1,15 @@ from __future__ import absolute_import from .base import ( - ExtensionsMixin, Extension, ExtensionModelAdmin, - prefetch_modeladmin_get_queryset) + ExtensionsMixin, + Extension, + ExtensionModelAdmin, + prefetch_modeladmin_get_queryset, +) __all__ = ( - 'ExtensionsMixin', 'Extension', 'ExtensionModelAdmin', - 'prefetch_modeladmin_get_queryset', + "ExtensionsMixin", + "Extension", + "ExtensionModelAdmin", + "prefetch_modeladmin_get_queryset", ) diff --git a/feincms/extensions/base.py b/feincms/extensions/base.py index 31773ff6f..55a0ebde9 100644 --- a/feincms/extensions/base.py +++ b/feincms/extensions/base.py @@ -27,7 +27,7 @@ def register_extensions(cls, *extensions): sufficient. """ - if not hasattr(cls, '_extensions'): + if not hasattr(cls, "_extensions"): cls._extensions = [] cls._extensions_seen = [] @@ -46,32 +46,31 @@ def register_extensions(cls, *extensions): except (AttributeError, ImportError, ValueError): if not extension: raise ImproperlyConfigured( - '%s is not a valid extension for %s' % ( - ext, cls.__name__)) + "%s is not a valid extension for %s" % (ext, cls.__name__) + ) - if hasattr(extension, 'Extension'): + if hasattr(extension, "Extension"): extension = extension.Extension - elif hasattr(extension, 'register'): + elif hasattr(extension, "register"): extension = extension.register - elif hasattr(extension, '__call__'): + elif hasattr(extension, "__call__"): pass else: raise ImproperlyConfigured( - '%s is not a valid extension for %s' % ( - ext, cls.__name__)) + "%s is not a valid extension for %s" % (ext, cls.__name__) + ) if extension in cls._extensions_seen: continue cls._extensions_seen.append(extension) - if hasattr(extension, 'handle_model'): + if hasattr(extension, "handle_model"): cls._extensions.append(extension(cls)) else: - raise ImproperlyConfigured( - '%r is an invalid extension.' % extension) + raise ImproperlyConfigured("%r is an invalid extension." % extension) class Extension(object): @@ -79,8 +78,10 @@ def __init__(self, model, **kwargs): self.model = model for key, value in kwargs.items(): if not hasattr(self, key): - raise TypeError('%s() received an invalid keyword %r' % ( - self.__class__.__name__, key)) + raise TypeError( + "%s() received an invalid keyword %r" + % (self.__class__.__name__, key) + ) setattr(self, key, value) self.handle_model() @@ -98,26 +99,26 @@ def __init__(self, *args, **kwargs): self.initialize_extensions() def initialize_extensions(self): - if not hasattr(self, '_extensions_initialized'): + if not hasattr(self, "_extensions_initialized"): self._extensions_initialized = True - for extension in getattr(self.model, '_extensions', []): + for extension in getattr(self.model, "_extensions", []): extension.handle_modeladmin(self) def add_extension_options(self, *f): if self.fieldsets is None: return - if isinstance(f[-1], dict): # called with a fieldset + if isinstance(f[-1], dict): # called with a fieldset self.fieldsets.insert(self.fieldset_insertion_index, f) - f[1]['classes'] = list(f[1].get('classes', [])) - f[1]['classes'].append('collapse') - elif f: # assume called with "other" fields + f[1]["classes"] = list(f[1].get("classes", [])) + f[1]["classes"].append("collapse") + elif f: # assume called with "other" fields try: - self.fieldsets[1][1]['fields'].extend(f) + self.fieldsets[1][1]["fields"].extend(f) except IndexError: # Fall back to first fieldset if second does not exist # XXX This is really messy. - self.fieldsets[0][1]['fields'].extend(f) + self.fieldsets[0][1]["fields"].extend(f) def extend_list(self, attribute, iterable): extended = list(getattr(self, attribute, ())) @@ -129,12 +130,14 @@ def prefetch_modeladmin_get_queryset(modeladmin, *lookups): """ Wraps default modeladmin ``get_queryset`` to prefetch related lookups. """ + def do_wrap(f): @wraps(f) def wrapper(request, *args, **kwargs): qs = f(request, *args, **kwargs) qs = qs.prefetch_related(*lookups) return qs + return wrapper modeladmin.get_queryset = do_wrap(modeladmin.get_queryset) diff --git a/feincms/extensions/changedate.py b/feincms/extensions/changedate.py index cb21d288a..b470ff387 100644 --- a/feincms/extensions/changedate.py +++ b/feincms/extensions/changedate.py @@ -38,10 +38,14 @@ def dt_to_utc_timestamp(dt): class Extension(extensions.Extension): def handle_model(self): - self.model.add_to_class('creation_date', models.DateTimeField( - _('creation date'), null=True, editable=False)) - self.model.add_to_class('modification_date', models.DateTimeField( - _('modification date'), null=True, editable=False)) + self.model.add_to_class( + "creation_date", + models.DateTimeField(_("creation date"), null=True, editable=False), + ) + self.model.add_to_class( + "modification_date", + models.DateTimeField(_("modification date"), null=True, editable=False), + ) self.model.last_modified = lambda p: p.modification_date @@ -51,16 +55,17 @@ def handle_model(self): # ------------------------------------------------------------------------ def last_modified_response_processor(page, request, response): # Don't include Last-Modified if we don't want to be cached - if "no-cache" in response.get('Cache-Control', ''): + if "no-cache" in response.get("Cache-Control", ""): return # If we already have a Last-Modified, take the later one last_modified = dt_to_utc_timestamp(page.last_modified()) - if response.has_header('Last-Modified'): + if response.has_header("Last-Modified"): last_modified = max( - last_modified, - mktime_tz(parsedate_tz(response['Last-Modified']))) + last_modified, mktime_tz(parsedate_tz(response["Last-Modified"])) + ) + + response["Last-Modified"] = http_date(last_modified) - response['Last-Modified'] = http_date(last_modified) # ------------------------------------------------------------------------ diff --git a/feincms/extensions/ct_tracker.py b/feincms/extensions/ct_tracker.py index 51308e5f8..9f263c535 100644 --- a/feincms/extensions/ct_tracker.py +++ b/feincms/extensions/ct_tracker.py @@ -46,34 +46,37 @@ def _fetch_content_type_counts(self): empty _ct_inventory. """ - if 'counts' not in self._cache: - if (self.item._ct_inventory and - self.item._ct_inventory.get('_version_', -1) == - INVENTORY_VERSION): + if "counts" not in self._cache: + if ( + self.item._ct_inventory + and self.item._ct_inventory.get("_version_", -1) == INVENTORY_VERSION + ): try: - self._cache['counts'] = self._from_inventory( - self.item._ct_inventory) + self._cache["counts"] = self._from_inventory( + self.item._ct_inventory + ) except KeyError: # It's possible that the inventory does not fit together # with the current models anymore, f.e. because a content # type has been removed. pass - if 'counts' not in self._cache: + if "counts" not in self._cache: super(TrackerContentProxy, self)._fetch_content_type_counts() - self.item._ct_inventory = self._to_inventory( - self._cache['counts']) + self.item._ct_inventory = self._to_inventory(self._cache["counts"]) self.item.__class__.objects.filter(id=self.item.id).update( - _ct_inventory=self.item._ct_inventory) + _ct_inventory=self.item._ct_inventory + ) # Run post save handler by hand - if hasattr(self.item, 'get_descendants'): + if hasattr(self.item, "get_descendants"): self.item.get_descendants(include_self=False).update( - _ct_inventory=None) - return self._cache['counts'] + _ct_inventory=None + ) + return self._cache["counts"] def _translation_map(self): cls = self.item.__class__ @@ -101,20 +104,20 @@ def _from_inventory(self, inventory): map = self._translation_map() - return dict((region, [ - (pk, map[-ct]) for pk, ct in items - ]) for region, items in inventory.items() if region != '_version_') + return dict( + (region, [(pk, map[-ct]) for pk, ct in items]) + for region, items in inventory.items() + if region != "_version_" + ) def _to_inventory(self, counts): map = self._translation_map() inventory = dict( - ( - region, - [(pk, map[ct]) for pk, ct in items], - ) for region, items in counts.items() + (region, [(pk, map[ct]) for pk, ct in items]) + for region, items in counts.items() ) - inventory['_version_'] = INVENTORY_VERSION + inventory["_version_"] = INVENTORY_VERSION return inventory @@ -151,12 +154,15 @@ def single_pre_save_handler(sender, instance, **kwargs): # ------------------------------------------------------------------------ class Extension(extensions.Extension): def handle_model(self): - self.model.add_to_class('_ct_inventory', JSONField( - _('content types'), editable=False, blank=True, null=True)) + self.model.add_to_class( + "_ct_inventory", + JSONField(_("content types"), editable=False, blank=True, null=True), + ) self.model.content_proxy_class = TrackerContentProxy pre_save.connect(single_pre_save_handler, sender=self.model) - if hasattr(self.model, 'get_descendants'): + if hasattr(self.model, "get_descendants"): post_save.connect(tree_post_save_handler, sender=self.model) + # ------------------------------------------------------------------------ diff --git a/feincms/extensions/datepublisher.py b/feincms/extensions/datepublisher.py index 39c26f6d5..bae2387d8 100644 --- a/feincms/extensions/datepublisher.py +++ b/feincms/extensions/datepublisher.py @@ -24,7 +24,7 @@ # ------------------------------------------------------------------------ -def format_date(d, if_none=''): +def format_date(d, if_none=""): """ Format a date in a nice human readable way: Omit the year if it's the current year. Also return a default value if no date is passed in. @@ -34,12 +34,12 @@ def format_date(d, if_none=''): return if_none now = timezone.now() - fmt = (d.year == now.year) and '%d.%m' or '%d.%m.%Y' + fmt = (d.year == now.year) and "%d.%m" or "%d.%m.%Y" return d.strftime(fmt) def latest_children(self): - return self.get_children().order_by('-publication_date') + return self.get_children().order_by("-publication_date") # ------------------------------------------------------------------------ @@ -70,8 +70,8 @@ def granular_now(n=None, default_tz=None): retval = timezone.make_aware(d, default_tz, is_dst=False) except TypeError: # Pre-Django 1.9 retval = timezone.make_aware( - datetime(n.year, n.month, n.day, n.hour + 1, rounded_minute), - default_tz) + datetime(n.year, n.month, n.day, n.hour + 1, rounded_minute), default_tz + ) return retval @@ -95,16 +95,19 @@ def datepublisher_response_processor(page, request, response): class Extension(extensions.Extension): def handle_model(self): self.model.add_to_class( - 'publication_date', - models.DateTimeField(_('publication date'), default=granular_now)) + "publication_date", + models.DateTimeField(_("publication date"), default=granular_now), + ) self.model.add_to_class( - 'publication_end_date', + "publication_end_date", models.DateTimeField( - _('publication end date'), - blank=True, null=True, - help_text=_( - 'Leave empty if the entry should stay active forever.'))) - self.model.add_to_class('latest_children', latest_children) + _("publication end date"), + blank=True, + null=True, + help_text=_("Leave empty if the entry should stay active forever."), + ), + ) + self.model.add_to_class("latest_children", latest_children) # Patch in rounding the pub and pub_end dates on save orig_save = self.model.save @@ -113,44 +116,52 @@ def granular_save(obj, *args, **kwargs): if obj.publication_date: obj.publication_date = granular_now(obj.publication_date) if obj.publication_end_date: - obj.publication_end_date = granular_now( - obj.publication_end_date) + obj.publication_end_date = granular_now(obj.publication_end_date) orig_save(obj, *args, **kwargs) + self.model.save = granular_save # Append publication date active check - if hasattr(self.model._default_manager, 'add_to_active_filters'): + if hasattr(self.model._default_manager, "add_to_active_filters"): self.model._default_manager.add_to_active_filters( lambda queryset: queryset.filter( - Q(publication_date__lte=granular_now()) & - (Q(publication_end_date__isnull=True) | - Q(publication_end_date__gt=granular_now()))), - key='datepublisher', + Q(publication_date__lte=granular_now()) + & ( + Q(publication_end_date__isnull=True) + | Q(publication_end_date__gt=granular_now()) + ) + ), + key="datepublisher", ) # Processor to patch up response headers for expiry date - self.model.register_response_processor( - datepublisher_response_processor) + self.model.register_response_processor(datepublisher_response_processor) def handle_modeladmin(self, modeladmin): def datepublisher_admin(self, obj): - return mark_safe('%s – %s' % ( - format_date(obj.publication_date), - format_date(obj.publication_end_date, '∞'), - )) - datepublisher_admin.short_description = _('visible from - to') + return mark_safe( + "%s – %s" + % ( + format_date(obj.publication_date), + format_date(obj.publication_end_date, "∞"), + ) + ) + + datepublisher_admin.short_description = _("visible from - to") modeladmin.__class__.datepublisher_admin = datepublisher_admin try: - pos = modeladmin.list_display.index('is_visible_admin') + pos = modeladmin.list_display.index("is_visible_admin") except ValueError: pos = len(modeladmin.list_display) - modeladmin.list_display.insert(pos + 1, 'datepublisher_admin') + modeladmin.list_display.insert(pos + 1, "datepublisher_admin") + + modeladmin.add_extension_options( + _("Date-based publishing"), + {"fields": ["publication_date", "publication_end_date"]}, + ) - modeladmin.add_extension_options(_('Date-based publishing'), { - 'fields': ['publication_date', 'publication_end_date'], - }) # ------------------------------------------------------------------------ diff --git a/feincms/extensions/featured.py b/feincms/extensions/featured.py index 139edcdda..9d5f2e92d 100644 --- a/feincms/extensions/featured.py +++ b/feincms/extensions/featured.py @@ -13,15 +13,10 @@ class Extension(extensions.Extension): def handle_model(self): self.model.add_to_class( - 'featured', - models.BooleanField( - _('featured'), - default=False, - ), + "featured", models.BooleanField(_("featured"), default=False) ) def handle_modeladmin(self, modeladmin): - modeladmin.add_extension_options(_('Featured'), { - 'fields': ('featured',), - 'classes': ('collapse',), - }) + modeladmin.add_extension_options( + _("Featured"), {"fields": ("featured",), "classes": ("collapse",)} + ) diff --git a/feincms/extensions/seo.py b/feincms/extensions/seo.py index f71d9f5f3..3f01152a0 100644 --- a/feincms/extensions/seo.py +++ b/feincms/extensions/seo.py @@ -12,24 +12,31 @@ class Extension(extensions.Extension): def handle_model(self): - self.model.add_to_class('meta_keywords', models.TextField( - _('meta keywords'), - blank=True, - help_text=_('Keywords are ignored by most search engines.'))) - self.model.add_to_class('meta_description', models.TextField( - _('meta description'), - blank=True, - help_text=_('This text is displayed on the search results page. ' - 'It is however not used for the SEO ranking. ' - 'Text longer than 140 characters is truncated.'))) + self.model.add_to_class( + "meta_keywords", + models.TextField( + _("meta keywords"), + blank=True, + help_text=_("Keywords are ignored by most search engines."), + ), + ) + self.model.add_to_class( + "meta_description", + models.TextField( + _("meta description"), + blank=True, + help_text=_( + "This text is displayed on the search results page. " + "It is however not used for the SEO ranking. " + "Text longer than 140 characters is truncated." + ), + ), + ) def handle_modeladmin(self, modeladmin): - modeladmin.extend_list( - 'search_fields', - ['meta_keywords', 'meta_description'], - ) + modeladmin.extend_list("search_fields", ["meta_keywords", "meta_description"]) - modeladmin.add_extension_options(_('Search engine optimization'), { - 'fields': ('meta_keywords', 'meta_description'), - 'classes': ('collapse',), - }) + modeladmin.add_extension_options( + _("Search engine optimization"), + {"fields": ("meta_keywords", "meta_description"), "classes": ("collapse",)}, + ) diff --git a/feincms/extensions/translations.py b/feincms/extensions/translations.py index 4c37cda1d..bbcda6d15 100644 --- a/feincms/extensions/translations.py +++ b/feincms/extensions/translations.py @@ -36,7 +36,7 @@ logger = logging.getLogger(__name__) LANGUAGE_COOKIE_NAME = django_settings.LANGUAGE_COOKIE_NAME -if hasattr(translation, 'LANGUAGE_SESSION_KEY'): +if hasattr(translation, "LANGUAGE_SESSION_KEY"): LANGUAGE_SESSION_KEY = translation.LANGUAGE_SESSION_KEY else: # Django 1.6 @@ -50,8 +50,10 @@ def user_has_language_set(request): This is taken later on as an indication that we should not mess with the site's language settings, after all, the user's decision is what counts. """ - if (hasattr(request, 'session') and - request.session.get(LANGUAGE_SESSION_KEY) is not None): + if ( + hasattr(request, "session") + and request.session.get(LANGUAGE_SESSION_KEY) is not None + ): return True if LANGUAGE_COOKIE_NAME in request.COOKIES: return True @@ -91,19 +93,18 @@ def translation_set_language(request, select_language): translation.activate(select_language) request.LANGUAGE_CODE = translation.get_language() - if hasattr(request, 'session'): + if hasattr(request, "session"): # User has a session, then set this language there if select_language != request.session.get(LANGUAGE_SESSION_KEY): request.session[LANGUAGE_SESSION_KEY] = select_language - elif request.method == 'GET' and not fallback: + elif request.method == "GET" and not fallback: # No session is active. We need to set a cookie for the language # so that it persists when users change their location to somewhere # not under the control of the CMS. # Only do this when request method is GET (mainly, do not abort # POST requests) response = HttpResponseRedirect(request.get_full_path()) - response.set_cookie( - str(LANGUAGE_COOKIE_NAME), select_language) + response.set_cookie(str(LANGUAGE_COOKIE_NAME), select_language) return response @@ -118,8 +119,8 @@ def translations_request_processor_explicit(page, request): desired_language = page.language # ...except if the user explicitely wants to switch language - if 'set_language' in request.GET: - desired_language = request.GET['set_language'] + if "set_language" in request.GET: + desired_language = request.GET["set_language"] # ...or the user already has explicitely set a language, bail out and # don't change it for them behind their back elif user_has_language_set(request): @@ -131,7 +132,7 @@ def translations_request_processor_explicit(page, request): # ------------------------------------------------------------------------ def translations_request_processor_standard(page, request): # If this page is just a redirect, don't do any language specific setup - if getattr(page, 'redirect_to', None): + if getattr(page, "redirect_to", None): return if page.language == translation.get_language(): @@ -142,51 +143,54 @@ def translations_request_processor_standard(page, request): # ------------------------------------------------------------------------ def get_current_language_code(request): - language_code = getattr(request, 'LANGUAGE_CODE', None) + language_code = getattr(request, "LANGUAGE_CODE", None) if language_code is None: logger.warning( "Could not access request.LANGUAGE_CODE. Is 'django.middleware." - "locale.LocaleMiddleware' in MIDDLEWARE_CLASSES?") + "locale.LocaleMiddleware' in MIDDLEWARE_CLASSES?" + ) return language_code # ------------------------------------------------------------------------ class Extension(extensions.Extension): - def handle_model(self): cls = self.model cls.add_to_class( - 'language', + "language", models.CharField( - _('language'), + _("language"), max_length=10, choices=django_settings.LANGUAGES, - default=django_settings.LANGUAGES[0][0])) + default=django_settings.LANGUAGES[0][0], + ), + ) cls.add_to_class( - 'translation_of', + "translation_of", models.ForeignKey( - 'self', + "self", on_delete=models.CASCADE, - blank=True, null=True, verbose_name=_('translation of'), - related_name='translations', - limit_choices_to={'language': django_settings.LANGUAGES[0][0]}, - help_text=_( - 'Leave this empty for entries in the primary language.'), - ) + blank=True, + null=True, + verbose_name=_("translation of"), + related_name="translations", + limit_choices_to={"language": django_settings.LANGUAGES[0][0]}, + help_text=_("Leave this empty for entries in the primary language."), + ), ) - if hasattr(cls, 'register_request_processor'): + if hasattr(cls, "register_request_processor"): if settings.FEINCMS_TRANSLATION_POLICY == "EXPLICIT": cls.register_request_processor( - translations_request_processor_explicit, - key='translations') + translations_request_processor_explicit, key="translations" + ) else: # STANDARD cls.register_request_processor( - translations_request_processor_standard, - key='translations') + translations_request_processor_standard, key="translations" + ) - if hasattr(cls, 'get_redirect_to_target'): + if hasattr(cls, "get_redirect_to_target"): original_get_redirect_to_target = cls.get_redirect_to_target @monkeypatch_method(cls) @@ -199,7 +203,7 @@ def get_redirect_to_target(self, request=None): redirection. """ target = original_get_redirect_to_target(self, request) - if target and target.find('//') == -1: + if target and target.find("//") == -1: # Not an offsite link http://bla/blubb try: page = cls.objects.page_for_path(target) @@ -217,9 +221,10 @@ def available_translations(self): if not self.id: # New, unsaved pages have no translations return [] - if hasattr(cls.objects, 'apply_active_filters'): + if hasattr(cls.objects, "apply_active_filters"): filter_active = cls.objects.apply_active_filters else: + def filter_active(queryset): return queryset @@ -228,9 +233,10 @@ def filter_active(queryset): elif self.translation_of: # reuse prefetched queryset, do not filter it res = [ - t for t - in filter_active(self.translation_of.translations.all()) - if t.language != self.language] + t + for t in filter_active(self.translation_of.translations.all()) + if t.language != self.language + ] res.insert(0, self.translation_of) return res else: @@ -244,7 +250,10 @@ def get_original_translation(self, *args, **kwargs): return self.translation_of logger.debug( "Page pk=%d (%s) has no primary language translation (%s)", - self.pk, self.language, django_settings.LANGUAGES[0][0]) + self.pk, + self.language, + django_settings.LANGUAGES[0][0], + ) return self @monkeypatch_property(cls) @@ -253,12 +262,12 @@ def original_translation(self): @monkeypatch_method(cls) def get_translation(self, language): - return self.original_translation.translations.get( - language=language) + return self.original_translation.translations.get(language=language) def handle_modeladmin(self, modeladmin): extensions.prefetch_modeladmin_get_queryset( - modeladmin, 'translation_of__translations', 'translations') + modeladmin, "translation_of__translations", "translations" + ) def available_translations_admin(self, page): # Do not use available_translations() because we don't care @@ -268,10 +277,7 @@ def available_translations_admin(self, page): if page.translation_of: translations.append(page.translation_of) translations.extend(page.translation_of.translations.all()) - translations = { - p.language: p.id - for p in translations - } + translations = {p.language: p.id for p in translations} links = [] @@ -280,33 +286,30 @@ def available_translations_admin(self, page): continue if key in translations: - links.append('%s' % ( - translations[key], _('Edit translation'), key.upper())) + links.append( + '%s' + % (translations[key], _("Edit translation"), key.upper()) + ) else: links.append( '%s' % ( - page.id, - key, - _('Create translation'), - key.upper() - ) + '%s&language=%s" title="%s">%s' + % (page.id, key, _("Create translation"), key.upper()) ) - return mark_safe(' | '.join(links)) + return mark_safe(" | ".join(links)) - available_translations_admin.short_description = _('translations') - modeladmin.__class__.available_translations_admin =\ - available_translations_admin + available_translations_admin.short_description = _("translations") + modeladmin.__class__.available_translations_admin = available_translations_admin - if hasattr(modeladmin, 'add_extension_options'): - modeladmin.add_extension_options('language', 'translation_of') + if hasattr(modeladmin, "add_extension_options"): + modeladmin.add_extension_options("language", "translation_of") modeladmin.extend_list( - 'list_display', - ['language', 'available_translations_admin'], + "list_display", ["language", "available_translations_admin"] ) - modeladmin.extend_list('list_filter', ['language']) - modeladmin.extend_list('raw_id_fields', ['translation_of']) + modeladmin.extend_list("list_filter", ["language"]) + modeladmin.extend_list("raw_id_fields", ["translation_of"]) + # ------------------------------------------------------------------------ diff --git a/feincms/management/commands/medialibrary_orphans.py b/feincms/management/commands/medialibrary_orphans.py index 7a27a56de..d1c1eb00b 100644 --- a/feincms/management/commands/medialibrary_orphans.py +++ b/feincms/management/commands/medialibrary_orphans.py @@ -12,10 +12,10 @@ class Command(NoArgsCommand): help = "Prints all orphaned files in the `media/medialibrary` folder" def handle_noargs(self, **options): - mediafiles = list(MediaFile.objects.values_list('file', flat=True)) + mediafiles = list(MediaFile.objects.values_list("file", flat=True)) # TODO make this smarter, and take MEDIA_ROOT into account - for base, dirs, files in os.walk('media/medialibrary'): + for base, dirs, files in os.walk("media/medialibrary"): for f in files: full = os.path.join(base[6:], f) if force_text(full) not in mediafiles: diff --git a/feincms/management/commands/medialibrary_to_filer.py b/feincms/management/commands/medialibrary_to_filer.py index ff34c8d41..2ac3fefec 100644 --- a/feincms/management/commands/medialibrary_to_filer.py +++ b/feincms/management/commands/medialibrary_to_filer.py @@ -17,31 +17,31 @@ PageFilerImageContent = Page.content_type_for(FilerImageContent) -assert all(( - PageMediaFileContent, - PageFilerFileContent, - PageFilerImageContent)), 'Not all required models available' +assert all( + (PageMediaFileContent, PageFilerFileContent, PageFilerImageContent) +), "Not all required models available" class Command(NoArgsCommand): help = "Migrate the medialibrary and contents to django-filer" def handle_noargs(self, **options): - user = User.objects.order_by('pk')[0] + user = User.objects.order_by("pk")[0] count = MediaFile.objects.count() - for i, mediafile in enumerate(MediaFile.objects.order_by('pk')): - model = Image if mediafile.type == 'image' else File - content_model = PageFilerImageContent if mediafile.type == 'image' else PageFilerFileContent # noqa + for i, mediafile in enumerate(MediaFile.objects.order_by("pk")): + model = Image if mediafile.type == "image" else File + content_model = ( + PageFilerImageContent + if mediafile.type == "image" + else PageFilerFileContent + ) # noqa filerfile = model.objects.create( owner=user, original_filename=mediafile.file.name, - file=DjangoFile( - mediafile.file.file, - name=mediafile.file.name, - ), + file=DjangoFile(mediafile.file.file, name=mediafile.file.name), ) contents = PageMediaFileContent.objects.filter(mediafile=mediafile) @@ -58,6 +58,6 @@ def handle_noargs(self, **options): content.delete() if not i % 10: - self.stdout.write('%s / %s files\n' % (i, count)) + self.stdout.write("%s / %s files\n" % (i, count)) - self.stdout.write('%s / %s files\n' % (count, count)) + self.stdout.write("%s / %s files\n" % (count, count)) diff --git a/feincms/management/commands/rebuild_mptt.py b/feincms/management/commands/rebuild_mptt.py index b99291d02..f290daa50 100644 --- a/feincms/management/commands/rebuild_mptt.py +++ b/feincms/management/commands/rebuild_mptt.py @@ -19,12 +19,10 @@ class Command(BaseCommand): - help = ( - "Run this manually to rebuild your mptt pointers. Only use in" - " emergencies.") + help = "Run this manually to rebuild your mptt pointers. Only use in emergencies." def handle_noargs(self, **options): - self.handle(**options) + self.handle(**options) def handle(self, **options): self.stdout.write("Rebuilding MPTT pointers for Page") diff --git a/feincms/models.py b/feincms/models.py index 0e3d49b2c..0bcddabfc 100644 --- a/feincms/models.py +++ b/feincms/models.py @@ -36,7 +36,7 @@ class Region(object): def __init__(self, key, title, *args): self.key = key self.title = title - self.inherited = args and args[0] == 'inherited' or False + self.inherited = args and args[0] == "inherited" or False self._content_types = [] def __str__(self): @@ -50,8 +50,7 @@ def content_types(self): """ return [ - (ct.__name__.lower(), ct._meta.verbose_name) - for ct in self._content_types + (ct.__name__.lower(), ct._meta.verbose_name) for ct in self._content_types ] @@ -62,8 +61,7 @@ class Template(object): CMS object, most commonly a page. """ - def __init__(self, title, path, regions, key=None, preview_image=None, - **kwargs): + def __init__(self, title, path, regions, key=None, preview_image=None, **kwargs): # The key is what will be stored in the database. If key is undefined # use the template path as fallback. if not key: @@ -73,10 +71,10 @@ def __init__(self, title, path, regions, key=None, preview_image=None, self.title = title self.path = path self.preview_image = preview_image - self.singleton = kwargs.get('singleton', False) - self.child_template = kwargs.get('child_template', None) - self.enforce_leaf = kwargs.get('enforce_leaf', False) - self.urlconf = kwargs.get('urlconf', None) + self.singleton = kwargs.get("singleton", False) + self.child_template = kwargs.get("child_template", None) + self.enforce_leaf = kwargs.get("enforce_leaf", False) + self.urlconf = kwargs.get("urlconf", None) def _make_region(data): if isinstance(data, Region): @@ -105,9 +103,7 @@ def __init__(self, item): item._needs_content_types() self.item = item self.db = item._state.db - self._cache = { - 'cts': {}, - } + self._cache = {"cts": {}} def _inherit_from(self): """ @@ -118,8 +114,7 @@ def _inherit_from(self): is good enough (tm) for pages. """ - return self.item.get_ancestors(ascending=True).values_list( - 'pk', flat=True) + return self.item.get_ancestors(ascending=True).values_list("pk", flat=True) def _fetch_content_type_counts(self): """ @@ -138,7 +133,7 @@ def _fetch_content_type_counts(self): } """ - if 'counts' not in self._cache: + if "counts" not in self._cache: counts = self._fetch_content_type_count_helper(self.item.pk) empty_inherited_regions = set() @@ -149,7 +144,8 @@ def _fetch_content_type_counts(self): if empty_inherited_regions: for parent in self._inherit_from(): parent_counts = self._fetch_content_type_count_helper( - parent, regions=tuple(empty_inherited_regions)) + parent, regions=tuple(empty_inherited_regions) + ) counts.update(parent_counts) for key in parent_counts.keys(): @@ -158,28 +154,27 @@ def _fetch_content_type_counts(self): if not empty_inherited_regions: break - self._cache['counts'] = counts - return self._cache['counts'] + self._cache["counts"] = counts + return self._cache["counts"] def _fetch_content_type_count_helper(self, pk, regions=None): - tmpl = [ - 'SELECT %d AS ct_idx, region, COUNT(id) FROM %s WHERE parent_id=%s' - ] + tmpl = ["SELECT %d AS ct_idx, region, COUNT(id) FROM %s WHERE parent_id=%s"] args = [] if regions: - tmpl.append( - 'AND region IN (' + ','.join(['%%s'] * len(regions)) + ')') + tmpl.append("AND region IN (" + ",".join(["%%s"] * len(regions)) + ")") args.extend(regions * len(self.item._feincms_content_types)) - tmpl.append('GROUP BY region') - tmpl = ' '.join(tmpl) + tmpl.append("GROUP BY region") + tmpl = " ".join(tmpl) - sql = ' UNION '.join([ - tmpl % (idx, cls._meta.db_table, pk) - for idx, cls in enumerate(self.item._feincms_content_types) - ]) - sql = 'SELECT * FROM ( ' + sql + ' ) AS ct ORDER BY ct_idx' + sql = " UNION ".join( + [ + tmpl % (idx, cls._meta.db_table, pk) + for idx, cls in enumerate(self.item._feincms_content_types) + ] + ) + sql = "SELECT * FROM ( " + sql + " ) AS ct ORDER BY ct_idx" cursor = connections[self.db].cursor() cursor.execute(sql, args) @@ -200,55 +195,56 @@ def _populate_content_type_caches(self, types): for region, counts in self._fetch_content_type_counts().items(): for pk, ct_idx in counts: counts_by_type.setdefault( - self.item._feincms_content_types[ct_idx], - [], + self.item._feincms_content_types[ct_idx], [] ).append((region, pk)) # Resolve abstract to concrete content types content_types = ( - cls for cls in self.item._feincms_content_types + cls + for cls in self.item._feincms_content_types if issubclass(cls, tuple(types)) ) for cls in content_types: counts = counts_by_type.get(cls) - if cls not in self._cache['cts']: + if cls not in self._cache["cts"]: if counts: - self._cache['cts'][cls] = list(cls.get_queryset(reduce( - operator.or_, - (Q(region=r[0], parent=r[1]) for r in counts) - ))) + self._cache["cts"][cls] = list( + cls.get_queryset( + reduce( + operator.or_, + (Q(region=r[0], parent=r[1]) for r in counts), + ) + ) + ) else: - self._cache['cts'][cls] = [] + self._cache["cts"][cls] = [] # share this content proxy object between all content items # so that each can use obj.parent.content to determine its # relationship to its siblings, etc. - for cls, objects in self._cache['cts'].items(): + for cls, objects in self._cache["cts"].items(): for obj in objects: - setattr(obj.parent, '_content_proxy', self) + setattr(obj.parent, "_content_proxy", self) def _fetch_regions(self): """ Fetches all content types and group content types into regions """ - if 'regions' not in self._cache: - self._populate_content_type_caches( - self.item._feincms_content_types) + if "regions" not in self._cache: + self._populate_content_type_caches(self.item._feincms_content_types) contents = {} - for cls, content_list in self._cache['cts'].items(): + for cls, content_list in self._cache["cts"].items(): for instance in content_list: contents.setdefault(instance.region, []).append(instance) - self._cache['regions'] = dict( - ( - region, - sorted(instances, key=lambda c: c.ordering), - ) for region, instances in contents.items() + self._cache["regions"] = dict( + (region, sorted(instances, key=lambda c: c.ordering)) + for region, instances in contents.items() ) - return self._cache['regions'] + return self._cache["regions"] def all_of_type(self, type_or_tuple): """ @@ -262,11 +258,11 @@ def all_of_type(self, type_or_tuple): """ content_list = [] - if not hasattr(type_or_tuple, '__iter__'): + if not hasattr(type_or_tuple, "__iter__"): type_or_tuple = (type_or_tuple,) self._populate_content_type_caches(type_or_tuple) - for type, contents in self._cache['cts'].items(): + for type, contents in self._cache["cts"].items(): if any(issubclass(type, t) for t in type_or_tuple): content_list.extend(contents) @@ -277,16 +273,17 @@ def _get_media(self): Collect the media files of all content types of the current object """ - if 'media' not in self._cache: + if "media" not in self._cache: media = Media() for contents in self._fetch_regions().values(): for content in contents: - if hasattr(content, 'media'): + if hasattr(content, "media"): media = media + content.media - self._cache['media'] = media - return self._cache['media'] + self._cache["media"] = media + return self._cache["media"] + media = property(_get_media) def __getattr__(self, attr): @@ -297,7 +294,7 @@ def __getattr__(self, attr): has the inherited flag set, this method will go up the ancestor chain until either some item contents have found or no ancestors are left. """ - if (attr.startswith('__')): + if attr.startswith("__"): raise AttributeError # Do not trigger loading of real content type models if not necessary @@ -337,15 +334,15 @@ def register_regions(cls, *regions): ) """ - if hasattr(cls, 'template'): + if hasattr(cls, "template"): warnings.warn( - 'Ignoring second call to register_regions.', - RuntimeWarning) + "Ignoring second call to register_regions.", RuntimeWarning + ) return # implicitly creates a dummy template object -- the item editor # depends on the presence of a template. - cls.template = Template('', '', regions) + cls.template = Template("", "", regions) cls._feincms_all_regions = cls.template.regions @classmethod @@ -374,7 +371,7 @@ def register_templates(cls, *templates): }) """ - if not hasattr(cls, '_feincms_templates'): + if not hasattr(cls, "_feincms_templates"): cls._feincms_templates = OrderedDict() cls.TEMPLATES_CHOICES = [] @@ -387,21 +384,33 @@ def register_templates(cls, *templates): instances[template.key] = template try: - field = next(iter( - field for field in cls._meta.local_fields - if field.name == 'template_key')) + field = next( + iter( + field + for field in cls._meta.local_fields + if field.name == "template_key" + ) + ) except (StopIteration,): cls.add_to_class( - 'template_key', - models.CharField(_('template'), max_length=255, choices=( - # Dummy choice to trick Django. Cannot be empty, - # otherwise admin.E023 happens. - ('__dummy', '__dummy'), - )) + "template_key", + models.CharField( + _("template"), + max_length=255, + choices=( + # Dummy choice to trick Django. Cannot be empty, + # otherwise admin.E023 happens. + ("__dummy", "__dummy"), + ), + ), + ) + field = next( + iter( + field + for field in cls._meta.local_fields + if field.name == "template_key" + ) ) - field = next(iter( - field for field in cls._meta.local_fields - if field.name == 'template_key')) def _template(self): ensure_completely_loaded() @@ -412,12 +421,13 @@ def _template(self): # return first template as a fallback if the template # has changed in-between return self._feincms_templates[ - list(self._feincms_templates.keys())[0]] + list(self._feincms_templates.keys())[0] + ] cls.template = property(_template) cls.TEMPLATE_CHOICES = [ - (template_.key, template_.title,) + (template_.key, template_.title) for template_ in cls._feincms_templates.values() ] try: @@ -445,7 +455,7 @@ def content(self): ``content_proxy_class`` member variable. """ - if not hasattr(self, '_content_proxy'): + if not hasattr(self, "_content_proxy"): self._content_proxy = self.content_proxy_class(self) return self._content_proxy @@ -472,19 +482,20 @@ def _create_content_base(cls): class Meta: abstract = True app_label = cls._meta.app_label - ordering = ['ordering'] + ordering = ["ordering"] def __str__(self): return ( - '%s, region=%s,' - ' ordering=%d>') % ( + "%s, region=%s," " ordering=%d>" + ) % ( self.__class__.__name__, self.pk, self.parent.__class__.__name__, self.parent.pk, self.parent, self.region, - self.ordering) + self.ordering, + ) def render(self, **kwargs): """ @@ -495,7 +506,7 @@ def render(self, **kwargs): time instead of adding region-specific render methods. """ - render_fn = getattr(self, 'render_%s' % self.region, None) + render_fn = getattr(self, "render_%s" % self.region, None) if render_fn: return render_fn(**kwargs) @@ -512,31 +523,33 @@ def get_queryset(cls, filter_args): # needs to know where a model comes # from, therefore we ensure that the # module is always known. - '__module__': cls.__module__, - '__str__': __str__, - 'render': render, - 'get_queryset': classmethod(get_queryset), - 'Meta': Meta, - 'parent': models.ForeignKey( - cls, related_name='%(class)s_set', - on_delete=models.CASCADE), - 'region': models.CharField(max_length=255), - 'ordering': models.IntegerField(_('ordering'), default=0), + "__module__": cls.__module__, + "__str__": __str__, + "render": render, + "get_queryset": classmethod(get_queryset), + "Meta": Meta, + "parent": models.ForeignKey( + cls, related_name="%(class)s_set", on_delete=models.CASCADE + ), + "region": models.CharField(max_length=255), + "ordering": models.IntegerField(_("ordering"), default=0), } # create content base type and save reference on CMS class - name = '_Internal%sContentTypeBase' % cls.__name__ + name = "_Internal%sContentTypeBase" % cls.__name__ if hasattr(sys.modules[cls.__module__], name): warnings.warn( - 'The class %s.%s has the same name as the class that ' - 'FeinCMS auto-generates based on %s.%s. To avoid database' - 'errors and import clashes, rename one of these classes.' + "The class %s.%s has the same name as the class that " + "FeinCMS auto-generates based on %s.%s. To avoid database" + "errors and import clashes, rename one of these classes." % (cls.__module__, name, cls.__module__, cls.__name__), - RuntimeWarning) + RuntimeWarning, + ) cls._feincms_content_model = python_2_unicode_compatible( - type(str(name), (models.Model,), attrs)) + type(str(name), (models.Model,), attrs) + ) # list of concrete content types cls._feincms_content_types = [] @@ -557,23 +570,24 @@ def get_queryset(cls, filter_args): # list of item editor context processors, will be extended by # content types - if hasattr(cls, 'feincms_item_editor_context_processors'): + if hasattr(cls, "feincms_item_editor_context_processors"): cls.feincms_item_editor_context_processors = list( - cls.feincms_item_editor_context_processors) + cls.feincms_item_editor_context_processors + ) else: cls.feincms_item_editor_context_processors = [] # list of templates which should be included in the item editor, # will be extended by content types - if hasattr(cls, 'feincms_item_editor_includes'): + if hasattr(cls, "feincms_item_editor_includes"): cls.feincms_item_editor_includes = dict( - cls.feincms_item_editor_includes) + cls.feincms_item_editor_includes + ) else: cls.feincms_item_editor_includes = {} @classmethod - def create_content_type(cls, model, regions=None, class_name=None, - **kwargs): + def create_content_type(cls, model, regions=None, class_name=None, **kwargs): """ This is the method you'll use to create concrete content types. @@ -622,14 +636,19 @@ def create_content_type(cls, model, regions=None, class_name=None, # content types with the same class name because of related_name # clashes try: - getattr(cls, '%s_set' % class_name.lower()) + getattr(cls, "%s_set" % class_name.lower()) warnings.warn( - 'Cannot create content type using %s.%s for %s.%s,' - ' because %s_set is already taken.' % ( - model.__module__, class_name, - cls.__module__, cls.__name__, - class_name.lower()), - RuntimeWarning) + "Cannot create content type using %s.%s for %s.%s," + " because %s_set is already taken." + % ( + model.__module__, + class_name, + cls.__module__, + cls.__name__, + class_name.lower(), + ), + RuntimeWarning, + ) return except AttributeError: # everything ok @@ -637,16 +656,16 @@ def create_content_type(cls, model, regions=None, class_name=None, if not model._meta.abstract: raise ImproperlyConfigured( - 'Cannot create content type from' - ' non-abstract model (yet).') + "Cannot create content type from" " non-abstract model (yet)." + ) - if not hasattr(cls, '_feincms_content_model'): + if not hasattr(cls, "_feincms_content_model"): cls._create_content_base() feincms_content_base = cls._feincms_content_model class Meta(feincms_content_base.Meta): - db_table = '%s_%s' % (cls._meta.db_table, class_name.lower()) + db_table = "%s_%s" % (cls._meta.db_table, class_name.lower()) verbose_name = model._meta.verbose_name verbose_name_plural = model._meta.verbose_name_plural permissions = model._meta.permissions @@ -659,26 +678,21 @@ class Meta(feincms_content_base.Meta): # content type may be used by several CMS # base models at the same time (f.e. in # the blog and the page module). - '__module__': cls.__module__, - 'Meta': Meta, + "__module__": cls.__module__, + "Meta": Meta, } - new_type = type( - str(class_name), - (model, feincms_content_base,), - attrs, - ) + new_type = type(str(class_name), (model, feincms_content_base), attrs) cls._feincms_content_types.append(new_type) - if hasattr(getattr(new_type, 'process', None), '__call__'): + if hasattr(getattr(new_type, "process", None), "__call__"): cls._feincms_content_types_with_process.append(new_type) - if hasattr(getattr(new_type, 'finalize', None), '__call__'): + if hasattr(getattr(new_type, "finalize", None), "__call__"): cls._feincms_content_types_with_finalize.append(new_type) # content types can be limited to a subset of regions if not regions: - regions = set([ - region.key for region in cls._feincms_all_regions]) + regions = set([region.key for region in cls._feincms_all_regions]) for region in cls._feincms_all_regions: if region.key in regions: @@ -689,7 +703,7 @@ class Meta(feincms_content_base.Meta): # f.e. for the update_rsscontent management command, which needs to # find all concrete RSSContent types, so that the RSS feeds can be # fetched - if not hasattr(model, '_feincms_content_models'): + if not hasattr(model, "_feincms_content_models"): model._feincms_content_models = [] model._feincms_content_models.append(new_type) @@ -699,36 +713,38 @@ class Meta(feincms_content_base.Meta): # Handle optgroup argument for grouping content types in the item # editor - optgroup = kwargs.pop('optgroup', None) + optgroup = kwargs.pop("optgroup", None) if optgroup: new_type.optgroup = optgroup # customization hook. - if hasattr(new_type, 'initialize_type'): + if hasattr(new_type, "initialize_type"): new_type.initialize_type(**kwargs) else: for k, v in kwargs.items(): setattr(new_type, k, v) # collect item editor context processors from the content type - if hasattr(model, 'feincms_item_editor_context_processors'): + if hasattr(model, "feincms_item_editor_context_processors"): cls.feincms_item_editor_context_processors.extend( - model.feincms_item_editor_context_processors) + model.feincms_item_editor_context_processors + ) # collect item editor includes from the content type - if hasattr(model, 'feincms_item_editor_includes'): + if hasattr(model, "feincms_item_editor_includes"): for key, incls in model.feincms_item_editor_includes.items(): - cls.feincms_item_editor_includes.setdefault( - key, set()).update(incls) + cls.feincms_item_editor_includes.setdefault(key, set()).update( + incls + ) ensure_completely_loaded(force=True) return new_type @property def _django_content_type(self): - if not getattr(self, '_cached_django_content_type', None): - self.__class__._cached_django_content_type = ( - ContentType.objects.get_for_model(self)) + if not getattr(self, "_cached_django_content_type", None): + ct = ContentType.objects.get_for_model(self) + self.__class__._cached_django_content_type = ct return self.__class__._cached_django_content_type @classmethod @@ -740,8 +756,10 @@ def content_type_for(cls, model): concrete_type = Page.content_type_for(VideoContent) """ - if (not hasattr(cls, '_feincms_content_types') or - not cls._feincms_content_types): + if ( + not hasattr(cls, "_feincms_content_types") + or not cls._feincms_content_types + ): return None for type in cls._feincms_content_types: @@ -756,10 +774,11 @@ def _needs_templates(cls): # helper which can be used to ensure that either register_regions # or register_templates has been executed before proceeding - if not hasattr(cls, 'template'): + if not hasattr(cls, "template"): raise ImproperlyConfigured( - 'You need to register at least one' - ' template or one region on %s.' % cls.__name__) + "You need to register at least one" + " template or one region on %s." % cls.__name__ + ) @classmethod def _needs_content_types(cls): @@ -767,10 +786,11 @@ def _needs_content_types(cls): # Check whether any content types have been created for this base # class - if not getattr(cls, '_feincms_content_types', None): + if not getattr(cls, "_feincms_content_types", None): raise ImproperlyConfigured( - 'You need to create at least one' - ' content type for the %s model.' % cls.__name__) + "You need to create at least one" + " content type for the %s model." % cls.__name__ + ) def copy_content_from(self, obj): """ @@ -781,8 +801,7 @@ def copy_content_from(self, obj): for cls in self._feincms_content_types: for content in cls.objects.filter(parent=obj): - new = copy_model_instance( - content, exclude=('id', 'parent')) + new = copy_model_instance(content, exclude=("id", "parent")) new.parent = self new.save() @@ -810,7 +829,7 @@ def register_with_reversion(cls): follow = [] for content_type in cls._feincms_content_types: - follow.append('%s_set' % content_type.__name__.lower()) + follow.append("%s_set" % content_type.__name__.lower()) register(content_type) register(cls, follow=follow) diff --git a/feincms/module/extensions/changedate.py b/feincms/module/extensions/changedate.py index dd543dbbc..fdfd93475 100644 --- a/feincms/module/extensions/changedate.py +++ b/feincms/module/extensions/changedate.py @@ -6,6 +6,8 @@ from feincms.extensions.changedate import * warnings.warn( - 'Import %(name)s from feincms.extensions.%(name)s' % { - 'name': __name__.split('.')[-1], - }, DeprecationWarning, stacklevel=2) + "Import %(name)s from feincms.extensions.%(name)s" + % {"name": __name__.split(".")[-1]}, + DeprecationWarning, + stacklevel=2, +) diff --git a/feincms/module/extensions/ct_tracker.py b/feincms/module/extensions/ct_tracker.py index 7b94fd55f..f810b346e 100644 --- a/feincms/module/extensions/ct_tracker.py +++ b/feincms/module/extensions/ct_tracker.py @@ -6,6 +6,8 @@ from feincms.extensions.ct_tracker import * warnings.warn( - 'Import %(name)s from feincms.extensions.%(name)s' % { - 'name': __name__.split('.')[-1], - }, DeprecationWarning, stacklevel=2) + "Import %(name)s from feincms.extensions.%(name)s" + % {"name": __name__.split(".")[-1]}, + DeprecationWarning, + stacklevel=2, +) diff --git a/feincms/module/extensions/datepublisher.py b/feincms/module/extensions/datepublisher.py index 44fed5edb..313568d74 100644 --- a/feincms/module/extensions/datepublisher.py +++ b/feincms/module/extensions/datepublisher.py @@ -6,6 +6,8 @@ from feincms.extensions.datepublisher import * warnings.warn( - 'Import %(name)s from feincms.extensions.%(name)s' % { - 'name': __name__.split('.')[-1], - }, DeprecationWarning, stacklevel=2) + "Import %(name)s from feincms.extensions.%(name)s" + % {"name": __name__.split(".")[-1]}, + DeprecationWarning, + stacklevel=2, +) diff --git a/feincms/module/extensions/featured.py b/feincms/module/extensions/featured.py index 05b59a87a..7713d026b 100644 --- a/feincms/module/extensions/featured.py +++ b/feincms/module/extensions/featured.py @@ -6,6 +6,8 @@ from feincms.extensions.featured import * warnings.warn( - 'Import %(name)s from feincms.extensions.%(name)s' % { - 'name': __name__.split('.')[-1], - }, DeprecationWarning, stacklevel=2) + "Import %(name)s from feincms.extensions.%(name)s" + % {"name": __name__.split(".")[-1]}, + DeprecationWarning, + stacklevel=2, +) diff --git a/feincms/module/extensions/seo.py b/feincms/module/extensions/seo.py index 8dc6add93..aa386950f 100644 --- a/feincms/module/extensions/seo.py +++ b/feincms/module/extensions/seo.py @@ -6,6 +6,8 @@ from feincms.extensions.seo import * warnings.warn( - 'Import %(name)s from feincms.extensions.%(name)s' % { - 'name': __name__.split('.')[-1], - }, DeprecationWarning, stacklevel=2) + "Import %(name)s from feincms.extensions.%(name)s" + % {"name": __name__.split(".")[-1]}, + DeprecationWarning, + stacklevel=2, +) diff --git a/feincms/module/extensions/translations.py b/feincms/module/extensions/translations.py index 7d8842186..c89c8d538 100644 --- a/feincms/module/extensions/translations.py +++ b/feincms/module/extensions/translations.py @@ -6,6 +6,8 @@ from feincms.extensions.translations import * warnings.warn( - 'Import %(name)s from feincms.extensions.%(name)s' % { - 'name': __name__.split('.')[-1], - }, DeprecationWarning, stacklevel=2) + "Import %(name)s from feincms.extensions.%(name)s" + % {"name": __name__.split(".")[-1]}, + DeprecationWarning, + stacklevel=2, +) diff --git a/feincms/module/medialibrary/__init__.py b/feincms/module/medialibrary/__init__.py index fc7e02a00..98a59597d 100644 --- a/feincms/module/medialibrary/__init__.py +++ b/feincms/module/medialibrary/__init__.py @@ -7,6 +7,6 @@ import logging # ------------------------------------------------------------------------ -logger = logging.getLogger('feincms.medialibrary') +logger = logging.getLogger("feincms.medialibrary") # ------------------------------------------------------------------------ diff --git a/feincms/module/medialibrary/contents.py b/feincms/module/medialibrary/contents.py index b5b2cd435..ada88df68 100644 --- a/feincms/module/medialibrary/contents.py +++ b/feincms/module/medialibrary/contents.py @@ -11,8 +11,8 @@ class MediaFileContentInline(FeinCMSInline): - raw_id_fields = ('mediafile',) - radio_fields = {'type': admin.VERTICAL} + raw_id_fields = ("mediafile",) + radio_fields = {"type": admin.VERTICAL} class MediaFileContent(ContentWithMediaFile): @@ -44,36 +44,35 @@ class MediaFileContent(ContentWithMediaFile): class Meta: abstract = True - verbose_name = _('media file') - verbose_name_plural = _('media files') + verbose_name = _("media file") + verbose_name_plural = _("media files") @classmethod def initialize_type(cls, TYPE_CHOICES=None): if TYPE_CHOICES is None: raise ImproperlyConfigured( - 'You have to set TYPE_CHOICES when' - ' creating a %s' % cls.__name__) + "You have to set TYPE_CHOICES when" " creating a %s" % cls.__name__ + ) cls.add_to_class( - 'type', + "type", models.CharField( - _('type'), + _("type"), max_length=20, choices=TYPE_CHOICES, default=TYPE_CHOICES[0][0], - ) + ), ) def render(self, **kwargs): return ct_render_to_string( [ - 'content/mediafile/%s_%s.html' % ( - self.mediafile.type, self.type), - 'content/mediafile/%s.html' % self.mediafile.type, - 'content/mediafile/%s.html' % self.type, - 'content/mediafile/default.html', + "content/mediafile/%s_%s.html" % (self.mediafile.type, self.type), + "content/mediafile/%s.html" % self.mediafile.type, + "content/mediafile/%s.html" % self.type, + "content/mediafile/default.html", ], - {'content': self}, - request=kwargs.get('request'), - context=kwargs.get('context'), + {"content": self}, + request=kwargs.get("request"), + context=kwargs.get("context"), ) diff --git a/feincms/module/medialibrary/fields.py b/feincms/module/medialibrary/fields.py index 95046060b..c27f8fb41 100644 --- a/feincms/module/medialibrary/fields.py +++ b/feincms/module/medialibrary/fields.py @@ -18,7 +18,7 @@ from .thumbnail import admin_thumbnail -__all__ = ('MediaFileForeignKey', 'ContentWithMediaFile') +__all__ = ("MediaFileForeignKey", "ContentWithMediaFile") # ------------------------------------------------------------------------ @@ -29,20 +29,21 @@ def __init__(self, original): def label_for_value(self, value): key = self.rel.get_related_field().name try: - obj = self.rel.to._default_manager.using(self.db).get( - **{key: value}) - label = [' %s' % escape( - shorten_string(six.text_type(obj)))] + obj = self.rel.to._default_manager.using(self.db).get(**{key: value}) + label = [ + " %s" % escape(shorten_string(six.text_type(obj))) + ] image = admin_thumbnail(obj) if image: label.append( '
' % image) + "/>" % image + ) - return ''.join(label) + return "".join(label) except (ValueError, self.rel.to.DoesNotExist): - return '' + return "" class MediaFileForeignKey(models.ForeignKey): @@ -53,24 +54,25 @@ class MediaFileForeignKey(models.ForeignKey): """ def __init__(self, *args, **kwargs): - if not args and 'to' not in kwargs: + if not args and "to" not in kwargs: args = (MediaFile,) super(MediaFileForeignKey, self).__init__(*args, **kwargs) def formfield(self, **kwargs): - if 'widget' in kwargs and isinstance( - kwargs['widget'], ForeignKeyRawIdWidget): - kwargs['widget'] = MediaFileForeignKeyRawIdWidget(kwargs['widget']) + if "widget" in kwargs and isinstance(kwargs["widget"], ForeignKeyRawIdWidget): + kwargs["widget"] = MediaFileForeignKeyRawIdWidget(kwargs["widget"]) return super(MediaFileForeignKey, self).formfield(**kwargs) class ContentWithMediaFile(models.Model): class feincms_item_editor_inline(FeinCMSInline): - raw_id_fields = ('mediafile',) + raw_id_fields = ("mediafile",) mediafile = MediaFileForeignKey( - MediaFile, verbose_name=_('media file'), related_name='+', - on_delete=models.PROTECT + MediaFile, + verbose_name=_("media file"), + related_name="+", + on_delete=models.PROTECT, ) class Meta: @@ -83,16 +85,22 @@ class AdminFileWithPreviewWidget(AdminFileWidget): Simple AdminFileWidget, but detects if the file is an image and tries to render a small thumbnail besides the input field. """ + def render(self, name, value, attrs=None, *args, **kwargs): r = super(AdminFileWithPreviewWidget, self).render( - name, value, attrs=attrs, *args, **kwargs) + name, value, attrs=attrs, *args, **kwargs + ) - if value and getattr(value, 'instance', None): + if value and getattr(value, "instance", None): image = admin_thumbnail(value.instance) if image: - r = mark_safe(( - '' % image) + r) + r = mark_safe( + ( + '" % image + ) + + r + ) return r diff --git a/feincms/module/medialibrary/forms.py b/feincms/module/medialibrary/forms.py index e592bd591..c43e91cc3 100644 --- a/feincms/module/medialibrary/forms.py +++ b/feincms/module/medialibrary/forms.py @@ -20,41 +20,43 @@ class MediaCategoryAdminForm(forms.ModelForm): class Meta: model = Category - fields = '__all__' + fields = "__all__" def clean_parent(self): - data = self.cleaned_data['parent'] + data = self.cleaned_data["parent"] if data is not None and self.instance in data.path_list(): - raise forms.ValidationError( - _("This would create a loop in the hierarchy")) + raise forms.ValidationError(_("This would create a loop in the hierarchy")) return data def __init__(self, *args, **kwargs): super(MediaCategoryAdminForm, self).__init__(*args, **kwargs) - self.fields['parent'].queryset =\ - self.fields['parent'].queryset.exclude(pk=self.instance.pk) + self.fields["parent"].queryset = self.fields["parent"].queryset.exclude( + pk=self.instance.pk + ) # ------------------------------------------------------------------------ class MediaFileAdminForm(forms.ModelForm): class Meta: model = MediaFile - widgets = {'file': AdminFileWithPreviewWidget} - fields = '__all__' + widgets = {"file": AdminFileWithPreviewWidget} + fields = "__all__" def __init__(self, *args, **kwargs): super(MediaFileAdminForm, self).__init__(*args, **kwargs) if settings.FEINCMS_MEDIAFILE_OVERWRITE and self.instance.id: field = self.instance.file.field - if not hasattr(field, '_feincms_generate_filename_patched'): + if not hasattr(field, "_feincms_generate_filename_patched"): original_generate = field.generate_filename def _gen_fname(instance, filename): - if instance.id and hasattr(instance, 'original_name'): - logger.info("Overwriting file %s with new data" % ( - instance.original_name)) + if instance.id and hasattr(instance, "original_name"): + logger.info( + "Overwriting file %s with new data" + % (instance.original_name) + ) instance.file.storage.delete(instance.original_name) return instance.original_name @@ -65,18 +67,21 @@ def _gen_fname(instance, filename): def clean_file(self): if settings.FEINCMS_MEDIAFILE_OVERWRITE and self.instance.id: - new_base, new_ext = os.path.splitext( - self.cleaned_data['file'].name) + new_base, new_ext = os.path.splitext(self.cleaned_data["file"].name) old_base, old_ext = os.path.splitext(self.instance.file.name) if new_ext.lower() != old_ext.lower(): - raise forms.ValidationError(_( - "Cannot overwrite with different file type (attempt to" - " overwrite a %(old_ext)s with a %(new_ext)s)" - ) % {'old_ext': old_ext, 'new_ext': new_ext}) + raise forms.ValidationError( + _( + "Cannot overwrite with different file type (attempt to" + " overwrite a %(old_ext)s with a %(new_ext)s)" + ) + % {"old_ext": old_ext, "new_ext": new_ext} + ) self.instance.original_name = self.instance.file.name - return self.cleaned_data['file'] + return self.cleaned_data["file"] + # ------------------------------------------------------------------------ diff --git a/feincms/module/medialibrary/modeladmins.py b/feincms/module/medialibrary/modeladmins.py index d52e9fb2c..ae20ae55b 100644 --- a/feincms/module/medialibrary/modeladmins.py +++ b/feincms/module/medialibrary/modeladmins.py @@ -19,6 +19,7 @@ from django.utils.safestring import mark_safe from django.utils.translation import ungettext, ugettext_lazy as _ from django.views.decorators.csrf import csrf_protect + try: from django.urls import reverse except ImportError: @@ -37,11 +38,11 @@ # ----------------------------------------------------------------------- class CategoryAdmin(admin.ModelAdmin): form = MediaCategoryAdminForm - list_display = ['path'] - list_filter = ['parent'] + list_display = ["path"] + list_filter = ["parent"] list_per_page = 25 - search_fields = ['title'] - prepopulated_fields = {'slug': ('title',)} + search_fields = ["title"] + prepopulated_fields = {"slug": ("title",)} # ------------------------------------------------------------------------ @@ -51,10 +52,10 @@ class AddCategoryForm(forms.Form): category = forms.ModelChoiceField(Category.objects.all()) form = None - if 'apply' in request.POST: + if "apply" in request.POST: form = AddCategoryForm(request.POST) if form.is_valid(): - category = form.cleaned_data['category'] + category = form.cleaned_data["category"] count = 0 for mediafile in queryset: @@ -62,28 +63,30 @@ class AddCategoryForm(forms.Form): count += 1 message = ungettext( - 'Successfully added %(count)d media file to %(category)s.', - 'Successfully added %(count)d media files to %(category)s.', - count) % {'count': count, 'category': category} + "Successfully added %(count)d media file to %(category)s.", + "Successfully added %(count)d media files to %(category)s.", + count, + ) % {"count": count, "category": category} modeladmin.message_user(request, message) return HttpResponseRedirect(request.get_full_path()) - if 'cancel' in request.POST: + if "cancel" in request.POST: return HttpResponseRedirect(request.get_full_path()) if not form: - form = AddCategoryForm(initial={ - '_selected_action': request.POST.getlist( - admin.ACTION_CHECKBOX_NAME), - }) + form = AddCategoryForm( + initial={ + "_selected_action": request.POST.getlist(admin.ACTION_CHECKBOX_NAME) + } + ) - return render(request, 'admin/medialibrary/add_to_category.html', { - 'mediafiles': queryset, - 'category_form': form, - 'opts': modeladmin.model._meta, - }) + return render( + request, + "admin/medialibrary/add_to_category.html", + {"mediafiles": queryset, "category_form": form, "opts": modeladmin.model._meta}, + ) -assign_category.short_description = _('Add selected media files to category') +assign_category.short_description = _("Add selected media files to category") # ------------------------------------------------------------------------- @@ -98,12 +101,10 @@ def save_as_zipfile(modeladmin, request, queryset): messages.error(request, _("ZIP file export failed: %s") % str(e)) return - return HttpResponseRedirect( - os.path.join(django_settings.MEDIA_URL, zip_name)) + return HttpResponseRedirect(os.path.join(django_settings.MEDIA_URL, zip_name)) -save_as_zipfile.short_description = _( - 'Export selected media files as zip file') +save_as_zipfile.short_description = _("Export selected media files as zip file") # ------------------------------------------------------------------------ @@ -111,14 +112,13 @@ class MediaFileAdmin(ExtensionModelAdmin): form = MediaFileAdminForm save_on_top = True - date_hierarchy = 'created' + date_hierarchy = "created" inlines = [admin_translationinline(MediaFileTranslation)] - list_display = [ - 'admin_thumbnail', '__str__', 'file_info', 'formatted_created'] - list_display_links = ['__str__'] - list_filter = ['type', 'categories'] + list_display = ["admin_thumbnail", "__str__", "file_info", "formatted_created"] + list_display_links = ["__str__"] + list_filter = ["type", "categories"] list_per_page = 25 - search_fields = ['copyright', 'file', 'translations__caption'] + search_fields = ["copyright", "file", "translations__caption"] filter_horizontal = ("categories",) actions = [assign_category, save_as_zipfile] @@ -127,46 +127,50 @@ def get_urls(self): return [ url( - r'^mediafile-bulk-upload/$', + r"^mediafile-bulk-upload/$", self.admin_site.admin_view(MediaFileAdmin.bulk_upload), {}, - name='mediafile_bulk_upload', - ), + name="mediafile_bulk_upload", + ) ] + super(MediaFileAdmin, self).get_urls() def changelist_view(self, request, extra_context=None): if extra_context is None: extra_context = {} - extra_context['categories'] = Category.objects.order_by('title') + extra_context["categories"] = Category.objects.order_by("title") return super(MediaFileAdmin, self).changelist_view( - request, extra_context=extra_context) + request, extra_context=extra_context + ) def admin_thumbnail(self, obj): image = admin_thumbnail(obj) if image: - return mark_safe(""" + return mark_safe( + """ - """ % { - 'url': obj.file.url, - 'image': image} + """ + % {"url": obj.file.url, "image": image} ) - return '' - admin_thumbnail.short_description = _('Preview') + return "" + + admin_thumbnail.short_description = _("Preview") def formatted_file_size(self, obj): return filesizeformat(obj.file_size) + formatted_file_size.short_description = _("file size") - formatted_file_size.admin_order_field = 'file_size' + formatted_file_size.admin_order_field = "file_size" def formatted_created(self, obj): return obj.created.strftime("%Y-%m-%d") + formatted_created.short_description = _("created") - formatted_created.admin_order_field = 'created' + formatted_created.admin_order_field = "created" def file_type(self, obj): t = obj.filetypes_dict[obj.type] - if obj.type == 'image': + if obj.type == "image": # get_image_dimensions is expensive / slow if the storage is not # local filesystem (indicated by availability the path property) try: @@ -180,8 +184,9 @@ def file_type(self, obj): except (IOError, TypeError, ValueError) as e: t += " (%s)" % e return mark_safe(t) - file_type.admin_order_field = 'type' - file_type.short_description = _('file type') + + file_type.admin_order_field = "type" + file_type.short_description = _("file type") def file_info(self, obj): """ @@ -191,47 +196,54 @@ def file_info(self, obj): the file name later on, this can be used to access the file name from JS, like for example a TinyMCE connector shim. """ - return mark_safe(( - '' - ' %s
%s, %s' - ) % ( - obj.id, - obj.file.name, - obj.id, - shorten_string(os.path.basename(obj.file.name), max_length=40), - self.file_type(obj), - self.formatted_file_size(obj), - )) - file_info.admin_order_field = 'file' - file_info.short_description = _('file info') + return mark_safe( + ( + '' + " %s
%s, %s" + ) + % ( + obj.id, + obj.file.name, + obj.id, + shorten_string(os.path.basename(obj.file.name), max_length=40), + self.file_type(obj), + self.formatted_file_size(obj), + ) + ) + + file_info.admin_order_field = "file" + file_info.short_description = _("file info") @staticmethod @csrf_protect - @permission_required('medialibrary.add_mediafile') + @permission_required("medialibrary.add_mediafile") def bulk_upload(request): - if request.method == 'POST' and 'data' in request.FILES: + if request.method == "POST" and "data" in request.FILES: try: count = import_zipfile( - request.POST.get('category'), - request.POST.get('overwrite', False), - request.FILES['data']) + request.POST.get("category"), + request.POST.get("overwrite", False), + request.FILES["data"], + ) messages.info(request, _("%d files imported") % count) except Exception as e: messages.error(request, _("ZIP import failed: %s") % e) else: messages.error(request, _("No input file given")) - return HttpResponseRedirect( - reverse('admin:medialibrary_mediafile_changelist')) + return HttpResponseRedirect(reverse("admin:medialibrary_mediafile_changelist")) def get_queryset(self, request): - return super(MediaFileAdmin, self).get_queryset(request).transform( - lookup_translations()) + return ( + super(MediaFileAdmin, self) + .get_queryset(request) + .transform(lookup_translations()) + ) def save_model(self, request, obj, form, change): obj.purge_translation_cache() - return super(MediaFileAdmin, self).save_model( - request, obj, form, change) + return super(MediaFileAdmin, self).save_model(request, obj, form, change) + # ------------------------------------------------------------------------ diff --git a/feincms/module/medialibrary/models.py b/feincms/module/medialibrary/models.py index 66fd1d069..424abd327 100644 --- a/feincms/module/medialibrary/models.py +++ b/feincms/module/medialibrary/models.py @@ -18,7 +18,10 @@ from feincms import settings from feincms.models import ExtensionsMixin from feincms.translations import ( - TranslatedObjectMixin, Translation, TranslatedObjectManager) + TranslatedObjectMixin, + Translation, + TranslatedObjectManager, +) from . import logger @@ -29,9 +32,9 @@ class CategoryManager(models.Manager): Simple manager which exists only to supply ``.select_related("parent")`` on querysets since we can't even __str__ efficiently without it. """ + def get_queryset(self): - return super(CategoryManager, self).get_queryset().select_related( - "parent") + return super(CategoryManager, self).get_queryset().select_related("parent") # ------------------------------------------------------------------------ @@ -42,26 +45,30 @@ class Category(models.Model): library. """ - title = models.CharField(_('title'), max_length=200) + title = models.CharField(_("title"), max_length=200) parent = models.ForeignKey( - 'self', blank=True, null=True, + "self", + blank=True, + null=True, on_delete=models.CASCADE, - related_name='children', limit_choices_to={'parent__isnull': True}, - verbose_name=_('parent')) + related_name="children", + limit_choices_to={"parent__isnull": True}, + verbose_name=_("parent"), + ) - slug = models.SlugField(_('slug'), max_length=150) + slug = models.SlugField(_("slug"), max_length=150) class Meta: - ordering = ['parent__title', 'title'] - verbose_name = _('category') - verbose_name_plural = _('categories') - app_label = 'medialibrary' + ordering = ["parent__title", "title"] + verbose_name = _("category") + verbose_name_plural = _("categories") + app_label = "medialibrary" objects = CategoryManager() def __str__(self): if self.parent_id: - return '%s - %s' % (self.parent.title, self.title) + return "%s - %s" % (self.parent.title, self.title) return self.title @@ -70,6 +77,7 @@ def save(self, *args, **kwargs): self.slug = slugify(self.title) super(Category, self).save(*args, **kwargs) + save.alters_data = True def path_list(self): @@ -80,7 +88,7 @@ def path_list(self): return p def path(self): - return ' - '.join((f.title for f in self.path_list())) + return " - ".join((f.title for f in self.path_list())) # ------------------------------------------------------------------------ @@ -93,26 +101,25 @@ class MediaFileBase(models.Model, ExtensionsMixin, TranslatedObjectMixin): """ file = models.FileField( - _('file'), max_length=255, - upload_to=settings.FEINCMS_MEDIALIBRARY_UPLOAD_TO) - type = models.CharField( - _('file type'), max_length=12, editable=False, - choices=()) - created = models.DateTimeField( - _('created'), editable=False, default=timezone.now) - copyright = models.CharField(_('copyright'), max_length=200, blank=True) + _("file"), max_length=255, upload_to=settings.FEINCMS_MEDIALIBRARY_UPLOAD_TO + ) + type = models.CharField(_("file type"), max_length=12, editable=False, choices=()) + created = models.DateTimeField(_("created"), editable=False, default=timezone.now) + copyright = models.CharField(_("copyright"), max_length=200, blank=True) file_size = models.IntegerField( - _("file size"), blank=True, null=True, editable=False) + _("file size"), blank=True, null=True, editable=False + ) categories = models.ManyToManyField( - Category, verbose_name=_('categories'), blank=True) + Category, verbose_name=_("categories"), blank=True + ) categories.category_filter = True class Meta: abstract = True - ordering = ['-created'] - verbose_name = _('media file') - verbose_name_plural = _('media files') + ordering = ["-created"] + verbose_name = _("media file") + verbose_name_plural = _("media files") objects = TranslatedObjectManager() @@ -121,7 +128,7 @@ class Meta: @classmethod def reconfigure(cls, upload_to=None, storage=None): - f = cls._meta.get_field('file') + f = cls._meta.get_field("file") # Ugh. Copied relevant parts from django/db/models/fields/files.py # FileField.__init__ (around line 225) if storage: @@ -136,7 +143,7 @@ def register_filetypes(cls, *types): cls.filetypes[0:0] = types choices = [t[0:2] for t in cls.filetypes] cls.filetypes_dict = dict(choices) - cls._meta.get_field('type').choices[:] = choices + cls._meta.get_field("type").choices[:] = choices def __init__(self, *args, **kwargs): super(MediaFileBase, self).__init__(*args, **kwargs) @@ -154,7 +161,7 @@ def __str__(self): pass if trans: - trans = '%s' % trans + trans = "%s" % trans if trans.strip(): return trans return os.path.basename(self.file.name) @@ -194,16 +201,19 @@ def save(self, *args, **kwargs): super(MediaFileBase, self).save(*args, **kwargs) - logger.info("Saved mediafile %d (%s, type %s, %d bytes)" % ( - self.id, self.file.name, self.type, self.file_size or 0)) + logger.info( + "Saved mediafile %d (%s, type %s, %d bytes)" + % (self.id, self.file.name, self.type, self.file_size or 0) + ) # User uploaded a new file. Try to get rid of the old file in # storage, to avoid having orphaned files hanging around. - if getattr(self, '_original_file_name', None): + if getattr(self, "_original_file_name", None): if self.file.name != self._original_file_name: self.delete_mediafile(self._original_file_name) self.purge_translation_cache() + save.alters_data = True def delete_mediafile(self, name=None): @@ -218,39 +228,61 @@ def delete_mediafile(self, name=None): # ------------------------------------------------------------------------ MediaFileBase.register_filetypes( # Should we be using imghdr.what instead of extension guessing? - ('image', _('Image'), lambda f: re.compile( - r'\.(bmp|jpe?g|jp2|jxr|gif|png|tiff?)$', re.IGNORECASE).search(f)), - ('video', _('Video'), lambda f: re.compile( - r'\.(mov|m[14]v|mp4|avi|mpe?g|qt|ogv|wmv|flv)$', - re.IGNORECASE).search(f)), - ('audio', _('Audio'), lambda f: re.compile( - r'\.(au|mp3|m4a|wma|oga|ram|wav)$', re.IGNORECASE).search(f)), - ('pdf', _('PDF document'), lambda f: f.lower().endswith('.pdf')), - ('swf', _('Flash'), lambda f: f.lower().endswith('.swf')), - ('txt', _('Text'), lambda f: f.lower().endswith('.txt')), - ('rtf', _('Rich Text'), lambda f: f.lower().endswith('.rtf')), - ('zip', _('Zip archive'), lambda f: f.lower().endswith('.zip')), - ('doc', _('Microsoft Word'), lambda f: re.compile( - r'\.docx?$', re.IGNORECASE).search(f)), - ('xls', _('Microsoft Excel'), lambda f: re.compile( - r'\.xlsx?$', re.IGNORECASE).search(f)), - ('ppt', _('Microsoft PowerPoint'), lambda f: re.compile( - r'\.pptx?$', re.IGNORECASE).search(f)), - ('other', _('Binary'), lambda f: True), # Must be last + ( + "image", + _("Image"), + lambda f: re.compile( + r"\.(bmp|jpe?g|jp2|jxr|gif|png|tiff?)$", re.IGNORECASE + ).search(f), + ), + ( + "video", + _("Video"), + lambda f: re.compile( + r"\.(mov|m[14]v|mp4|avi|mpe?g|qt|ogv|wmv|flv)$", re.IGNORECASE + ).search(f), + ), + ( + "audio", + _("Audio"), + lambda f: re.compile(r"\.(au|mp3|m4a|wma|oga|ram|wav)$", re.IGNORECASE).search( + f + ), + ), + ("pdf", _("PDF document"), lambda f: f.lower().endswith(".pdf")), + ("swf", _("Flash"), lambda f: f.lower().endswith(".swf")), + ("txt", _("Text"), lambda f: f.lower().endswith(".txt")), + ("rtf", _("Rich Text"), lambda f: f.lower().endswith(".rtf")), + ("zip", _("Zip archive"), lambda f: f.lower().endswith(".zip")), + ( + "doc", + _("Microsoft Word"), + lambda f: re.compile(r"\.docx?$", re.IGNORECASE).search(f), + ), + ( + "xls", + _("Microsoft Excel"), + lambda f: re.compile(r"\.xlsx?$", re.IGNORECASE).search(f), + ), + ( + "ppt", + _("Microsoft PowerPoint"), + lambda f: re.compile(r"\.pptx?$", re.IGNORECASE).search(f), + ), + ("other", _("Binary"), lambda f: True), # Must be last ) # ------------------------------------------------------------------------ class MediaFile(MediaFileBase): class Meta: - app_label = 'medialibrary' + app_label = "medialibrary" @receiver(post_delete, sender=MediaFile) def _mediafile_post_delete(sender, instance, **kwargs): instance.delete_mediafile() - logger.info("Deleted mediafile %d (%s)" % ( - instance.id, instance.file.name)) + logger.info("Deleted mediafile %d (%s)" % (instance.id, instance.file.name)) # ------------------------------------------------------------------------ @@ -260,14 +292,14 @@ class MediaFileTranslation(Translation(MediaFile)): Translated media file caption and description. """ - caption = models.CharField(_('caption'), max_length=1000) - description = models.TextField(_('description'), blank=True) + caption = models.CharField(_("caption"), max_length=1000) + description = models.TextField(_("description"), blank=True) class Meta: - verbose_name = _('media file translation') - verbose_name_plural = _('media file translations') - unique_together = ('parent', 'language_code') - app_label = 'medialibrary' + verbose_name = _("media file translation") + verbose_name_plural = _("media file translations") + unique_together = ("parent", "language_code") + app_label = "medialibrary" def __str__(self): return self.caption diff --git a/feincms/module/medialibrary/thumbnail.py b/feincms/module/medialibrary/thumbnail.py index a70c2dc27..5dce9cce9 100644 --- a/feincms/module/medialibrary/thumbnail.py +++ b/feincms/module/medialibrary/thumbnail.py @@ -5,8 +5,8 @@ from feincms.utils import get_object -def default_admin_thumbnail(mediafile, dimensions='100x100', **kwargs): - if mediafile.type != 'image': +def default_admin_thumbnail(mediafile, dimensions="100x100", **kwargs): + if mediafile.type != "image": return None return feincms_thumbnail.thumbnail(mediafile.file, dimensions) @@ -15,9 +15,8 @@ def default_admin_thumbnail(mediafile, dimensions='100x100', **kwargs): _cached_thumbnailer = None -def admin_thumbnail(mediafile, dimensions='100x100'): +def admin_thumbnail(mediafile, dimensions="100x100"): global _cached_thumbnailer if not _cached_thumbnailer: - _cached_thumbnailer = get_object( - settings.FEINCMS_MEDIALIBRARY_THUMBNAIL) + _cached_thumbnailer = get_object(settings.FEINCMS_MEDIALIBRARY_THUMBNAIL) return _cached_thumbnailer(mediafile, dimensions=dimensions) diff --git a/feincms/module/medialibrary/zip.py b/feincms/module/medialibrary/zip.py index 557e82566..2e91376f6 100644 --- a/feincms/module/medialibrary/zip.py +++ b/feincms/module/medialibrary/zip.py @@ -23,7 +23,7 @@ # ------------------------------------------------------------------------ -export_magic = 'feincms-export-01' +export_magic = "feincms-export-01" # ------------------------------------------------------------------------ @@ -47,7 +47,7 @@ def import_zipfile(category_id, overwrite, data): info = {} try: info = json.loads(z.comment) - if info['export_magic'] == export_magic: + if info["export_magic"] == export_magic: is_export_file = True except Exception: pass @@ -57,21 +57,21 @@ def import_zipfile(category_id, overwrite, data): category_id_map = {} if is_export_file: for cat in sorted( - info.get('categories', []), - key=lambda k: k.get('level', 999)): + info.get("categories", []), key=lambda k: k.get("level", 999) + ): new_cat, created = Category.objects.get_or_create( - slug=cat['slug'], - title=cat['title']) - category_id_map[cat['id']] = new_cat - if created and cat.get('parent', 0): - parent_cat = category_id_map.get(cat.get('parent', 0), None) + slug=cat["slug"], title=cat["title"] + ) + category_id_map[cat["id"]] = new_cat + if created and cat.get("parent", 0): + parent_cat = category_id_map.get(cat.get("parent", 0), None) if parent_cat: new_cat.parent = parent_cat new_cat.save() count = 0 for zi in z.infolist(): - if not zi.filename.endswith('/'): + if not zi.filename.endswith("/"): bname = os.path.basename(zi.filename) if bname and not bname.startswith(".") and "." in bname: fname, ext = os.path.splitext(bname) @@ -95,36 +95,34 @@ def import_zipfile(category_id, overwrite, data): mf = MediaFile() if overwrite: mf.file.field.upload_to = wanted_dir - mf.copyright = info.get('copyright', '') - mf.file.save( - target_fname, - ContentFile(z.read(zi.filename)), - save=False) + mf.copyright = info.get("copyright", "") + mf.file.save(target_fname, ContentFile(z.read(zi.filename)), save=False) mf.save() found_metadata = False if is_export_file: try: - for tr in info['translations']: + for tr in info["translations"]: found_metadata = True - mt, mt_created =\ - MediaFileTranslation.objects.get_or_create( - parent=mf, language_code=tr['lang']) - mt.caption = tr['caption'] - mt.description = tr.get('description', None) + mt, mt_created = MediaFileTranslation.objects.get_or_create( + parent=mf, language_code=tr["lang"] + ) + mt.caption = tr["caption"] + mt.description = tr.get("description", None) mt.save() # Add categories mf.categories = ( category_id_map[cat_id] - for cat_id in info.get('categories', [])) + for cat_id in info.get("categories", []) + ) except Exception: pass if not found_metadata: mt = MediaFileTranslation() mt.parent = mf - mt.caption = fname.replace('_', ' ') + mt.caption = fname.replace("_", " ") mt.save() if category: @@ -139,10 +137,14 @@ def import_zipfile(category_id, overwrite, data): def export_zipfile(site, queryset): now = timezone.now() zip_name = "export_%s_%04d%02d%02d.zip" % ( - slugify(site.domain), now.year, now.month, now.day) + slugify(site.domain), + now.year, + now.month, + now.day, + ) zip_data = open(os.path.join(django_settings.MEDIA_ROOT, zip_name), "w") - zip_file = zipfile.ZipFile(zip_data, 'w', allowZip64=True) + zip_file = zipfile.ZipFile(zip_data, "w", allowZip64=True) # Save the used categories in the zip file's global comment used_categories = set() @@ -151,28 +153,36 @@ def export_zipfile(site, queryset): used_categories.update(cat.path_list()) info = { - 'export_magic': export_magic, - 'categories': [{ - 'id': cat.id, - 'title': cat.title, - 'slug': cat.slug, - 'parent': cat.parent_id or 0, - 'level': len(cat.path_list()), - } for cat in used_categories], + "export_magic": export_magic, + "categories": [ + { + "id": cat.id, + "title": cat.title, + "slug": cat.slug, + "parent": cat.parent_id or 0, + "level": len(cat.path_list()), + } + for cat in used_categories + ], } zip_file.comment = json.dumps(info) for mf in queryset: ctime = time.localtime(os.stat(mf.file.path).st_ctime) - info = json.dumps({ - 'copyright': mf.copyright, - 'categories': [cat.id for cat in mf.categories.all()], - 'translations': [{ - 'lang': t.language_code, - 'caption': t.caption, - 'description': t.description, - } for t in mf.translations.all()], - }) + info = json.dumps( + { + "copyright": mf.copyright, + "categories": [cat.id for cat in mf.categories.all()], + "translations": [ + { + "lang": t.language_code, + "caption": t.caption, + "description": t.description, + } + for t in mf.translations.all() + ], + } + ) with open(mf.file.path, "r") as file_data: zip_info = zipfile.ZipInfo( @@ -183,10 +193,13 @@ def export_zipfile(site, queryset): ctime.tm_mday, ctime.tm_hour, ctime.tm_min, - ctime.tm_sec)) + ctime.tm_sec, + ), + ) zip_info.comment = info zip_file.writestr(zip_info, file_data.read()) return zip_name + # ------------------------------------------------------------------------ diff --git a/feincms/module/mixins.py b/feincms/module/mixins.py index ed873334d..56f2d33b5 100644 --- a/feincms/module/mixins.py +++ b/feincms/module/mixins.py @@ -79,7 +79,7 @@ class ContentObjectMixin(TemplateResponseMixin): context_object_name = None def handler(self, request, *args, **kwargs): - if not hasattr(self.request, '_feincms_extra_context'): + if not hasattr(self.request, "_feincms_extra_context"): self.request._feincms_extra_context = {} r = self.run_request_processors() @@ -121,7 +121,7 @@ def get_template_names(self): def get_context_data(self, **kwargs): context = self.request._feincms_extra_context - context[self.context_object_name or 'feincms_object'] = self.object + context[self.context_object_name or "feincms_object"] = self.object context.update(kwargs) return super(ContentObjectMixin, self).get_context_data(**context) @@ -140,7 +140,7 @@ def run_request_processors(self): also return a ``HttpResponse`` for shortcutting the rendering and returning that response immediately to the client. """ - if not getattr(self.object, 'request_processors', None): + if not getattr(self.object, "request_processors", None): return for fn in reversed(list(self.object.request_processors.values())): @@ -154,7 +154,7 @@ def run_response_processors(self, response): processors are called to modify the response, eg. for setting cache or expiration headers, keeping statistics, etc. """ - if not getattr(self.object, 'response_processors', None): + if not getattr(self.object, "response_processors", None): return for fn in self.object.response_processors.values(): @@ -172,8 +172,9 @@ def process_content_types(self): # did any content type successfully end processing? successful = False - for content in self.object.content.all_of_type(tuple( - self.object._feincms_content_types_with_process)): + for content in self.object.content.all_of_type( + tuple(self.object._feincms_content_types_with_process) + ): try: r = content.process(self.request, view=self) @@ -191,15 +192,17 @@ def process_content_types(self): extra_context = self.request._feincms_extra_context - if (not settings.FEINCMS_ALLOW_EXTRA_PATH and - extra_context.get('extra_path', '/') != '/' and - # XXX Already inside application content. I'm not sure - # whether this fix is really correct... - not extra_context.get('app_config')): - raise Http404(str('Not found (extra_path %r on %r)') % ( - extra_context.get('extra_path', '/'), - self.object, - )) + if ( + not settings.FEINCMS_ALLOW_EXTRA_PATH + and extra_context.get("extra_path", "/") != "/" + # XXX Already inside application content. I'm not sure + # whether this fix is really correct... + and not extra_context.get("app_config") + ): + raise Http404( + str("Not found (extra_path %r on %r)") + % (extra_context.get("extra_path", "/"), self.object) + ) def finalize_content_types(self, response): """ @@ -207,8 +210,9 @@ def finalize_content_types(self, response): returns the final response. """ - for content in self.object.content.all_of_type(tuple( - self.object._feincms_content_types_with_finalize)): + for content in self.object.content.all_of_type( + tuple(self.object._feincms_content_types_with_finalize) + ): r = content.finalize(self.request, response) if r: diff --git a/feincms/module/page/admin.py b/feincms/module/page/admin.py index 4abea4668..1fde24864 100644 --- a/feincms/module/page/admin.py +++ b/feincms/module/page/admin.py @@ -17,7 +17,7 @@ if settings.FEINCMS_USE_PAGE_ADMIN: ensure_completely_loaded() try: - Page._meta.get_field('template_key') + Page._meta.get_field("template_key") except FieldDoesNotExist: raise ImproperlyConfigured( "The page module requires a 'Page.register_templates()' call " diff --git a/feincms/module/page/extensions/excerpt.py b/feincms/module/page/extensions/excerpt.py index 25fe9004a..6eb0c583d 100644 --- a/feincms/module/page/extensions/excerpt.py +++ b/feincms/module/page/extensions/excerpt.py @@ -13,16 +13,17 @@ class Extension(extensions.Extension): def handle_model(self): self.model.add_to_class( - 'excerpt', + "excerpt", models.TextField( - _('excerpt'), + _("excerpt"), blank=True, help_text=_( - 'Add a brief excerpt summarizing the content' - ' of this page.'))) + "Add a brief excerpt summarizing the content" " of this page." + ), + ), + ) def handle_modeladmin(self, modeladmin): - modeladmin.add_extension_options(_('Excerpt'), { - 'fields': ('excerpt',), - 'classes': ('collapse',), - }) + modeladmin.add_extension_options( + _("Excerpt"), {"fields": ("excerpt",), "classes": ("collapse",)} + ) diff --git a/feincms/module/page/extensions/navigation.py b/feincms/module/page/extensions/navigation.py index f44e8f22b..d1564fcca 100644 --- a/feincms/module/page/extensions/navigation.py +++ b/feincms/module/page/extensions/navigation.py @@ -30,7 +30,7 @@ class TypeRegistryMetaClass(type): """ def __init__(cls, name, bases, attrs): - if not hasattr(cls, 'types'): + if not hasattr(cls, "types"): cls.types = [] else: cls.types.append(cls) @@ -46,11 +46,12 @@ class PagePretender(object): parameters on creation: title, url, level. If using the translation extension, also add language. """ + pk = None # emulate mptt properties to get the template tags working class _mptt_meta: - level_attr = 'level' + level_attr = "level" def __init__(self, **kwargs): for k, v in kwargs.items(): @@ -86,7 +87,7 @@ class NavigationExtension(six.with_metaclass(TypeRegistryMetaClass)): The name attribute is shown to the website administrator. """ - name = _('navigation extension') + name = _("navigation extension") def children(self, page, **kwargs): """ @@ -103,51 +104,56 @@ def children(self, page, **kwargs): def navigation_extension_choices(): for ext in NavigationExtension.types: - if (issubclass(ext, NavigationExtension) and - ext is not NavigationExtension): - yield ('%s.%s' % (ext.__module__, ext.__name__), ext.name) + if issubclass(ext, NavigationExtension) and ext is not NavigationExtension: + yield ("%s.%s" % (ext.__module__, ext.__name__), ext.name) def get_extension_class(extension): extension = get_object(extension) if isinstance(extension, types.ModuleType): - return getattr(extension, 'Extension') + return getattr(extension, "Extension") return extension class Extension(extensions.Extension): - ident = 'navigation' # TODO actually use this + ident = "navigation" # TODO actually use this navigation_extensions = None @cached_property def _extensions(self): if self.navigation_extensions is None: return OrderedDict( - ('%s.%s' % (ext.__module__, ext.__name__), ext) + ("%s.%s" % (ext.__module__, ext.__name__), ext) for ext in NavigationExtension.types if ( - issubclass(ext, NavigationExtension) and - ext is not NavigationExtension)) + issubclass(ext, NavigationExtension) + and ext is not NavigationExtension + ) + ) else: return OrderedDict( - ('%s.%s' % (ext.__module__, ext.__name__), ext) - for ext - in map(get_extension_class, self.navigation_extensions)) + ("%s.%s" % (ext.__module__, ext.__name__), ext) + for ext in map(get_extension_class, self.navigation_extensions) + ) def handle_model(self): - choices = [ - (path, ext.name) for path, ext in self._extensions.items()] + choices = [(path, ext.name) for path, ext in self._extensions.items()] self.model.add_to_class( - 'navigation_extension', + "navigation_extension", models.CharField( - _('navigation extension'), + _("navigation extension"), choices=choices, - blank=True, null=True, max_length=200, + blank=True, + null=True, + max_length=200, help_text=_( - 'Select the module providing subpages for this page if' - ' you need to customize the navigation.'))) + "Select the module providing subpages for this page if" + " you need to customize the navigation." + ), + ), + ) extension = self @@ -169,7 +175,7 @@ def extended_navigation(self, **kwargs): return self.children.in_navigation() def handle_modeladmin(self, modeladmin): - modeladmin.add_extension_options(_('Navigation extension'), { - 'fields': ('navigation_extension',), - 'classes': ('collapse',), - }) + modeladmin.add_extension_options( + _("Navigation extension"), + {"fields": ("navigation_extension",), "classes": ("collapse",)}, + ) diff --git a/feincms/module/page/extensions/navigationgroups.py b/feincms/module/page/extensions/navigationgroups.py index fc47ec033..eef662295 100644 --- a/feincms/module/page/extensions/navigationgroups.py +++ b/feincms/module/page/extensions/navigationgroups.py @@ -12,24 +12,23 @@ class Extension(extensions.Extension): - ident = 'navigationgroups' - groups = [ - ('default', _('Default')), - ('footer', _('Footer')), - ] + ident = "navigationgroups" + groups = [("default", _("Default")), ("footer", _("Footer"))] def handle_model(self): self.model.add_to_class( - 'navigation_group', + "navigation_group", models.CharField( - _('navigation group'), + _("navigation group"), choices=self.groups, default=self.groups[0][0], max_length=20, blank=True, - db_index=True)) + db_index=True, + ), + ) def handle_modeladmin(self, modeladmin): - modeladmin.add_extension_options('navigation_group') - modeladmin.extend_list('list_display', ['navigation_group']) - modeladmin.extend_list('list_filter', ['navigation_group']) + modeladmin.add_extension_options("navigation_group") + modeladmin.extend_list("list_display", ["navigation_group"]) + modeladmin.extend_list("list_filter", ["navigation_group"]) diff --git a/feincms/module/page/extensions/relatedpages.py b/feincms/module/page/extensions/relatedpages.py index 2ddfb0ef7..2a5cf02d7 100644 --- a/feincms/module/page/extensions/relatedpages.py +++ b/feincms/module/page/extensions/relatedpages.py @@ -12,17 +12,19 @@ class Extension(extensions.Extension): def handle_model(self): - self.model.add_to_class('related_pages', models.ManyToManyField( - settings.FEINCMS_DEFAULT_PAGE_MODEL, - blank=True, - related_name='%(app_label)s_%(class)s_related', - help_text=_( - 'Select pages that should be listed as related content.'))) + self.model.add_to_class( + "related_pages", + models.ManyToManyField( + settings.FEINCMS_DEFAULT_PAGE_MODEL, + blank=True, + related_name="%(app_label)s_%(class)s_related", + help_text=_("Select pages that should be listed as related content."), + ), + ) def handle_modeladmin(self, modeladmin): - modeladmin.extend_list('filter_horizontal', ['related_pages']) + modeladmin.extend_list("filter_horizontal", ["related_pages"]) - modeladmin.add_extension_options(_('Related pages'), { - 'fields': ('related_pages',), - 'classes': ('collapse',), - }) + modeladmin.add_extension_options( + _("Related pages"), {"fields": ("related_pages",), "classes": ("collapse",)} + ) diff --git a/feincms/module/page/extensions/sites.py b/feincms/module/page/extensions/sites.py index b0b25d245..69ad32cd9 100644 --- a/feincms/module/page/extensions/sites.py +++ b/feincms/module/page/extensions/sites.py @@ -16,14 +16,18 @@ def current_site(queryset): class Extension(extensions.Extension): def handle_model(self): self.model.add_to_class( - 'site', + "site", models.ForeignKey( - Site, verbose_name=_('Site'), default=settings.SITE_ID, - on_delete=models.CASCADE)) + Site, + verbose_name=_("Site"), + default=settings.SITE_ID, + on_delete=models.CASCADE, + ), + ) - PageManager.add_to_active_filters(current_site, key='current_site') + PageManager.add_to_active_filters(current_site, key="current_site") def handle_modeladmin(self, modeladmin): - modeladmin.extend_list('list_display', ['site']) - modeladmin.extend_list('list_filter', ['site']) - modeladmin.add_extension_options('site') + modeladmin.extend_list("list_display", ["site"]) + modeladmin.extend_list("list_filter", ["site"]) + modeladmin.add_extension_options("site") diff --git a/feincms/module/page/extensions/symlinks.py b/feincms/module/page/extensions/symlinks.py index c5e4c095b..549d03342 100644 --- a/feincms/module/page/extensions/symlinks.py +++ b/feincms/module/page/extensions/symlinks.py @@ -14,26 +14,29 @@ class Extension(extensions.Extension): def handle_model(self): - self.model.add_to_class('symlinked_page', models.ForeignKey( - 'self', - blank=True, - null=True, - on_delete=models.CASCADE, - related_name='%(app_label)s_%(class)s_symlinks', - verbose_name=_('symlinked page'), - help_text=_('All content is inherited from this page if given.'))) + self.model.add_to_class( + "symlinked_page", + models.ForeignKey( + "self", + blank=True, + null=True, + on_delete=models.CASCADE, + related_name="%(app_label)s_%(class)s_symlinks", + verbose_name=_("symlinked page"), + help_text=_("All content is inherited from this page if given."), + ), + ) @monkeypatch_property(self.model) def content(self): - if not hasattr(self, '_content_proxy'): + if not hasattr(self, "_content_proxy"): if self.symlinked_page: - self._content_proxy = self.content_proxy_class( - self.symlinked_page) + self._content_proxy = self.content_proxy_class(self.symlinked_page) else: self._content_proxy = self.content_proxy_class(self) return self._content_proxy def handle_modeladmin(self, modeladmin): - modeladmin.extend_list('raw_id_fields', ['symlinked_page']) - modeladmin.add_extension_options('symlinked_page') + modeladmin.extend_list("raw_id_fields", ["symlinked_page"]) + modeladmin.add_extension_options("symlinked_page") diff --git a/feincms/module/page/extensions/titles.py b/feincms/module/page/extensions/titles.py index 68529b113..d70da3c43 100644 --- a/feincms/module/page/extensions/titles.py +++ b/feincms/module/page/extensions/titles.py @@ -15,20 +15,30 @@ class Extension(extensions.Extension): def handle_model(self): - self.model.add_to_class('_content_title', models.TextField( - _('content title'), - blank=True, - help_text=_( - 'The first line is the main title, the following' - ' lines are subtitles.'))) - - self.model.add_to_class('_page_title', models.CharField( - _('page title'), - max_length=69, - blank=True, - help_text=_( - 'Page title for browser window. Same as title by' - ' default. Must be 69 characters or fewer.'))) + self.model.add_to_class( + "_content_title", + models.TextField( + _("content title"), + blank=True, + help_text=_( + "The first line is the main title, the following" + " lines are subtitles." + ), + ), + ) + + self.model.add_to_class( + "_page_title", + models.CharField( + _("page title"), + max_length=69, + blank=True, + help_text=_( + "Page title for browser window. Same as title by" + " default. Must be 69 characters or fewer." + ), + ), + ) @monkeypatch_property(self.model) def page_title(self): @@ -54,10 +64,10 @@ def content_title(self): @monkeypatch_property(self.model) def content_subtitle(self): - return '\n'.join(self._content_title.splitlines()[1:]) + return "\n".join(self._content_title.splitlines()[1:]) def handle_modeladmin(self, modeladmin): - modeladmin.add_extension_options(_('Titles'), { - 'fields': ('_content_title', '_page_title'), - 'classes': ('collapse',), - }) + modeladmin.add_extension_options( + _("Titles"), + {"fields": ("_content_title", "_page_title"), "classes": ("collapse",)}, + ) diff --git a/feincms/module/page/forms.py b/feincms/module/page/forms.py index 1e7f3abd8..9d115b503 100644 --- a/feincms/module/page/forms.py +++ b/feincms/module/page/forms.py @@ -21,28 +21,38 @@ class RedirectToWidget(ForeignKeyRawIdWidget): def label_for_value(self, value): match = re.match( # XXX this regex would be available as .models.REDIRECT_TO_RE - r'^(?P\w+).(?P\w+):(?P\d+)$', - value) + r"^(?P\w+).(?P\w+):(?P\d+)$", + value, + ) if match: matches = match.groupdict() - model = apps.get_model(matches['app_label'], matches['model_name']) + model = apps.get_model(matches["app_label"], matches["model_name"]) try: - instance = model._default_manager.get(pk=int(matches['pk'])) - return ' %s (%s)' % ( - instance, instance.get_absolute_url()) + instance = model._default_manager.get(pk=int(matches["pk"])) + return " %s (%s)" % ( + instance, + instance.get_absolute_url(), + ) except model.DoesNotExist: pass - return '' + return "" # ------------------------------------------------------------------------ class PageAdminForm(MPTTAdminForm): never_copy_fields = ( - 'title', 'slug', 'parent', 'active', 'override_url', - 'translation_of', '_content_title', '_page_title') + "title", + "slug", + "parent", + "active", + "override_url", + "translation_of", + "_content_title", + "_page_title", + ) @property def page_model(self): @@ -55,12 +65,11 @@ def page_manager(self): def __init__(self, *args, **kwargs): ensure_completely_loaded() - if 'initial' in kwargs: - if 'parent' in kwargs['initial']: + if "initial" in kwargs: + if "parent" in kwargs["initial"]: # Prefill a few form values from the parent page try: - page = self.page_manager.get( - pk=kwargs['initial']['parent']) + page = self.page_manager.get(pk=kwargs["initial"]["parent"]) data = model_to_dict(page) @@ -73,76 +82,80 @@ def __init__(self, *args, **kwargs): if field in data: del data[field] - data.update(kwargs['initial']) + data.update(kwargs["initial"]) if page.template.child_template: - data['template_key'] = page.template.child_template - kwargs['initial'] = data + data["template_key"] = page.template.child_template + kwargs["initial"] = data except self.page_model.DoesNotExist: pass - elif 'translation_of' in kwargs['initial']: + elif "translation_of" in kwargs["initial"]: # Only if translation extension is active try: - page = self.page_manager.get( - pk=kwargs['initial']['translation_of']) + page = self.page_manager.get(pk=kwargs["initial"]["translation_of"]) original = page.original_translation data = { - 'translation_of': original.id, - 'template_key': original.template_key, - 'active': original.active, - 'in_navigation': original.in_navigation, + "translation_of": original.id, + "template_key": original.template_key, + "active": original.active, + "in_navigation": original.in_navigation, } if original.parent: try: - data['parent'] = original.parent.get_translation( - kwargs['initial']['language'] + data["parent"] = original.parent.get_translation( + kwargs["initial"]["language"] ).id except self.page_model.DoesNotExist: # ignore this -- the translation does not exist pass - data.update(kwargs['initial']) - kwargs['initial'] = data + data.update(kwargs["initial"]) + kwargs["initial"] = data except (AttributeError, self.page_model.DoesNotExist): pass # Not required, only a nice-to-have for the `redirect_to` field - modeladmin = kwargs.pop('modeladmin', None) + modeladmin = kwargs.pop("modeladmin", None) super(PageAdminForm, self).__init__(*args, **kwargs) - if modeladmin and 'redirect_to' in self.fields: + if modeladmin and "redirect_to" in self.fields: # Note: Using `parent` is not strictly correct, but we can be # sure that `parent` always points to another page instance, # and that's good enough for us. - field = self.page_model._meta.get_field('parent') - self.fields['redirect_to'].widget = RedirectToWidget( - field.remote_field if hasattr(field, 'remote_field') else field.rel, # noqa - modeladmin.admin_site) - - if 'template_key' in self.fields: + field = self.page_model._meta.get_field("parent") + self.fields["redirect_to"].widget = RedirectToWidget( + field.remote_field + if hasattr(field, "remote_field") + else field.rel, # noqa + modeladmin.admin_site, + ) + + if "template_key" in self.fields: choices = [] for key, template_name in self.page_model.TEMPLATE_CHOICES: template = self.page_model._feincms_templates[key] pages_for_template = self.page_model._default_manager.filter( - template_key=key) - pk = kwargs['instance'].pk if kwargs.get('instance') else None + template_key=key + ) + pk = kwargs["instance"].pk if kwargs.get("instance") else None other_pages_for_template = pages_for_template.exclude(pk=pk) if template.singleton and other_pages_for_template.exists(): continue # don't allow selection of singleton if in use if template.preview_image: - choices.append(( - template.key, - mark_safe('%s %s' % ( - template.preview_image, + choices.append( + ( template.key, - template.title, - )) - )) + mark_safe( + '%s %s' + % (template.preview_image, template.key, template.title) + ), + ) + ) else: choices.append((template.key, template.title)) - self.fields['template_key'].choices = choices + self.fields["template_key"].choices = choices def clean(self): cleaned_data = super(PageAdminForm, self).clean() @@ -160,18 +173,21 @@ def clean(self): current_id = self.instance.id active_pages = active_pages.exclude(id=current_id) - sites_is_installed = apps.is_installed('django.contrib.sites') - if sites_is_installed and 'site' in cleaned_data: - active_pages = active_pages.filter(site=cleaned_data['site']) + sites_is_installed = apps.is_installed("django.contrib.sites") + if sites_is_installed and "site" in cleaned_data: + active_pages = active_pages.filter(site=cleaned_data["site"]) # Convert PK in redirect_to field to something nicer for the future - redirect_to = cleaned_data.get('redirect_to') - if redirect_to and re.match(r'^\d+$', redirect_to): + redirect_to = cleaned_data.get("redirect_to") + if redirect_to and re.match(r"^\d+$", redirect_to): opts = self.page_model._meta - cleaned_data['redirect_to'] = '%s.%s:%s' % ( - opts.app_label, opts.model_name, redirect_to) + cleaned_data["redirect_to"] = "%s.%s:%s" % ( + opts.app_label, + opts.model_name, + redirect_to, + ) - if 'active' in cleaned_data and not cleaned_data['active']: + if "active" in cleaned_data and not cleaned_data["active"]: # If the current item is inactive, we do not need to conduct # further validation. Note that we only check for the flag, not # for any other active filters. This is because we do not want @@ -179,12 +195,12 @@ def clean(self): # really won't be active at the same time. return cleaned_data - if 'override_url' in cleaned_data and cleaned_data['override_url']: - if active_pages.filter( - _cached_url=cleaned_data['override_url']).count(): - self._errors['override_url'] = self.error_class([ - _('This URL is already taken by an active page.')]) - del cleaned_data['override_url'] + if "override_url" in cleaned_data and cleaned_data["override_url"]: + if active_pages.filter(_cached_url=cleaned_data["override_url"]).count(): + self._errors["override_url"] = self.error_class( + [_("This URL is already taken by an active page.")] + ) + del cleaned_data["override_url"] return cleaned_data @@ -193,24 +209,27 @@ def clean(self): parent = self.page_manager.get(pk=current_id).parent else: # The user tries to create a new page - parent = cleaned_data['parent'] + parent = cleaned_data["parent"] if parent: - new_url = '%s%s/' % (parent._cached_url, cleaned_data['slug']) + new_url = "%s%s/" % (parent._cached_url, cleaned_data["slug"]) else: - new_url = '/%s/' % cleaned_data['slug'] + new_url = "/%s/" % cleaned_data["slug"] if active_pages.filter(_cached_url=new_url).count(): - self._errors['active'] = self.error_class([ - _('This URL is already taken by another active page.')]) - del cleaned_data['active'] + self._errors["active"] = self.error_class( + [_("This URL is already taken by another active page.")] + ) + del cleaned_data["active"] if parent and parent.template.enforce_leaf: - self._errors['parent'] = self.error_class( - [_('This page does not allow attachment of child pages')]) - del cleaned_data['parent'] + self._errors["parent"] = self.error_class( + [_("This page does not allow attachment of child pages")] + ) + del cleaned_data["parent"] return cleaned_data + # ------------------------------------------------------------------------ # ------------------------------------------------------------------------ diff --git a/feincms/module/page/modeladmins.py b/feincms/module/page/modeladmins.py index a913c0db8..4a6847537 100644 --- a/feincms/module/page/modeladmins.py +++ b/feincms/module/page/modeladmins.py @@ -15,6 +15,7 @@ from django.http import HttpResponseRedirect from django.utils.functional import curry from django.utils.translation import ugettext_lazy as _ + try: from django.urls import reverse except ImportError: @@ -41,57 +42,58 @@ class Media: fieldset_insertion_index = 2 fieldsets = [ - (None, { - 'fields': [ - ('title', 'slug'), - ('active', 'in_navigation'), - ], - }), - (_('Other options'), { - 'classes': ['collapse'], - 'fields': [ - 'template_key', 'parent', 'override_url', 'redirect_to'], - }), + (None, {"fields": [("title", "slug"), ("active", "in_navigation")]}), + ( + _("Other options"), + { + "classes": ["collapse"], + "fields": ["template_key", "parent", "override_url", "redirect_to"], + }, + ), # <-- insertion point, extensions appear here, see insertion_index # above item_editor.FEINCMS_CONTENT_FIELDSET, ] readonly_fields = [] list_display = [ - 'short_title', 'is_visible_admin', 'in_navigation_toggle', 'template'] - list_filter = ['active', 'in_navigation', 'template_key', 'parent'] - search_fields = ['title', 'slug'] - prepopulated_fields = {'slug': ('title',)} + "short_title", + "is_visible_admin", + "in_navigation_toggle", + "template", + ] + list_filter = ["active", "in_navigation", "template_key", "parent"] + search_fields = ["title", "slug"] + prepopulated_fields = {"slug": ("title",)} - raw_id_fields = ['parent'] - radio_fields = {'template_key': admin.HORIZONTAL} + raw_id_fields = ["parent"] + radio_fields = {"template_key": admin.HORIZONTAL} @classmethod def add_extension_options(cls, *f): - if isinstance(f[-1], dict): # called with a fieldset + if isinstance(f[-1], dict): # called with a fieldset cls.fieldsets.insert(cls.fieldset_insertion_index, f) - f[1]['classes'] = list(f[1].get('classes', [])) - f[1]['classes'].append('collapse') - else: # assume called with "other" fields - cls.fieldsets[1][1]['fields'].extend(f) + f[1]["classes"] = list(f[1].get("classes", [])) + f[1]["classes"].append("collapse") + else: # assume called with "other" fields + cls.fieldsets[1][1]["fields"].extend(f) def __init__(self, model, admin_site): ensure_completely_loaded() - if len(model._feincms_templates) > 4 and \ - 'template_key' in self.radio_fields: - del(self.radio_fields['template_key']) + if len(model._feincms_templates) > 4 and "template_key" in self.radio_fields: + del (self.radio_fields["template_key"]) super(PageAdmin, self).__init__(model, admin_site) in_navigation_toggle = tree_editor.ajax_editable_boolean( - 'in_navigation', _('in navigation')) + "in_navigation", _("in navigation") + ) def get_readonly_fields(self, request, obj=None): readonly = super(PageAdmin, self).get_readonly_fields(request, obj=obj) if not settings.FEINCMS_SINGLETON_TEMPLATE_CHANGE_ALLOWED: if obj and obj.template and obj.template.singleton: - return tuple(readonly) + ('template_key',) + return tuple(readonly) + ("template_key",) return readonly def get_form(self, *args, **kwargs): @@ -99,11 +101,12 @@ def get_form(self, *args, **kwargs): return curry(form, modeladmin=self) def _actions_column(self, page): - addable = getattr(page, 'feincms_addable', True) + addable = getattr(page, "feincms_addable", True) preview_url = "../../r/%s/%s/" % ( ContentType.objects.get_for_model(self.model).id, - page.id) + page.id, + ) actions = super(PageAdmin, self)._actions_column(page) if addable: @@ -112,61 +115,63 @@ def _actions_column(self, page): 0, '' '%s' - '' % ( + "" + % ( page.pk, - _('Add child page'), - static('feincms/img/icon_addlink.gif'), - _('Add child page'), - ) + _("Add child page"), + static("feincms/img/icon_addlink.gif"), + _("Add child page"), + ), ) actions.insert( 0, '' '%s' - '' % ( + "" + % ( preview_url, - _('View on site'), - static('feincms/img/selector-search.gif'), - _('View on site'), - ) + _("View on site"), + static("feincms/img/selector-search.gif"), + _("View on site"), + ), ) return actions def add_view(self, request, **kwargs): - kwargs['form_url'] = request.get_full_path() # Preserve GET parameters - if 'translation_of' in request.GET and 'language' in request.GET: + kwargs["form_url"] = request.get_full_path() # Preserve GET parameters + if "translation_of" in request.GET and "language" in request.GET: try: original = self.model._tree_manager.get( - pk=request.GET.get('translation_of')) + pk=request.GET.get("translation_of") + ) except (AttributeError, self.model.DoesNotExist): pass else: - language_code = request.GET['language'] - language = dict( - django_settings.LANGUAGES).get(language_code, '') - kwargs['extra_context'] = { - 'adding_translation': True, - 'title': _( - 'Add %(language)s translation of "%(page)s"') % { - 'language': language, - 'page': original, - }, - 'language_name': language, - 'translation_of': original, + language_code = request.GET["language"] + language = dict(django_settings.LANGUAGES).get(language_code, "") + kwargs["extra_context"] = { + "adding_translation": True, + "title": _('Add %(language)s translation of "%(page)s"') + % {"language": language, "page": original}, + "language_name": language, + "translation_of": original, } return super(PageAdmin, self).add_view(request, **kwargs) def response_add(self, request, obj, *args, **kwargs): - response = super(PageAdmin, self).response_add( - request, obj, *args, **kwargs) - if ('parent' in request.GET and - '_addanother' in request.POST and - response.status_code in (301, 302)): + response = super(PageAdmin, self).response_add(request, obj, *args, **kwargs) + if ( + "parent" in request.GET + and "_addanother" in request.POST + and response.status_code in (301, 302) + ): # Preserve GET parameters if we are about to add another page - response['Location'] += '?parent=%s' % request.GET['parent'] + response["Location"] += "?parent=%s" % request.GET["parent"] - if ('translation_of' in request.GET and - '_copy_content_from_original' in request.POST): + if ( + "translation_of" in request.GET + and "_copy_content_from_original" in request.POST + ): # Copy all contents for content_type in obj._feincms_content_types: if content_type.objects.filter(parent=obj).exists(): @@ -176,14 +181,19 @@ def response_add(self, request, obj, *args, **kwargs): try: original = self.model._tree_manager.get( - pk=request.GET.get('translation_of')) + pk=request.GET.get("translation_of") + ) original = original.original_translation obj.copy_content_from(original) obj.save() - self.message_user(request, _( - 'The content from the original translation has been copied' - ' to the newly created page.')) + self.message_user( + request, + _( + "The content from the original translation has been copied" + " to the newly created page." + ), + ) except (AttributeError, self.model.DoesNotExist): pass @@ -191,18 +201,14 @@ def response_add(self, request, obj, *args, **kwargs): def change_view(self, request, object_id, **kwargs): try: - return super(PageAdmin, self).change_view( - request, object_id, **kwargs) + return super(PageAdmin, self).change_view(request, object_id, **kwargs) except PermissionDenied: messages.add_message( request, messages.ERROR, - _( - "You don't have the necessary permissions to edit this" - " object" - ) + _("You don't have the necessary permissions to edit this" " object"), ) - return HttpResponseRedirect(reverse('admin:page_page_changelist')) + return HttpResponseRedirect(reverse("admin:page_page_changelist")) def has_delete_permission(self, request, obj=None): if not settings.FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED: @@ -212,7 +218,8 @@ def has_delete_permission(self, request, obj=None): def changelist_view(self, request, *args, **kwargs): _local.visible_pages = list( - self.model.objects.active().values_list('id', flat=True)) + self.model.objects.active().values_list("id", flat=True) + ) return super(PageAdmin, self).changelist_view(request, *args, **kwargs) def is_visible_admin(self, page): @@ -225,30 +232,36 @@ def is_visible_admin(self, page): if page.id in _local.visible_pages: _local.visible_pages.remove(page.id) return tree_editor.ajax_editable_boolean_cell( - page, 'active', override=False, text=_('inherited')) + page, "active", override=False, text=_("inherited") + ) if page.active and page.id not in _local.visible_pages: # is active but should not be shown, so visibility limited by # extension: show a "not active" return tree_editor.ajax_editable_boolean_cell( - page, 'active', override=False, text=_('extensions')) + page, "active", override=False, text=_("extensions") + ) + + return tree_editor.ajax_editable_boolean_cell(page, "active") - return tree_editor.ajax_editable_boolean_cell(page, 'active') - is_visible_admin.short_description = _('is active') - is_visible_admin.editable_boolean_field = 'active' + is_visible_admin.short_description = _("is active") + is_visible_admin.editable_boolean_field = "active" # active toggle needs more sophisticated result function def is_visible_recursive(self, page): # Have to refresh visible_pages here, because TreeEditor.toggle_boolean # will have changed the value when inside this code path. _local.visible_pages = list( - self.model.objects.active().values_list('id', flat=True)) + self.model.objects.active().values_list("id", flat=True) + ) retval = [] for c in page.get_descendants(include_self=True): retval.append(self.is_visible_admin(c)) return retval + is_visible_admin.editable_boolean_result = is_visible_recursive + # ------------------------------------------------------------------------ # ------------------------------------------------------------------------ diff --git a/feincms/module/page/models.py b/feincms/module/page/models.py index 1f6f5262f..10932feec 100644 --- a/feincms/module/page/models.py +++ b/feincms/module/page/models.py @@ -10,6 +10,7 @@ from django.http import Http404 from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ + try: from django.urls import reverse except ImportError: @@ -22,9 +23,7 @@ from feincms.module.mixins import ContentModelMixin from feincms.module.page import processors from feincms.utils.managers import ActiveAwareContentManagerMixin -from feincms.utils import ( - shorten_string, match_model_string, get_model_instance -) +from feincms.utils import shorten_string, match_model_string, get_model_instance # ------------------------------------------------------------------------ @@ -35,8 +34,7 @@ class BasePageManager(ActiveAwareContentManagerMixin, TreeManager): """ # The fields which should be excluded when creating a copy. - exclude_from_copy = [ - 'id', 'tree_id', 'lft', 'rght', 'level', 'redirect_to'] + exclude_from_copy = ["id", "tree_id", "lft", "rght", "level", "redirect_to"] def page_for_path(self, path, raise404=False): """ @@ -47,14 +45,13 @@ def page_for_path(self, path, raise404=False): Page.objects.page_for_path(request.path) """ - stripped = path.strip('/') + stripped = path.strip("/") try: - page = self.active().get( - _cached_url='/%s/' % stripped if stripped else '/') + page = self.active().get(_cached_url="/%s/" % stripped if stripped else "/") if not page.are_ancestors_active(): - raise self.model.DoesNotExist('Parents are inactive.') + raise self.model.DoesNotExist("Parents are inactive.") return page @@ -75,22 +72,23 @@ def best_match_for_path(self, path, raise404=False): page with url '/photos/album/'. """ - paths = ['/'] - path = path.strip('/') + paths = ["/"] + path = path.strip("/") if path: - tokens = path.split('/') - paths += [ - '/%s/' % '/'.join(tokens[:i]) - for i in range(1, len(tokens) + 1)] + tokens = path.split("/") + paths += ["/%s/" % "/".join(tokens[:i]) for i in range(1, len(tokens) + 1)] try: - page = self.active().filter(_cached_url__in=paths).extra( - select={'_url_length': 'LENGTH(_cached_url)'} - ).order_by('-_url_length')[0] + page = ( + self.active() + .filter(_cached_url__in=paths) + .extra(select={"_url_length": "LENGTH(_cached_url)"}) + .order_by("-_url_length")[0] + ) if not page.are_ancestors_active(): - raise IndexError('Parents are inactive.') + raise IndexError("Parents are inactive.") return page @@ -114,8 +112,7 @@ def toplevel_navigation(self): return self.in_navigation().filter(parent__isnull=True) - def for_request(self, request, raise404=False, best_match=False, - path=None): + def for_request(self, request, raise404=False, best_match=False, path=None): """ Return a page for the request @@ -129,15 +126,15 @@ def for_request(self, request, raise404=False, best_match=False, could be determined. """ - if not hasattr(request, '_feincms_page'): + if not hasattr(request, "_feincms_page"): path = path or request.path_info or request.path if best_match: request._feincms_page = self.best_match_for_path( - path, raise404=raise404) + path, raise404=raise404 + ) else: - request._feincms_page = self.page_for_path( - path, raise404=raise404) + request._feincms_page = self.page_for_path(path, raise404=raise404) return request._feincms_page @@ -147,45 +144,65 @@ class PageManager(BasePageManager): pass -PageManager.add_to_active_filters(Q(active=True), key='is_active') +PageManager.add_to_active_filters(Q(active=True), key="is_active") # ------------------------------------------------------------------------ @python_2_unicode_compatible class BasePage(create_base_model(MPTTModel), ContentModelMixin): - active = models.BooleanField(_('active'), default=True) + active = models.BooleanField(_("active"), default=True) # structure and navigation - title = models.CharField(_('title'), max_length=200, help_text=_( - 'This title is also used for navigation menu items.')) + title = models.CharField( + _("title"), + max_length=200, + help_text=_("This title is also used for navigation menu items."), + ) slug = models.SlugField( - _('slug'), max_length=150, - help_text=_('This is used to build the URL for this page')) + _("slug"), + max_length=150, + help_text=_("This is used to build the URL for this page"), + ) parent = models.ForeignKey( - 'self', verbose_name=_('Parent'), blank=True, + "self", + verbose_name=_("Parent"), + blank=True, on_delete=models.CASCADE, - null=True, related_name='children') + null=True, + related_name="children", + ) # Custom list_filter - see admin/filterspecs.py parent.parent_filter = True - in_navigation = models.BooleanField(_('in navigation'), default=False) + in_navigation = models.BooleanField(_("in navigation"), default=False) override_url = models.CharField( - _('override URL'), max_length=255, - blank=True, help_text=_( - 'Override the target URL. Be sure to include slashes at the ' - 'beginning and at the end if it is a local URL. This ' - 'affects both the navigation and subpages\' URLs.')) + _("override URL"), + max_length=255, + blank=True, + help_text=_( + "Override the target URL. Be sure to include slashes at the " + "beginning and at the end if it is a local URL. This " + "affects both the navigation and subpages' URLs." + ), + ) redirect_to = models.CharField( - _('redirect to'), max_length=255, + _("redirect to"), + max_length=255, blank=True, help_text=_( - 'Target URL for automatic redirects' - ' or the primary key of a page.')) + "Target URL for automatic redirects" " or the primary key of a page." + ), + ) _cached_url = models.CharField( - _('Cached URL'), max_length=255, blank=True, - editable=False, default='', db_index=True) + _("Cached URL"), + max_length=255, + blank=True, + editable=False, + default="", + db_index=True, + ) class Meta: - ordering = ['tree_id', 'lft'] + ordering = ["tree_id", "lft"] abstract = True objects = PageManager() @@ -206,11 +223,11 @@ def is_active(self): return False pages = self.__class__.objects.active().filter( - tree_id=self.tree_id, - lft__lte=self.lft, - rght__gte=self.rght) + tree_id=self.tree_id, lft__lte=self.lft, rght__gte=self.rght + ) return pages.count() > self.level - is_active.short_description = _('is active') + + is_active.short_description = _("is active") def are_ancestors_active(self): """ @@ -228,8 +245,9 @@ def short_title(self): Title shortened for display. """ return shorten_string(self.title) - short_title.admin_order_field = 'title' - short_title.short_description = _('title') + + short_title.admin_order_field = "title" + short_title.short_description = _("title") def __init__(self, *args, **kwargs): super(BasePage, self).__init__(*args, **kwargs) @@ -250,9 +268,9 @@ def save(self, *args, **kwargs): if self.override_url: self._cached_url = self.override_url elif self.is_root_node(): - self._cached_url = '/%s/' % self.slug + self._cached_url = "/%s/" % self.slug else: - self._cached_url = '%s%s/' % (self.parent._cached_url, self.slug) + self._cached_url = "%s%s/" % (self.parent._cached_url, self.slug) cached_page_urls[self.id] = self._cached_url super(BasePage, self).save(*args, **kwargs) @@ -264,29 +282,35 @@ def save(self, *args, **kwargs): if self._cached_url == self._original_cached_url: return - pages = self.get_descendants().order_by('lft') + pages = self.get_descendants().order_by("lft") for page in pages: if page.override_url: page._cached_url = page.override_url else: # cannot be root node by definition - page._cached_url = '%s%s/' % ( + page._cached_url = "%s%s/" % ( cached_page_urls[page.parent_id], - page.slug) + page.slug, + ) cached_page_urls[page.id] = page._cached_url super(BasePage, page).save() # do not recurse + save.alters_data = True def delete(self, *args, **kwargs): if not settings.FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED: if self.template.singleton: - raise PermissionDenied(_( - 'This %(page_class)s uses a singleton template, and ' - 'FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED=False' % { - 'page_class': self._meta.verbose_name})) + raise PermissionDenied( + _( + "This %(page_class)s uses a singleton template, and " + "FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED=False" + % {"page_class": self._meta.verbose_name} + ) + ) super(BasePage, self).delete(*args, **kwargs) + delete.alters_data = True def get_absolute_url(self): @@ -294,10 +318,10 @@ def get_absolute_url(self): Return the absolute URL of this page. """ # result url never begins or ends with a slash - url = self._cached_url.strip('/') + url = self._cached_url.strip("/") if url: - return reverse('feincms_handler', args=(url,)) - return reverse('feincms_home') + return reverse("feincms_handler", args=(url,)) + return reverse("feincms_home") def get_navigation_url(self): """ @@ -360,18 +384,20 @@ def register_default_processors(cls): Page experience. """ cls.register_request_processor( - processors.redirect_request_processor, key='redirect') + processors.redirect_request_processor, key="redirect" + ) cls.register_request_processor( - processors.extra_context_request_processor, key='extra_context') + processors.extra_context_request_processor, key="extra_context" + ) # ------------------------------------------------------------------------ class Page(BasePage): class Meta: - ordering = ['tree_id', 'lft'] - verbose_name = _('page') - verbose_name_plural = _('pages') - app_label = 'page' + ordering = ["tree_id", "lft"] + verbose_name = _("page") + verbose_name_plural = _("pages") + app_label = "page" # not yet # permissions = (("edit_page", _("Can edit page metadata")),) diff --git a/feincms/module/page/processors.py b/feincms/module/page/processors.py index 44441e824..894e4c0f9 100644 --- a/feincms/module/page/processors.py +++ b/feincms/module/page/processors.py @@ -18,12 +18,14 @@ def redirect_request_processor(page, request): """ target = page.get_redirect_to_target(request) if target: - extra_path = request._feincms_extra_context.get('extra_path', '/') - if extra_path == '/': + extra_path = request._feincms_extra_context.get("extra_path", "/") + if extra_path == "/": return HttpResponseRedirect(target) logger.debug( "Page redirect on '%s' not taken because extra path '%s' present", - page.get_absolute_url(), extra_path) + page.get_absolute_url(), + extra_path, + ) raise Http404() @@ -31,22 +33,24 @@ def extra_context_request_processor(page, request): """ Fills ``request._feincms_extra_context`` with a few useful variables. """ - request._feincms_extra_context.update({ - # XXX This variable name isn't accurate anymore. - 'in_appcontent_subpage': False, - 'extra_path': '/', - }) + request._feincms_extra_context.update( + { + # XXX This variable name isn't accurate anymore. + "in_appcontent_subpage": False, + "extra_path": "/", + } + ) url = page.get_absolute_url() if request.path != url: - request._feincms_extra_context.update({ - 'in_appcontent_subpage': True, - 'extra_path': re.sub( - '^' + re.escape(url.rstrip('/')), - '', - request.path, - ), - }) + request._feincms_extra_context.update( + { + "in_appcontent_subpage": True, + "extra_path": re.sub( + "^" + re.escape(url.rstrip("/")), "", request.path + ), + } + ) def etag_request_processor(page, request): @@ -62,6 +66,7 @@ class DummyResponse(dict): This is a dummy class with enough behaviour of HttpResponse so we can use the condition decorator without too much pain. """ + def has_header(page, what): return False @@ -85,7 +90,8 @@ def lastmodifier(request, page, *args, **kwargs): # the handler if processing is to continue and a non-DummyResponse # (should be a "304 not modified") if the etag matches. rsp = condition(etag_func=etagger, last_modified_func=lastmodifier)( - dummy_response_handler)(request, page) + dummy_response_handler + )(request, page) # If dummy then don't do anything, if a real response, return and # thus shortcut the request processing. @@ -101,7 +107,7 @@ def etag_response_processor(page, request, response): """ etag = page.etag(request) if etag is not None: - response['ETag'] = '"' + etag + '"' + response["ETag"] = '"' + etag + '"' def debug_sql_queries_response_processor(verbose=False, file=sys.stderr): @@ -127,9 +133,10 @@ def processor(page, request, response): import sqlparse def print_sql(x): - return sqlparse.format( - x, reindent=True, keyword_case='upper') + return sqlparse.format(x, reindent=True, keyword_case="upper") + except Exception: + def print_sql(x): return x @@ -140,9 +147,10 @@ def print_sql(x): for q in connection.queries: i += 1 if verbose: - print("%d : [%s]\n%s\n" % ( - i, q['time'], print_sql(q['sql'])), file=file) - time += float(q['time']) + print( + "%d : [%s]\n%s\n" % (i, q["time"], print_sql(q["sql"])), file=file + ) + time += float(q["time"]) print("-" * 60, file=file) print("Total: %d queries, %.3f ms" % (i, time), file=file) diff --git a/feincms/module/page/sitemap.py b/feincms/module/page/sitemap.py index 40f2853d4..c5cbd5850 100644 --- a/feincms/module/page/sitemap.py +++ b/feincms/module/page/sitemap.py @@ -17,10 +17,19 @@ class PageSitemap(Sitemap): The PageSitemap can be used to automatically generate sitemap.xml files for submission to index engines. See http://www.sitemaps.org/ for details. """ - def __init__(self, navigation_only=False, max_depth=0, changefreq=None, - queryset=None, filter=None, extended_navigation=False, - page_model=settings.FEINCMS_DEFAULT_PAGE_MODEL, - *args, **kwargs): + + def __init__( + self, + navigation_only=False, + max_depth=0, + changefreq=None, + queryset=None, + filter=None, + extended_navigation=False, + page_model=settings.FEINCMS_DEFAULT_PAGE_MODEL, + *args, + **kwargs + ): """ The PageSitemap accepts the following parameters for customisation of the resulting sitemap.xml output: @@ -48,7 +57,7 @@ def __init__(self, navigation_only=False, max_depth=0, changefreq=None, if queryset is not None: self.queryset = queryset else: - Page = apps.get_model(*page_model.split('.')) + Page = apps.get_model(*page_model.split(".")) self.queryset = Page.objects.active() def items(self): @@ -60,7 +69,7 @@ def items(self): if callable(base_qs): base_qs = base_qs() - self.max_depth = base_qs.aggregate(Max('level'))['level__max'] or 0 + self.max_depth = base_qs.aggregate(Max("level"))["level__max"] or 0 if self.depth_cutoff > 0: self.max_depth = min(self.depth_cutoff, self.max_depth) @@ -78,14 +87,13 @@ def items(self): for idx, page in enumerate(pages): if self.depth_cutoff > 0 and page.level == self.max_depth: continue - if getattr(page, 'navigation_extension', None): + if getattr(page, "navigation_extension", None): cnt = 0 for p in page.extended_navigation(): depth_too_deep = ( - self.depth_cutoff > 0 and - p.level > self.depth_cutoff) - not_in_nav = ( - self.navigation_only and not p.in_navigation) + self.depth_cutoff > 0 and p.level > self.depth_cutoff + ) + not_in_nav = self.navigation_only and not p.in_navigation if depth_too_deep or not_in_nav: continue cnt += 1 @@ -97,7 +105,7 @@ def items(self): return pages def lastmod(self, obj): - return getattr(obj, 'modification_date', None) + return getattr(obj, "modification_date", None) # the priority is computed of the depth in the tree of a page # may we should make an extension to give control to the user for priority @@ -107,7 +115,7 @@ def priority(self, obj): the site. Top level get highest priority, then each level is decreased by per_level. """ - if getattr(obj, 'override_url', '') == '/': + if getattr(obj, "override_url", "") == "/": prio = 1.0 else: prio = 1.0 - (obj.level + 1) * self.per_level @@ -119,4 +127,5 @@ def priority(self, obj): return "%0.2g" % min(1.0, prio) + # ------------------------------------------------------------------------ diff --git a/feincms/shortcuts.py b/feincms/shortcuts.py index 936460915..a24be3fe5 100644 --- a/feincms/shortcuts.py +++ b/feincms/shortcuts.py @@ -11,6 +11,6 @@ def render_to_response_best_match(request, template_name, dictionary=None): """ dictionary = dictionary or {} - dictionary['feincms_page'] = Page.objects.best_match_for_request(request) + dictionary["feincms_page"] = Page.objects.best_match_for_request(request) return render(request, template_name, dictionary) diff --git a/feincms/templatetags/applicationcontent_tags.py b/feincms/templatetags/applicationcontent_tags.py index 531e94eda..37dd2aae2 100644 --- a/feincms/templatetags/applicationcontent_tags.py +++ b/feincms/templatetags/applicationcontent_tags.py @@ -4,6 +4,7 @@ from django.template import TemplateSyntaxError from django.template.defaulttags import kwarg_re from django.utils.encoding import smart_str + try: from django.urls import NoReverseMatch except ImportError: @@ -11,9 +12,9 @@ from feincms.apps import ApplicationContent, app_reverse as do_app_reverse from feincms.templatetags.feincms_tags import _render_content + # backwards compatibility import -from feincms.templatetags.fragment_tags import ( - fragment, get_fragment, has_fragment) +from feincms.templatetags.fragment_tags import fragment, get_fragment, has_fragment register = template.Library() @@ -37,10 +38,11 @@ def feincms_render_region_appcontent(page, region, request): {% feincms_render_region_appcontent feincms_page "main" request %} {% endif %} """ - return ''.join( + return "".join( _render_content(content, request=request) for content in page.content.all_of_type(ApplicationContent) - if content.region == region) + if content.region == region + ) def _current_app(context): @@ -50,7 +52,7 @@ def _current_app(context): try: return context.request.resolver_match.namespace except AttributeError: - return getattr(context, 'current_app', None) + return getattr(context, "current_app", None) class AppReverseNode(template.Node): @@ -63,24 +65,31 @@ def __init__(self, view_name, urlconf, args, kwargs, asvar): def render(self, context): args = [arg.resolve(context) for arg in self.args] - kwargs = dict([ - (smart_str(k, 'ascii'), v.resolve(context)) - for k, v in self.kwargs.items()]) + kwargs = dict( + [ + (smart_str(k, "ascii"), v.resolve(context)) + for k, v in self.kwargs.items() + ] + ) view_name = self.view_name.resolve(context) urlconf = self.urlconf.resolve(context) try: url = do_app_reverse( - view_name, urlconf, args=args, kwargs=kwargs, - current_app=_current_app(context)) + view_name, + urlconf, + args=args, + kwargs=kwargs, + current_app=_current_app(context), + ) except NoReverseMatch: if self.asvar is None: raise - url = '' + url = "" if self.asvar: context[self.asvar] = url - return '' + return "" else: return url @@ -118,14 +127,15 @@ def app_reverse(parser, token): if len(bits) < 3: raise TemplateSyntaxError( "'%s' takes at least two arguments" - " (path to a view and a urlconf)" % bits[0]) + " (path to a view and a urlconf)" % bits[0] + ) viewname = parser.compile_filter(bits[1]) urlconf = parser.compile_filter(bits[2]) args = [] kwargs = {} asvar = None bits = bits[3:] - if len(bits) >= 2 and bits[-2] == 'as': + if len(bits) >= 2 and bits[-2] == "as": asvar = bits[-1] bits = bits[:-2] @@ -133,8 +143,7 @@ def app_reverse(parser, token): for bit in bits: match = kwarg_re.match(bit) if not match: - raise TemplateSyntaxError( - "Malformed arguments to app_reverse tag") + raise TemplateSyntaxError("Malformed arguments to app_reverse tag") name, value = match.groups() if name: kwargs[name] = parser.compile_filter(value) diff --git a/feincms/templatetags/feincms_admin_tags.py b/feincms/templatetags/feincms_admin_tags.py index 5292b410f..e8127eb39 100644 --- a/feincms/templatetags/feincms_admin_tags.py +++ b/feincms/templatetags/feincms_admin_tags.py @@ -23,7 +23,7 @@ def post_process_fieldsets(context, fieldset): return fieldset fields_to_include = set(fieldset.form.fields.keys()) - for f in ('id', 'DELETE', 'ORDER'): + for f in ("id", "DELETE", "ORDER"): fields_to_include.discard(f) def _filter_recursive(fields): @@ -46,37 +46,38 @@ def _filter_recursive(fields): for f in fields_to_include: new_fields.append(f) - if context.get('request'): - new_fields.extend(list( - fieldset.model_admin.get_readonly_fields( - context.get('request'), - context.get('original'), + if context.get("request"): + new_fields.extend( + list( + fieldset.model_admin.get_readonly_fields( + context.get("request"), context.get("original") + ) ) - )) + ) fieldset.fields = new_fields - return '' + return "" -@register.inclusion_tag('admin/feincms/content_type_selection_widget.html', - takes_context=True) +@register.inclusion_tag( + "admin/feincms/content_type_selection_widget.html", takes_context=True +) def show_content_type_selection_widget(context, region): """ {% show_content_type_selection_widget region %} """ - user = context['request'].user + user = context["request"].user types = OrderedDict({None: []}) for ct in region._content_types: # Skip cts that we shouldn't be adding anyway opts = ct._meta - perm = opts.app_label + "." + get_permission_codename('add', opts) + perm = opts.app_label + "." + get_permission_codename("add", opts) if not user.has_perm(perm): continue - types.setdefault( - getattr(ct, 'optgroup', None), - [], - ).append((ct.__name__.lower, ct._meta.verbose_name)) + types.setdefault(getattr(ct, "optgroup", None), []).append( + (ct.__name__.lower, ct._meta.verbose_name) + ) - return {'types': types} + return {"types": types} diff --git a/feincms/templatetags/feincms_page_tags.py b/feincms/templatetags/feincms_page_tags.py index 2ea6cd7c1..60eca87c8 100644 --- a/feincms/templatetags/feincms_page_tags.py +++ b/feincms/templatetags/feincms_page_tags.py @@ -18,21 +18,20 @@ from feincms.module.page.extensions.navigation import PagePretender from feincms.utils.templatetags import ( SimpleAssignmentNodeWithVarAndArgs, - do_simple_assignment_node_with_var_and_args_helper) + do_simple_assignment_node_with_var_and_args_helper, +) -logger = logging.getLogger('feincms.templatetags.page') +logger = logging.getLogger("feincms.templatetags.page") register = template.Library() assignment_tag = ( - register.simple_tag if django.VERSION >= (1, 9) - else register.assignment_tag + register.simple_tag if django.VERSION >= (1, 9) else register.assignment_tag ) def _get_page_model(): - return apps.get_model( - *feincms_settings.FEINCMS_DEFAULT_PAGE_MODEL.split('.')) + return apps.get_model(*feincms_settings.FEINCMS_DEFAULT_PAGE_MODEL.split(".")) # ------------------------------------------------------------------------ @@ -56,8 +55,7 @@ def feincms_nav(context, feincms_page, level=1, depth=1, group=None): if isinstance(feincms_page, HttpRequest): try: - feincms_page = page_class.objects.for_request( - feincms_page, best_match=True) + feincms_page = page_class.objects.for_request(feincms_page, best_match=True) except page_class.DoesNotExist: return [] @@ -68,8 +66,8 @@ def feincms_nav(context, feincms_page, level=1, depth=1, group=None): queryset = feincms_page.__class__._default_manager.in_navigation().filter( **{ - '%s__gte' % mptt_opts.level_attr: mptt_level_range[0], - '%s__lt' % mptt_opts.level_attr: mptt_level_range[1], + "%s__gte" % mptt_opts.level_attr: mptt_level_range[0], + "%s__lt" % mptt_opts.level_attr: mptt_level_range[1], } ) @@ -98,10 +96,13 @@ def feincms_nav(context, feincms_page, level=1, depth=1, group=None): queryset = page_class.objects.none() if parent: - if getattr(parent, 'navigation_extension', None): + if getattr(parent, "navigation_extension", None): # Special case for navigation extensions - return list(parent.extended_navigation( - depth=depth, request=context.get('request'))) + return list( + parent.extended_navigation( + depth=depth, request=context.get("request") + ) + ) # Apply descendant filter queryset &= parent.get_descendants() @@ -126,40 +127,44 @@ def _parentactive_filter(iterable): # navigationgroups extension support def _navigationgroup_filter(iterable): for elem in iterable: - if getattr(elem, 'navigation_group', None) == group: + if getattr(elem, "navigation_group", None) == group: yield elem queryset = _navigationgroup_filter(queryset) - if hasattr(feincms_page, 'navigation_extension'): + if hasattr(feincms_page, "navigation_extension"): # Filter out children of nodes which have a navigation extension def _navext_filter(iterable): current_navextension_node = None for elem in iterable: # Eliminate all subitems of last processed nav extension - if current_navextension_node is not None and \ - current_navextension_node.is_ancestor_of(elem): + if ( + current_navextension_node is not None + and current_navextension_node.is_ancestor_of(elem) + ): continue yield elem - if getattr(elem, 'navigation_extension', None): + if getattr(elem, "navigation_extension", None): current_navextension_node = elem try: for extended in elem.extended_navigation( - depth=depth, request=context.get('request')): + depth=depth, request=context.get("request") + ): # Only return items from the extended navigation # which are inside the requested level+depth # values. The "-1" accounts for the differences in # MPTT and navigation level counting - this_level = getattr( - extended, mptt_opts.level_attr, 0) + this_level = getattr(extended, mptt_opts.level_attr, 0) if this_level < level + depth - 1: yield extended except Exception as e: logger.warn( "feincms_nav caught exception in navigation" " extension for page %d: %s", - current_navextension_node.id, format_exception(e)) + current_navextension_node.id, + format_exception(e), + ) else: current_navextension_node = None @@ -200,21 +205,19 @@ class LanguageLinksNode(SimpleAssignmentNodeWithVarAndArgs): """ def what(self, page, args): - only_existing = args.get('existing', False) - exclude_current = args.get('excludecurrent', False) + only_existing = args.get("existing", False) + exclude_current = args.get("excludecurrent", False) # Preserve the trailing path when switching languages if extra_path # exists (this is mostly the case when we are working inside an # ApplicationContent-managed page subtree) - trailing_path = '' - request = args.get('request', None) + trailing_path = "" + request = args.get("request", None) if request: # Trailing path without first slash - trailing_path = request._feincms_extra_context.get( - 'extra_path', '')[1:] + trailing_path = request._feincms_extra_context.get("extra_path", "")[1:] - translations = dict( - (t.language, t) for t in page.available_translations()) + translations = dict((t.language, t) for t in page.available_translations()) translations[page.language] = page links = [] @@ -224,10 +227,9 @@ def what(self, page, args): # hardcoded paths... bleh if key in translations: - links.append(( - key, - name, - translations[key].get_absolute_url() + trailing_path)) + links.append( + (key, name, translations[key].get_absolute_url() + trailing_path) + ) elif not only_existing: links.append((key, name, None)) @@ -235,8 +237,9 @@ def what(self, page, args): register.tag( - 'feincms_languagelinks', - do_simple_assignment_node_with_var_and_args_helper(LanguageLinksNode)) + "feincms_languagelinks", + do_simple_assignment_node_with_var_and_args_helper(LanguageLinksNode), +) # ------------------------------------------------------------------------ @@ -251,14 +254,13 @@ def _translate_page_into(page, language, default=None): return page if language is not None: - translations = dict( - (t.language, t) for t in page.available_translations()) + translations = dict((t.language, t) for t in page.available_translations()) if language in translations: return translations[language] except AttributeError: pass - if hasattr(default, '__call__'): + if hasattr(default, "__call__"): return default(page=page) return default @@ -284,16 +286,16 @@ class TranslatedPageNode(SimpleAssignmentNodeWithVarAndArgs): whether settings LANGUAGES contains that code -- so naming a variable "en" will probably not do what is intended. """ + def what(self, page, args, default=None): - language = args.get('language', None) + language = args.get("language", None) if language is None: language = settings.LANGUAGES[0][0] else: if language not in (x[0] for x in settings.LANGUAGES): try: - language = template.Variable(language).resolve( - self.render_context) + language = template.Variable(language).resolve(self.render_context) except template.VariableDoesNotExist: language = settings.LANGUAGES[0][0] @@ -301,32 +303,34 @@ def what(self, page, args, default=None): register.tag( - 'feincms_translatedpage', - do_simple_assignment_node_with_var_and_args_helper(TranslatedPageNode)) + "feincms_translatedpage", + do_simple_assignment_node_with_var_and_args_helper(TranslatedPageNode), +) # ------------------------------------------------------------------------ class TranslatedPageNodeOrBase(TranslatedPageNode): def what(self, page, args): return super(TranslatedPageNodeOrBase, self).what( - page, args, - default=getattr(page, 'get_original_translation', page)) + page, args, default=getattr(page, "get_original_translation", page) + ) register.tag( - 'feincms_translatedpage_or_base', - do_simple_assignment_node_with_var_and_args_helper( - TranslatedPageNodeOrBase)) + "feincms_translatedpage_or_base", + do_simple_assignment_node_with_var_and_args_helper(TranslatedPageNodeOrBase), +) # ------------------------------------------------------------------------ @register.filter def feincms_translated_or_base(pages, language=None): - if not hasattr(pages, '__iter__'): + if not hasattr(pages, "__iter__"): pages = [pages] for page in pages: yield _translate_page_into( - page, language, default=page.get_original_translation) + page, language, default=page.get_original_translation + ) # ------------------------------------------------------------------------ @@ -443,8 +447,10 @@ def siblings_along_path_to(page_list, page2): # comes from feincms_nav). We'll cope with the fall-out of that # assumption when it happens... ancestors = [ - a_page for a_page in page_list - if a_page.is_ancestor_of(page2, include_self=True)] + a_page + for a_page in page_list + if a_page.is_ancestor_of(page2, include_self=True) + ] top_level = min((a_page.level for a_page in page_list)) if not ancestors: @@ -454,25 +460,25 @@ def siblings_along_path_to(page_list, page2): page_class = _get_page_model() p = page_class( - title="dummy", - tree_id=-1, - parent_id=None, - in_navigation=False) + title="dummy", tree_id=-1, parent_id=None, in_navigation=False + ) ancestors = (p,) siblings = [ - a_page for a_page in page_list if ( - a_page.parent_id == page2.id or - a_page.level == top_level or - any((_is_sibling_of(a_page, a) for a in ancestors)) + a_page + for a_page in page_list + if ( + a_page.parent_id == page2.id + or a_page.level == top_level + or any((_is_sibling_of(a_page, a) for a in ancestors)) ) ] return siblings except (AttributeError, ValueError) as e: logger.warn( - "siblings_along_path_to caught exception: %s", - format_exception(e)) + "siblings_along_path_to caught exception: %s", format_exception(e) + ) return () @@ -495,25 +501,25 @@ def page_is_active(context, page, feincms_page=None, path=None): """ if isinstance(page, PagePretender): if path is None: - path = context['request'].path_info + path = context["request"].path_info return path.startswith(page.get_absolute_url()) else: if feincms_page is None: - feincms_page = context['feincms_page'] + feincms_page = context["feincms_page"] return page.is_ancestor_of(feincms_page, include_self=True) # ------------------------------------------------------------------------ @register.simple_tag def feincms_parentlink(of_, feincms_page, **kwargs): - level = int(kwargs.get('level', 1)) + level = int(kwargs.get("level", 1)) if feincms_page.level + 1 == level: return feincms_page.get_absolute_url() elif feincms_page.level + 1 < level: - return '#' + return "#" try: return feincms_page.get_ancestors()[level - 1].get_absolute_url() except IndexError: - return '#' + return "#" diff --git a/feincms/templatetags/feincms_tags.py b/feincms/templatetags/feincms_tags.py index 1450d7ace..faeed6c57 100644 --- a/feincms/templatetags/feincms_tags.py +++ b/feincms/templatetags/feincms_tags.py @@ -16,8 +16,7 @@ register = template.Library() assignment_tag = ( - register.simple_tag if django.VERSION >= (1, 9) - else register.assignment_tag + register.simple_tag if django.VERSION >= (1, 9) else register.assignment_tag ) @@ -25,15 +24,15 @@ def _render_content(content, **kwargs): # Track current render level and abort if we nest too deep. Avoids # crashing in recursive page contents (eg. a page list that contains # itself or similar). - request = kwargs.get('request') + request = kwargs.get("request") if request is not None: - level = getattr(request, 'feincms_render_level', 0) + level = getattr(request, "feincms_render_level", 0) if level > 10: - logging.getLogger('feincms').error( - 'Refusing to render %r, render level is already %s' % ( - content, level)) + logging.getLogger("feincms").error( + "Refusing to render %r, render level is already %s" % (content, level) + ) return - setattr(request, 'feincms_render_level', level + 1) + setattr(request, "feincms_render_level", level + 1) r = content.render(**kwargs) @@ -62,8 +61,8 @@ def _render_content(content, **kwargs): return plugin_template.render(context) if request is not None: - level = getattr(request, 'feincms_render_level', 1) - setattr(request, 'feincms_render_level', max(level - 1, 0)) + level = getattr(request, "feincms_render_level", 1) + setattr(request, "feincms_render_level", max(level - 1, 0)) return r @@ -74,11 +73,14 @@ def feincms_render_region(context, feincms_object, region, request=None): {% feincms_render_region feincms_page "main" request %} """ if not feincms_object: - return '' + return "" - return mark_safe(''.join( - _render_content(content, request=request, context=context) - for content in getattr(feincms_object.content, region))) + return mark_safe( + "".join( + _render_content(content, request=request, context=context) + for content in getattr(feincms_object.content, region) + ) + ) @register.simple_tag(takes_context=True) @@ -87,7 +89,7 @@ def feincms_render_content(context, content, request=None): {% feincms_render_content content request %} """ if not content: - return '' + return "" return _render_content(content, request=request, context=context) diff --git a/feincms/templatetags/feincms_thumbnail.py b/feincms/templatetags/feincms_thumbnail.py index 09f397746..299889a74 100644 --- a/feincms/templatetags/feincms_thumbnail.py +++ b/feincms/templatetags/feincms_thumbnail.py @@ -19,16 +19,16 @@ from feincms import settings -logger = logging.getLogger('feincms.templatetags.thumbnail') +logger = logging.getLogger("feincms.templatetags.thumbnail") register = template.Library() @python_2_unicode_compatible class Thumbnailer(object): - THUMBNAIL_SIZE_RE = re.compile(r'^(?P\d+)x(?P\d+)$') - MARKER = '_thumb_' + THUMBNAIL_SIZE_RE = re.compile(r"^(?P\d+)x(?P\d+)$") + MARKER = "_thumb_" - def __init__(self, filename, size='200x200'): + def __init__(self, filename, size="200x200"): self.filename = filename self.size = size @@ -39,39 +39,41 @@ def url(self): def __str__(self): match = self.THUMBNAIL_SIZE_RE.match(self.size) if not (self.filename and match): - return '' + return "" matches = match.groupdict() # figure out storage - if hasattr(self.filename, 'storage'): + if hasattr(self.filename, "storage"): storage = self.filename.storage else: storage = default_storage # figure out name - if hasattr(self.filename, 'name'): + if hasattr(self.filename, "name"): filename = self.filename.name else: filename = force_text(self.filename) # defining the filename and the miniature filename try: - basename, format = filename.rsplit('.', 1) + basename, format = filename.rsplit(".", 1) except ValueError: - basename, format = filename, 'jpg' - - miniature = ''.join([ - settings.FEINCMS_THUMBNAIL_DIR, - basename, - self.MARKER, - self.size, - '.', - format, - ]) + basename, format = filename, "jpg" + + miniature = "".join( + [ + settings.FEINCMS_THUMBNAIL_DIR, + basename, + self.MARKER, + self.size, + ".", + format, + ] + ) if settings.FEINCMS_THUMBNAIL_CACHE_TIMEOUT != 0: - cache_key = 'thumb_url_%s' % miniature + cache_key = "thumb_url_%s" % miniature url = cache.get(cache_key) if url: return url @@ -80,15 +82,15 @@ def __str__(self): generate = True else: try: - generate = ( - storage.modified_time(miniature) < - storage.modified_time(filename)) + generate = storage.modified_time(miniature) < storage.modified_time( + filename + ) except (NotImplementedError, AttributeError): # storage does NOT support modified_time generate = False except (OSError, IOError): # Someone might have delete the file - return '' + return "" if generate: try: @@ -96,13 +98,15 @@ def __str__(self): storage=storage, original=filename, size=matches, - miniature=miniature) + miniature=miniature, + ) except Exception as exc: logger.warning( - 'Rendering a thumbnail failed: %r', + "Rendering a thumbnail failed: %r", exc, exc_info=True, - extra={'stack': True, 'exception': exc}) + extra={"stack": True, "exception": exc}, + ) # PIL raises a plethora of Exceptions if reading the image # is not possible. Since we cannot be sure what Exception will # happen, catch them all so the thumbnailer will never fail. @@ -110,11 +114,7 @@ def __str__(self): url = storage.url(miniature) if settings.FEINCMS_THUMBNAIL_CACHE_TIMEOUT != 0: - cache.set( - cache_key, - url, - timeout=settings.FEINCMS_THUMBNAIL_CACHE_TIMEOUT - ) + cache.set(cache_key, url, timeout=settings.FEINCMS_THUMBNAIL_CACHE_TIMEOUT) return url def generate(self, storage, original, size, miniature): @@ -123,15 +123,15 @@ def generate(self, storage, original, size, miniature): image = Image.open(original_bytes) # defining the size - w, h = int(size['w']), int(size['h']) + w, h = int(size["w"]), int(size["h"]) format = image.format # Save format for the save() call later image.thumbnail([w, h], Image.ANTIALIAS) buf = BytesIO() - if image.mode not in ('RGBA', 'RGB', 'L'): - image = image.convert('RGBA') - if format.lower() not in ('jpg', 'jpeg', 'png'): - format = 'jpeg' + if image.mode not in ("RGBA", "RGB", "L"): + image = image.convert("RGBA") + if format.lower() not in ("jpg", "jpeg", "png"): + format = "jpeg" image.save(buf, format, quality=90) raw_data = buf.getvalue() buf.close() @@ -143,19 +143,18 @@ def generate(self, storage, original, size, miniature): class CropscaleThumbnailer(Thumbnailer): - THUMBNAIL_SIZE_RE = re.compile( - r'^(?P\d+)x(?P\d+)(-(?P\d+)x(?P\d+))?$') - MARKER = '_cropscale_' + THUMBNAIL_SIZE_RE = re.compile(r"^(?P\d+)x(?P\d+)(-(?P\d+)x(?P\d+))?$") + MARKER = "_cropscale_" def generate(self, storage, original, size, miniature): with storage.open(original) as original_handle: with BytesIO(original_handle.read()) as original_bytes: image = Image.open(original_bytes) - w, h = int(size['w']), int(size['h']) + w, h = int(size["w"]), int(size["h"]) - if size['x'] and size['y']: - x, y = int(size['x']), int(size['y']) + if size["x"] and size["y"]: + x, y = int(size["x"]), int(size["y"]) else: x, y = 50, 50 @@ -176,18 +175,21 @@ def generate(self, storage, original, size, miniature): y_offset = int(float(src_height - crop_height) * y / 100) format = image.format # Save format for the save() call later - image = image.crop(( - x_offset, - y_offset, - x_offset + int(crop_width), - y_offset + int(crop_height))) + image = image.crop( + ( + x_offset, + y_offset, + x_offset + int(crop_width), + y_offset + int(crop_height), + ) + ) image = image.resize((dst_width, dst_height), Image.ANTIALIAS) buf = BytesIO() - if image.mode not in ('RGBA', 'RGB', 'L'): - image = image.convert('RGBA') - if format.lower() not in ('jpg', 'jpeg', 'png'): - format = 'jpeg' + if image.mode not in ("RGBA", "RGB", "L"): + image = image.convert("RGBA") + if format.lower() not in ("jpg", "jpeg", "png"): + format = "jpeg" image.save(buf, format, quality=90) raw_data = buf.getvalue() buf.close() @@ -199,7 +201,7 @@ def generate(self, storage, original, size, miniature): @register.filter -def thumbnail(filename, size='200x200'): +def thumbnail(filename, size="200x200"): """ Creates a thumbnail from the image passed, returning its path:: @@ -224,7 +226,7 @@ def thumbnail(filename, size='200x200'): @register.filter -def cropscale(filename, size='200x200'): +def cropscale(filename, size="200x200"): """ Scales the image down and crops it so that its size equals exactly the size passed (as long as the initial image is bigger than the specification). diff --git a/feincms/templatetags/fragment_tags.py b/feincms/templatetags/fragment_tags.py index 5c5e7098b..fd8fc4904 100644 --- a/feincms/templatetags/fragment_tags.py +++ b/feincms/templatetags/fragment_tags.py @@ -7,7 +7,7 @@ class FragmentNode(template.Node): - def __init__(self, nodelist, request, identifier, mode='append'): + def __init__(self, nodelist, request, identifier, mode="append"): self.nodelist = nodelist self.request_var = template.Variable(request) self.identifier_var = template.Variable(identifier) @@ -18,19 +18,19 @@ def render(self, context): identifier = self.identifier_var.resolve(context) rendered = self.nodelist.render(context) - if not hasattr(request, '_feincms_fragments'): + if not hasattr(request, "_feincms_fragments"): request._feincms_fragments = {} - old = request._feincms_fragments.get(identifier, '') + old = request._feincms_fragments.get(identifier, "") - if self.mode == 'prepend': + if self.mode == "prepend": request._feincms_fragments[identifier] = rendered + old - elif self.mode == 'replace': + elif self.mode == "replace": request._feincms_fragments[identifier] = rendered else: # append request._feincms_fragments[identifier] = old + rendered - return '' + return "" @register.tag @@ -50,7 +50,7 @@ def fragment(parser, token): {% endfragment %} """ - nodelist = parser.parse(('endfragment'),) + nodelist = parser.parse(("endfragment")) parser.delete_first_token() return FragmentNode(nodelist, *token.contents.split()[1:]) @@ -69,11 +69,11 @@ def render(self, context): try: value = request._feincms_fragments[fragment] except (AttributeError, KeyError): - value = '' + value = "" if self.as_var: context[self.as_var] = value - return '' + return "" return value @@ -95,10 +95,11 @@ def get_fragment(parser, token): if len(fragments) == 3: return GetFragmentNode(fragments[1], fragments[2]) - elif len(fragments) == 5 and fragments[3] == 'as': + elif len(fragments) == 5 and fragments[3] == "as": return GetFragmentNode(fragments[1], fragments[2], fragments[4]) raise template.TemplateSyntaxError( - 'Invalid syntax for get_fragment: %s' % token.contents) + "Invalid syntax for get_fragment: %s" % token.contents + ) @register.filter @@ -108,4 +109,4 @@ def has_fragment(request, identifier): {% if request|has_fragment:"title" %} ... {% endif %} """ - return getattr(request, '_feincms_fragments', {}).get(identifier) + return getattr(request, "_feincms_fragments", {}).get(identifier) diff --git a/feincms/translations.py b/feincms/translations.py index eaab23da9..68e3a2cf8 100644 --- a/feincms/translations.py +++ b/feincms/translations.py @@ -47,6 +47,7 @@ class _NoTranslation(object): """Simple marker for when no translations exist for a certain object Only used for caching.""" + pass @@ -65,7 +66,7 @@ def short_language_code(code=None): if code is None: code = translation.get_language() - pos = code.find('-') + pos = code.find("-") if pos > -1: return code[:pos] return code @@ -91,6 +92,7 @@ def lookup_translations(language_code=None): The current language is used if ``language_code`` isn't specified. """ + def _transform(qs): lang_ = language_code if language_code else translation.get_language() @@ -112,22 +114,16 @@ def _transform(qs): if not instance_dict: return - candidates = list( - instance_dict.values() - )[0].translations.model._default_manager.all() + candidates = list(instance_dict.values())[ + 0 + ].translations.model._default_manager.all() if instance_dict: - _process(candidates, instance_dict, lang_, 'iexact') + _process(candidates, instance_dict, lang_, "iexact") if instance_dict: - _process( - candidates, - instance_dict, - settings.LANGUAGE_CODE, - 'istartswith', - ) + _process(candidates, instance_dict, settings.LANGUAGE_CODE, "istartswith") if instance_dict: - for candidate in candidates.filter( - parent__pk__in=instance_dict.keys()): + for candidate in candidates.filter(parent__pk__in=instance_dict.keys()): if candidate.parent_id in instance_dict: _found(instance_dict, candidate) @@ -145,9 +141,9 @@ def _found(instance_dict, candidate): def _process(candidates, instance_dict, lang_, op_): candidates = candidates.filter( Q(parent__pk__in=instance_dict.keys()), - Q(**{'language_code__' + op_: lang_}) | - Q(**{'language_code__' + op_: short_language_code(lang_)}) - ).order_by('-language_code') + Q(**{"language_code__" + op_: lang_}) + | Q(**{"language_code__" + op_: short_language_code(lang_)}), + ).order_by("-language_code") for candidate in candidates: # The candidate's parent might already have a translation by now @@ -169,9 +165,9 @@ def only_language(self, language=short_language_code): Uses the currently active language by default. """ - return self.filter(translations__language_code=( - language() if callable(language) else language - )) + return self.filter( + translations__language_code=(language() if callable(language) else language) + ) @python_2_unicode_compatible @@ -183,16 +179,19 @@ class TranslatedObjectMixin(object): def _get_translation_object(self, queryset, language_code): try: return queryset.filter( - Q(language_code__iexact=language_code) | - Q(language_code__iexact=short_language_code(language_code)) - ).order_by('-language_code')[0] + Q(language_code__iexact=language_code) + | Q(language_code__iexact=short_language_code(language_code)) + ).order_by("-language_code")[0] except IndexError: try: return queryset.filter( - Q(language_code__istartswith=settings.LANGUAGE_CODE) | - Q(language_code__istartswith=short_language_code( - settings.LANGUAGE_CODE)) - ).order_by('-language_code')[0] + Q(language_code__istartswith=settings.LANGUAGE_CODE) + | Q( + language_code__istartswith=short_language_code( + settings.LANGUAGE_CODE + ) + ) + ).order_by("-language_code")[0] except IndexError: try: return queryset.all()[0] @@ -204,15 +203,8 @@ def get_translation_cache_key(self, language_code=None): can purge on-demand""" if not language_code: language_code = translation.get_language() - return ( - ('FEINCMS:%d:XLATION:' % getattr(settings, 'SITE_ID', 0)) + - '-'.join( - ['%s' % s for s in ( - self._meta.db_table, - self.id, - language_code, - )] - ) + return ("FEINCMS:%d:XLATION:" % getattr(settings, "SITE_ID", 0)) + "-".join( + ["%s" % s for s in (self._meta.db_table, self.id, language_code)] ) def get_translation(self, language_code=None): @@ -226,7 +218,8 @@ def get_translation(self, language_code=None): if trans is None: try: trans = self._get_translation_object( - self.translations.all(), language_code) + self.translations.all(), language_code + ) except ObjectDoesNotExist: trans = _NoTranslation cache.set(key, trans) @@ -240,13 +233,13 @@ def get_translation(self, language_code=None): @property def translation(self): - if not hasattr(self, '_cached_translation'): + if not hasattr(self, "_cached_translation"): self._cached_translation = self.get_translation() return self._cached_translation @property def available_translations(self): - return self.translations.values_list('language_code', flat=True) + return self.translations.values_list("language_code", flat=True) def __str__(self): try: @@ -255,7 +248,7 @@ def __str__(self): return self.__class__.__name__ if translation: - return '%s' % translation + return "%s" % translation return self.__class__.__name__ @@ -280,14 +273,18 @@ def Translation(model): class Inner(models.Model): parent = models.ForeignKey( - model, related_name='translations', on_delete=models.CASCADE) + model, related_name="translations", on_delete=models.CASCADE + ) language_code = models.CharField( - _('language'), max_length=10, - choices=settings.LANGUAGES, default=settings.LANGUAGES[0][0], - editable=len(settings.LANGUAGES) > 1) + _("language"), + max_length=10, + choices=settings.LANGUAGES, + default=settings.LANGUAGES[0][0], + editable=len(settings.LANGUAGES) > 1, + ) class Meta: - unique_together = ('parent', 'language_code') + unique_together = ("parent", "language_code") # (beware the above will not be inherited automatically if you # provide a Meta class within your translation subclass) abstract = True @@ -298,11 +295,13 @@ def short_language_code(self): def save(self, *args, **kwargs): super(Inner, self).save(*args, **kwargs) self.parent.purge_translation_cache() + save.alters_data = True def delete(self, *args, **kwargs): super(Inner, self).delete(*args, **kwargs) self.parent.purge_translation_cache() + delete.alters_data = True return Inner @@ -322,8 +321,7 @@ def admin_translationinline(model, inline_class=admin.StackedInline, **kwargs): ) """ - kwargs['extra'] = 1 - kwargs['max_num'] = len(settings.LANGUAGES) - kwargs['model'] = model - return type( - str(model.__class__.__name__ + 'Inline'), (inline_class,), kwargs) + kwargs["extra"] = 1 + kwargs["max_num"] = len(settings.LANGUAGES) + kwargs["model"] = model + return type(str(model.__class__.__name__ + "Inline"), (inline_class,), kwargs) diff --git a/feincms/urls.py b/feincms/urls.py index 357f72e9b..bb7bd851b 100644 --- a/feincms/urls.py +++ b/feincms/urls.py @@ -8,6 +8,6 @@ handler = Handler.as_view() urlpatterns = [ - url(r'^$', handler, name='feincms_home'), - url(r'^(.*)/$', handler, name='feincms_handler'), + url(r"^$", handler, name="feincms_home"), + url(r"^(.*)/$", handler, name="feincms_handler"), ] diff --git a/feincms/utils/__init__.py b/feincms/utils/__init__.py index aef8b207b..75397f11f 100644 --- a/feincms/utils/__init__.py +++ b/feincms/utils/__init__.py @@ -27,8 +27,8 @@ def get_object(path, fail_silently=False): return import_module(path) except ImportError: try: - dot = path.rindex('.') - mod, fn = path[:dot], path[dot + 1:] + dot = path.rindex(".") + mod, fn = path[:dot], path[dot + 1 :] return getattr(import_module(mod), fn) except (AttributeError, ImportError): @@ -60,8 +60,7 @@ def get_model_instance(app_label, model_name, pk): # ------------------------------------------------------------------------ -REDIRECT_TO_RE = re.compile( - r'^(?P\w+).(?P\w+):(?P\d+)$') +REDIRECT_TO_RE = re.compile(r"^(?P\w+).(?P\w+):(?P\d+)$") def match_model_string(s): @@ -77,7 +76,7 @@ def match_model_string(s): if not match: return None matches = match.groupdict() - return (matches['app_label'], matches['model_name'], int(matches['pk'])) + return (matches["app_label"], matches["model_name"], int(matches["pk"])) # ------------------------------------------------------------------------ @@ -89,14 +88,17 @@ def copy_model_instance(obj, exclude=None): exclude = exclude or () initial = dict( - (f.name, getattr(obj, f.name)) for f in obj._meta.fields - if not isinstance(f, AutoField) and f.name not in exclude and - f not in obj._meta.parents.values()) + (f.name, getattr(obj, f.name)) + for f in obj._meta.fields + if not isinstance(f, AutoField) + and f.name not in exclude + and f not in obj._meta.parents.values() + ) return obj.__class__(**initial) # ------------------------------------------------------------------------ -def shorten_string(str, max_length=50, ellipsis=' … '): +def shorten_string(str, max_length=50, ellipsis=" … "): """ Shorten a string for display, truncate it intelligently when too long. Try to cut it in 2/3 + ellipsis + 1/3 of the original title. Also try to @@ -105,14 +107,14 @@ def shorten_string(str, max_length=50, ellipsis=' … '): if len(str) >= max_length: first_part = int(max_length * 0.6) - next_space = str[first_part:(max_length // 2 - first_part)].find(' ') - if (next_space >= 0 and - first_part + next_space + len(ellipsis) < max_length): + next_space = str[first_part : (max_length // 2 - first_part)].find(" ") + if next_space >= 0 and first_part + next_space + len(ellipsis) < max_length: first_part += next_space return ( - str[:first_part] + - ellipsis + - str[-(max_length - first_part - len(ellipsis)):]) + str[:first_part] + + ellipsis + + str[-(max_length - first_part - len(ellipsis)) :] + ) return str @@ -120,31 +122,22 @@ def shorten_string(str, max_length=50, ellipsis=' … '): def get_singleton(template_key, cls=None, raise_exception=True): cls = cls or settings.FEINCMS_DEFAULT_PAGE_MODEL try: - model = apps.get_model(*cls.split('.')) + model = apps.get_model(*cls.split(".")) if not model: raise ImproperlyConfigured('Cannot load model "%s"' % cls) try: assert model._feincms_templates[template_key].singleton except AttributeError as e: raise ImproperlyConfigured( - '%r does not seem to be a valid FeinCMS base class (%r)' % ( - model, - e, - ) + "%r does not seem to be a valid FeinCMS base class (%r)" % (model, e) ) except KeyError: raise ImproperlyConfigured( - '%r is not a registered template for %r!' % ( - template_key, - model, - ) + "%r is not a registered template for %r!" % (template_key, model) ) except AssertionError: raise ImproperlyConfigured( - '%r is not a *singleton* template for %r!' % ( - template_key, - model, - ) + "%r is not a *singleton* template for %r!" % (template_key, model) ) try: return model._default_manager.get(template_key=template_key) @@ -161,4 +154,4 @@ def get_singleton(template_key, cls=None, raise_exception=True): def get_singleton_url(template_key, cls=None, raise_exception=True): obj = get_singleton(template_key, cls, raise_exception) - return obj.get_absolute_url() if obj else '#broken-link' + return obj.get_absolute_url() if obj else "#broken-link" diff --git a/feincms/utils/queryset_transform.py b/feincms/utils/queryset_transform.py index e7110a7d1..7ded0c45b 100644 --- a/feincms/utils/queryset_transform.py +++ b/feincms/utils/queryset_transform.py @@ -91,7 +91,7 @@ class TransformQuerySet(models.query.QuerySet): def __init__(self, *args, **kwargs): super(TransformQuerySet, self).__init__(*args, **kwargs) self._transform_fns = [] - self._orig_iterable_class = getattr(self, '_iterable_class', None) + self._orig_iterable_class = getattr(self, "_iterable_class", None) def _clone(self, *args, **kwargs): c = super(TransformQuerySet, self)._clone(*args, **kwargs) @@ -104,13 +104,16 @@ def transform(self, *fn): return c if django.VERSION < (1, 11): + def iterator(self): result_iter = super(TransformQuerySet, self).iterator() if not self._transform_fns: return result_iter - if getattr(self, '_iterable_class', None) != self._orig_iterable_class: # noqa + if ( + getattr(self, "_iterable_class", None) != self._orig_iterable_class + ): # noqa # Do not process the result of values() and values_list() return result_iter @@ -120,17 +123,21 @@ def iterator(self): return iter(results) else: + def _fetch_all(self): super(TransformQuerySet, self)._fetch_all() - if getattr(self, '_iterable_class', None) == self._orig_iterable_class: # noqa + if ( + getattr(self, "_iterable_class", None) == self._orig_iterable_class + ): # noqa for fn in self._transform_fns: fn(self._result_cache) -if hasattr(models.Manager, 'from_queryset'): +if hasattr(models.Manager, "from_queryset"): TransformManager = models.Manager.from_queryset(TransformQuerySet) else: + class TransformManager(models.Manager): def get_queryset(self): return TransformQuerySet(self.model, using=self._db) diff --git a/feincms/utils/templatetags.py b/feincms/utils/templatetags.py index 6df5592c5..18258b98d 100644 --- a/feincms/utils/templatetags.py +++ b/feincms/utils/templatetags.py @@ -1,4 +1,4 @@ -''' +""" I really hate repeating myself. These are helpers that avoid typing the whole thing over and over when implementing additional template tags @@ -7,7 +7,7 @@ {% tag as var_name %} {% tag of template_var as var_name %} {% tag of template_var as var_name arg1,arg2,kwarg3=4 %} -''' +""" from __future__ import absolute_import, unicode_literals @@ -17,9 +17,9 @@ def _parse_args(argstr, context=None): try: args = {} - for token in argstr.split(','): - if '=' in token: - k, v = token.split('=', 1) + for token in argstr.split(","): + if "=" in token: + k, v = token.split("=", 1) if context: try: args[k] = template.Variable(v).resolve(context) @@ -33,23 +33,21 @@ def _parse_args(argstr, context=None): return args except TypeError: - raise template.TemplateSyntaxError('Malformed arguments') + raise template.TemplateSyntaxError("Malformed arguments") def do_simple_assignment_node_with_var_and_args_helper(cls): def _func(parser, token): try: - tag_name, of_, in_var_name, as_, var_name, args =\ - token.contents.split() + tag_name, of_, in_var_name, as_, var_name, args = token.contents.split() except ValueError: try: - tag_name, of_, in_var_name, as_, var_name =\ - token.contents.split() - args = '' + tag_name, of_, in_var_name, as_, var_name = token.contents.split() + args = "" except ValueError: raise template.TemplateSyntaxError( - 'Invalid syntax for %s node: %s' % ( - cls.__name__, token.contents)) + "Invalid syntax for %s node: %s" % (cls.__name__, token.contents) + ) return cls(tag_name, in_var_name, var_name, args) @@ -69,9 +67,8 @@ def render(self, context): instance = self.in_var.resolve(context) except template.VariableDoesNotExist: context[self.var_name] = [] - return '' + return "" - context[self.var_name] = self.what( - instance, _parse_args(self.args, context)) + context[self.var_name] = self.what(instance, _parse_args(self.args, context)) - return '' + return "" diff --git a/feincms/views/__init__.py b/feincms/views/__init__.py index 416ab850f..014a6adc8 100644 --- a/feincms/views/__init__.py +++ b/feincms/views/__init__.py @@ -15,19 +15,20 @@ class Handler(ContentView): page_model_path = None - context_object_name = 'feincms_page' + context_object_name = "feincms_page" @cached_property def page_model(self): model = self.page_model_path or settings.FEINCMS_DEFAULT_PAGE_MODEL - return apps.get_model(*model.split('.')) + return apps.get_model(*model.split(".")) def get_object(self): path = None if self.args: path = self.args[0] return self.page_model._default_manager.for_request( - self.request, raise404=True, best_match=True, path=path) + self.request, raise404=True, best_match=True, path=path + ) def dispatch(self, request, *args, **kwargs): try: @@ -36,7 +37,9 @@ def dispatch(self, request, *args, **kwargs): if settings.FEINCMS_CMS_404_PAGE is not None: logger.info( "Http404 raised for '%s', attempting redirect to" - " FEINCMS_CMS_404_PAGE", args[0]) + " FEINCMS_CMS_404_PAGE", + args[0], + ) try: # Fudge environment so that we end up resolving the right # page. @@ -45,12 +48,12 @@ def dispatch(self, request, *args, **kwargs): # page url. # Also clear out the _feincms_page attribute which caches # page lookups (and would just re-raise a 404). - request.path = request.path_info =\ - settings.FEINCMS_CMS_404_PAGE - if hasattr(request, '_feincms_page'): - delattr(request, '_feincms_page') + request.path = request.path_info = settings.FEINCMS_CMS_404_PAGE + if hasattr(request, "_feincms_page"): + delattr(request, "_feincms_page") response = super(Handler, self).dispatch( - request, settings.FEINCMS_CMS_404_PAGE, **kwargs) + request, settings.FEINCMS_CMS_404_PAGE, **kwargs + ) # Only set status if we actually have a page. If we get for # example a redirect, overwriting would yield a blank page if response.status_code == 200: @@ -58,9 +61,9 @@ def dispatch(self, request, *args, **kwargs): return response except Http404: logger.error( - "Http404 raised while resolving" - " FEINCMS_CMS_404_PAGE=%s", - settings.FEINCMS_CMS_404_PAGE) + "Http404 raised while resolving" " FEINCMS_CMS_404_PAGE=%s", + settings.FEINCMS_CMS_404_PAGE, + ) raise e else: raise diff --git a/feincms/views/decorators.py b/feincms/views/decorators.py index 2fab30127..656e50800 100644 --- a/feincms/views/decorators.py +++ b/feincms/views/decorators.py @@ -6,5 +6,7 @@ from feincms.apps import * warnings.warn( - 'Import ApplicationContent and friends from feincms.content.application.models', - DeprecationWarning, stacklevel=2) + "Import ApplicationContent and friends from feincms.content.application.models", + DeprecationWarning, + stacklevel=2, +) diff --git a/setup.cfg b/setup.cfg index cc4bd75fa..948dea0f3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,8 @@ [flake8] -exclude=venv,.tox,build,docs,migrations +exclude=venv,build,docs,.tox,migrations +ignore=E203,W503 +max-line-length=88 +# max-complexity=10 [wheel] universal = 1 diff --git a/setup.py b/setup.py index 2f1156902..a9fdce423 100755 --- a/setup.py +++ b/setup.py @@ -7,53 +7,51 @@ def read(filename): path = os.path.join(os.path.dirname(__file__), filename) - with open(path, encoding='utf-8') as handle: + with open(path, encoding="utf-8") as handle: return handle.read() -version = __import__('feincms').__version__ -devstatus = 'Development Status :: 5 - Production/Stable' -if '.dev' in version: - devstatus = 'Development Status :: 3 - Alpha' -elif '.pre' in version: - devstatus = 'Development Status :: 4 - Beta' +version = __import__("feincms").__version__ +devstatus = "Development Status :: 5 - Production/Stable" +if ".dev" in version: + devstatus = "Development Status :: 3 - Alpha" +elif ".pre" in version: + devstatus = "Development Status :: 4 - Beta" setup( - name='FeinCMS', + name="FeinCMS", version=version, - description='Django-based Page CMS and CMS building toolkit.', - long_description=read('README.rst'), - author='Matthias Kestenholz', - author_email='mk@feinheit.ch', - url='http://github.com/feincms/feincms/', - license='BSD License', - platforms=['OS Independent'], - packages=find_packages( - exclude=['tests'] - ), + description="Django-based Page CMS and CMS building toolkit.", + long_description=read("README.rst"), + author="Matthias Kestenholz", + author_email="mk@feinheit.ch", + url="http://github.com/feincms/feincms/", + license="BSD License", + platforms=["OS Independent"], + packages=find_packages(exclude=["tests"]), include_package_data=True, install_requires=[ - 'Django>=1.7', - 'django-mptt>=0.7.1', - 'Pillow>=2.0.0', - 'pytz>=2014.10', + "Django>=1.7", + "django-mptt>=0.7.1", + "Pillow>=2.0.0", + "pytz>=2014.10", ], classifiers=[ devstatus, - 'Environment :: Web Environment', - 'Framework :: Django', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', - 'Topic :: Software Development', - 'Topic :: Software Development :: Libraries :: Application Frameworks', + "Environment :: Web Environment", + "Framework :: Django", + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Topic :: Internet :: WWW/HTTP :: Dynamic Content", + "Topic :: Software Development", + "Topic :: Software Development :: Libraries :: Application Frameworks", ], zip_safe=False, ) diff --git a/tests/manage.py b/tests/manage.py index 817e86b82..e20cc5e6d 100755 --- a/tests/manage.py +++ b/tests/manage.py @@ -5,8 +5,7 @@ if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "testapp.settings") - sys.path.insert( - 0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from django.core.management import execute_from_command_line diff --git a/tests/testapp/applicationcontent_urls.py b/tests/testapp/applicationcontent_urls.py index 5a1ba3cb4..92a58d13f 100644 --- a/tests/testapp/applicationcontent_urls.py +++ b/tests/testapp/applicationcontent_urls.py @@ -13,57 +13,54 @@ def module_root(request): - return HttpResponse('module_root') + return HttpResponse("module_root") def args_test(request, kwarg1, kwarg2): - return HttpResponse('%s-%s' % (kwarg1, kwarg2)) + return HttpResponse("%s-%s" % (kwarg1, kwarg2)) def full_reverse_test(request): - return render_to_string('full_reverse_test.html', {}) + return render_to_string("full_reverse_test.html", {}) def alias_reverse_test(request): - return render_to_string('alias_reverse_test.html', {}) + return render_to_string("alias_reverse_test.html", {}) def fragment(request): - return render_to_string('fragment.html', {'request': request}) + return render_to_string("fragment.html", {"request": request}) def redirect(request): - return HttpResponseRedirect(request.build_absolute_uri('../')) + return HttpResponseRedirect(request.build_absolute_uri("../")) def response(request): - return HttpResponse('Anything') + return HttpResponse("Anything") def inheritance20(request): - return 'inheritance20.html', {'from_appcontent': 42} + return "inheritance20.html", {"from_appcontent": 42} @unpack def inheritance20_unpack(request): - response = TemplateResponse( - request, - 'inheritance20.html', - {'from_appcontent': 43}) - response['Cache-Control'] = 'yabba dabba' + response = TemplateResponse(request, "inheritance20.html", {"from_appcontent": 43}) + response["Cache-Control"] = "yabba dabba" return response urlpatterns = [ - url(r'^$', module_root, name='ac_module_root'), - url(r'^args_test/([^/]+)/([^/]+)/$', args_test, name='ac_args_test'), - url(r'^kwargs_test/(?P[^/]+)/(?P[^/]+)/$', args_test), - url(r'^full_reverse_test/$', full_reverse_test), - url(r'^alias_reverse_test/$', alias_reverse_test), - url(r'^fragment/$', fragment), - url(r'^redirect/$', redirect), - url(r'^response/$', response), - url(r'^response_decorated/$', standalone(response)), - url(r'^inheritance20/$', inheritance20), - url(r'^inheritance20_unpack/$', inheritance20_unpack), + url(r"^$", module_root, name="ac_module_root"), + url(r"^args_test/([^/]+)/([^/]+)/$", args_test, name="ac_args_test"), + url(r"^kwargs_test/(?P[^/]+)/(?P[^/]+)/$", args_test), + url(r"^full_reverse_test/$", full_reverse_test), + url(r"^alias_reverse_test/$", alias_reverse_test), + url(r"^fragment/$", fragment), + url(r"^redirect/$", redirect), + url(r"^response/$", response), + url(r"^response_decorated/$", standalone(response)), + url(r"^inheritance20/$", inheritance20), + url(r"^inheritance20_unpack/$", inheritance20_unpack), ] diff --git a/tests/testapp/context_processors.py b/tests/testapp/context_processors.py index 7d0253c20..c477b6489 100644 --- a/tests/testapp/context_processors.py +++ b/tests/testapp/context_processors.py @@ -1,2 +1,2 @@ def test_context(request): - return {'THE_ANSWER': 42} + return {"THE_ANSWER": 42} diff --git a/tests/testapp/migrations/0001_initial.py b/tests/testapp/migrations/0001_initial.py index a851c06a8..dbd8263e1 100644 --- a/tests/testapp/migrations/0001_initial.py +++ b/tests/testapp/migrations/0001_initial.py @@ -9,77 +9,123 @@ class Migration(migrations.Migration): initial = True - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( - name='Category', + name="Category", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=20)), - ('slug', models.SlugField()), - ('lft', models.PositiveIntegerField(db_index=True, editable=False)), - ('rght', models.PositiveIntegerField(db_index=True, editable=False)), - ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)), - ('level', models.PositiveIntegerField(db_index=True, editable=False)), - ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='testapp.Category')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=20)), + ("slug", models.SlugField()), + ("lft", models.PositiveIntegerField(db_index=True, editable=False)), + ("rght", models.PositiveIntegerField(db_index=True, editable=False)), + ("tree_id", models.PositiveIntegerField(db_index=True, editable=False)), + ("level", models.PositiveIntegerField(db_index=True, editable=False)), + ( + "parent", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="children", + to="testapp.Category", + ), + ), ], options={ - 'verbose_name': 'category', - 'verbose_name_plural': 'categories', - 'ordering': ['tree_id', 'lft'], + "verbose_name": "category", + "verbose_name_plural": "categories", + "ordering": ["tree_id", "lft"], }, ), migrations.CreateModel( - name='CustomContentType', + name="CustomContentType", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('region', models.CharField(max_length=255)), - ('ordering', models.IntegerField(default=0, verbose_name='ordering')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("region", models.CharField(max_length=255)), + ("ordering", models.IntegerField(default=0, verbose_name="ordering")), ], options={ - 'verbose_name': 'custom content type', - 'verbose_name_plural': 'custom content types', - 'db_table': 'testapp_mymodel_customcontenttype', - 'ordering': ['ordering'], - 'permissions': [], - 'abstract': False, + "verbose_name": "custom content type", + "verbose_name_plural": "custom content types", + "db_table": "testapp_mymodel_customcontenttype", + "ordering": ["ordering"], + "permissions": [], + "abstract": False, }, ), migrations.CreateModel( - name='ExampleCMSBase', + name="ExampleCMSBase", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ) ], - options={ - 'abstract': False, - }, + options={"abstract": False}, bases=(models.Model, feincms.extensions.base.ExtensionsMixin), ), migrations.CreateModel( - name='ExampleCMSBase2', + name="ExampleCMSBase2", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ) ], - options={ - 'abstract': False, - }, + options={"abstract": False}, bases=(models.Model, feincms.extensions.base.ExtensionsMixin), ), migrations.CreateModel( - name='MyModel', + name="MyModel", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ) ], - options={ - 'abstract': False, - }, + options={"abstract": False}, bases=(models.Model, feincms.extensions.base.ExtensionsMixin), ), migrations.AddField( - model_name='customcontenttype', - name='parent', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='customcontenttype_set', to='testapp.MyModel'), + model_name="customcontenttype", + name="parent", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="customcontenttype_set", + to="testapp.MyModel", + ), ), ] diff --git a/tests/testapp/models.py b/tests/testapp/models.py index 05328489a..c51402d95 100644 --- a/tests/testapp/models.py +++ b/tests/testapp/models.py @@ -17,69 +17,69 @@ from .content import CustomContentType -Page.register_templates({ - 'key': 'base', - 'title': 'Base Template', - 'path': 'base.html', - 'regions': ( - ('main', 'Main region'), - ('sidebar', 'Sidebar', 'inherited'), - ), -}) +Page.register_templates( + { + "key": "base", + "title": "Base Template", + "path": "base.html", + "regions": (("main", "Main region"), ("sidebar", "Sidebar", "inherited")), + } +) Page.create_content_type(RawContent) Page.create_content_type( - MediaFileContent, - TYPE_CHOICES=( - ('default', 'Default position'), - ) + MediaFileContent, TYPE_CHOICES=(("default", "Default position"),) +) +Page.create_content_type( + TemplateContent, TEMPLATES=[("templatecontent_1.html", "template 1")] ) -Page.create_content_type(TemplateContent, TEMPLATES=[ - ('templatecontent_1.html', 'template 1'), -]) Page.register_request_processor(processors.etag_request_processor) Page.register_response_processor(processors.etag_response_processor) -Page.register_response_processor( - processors.debug_sql_queries_response_processor()) +Page.register_response_processor(processors.debug_sql_queries_response_processor()) def get_admin_fields(form, *args, **kwargs): return { - 'exclusive_subpages': forms.BooleanField( - label=capfirst(_('exclusive subpages')), + "exclusive_subpages": forms.BooleanField( + label=capfirst(_("exclusive subpages")), required=False, - initial=form.instance.parameters.get('exclusive_subpages', False), + initial=form.instance.parameters.get("exclusive_subpages", False), help_text=_( - 'Exclude everything other than the application\'s content' - ' when rendering subpages.'), + "Exclude everything other than the application's content" + " when rendering subpages." + ), ), - 'custom_field': forms.CharField(), + "custom_field": forms.CharField(), } Page.create_content_type( ApplicationContent, APPLICATIONS=( - ('whatever', 'Test Urls', { - 'admin_fields': get_admin_fields, - 'urls': 'testapp.applicationcontent_urls', - }), - ) + ( + "whatever", + "Test Urls", + { + "admin_fields": get_admin_fields, + "urls": "testapp.applicationcontent_urls", + }, + ), + ), ) Page.register_extensions( - 'feincms.module.page.extensions.navigation', - 'feincms.module.page.extensions.sites', - 'feincms.extensions.translations', - 'feincms.extensions.datepublisher', - 'feincms.extensions.translations', - 'feincms.extensions.ct_tracker', - 'feincms.extensions.seo', - 'feincms.extensions.changedate', - 'feincms.extensions.seo', # duplicate - 'feincms.module.page.extensions.navigation', - 'feincms.module.page.extensions.symlinks', - 'feincms.module.page.extensions.titles', - 'feincms.module.page.extensions.navigationgroups', + "feincms.module.page.extensions.navigation", + "feincms.module.page.extensions.sites", + "feincms.extensions.translations", + "feincms.extensions.datepublisher", + "feincms.extensions.translations", + "feincms.extensions.ct_tracker", + "feincms.extensions.seo", + "feincms.extensions.changedate", + "feincms.extensions.seo", # duplicate + "feincms.module.page.extensions.navigation", + "feincms.module.page.extensions.symlinks", + "feincms.module.page.extensions.titles", + "feincms.module.page.extensions.navigationgroups", ) @@ -88,13 +88,13 @@ class Category(MPTTModel): name = models.CharField(max_length=20) slug = models.SlugField() parent = models.ForeignKey( - 'self', blank=True, null=True, related_name='children', - on_delete=models.CASCADE) + "self", blank=True, null=True, related_name="children", on_delete=models.CASCADE + ) class Meta: - ordering = ['tree_id', 'lft'] - verbose_name = 'category' - verbose_name_plural = 'categories' + ordering = ["tree_id", "lft"] + verbose_name = "category" + verbose_name_plural = "categories" def __str__(self): return self.name @@ -105,24 +105,24 @@ class ExampleCMSBase(Base): ExampleCMSBase.register_regions( - ('region', 'region title'), - ('region2', 'region2 title')) + ("region", "region title"), ("region2", "region2 title") +) class ExampleCMSBase2(Base): - pass + pass ExampleCMSBase2.register_regions( - ('region', 'region title'), - ('region2', 'region2 title')) + ("region", "region title"), ("region2", "region2 title") +) class MyModel(create_base_model()): pass -MyModel.register_regions(('main', 'Main region')) +MyModel.register_regions(("main", "Main region")) unchanged = CustomContentType diff --git a/tests/testapp/navigation_extensions.py b/tests/testapp/navigation_extensions.py index bbd691839..6400375fb 100644 --- a/tests/testapp/navigation_extensions.py +++ b/tests/testapp/navigation_extensions.py @@ -1,12 +1,11 @@ from __future__ import absolute_import, unicode_literals -from feincms.module.page.extensions.navigation import ( - NavigationExtension, PagePretender) +from feincms.module.page.extensions.navigation import NavigationExtension, PagePretender class PassthroughExtension(NavigationExtension): # See PagesTestCase.test_23_navigation_extension - name = 'passthrough extension' + name = "passthrough extension" def children(self, page, **kwargs): for p in page.children.in_navigation(): @@ -14,7 +13,7 @@ def children(self, page, **kwargs): class PretenderExtension(NavigationExtension): - name = 'pretender extension' + name = "pretender extension" def children(self, page, **kwargs): - return [PagePretender(title='blabla', url='/asdsa/')] + return [PagePretender(title="blabla", url="/asdsa/")] diff --git a/tests/testapp/settings.py b/tests/testapp/settings.py index 560875262..4df48ea5b 100644 --- a/tests/testapp/settings.py +++ b/tests/testapp/settings.py @@ -5,73 +5,68 @@ SITE_ID = 1 -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': ':memory:', - } -} +DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}} INSTALLED_APPS = [ - 'django.contrib.auth', - 'django.contrib.admin', - 'django.contrib.contenttypes', - 'django.contrib.messages', - 'django.contrib.sessions', - 'django.contrib.sitemaps', - 'django.contrib.sites', - 'django.contrib.staticfiles', - 'feincms', - 'feincms.module.medialibrary', - 'feincms.module.page', - 'mptt', - 'testapp', + "django.contrib.auth", + "django.contrib.admin", + "django.contrib.contenttypes", + "django.contrib.messages", + "django.contrib.sessions", + "django.contrib.sitemaps", + "django.contrib.sites", + "django.contrib.staticfiles", + "feincms", + "feincms.module.medialibrary", + "feincms.module.page", + "mptt", + "testapp", ] -MEDIA_URL = '/media/' -STATIC_URL = '/static/' +MEDIA_URL = "/media/" +STATIC_URL = "/static/" BASEDIR = os.path.dirname(__file__) -MEDIA_ROOT = os.path.join(BASEDIR, 'media/') -STATIC_ROOT = os.path.join(BASEDIR, 'static/') -SECRET_KEY = 'supersikret' +MEDIA_ROOT = os.path.join(BASEDIR, "media/") +STATIC_ROOT = os.path.join(BASEDIR, "static/") +SECRET_KEY = "supersikret" USE_TZ = True -ROOT_URLCONF = 'testapp.urls' -LANGUAGES = (('en', 'English'), ('de', 'German')) +ROOT_URLCONF = "testapp.urls" +LANGUAGES = (("en", "English"), ("de", "German")) TEMPLATE_CONTEXT_PROCESSORS = ( - 'django.contrib.auth.context_processors.auth', - 'django.core.context_processors.debug', - 'django.core.context_processors.i18n', - 'django.core.context_processors.media', - 'django.core.context_processors.static', + "django.contrib.auth.context_processors.auth", + "django.core.context_processors.debug", + "django.core.context_processors.i18n", + "django.core.context_processors.media", + "django.core.context_processors.static", # request context processor is needed - 'django.core.context_processors.request', - 'testapp.context_processors.test_context', + "django.core.context_processors.request", + "testapp.context_processors.test_context", ) TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - 'testapp.context_processors.test_context', - ], + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + "testapp.context_processors.test_context", + ] }, - }, + } ] MIDDLEWARE = ( - 'django.middleware.common.CommonMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.locale.LocaleMiddleware' + "django.middleware.common.CommonMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.locale.LocaleMiddleware", ) if django.VERSION < (1, 11): @@ -79,10 +74,12 @@ if (2,) <= django.VERSION < (2, 1): from django.utils import deprecation + # Anything to make mptt.templatetags.mptt_admin importable deprecation.RemovedInDjango20Warning = deprecation.RemovedInDjango21Warning elif django.VERSION >= (2,): from django.utils import deprecation + # Anything to make mptt.templatetags.mptt_admin importable deprecation.RemovedInDjango20Warning = deprecation.RemovedInDjango30Warning diff --git a/tests/testapp/tests/test_cms.py b/tests/testapp/tests/test_cms.py index 557b65125..1e818ed72 100644 --- a/tests/testapp/tests/test_cms.py +++ b/tests/testapp/tests/test_cms.py @@ -21,13 +21,13 @@ from django.utils.unittest import skipIf skipUnlessLegacy = skipIf( - django.VERSION >= (1, 8), - "Legacy tests only necessary in Django < 1.8") + django.VERSION >= (1, 8), "Legacy tests only necessary in Django < 1.8" +) # ------------------------------------------------------------------------ class SubRawContent(RawContent): - title = models.CharField('title', max_length=100, blank=True) + title = models.CharField("title", max_length=100, blank=True) class Meta: abstract = True @@ -37,7 +37,7 @@ class CMSBaseTest(TestCase): def test_01_simple_content_type_creation(self): self.assertEqual(ExampleCMSBase.content_type_for(RawContent), None) - ExampleCMSBase.create_content_type(RawContent, regions=('main2',)) + ExampleCMSBase.create_content_type(RawContent, regions=("main2",)) ExampleCMSBase.create_content_type(RichTextContent) # content_type_for should return None if it does not have a subclass @@ -45,8 +45,9 @@ def test_01_simple_content_type_creation(self): self.assertEqual(ExampleCMSBase.content_type_for(Empty), None) self.assertTrue( - 'rawcontent' not in dict( - ExampleCMSBase.template.regions[0].content_types).keys()) + "rawcontent" + not in dict(ExampleCMSBase.template.regions[0].content_types).keys() + ) def test_04_mediafilecontent_creation(self): # the medialibrary needs to be enabled, otherwise this test fails @@ -54,7 +55,8 @@ def test_04_mediafilecontent_creation(self): # no TYPE_CHOICES, should raise self.assertRaises( ImproperlyConfigured, - lambda: ExampleCMSBase.create_content_type(MediaFileContent)) + lambda: ExampleCMSBase.create_content_type(MediaFileContent), + ) def test_05_non_abstract_content_type(self): # Should not be able to create a content type from a non-abstract base @@ -64,7 +66,8 @@ class TestContentType(models.Model): self.assertRaises( ImproperlyConfigured, - lambda: ExampleCMSBase.create_content_type(TestContentType)) + lambda: ExampleCMSBase.create_content_type(TestContentType), + ) def test_07_default_render_method(self): class SomethingElse(models.Model): @@ -72,29 +75,23 @@ class Meta: abstract = True def render_region(self): - return 'hello' + return "hello" type = ExampleCMSBase.create_content_type(SomethingElse) obj = type() self.assertRaises(NotImplementedError, lambda: obj.render()) - obj.region = 'region' - self.assertEqual(obj.render(), 'hello') + obj.region = "region" + self.assertEqual(obj.render(), "hello") def test_08_creating_two_content_types_in_same_application(self): ExampleCMSBase.create_content_type(RawContent) ct = ExampleCMSBase.content_type_for(RawContent) - self.assertEqual( - ct._meta.db_table, - 'testapp_examplecmsbase_rawcontent') + self.assertEqual(ct._meta.db_table, "testapp_examplecmsbase_rawcontent") - ExampleCMSBase2.create_content_type( - RawContent, - class_name='RawContent2') + ExampleCMSBase2.create_content_type(RawContent, class_name="RawContent2") ct2 = ExampleCMSBase2.content_type_for(RawContent) - self.assertEqual( - ct2._meta.db_table, - 'testapp_examplecmsbase2_rawcontent2') + self.assertEqual(ct2._meta.db_table, "testapp_examplecmsbase2_rawcontent2") @skipUnlessLegacy def test_09_related_objects_cache(self): @@ -111,33 +108,31 @@ def test_09_related_objects_cache(self): It also fails on Django 1.7 since the introduction of django.apps """ + class Attachment(models.Model): - base = models.ForeignKey( - ExampleCMSBase, - related_name='test_related_name') + base = models.ForeignKey(ExampleCMSBase, related_name="test_related_name") # See issue #323 on Github. ExampleCMSBase._meta._fill_related_objects_cache() related_models = map( - lambda x: x.model, ExampleCMSBase._meta.get_all_related_objects()) + lambda x: x.model, ExampleCMSBase._meta.get_all_related_objects() + ) self.assertTrue(Attachment in related_models) - self.assertTrue(hasattr(ExampleCMSBase, 'test_related_name')) + self.assertTrue(hasattr(ExampleCMSBase, "test_related_name")) # self.assertFalse(hasattr(Attachment, 'anycontents')) class AnyContent(models.Model): - attachment = models.ForeignKey( - Attachment, - related_name='anycontents') + attachment = models.ForeignKey(Attachment, related_name="anycontents") class Meta: abstract = True ExampleCMSBase.create_content_type(AnyContent) - self.assertTrue(hasattr(ExampleCMSBase, 'test_related_name')) - self.assertTrue(hasattr(Attachment, 'anycontents')) + self.assertTrue(hasattr(ExampleCMSBase, "test_related_name")) + self.assertTrue(hasattr(Attachment, "anycontents")) def test_10_content_type_subclasses(self): """ diff --git a/tests/testapp/tests/test_extensions.py b/tests/testapp/tests/test_extensions.py index 10c7dc987..4994ceabb 100644 --- a/tests/testapp/tests/test_extensions.py +++ b/tests/testapp/tests/test_extensions.py @@ -15,20 +15,22 @@ class TranslationTestCase(TestCase): def setUp(self): - Page.register_templates({ - 'key': 'base', - 'title': 'Standard template', - 'path': 'feincms_base.html', - 'regions': ( - ('main', 'Main content area'), - ('sidebar', 'Sidebar', 'inherited'), - ), - }) + Page.register_templates( + { + "key": "base", + "title": "Standard template", + "path": "feincms_base.html", + "regions": ( + ("main", "Main content area"), + ("sidebar", "Sidebar", "inherited"), + ), + } + ) self.site_1 = Site.objects.all()[0] # create a bunch of pages - en = self.create_default_page_set(language='en') - de = self.create_default_page_set(language='de', title='Testseite') + en = self.create_default_page_set(language="en") + de = self.create_default_page_set(language="de", title="Testseite") de.translation_of = en de.save() de.parent.translation_of = en.parent @@ -36,36 +38,34 @@ def setUp(self): self.page_de = de.parent self.page_en = en.parent - if hasattr(translation, 'LANGUAGE_SESSION_KEY'): + if hasattr(translation, "LANGUAGE_SESSION_KEY"): self.language_session_key = translation.LANGUAGE_SESSION_KEY else: # Django 1.6 self.language_session_key = django_settings.LANGUAGE_COOKIE_NAME - def create_page(self, title='Test page', parent=None, **kwargs): + def create_page(self, title="Test page", parent=None, **kwargs): defaults = { - 'template_key': 'base', - 'site': self.site_1, - 'in_navigation': False, - 'active': False, + "template_key": "base", + "site": self.site_1, + "in_navigation": False, + "active": False, } defaults.update(kwargs) return Page.objects.create( title=title, - slug=kwargs.get('slug', slugify(title)), + slug=kwargs.get("slug", slugify(title)), parent=parent, - **defaults) + **defaults + ) def create_default_page_set(self, **kwargs): - return self.create_page( - 'Test child page', - parent=self.create_page(**kwargs), - ) + return self.create_page("Test child page", parent=self.create_page(**kwargs)) def testPage(self): page = Page() - self.assertTrue(hasattr(page, 'language')) - self.assertTrue(hasattr(page, 'translation_of')) + self.assertTrue(hasattr(page, "language")) + self.assertTrue(hasattr(page, "translation_of")) self.assertEqual(self.page_de.translation_of, self.page_en) self.assertEqual(self.page_de.original_translation, self.page_en) @@ -75,32 +75,32 @@ def testPage(self): def test_user_has_language_set_with_session(self): factory = RequestFactory() request = factory.get(self.page_en.get_navigation_url()) - setattr(request, 'session', dict()) - request.session[self.language_session_key] = 'en' + setattr(request, "session", dict()) + request.session[self.language_session_key] = "en" self.assertEqual(user_has_language_set(request), True) def test_user_has_language_set_with_cookie(self): factory = RequestFactory() request = factory.get(self.page_en.get_navigation_url()) - request.COOKIES[django_settings.LANGUAGE_COOKIE_NAME] = 'en' + request.COOKIES[django_settings.LANGUAGE_COOKIE_NAME] = "en" self.assertEqual(user_has_language_set(request), True) def test_translation_set_language_to_session(self): factory = RequestFactory() request = factory.get(self.page_en.get_navigation_url()) - setattr(request, 'session', dict()) - translation_set_language(request, 'en') + setattr(request, "session", dict()) + translation_set_language(request, "en") - self.assertEqual(request.LANGUAGE_CODE, 'en') - self.assertEqual(request.session[self.language_session_key], 'en') + self.assertEqual(request.LANGUAGE_CODE, "en") + self.assertEqual(request.session[self.language_session_key], "en") def test_translation_set_language_to_cookie(self): factory = RequestFactory() request = factory.get(self.page_en.get_navigation_url()) - response = translation_set_language(request, 'en') + response = translation_set_language(request, "en") - self.assertEqual(request.LANGUAGE_CODE, 'en') + self.assertEqual(request.LANGUAGE_CODE, "en") c_key = django_settings.LANGUAGE_COOKIE_NAME - self.assertEqual(response.cookies[c_key].value, 'en') + self.assertEqual(response.cookies[c_key].value, "en") diff --git a/tests/testapp/tests/test_page.py b/tests/testapp/tests/test_page.py index a9c48e0e8..789222679 100644 --- a/tests/testapp/tests/test_page.py +++ b/tests/testapp/tests/test_page.py @@ -20,6 +20,7 @@ from django.test import TestCase from django.utils import timezone from django.utils.encoding import force_text + try: from django.urls import reverse except ImportError: @@ -45,202 +46,189 @@ # ------------------------------------------------------------------------ class PagesTestCase(TestCase): def setUp(self): - u = User( - username='test', - is_active=True, - is_staff=True, - is_superuser=True) - u.set_password('test') + u = User(username="test", is_active=True, is_staff=True, is_superuser=True) + u.set_password("test") u.save() self.site_1 = Site.objects.all()[0] - Page.register_templates({ - 'key': 'base', - 'title': 'Standard template', - 'path': 'feincms_base.html', - 'regions': ( - ('main', 'Main content area'), - ('sidebar', 'Sidebar', 'inherited'), - ), - }, { - 'key': 'theother', - 'title': 'This actually exists', - 'path': 'base.html', - 'regions': ( - ('main', 'Main content area'), - ('sidebar', 'Sidebar', 'inherited'), - ), - }) + Page.register_templates( + { + "key": "base", + "title": "Standard template", + "path": "feincms_base.html", + "regions": ( + ("main", "Main content area"), + ("sidebar", "Sidebar", "inherited"), + ), + }, + { + "key": "theother", + "title": "This actually exists", + "path": "base.html", + "regions": ( + ("main", "Main content area"), + ("sidebar", "Sidebar", "inherited"), + ), + }, + ) def login(self): - self.assertTrue(self.client.login(username='test', password='test')) + self.assertTrue(self.client.login(username="test", password="test")) - def create_page_through_admin(self, title='Test page', parent='', - **kwargs): + def create_page_through_admin(self, title="Test page", parent="", **kwargs): dic = { - 'title': title, - 'slug': kwargs.get('slug', slugify(title)), - 'parent': parent, - 'template_key': 'base', - 'publication_date_0': '2009-01-01', - 'publication_date_1': '00:00:00', - 'initial-publication_date_0': '2009-01-01', - 'initial-publication_date_1': '00:00:00', - 'language': 'en', - 'navigation_group': 'default', - 'site': self.site_1.id, - - 'rawcontent_set-TOTAL_FORMS': 0, - 'rawcontent_set-INITIAL_FORMS': 0, - 'rawcontent_set-MAX_NUM_FORMS': 10, - - 'mediafilecontent_set-TOTAL_FORMS': 0, - 'mediafilecontent_set-INITIAL_FORMS': 0, - 'mediafilecontent_set-MAX_NUM_FORMS': 10, - - 'imagecontent_set-TOTAL_FORMS': 0, - 'imagecontent_set-INITIAL_FORMS': 0, - 'imagecontent_set-MAX_NUM_FORMS': 10, - - 'contactformcontent_set-TOTAL_FORMS': 0, - 'contactformcontent_set-INITIAL_FORMS': 0, - 'contactformcontent_set-MAX_NUM_FORMS': 10, - - 'filecontent_set-TOTAL_FORMS': 0, - 'filecontent_set-INITIAL_FORMS': 0, - 'filecontent_set-MAX_NUM_FORMS': 10, - - 'templatecontent_set-TOTAL_FORMS': 0, - 'templatecontent_set-INITIAL_FORMS': 0, - 'templatecontent_set-MAX_NUM_FORMS': 10, - - 'applicationcontent_set-TOTAL_FORMS': 0, - 'applicationcontent_set-INITIAL_FORMS': 0, - 'applicationcontent_set-MAX_NUM_FORMS': 10, + "title": title, + "slug": kwargs.get("slug", slugify(title)), + "parent": parent, + "template_key": "base", + "publication_date_0": "2009-01-01", + "publication_date_1": "00:00:00", + "initial-publication_date_0": "2009-01-01", + "initial-publication_date_1": "00:00:00", + "language": "en", + "navigation_group": "default", + "site": self.site_1.id, + "rawcontent_set-TOTAL_FORMS": 0, + "rawcontent_set-INITIAL_FORMS": 0, + "rawcontent_set-MAX_NUM_FORMS": 10, + "mediafilecontent_set-TOTAL_FORMS": 0, + "mediafilecontent_set-INITIAL_FORMS": 0, + "mediafilecontent_set-MAX_NUM_FORMS": 10, + "imagecontent_set-TOTAL_FORMS": 0, + "imagecontent_set-INITIAL_FORMS": 0, + "imagecontent_set-MAX_NUM_FORMS": 10, + "contactformcontent_set-TOTAL_FORMS": 0, + "contactformcontent_set-INITIAL_FORMS": 0, + "contactformcontent_set-MAX_NUM_FORMS": 10, + "filecontent_set-TOTAL_FORMS": 0, + "filecontent_set-INITIAL_FORMS": 0, + "filecontent_set-MAX_NUM_FORMS": 10, + "templatecontent_set-TOTAL_FORMS": 0, + "templatecontent_set-INITIAL_FORMS": 0, + "templatecontent_set-MAX_NUM_FORMS": 10, + "applicationcontent_set-TOTAL_FORMS": 0, + "applicationcontent_set-INITIAL_FORMS": 0, + "applicationcontent_set-MAX_NUM_FORMS": 10, } dic.update(kwargs) - return self.client.post('/admin/page/page/add/', dic) + return self.client.post("/admin/page/page/add/", dic) def create_default_page_set_through_admin(self): self.login() self.create_page_through_admin() - return self.create_page_through_admin('Test child page', 1) + return self.create_page_through_admin("Test child page", 1) - def create_page(self, title='Test page', parent=None, **kwargs): + def create_page(self, title="Test page", parent=None, **kwargs): defaults = { - 'template_key': 'base', - 'site': self.site_1, - 'in_navigation': False, - 'active': False, - 'navigation_group': 'default', + "template_key": "base", + "site": self.site_1, + "in_navigation": False, + "active": False, + "navigation_group": "default", } defaults.update(kwargs) return Page.objects.create( title=title, - slug=kwargs.get('slug', slugify(title)), + slug=kwargs.get("slug", slugify(title)), parent=parent, - **defaults) + **defaults + ) def create_default_page_set(self): - self.create_page( - 'Test child page', - parent=self.create_page(), - ) + self.create_page("Test child page", parent=self.create_page()) def is_published(self, url, should_be=True): try: self.client.get(url) except TemplateDoesNotExist as e: if should_be: - if e.args != ('feincms_base.html',): + if e.args != ("feincms_base.html",): raise else: - if e.args != ('404.html',): + if e.args != ("404.html",): raise def test_01_tree_editor(self): self.login() - self.assertEqual( - self.client.get('/admin/page/page/').status_code, 200) + self.assertEqual(self.client.get("/admin/page/page/").status_code, 200) self.assertRedirects( - self.client.get('/admin/page/page/?anything=anything'), - '/admin/page/page/?e=1') + self.client.get("/admin/page/page/?anything=anything"), + "/admin/page/page/?e=1", + ) def test_02_add_page(self): self.login() self.assertRedirects( - self.create_page_through_admin( - title='Test page ' * 10, - slug='test-page'), - '/admin/page/page/') + self.create_page_through_admin(title="Test page " * 10, slug="test-page"), + "/admin/page/page/", + ) self.assertEqual(Page.objects.count(), 1) - self.assertContains(self.client.get('/admin/page/page/'), '…') + self.assertContains(self.client.get("/admin/page/page/"), "…") def test_03_item_editor(self): self.login() self.assertRedirects( self.create_page_through_admin(_continue=1), - reverse('admin:page_page_change', args=(1,))) + reverse("admin:page_page_change", args=(1,)), + ) self.assertEqual( - self.client.get( - reverse('admin:page_page_change', args=(1,)) - ).status_code, 200) + self.client.get(reverse("admin:page_page_change", args=(1,))).status_code, + 200, + ) self.is_published( - reverse('admin:page_page_change', args=(42,)), - should_be=False) + reverse("admin:page_page_change", args=(42,)), should_be=False + ) def test_03_add_another(self): self.login() self.assertRedirects( - self.create_page_through_admin(_addanother=1), - '/admin/page/page/add/') + self.create_page_through_admin(_addanother=1), "/admin/page/page/add/" + ) def test_04_add_child(self): response = self.create_default_page_set_through_admin() - self.assertRedirects(response, '/admin/page/page/') + self.assertRedirects(response, "/admin/page/page/") self.assertEqual(Page.objects.count(), 2) page = Page.objects.get(pk=2) - self.assertEqual( - page.get_absolute_url(), '/test-page/test-child-page/') + self.assertEqual(page.get_absolute_url(), "/test-page/test-child-page/") page.active = True page.in_navigation = True page.save() # page2 inherited the inactive flag from the toplevel page - self.assertContains(self.client.get('/admin/page/page/'), 'inherited') + self.assertContains(self.client.get("/admin/page/page/"), "inherited") page1 = Page.objects.get(pk=1) page1.active = True page1.save() - content = self.client.get('/admin/page/page/').content.decode('utf-8') + content = self.client.get("/admin/page/page/").content.decode("utf-8") self.assertEqual(len(content.split('checked="checked"')), 4) def test_05_override_url(self): self.create_default_page_set() page = Page.objects.get(pk=1) - page.override_url = '/something/' + page.override_url = "/something/" page.save() page2 = Page.objects.get(pk=2) - self.assertEqual( - page2.get_absolute_url(), '/something/test-child-page/') + self.assertEqual(page2.get_absolute_url(), "/something/test-child-page/") - page.override_url = '/' + page.override_url = "/" page.save() page2 = Page.objects.get(pk=2) - self.assertEqual(page2.get_absolute_url(), '/test-child-page/') + self.assertEqual(page2.get_absolute_url(), "/test-child-page/") - self.is_published('/', False) + self.is_published("/", False) page.active = True - page.template_key = 'theother' + page.template_key = "theother" page.save() - self.is_published('/', True) + self.is_published("/", True) def test_06_tree_editor_save(self): self.create_default_page_set() @@ -248,29 +236,32 @@ def test_06_tree_editor_save(self): page1 = Page.objects.get(pk=1) page2 = Page.objects.get(pk=2) - page3 = Page.objects.create(title='page3', slug='page3', parent=page2) - page4 = Page.objects.create(title='page4', slug='page4', parent=page1) - page5 = Page.objects.create(title='page5', slug='page5', parent=None) + page3 = Page.objects.create(title="page3", slug="page3", parent=page2) + page4 = Page.objects.create(title="page4", slug="page4", parent=page1) + page5 = Page.objects.create(title="page5", slug="page5", parent=None) - self.assertEqual( - page3.get_absolute_url(), '/test-page/test-child-page/page3/') - self.assertEqual(page4.get_absolute_url(), '/test-page/page4/') - self.assertEqual(page5.get_absolute_url(), '/page5/') + self.assertEqual(page3.get_absolute_url(), "/test-page/test-child-page/page3/") + self.assertEqual(page4.get_absolute_url(), "/test-page/page4/") + self.assertEqual(page5.get_absolute_url(), "/page5/") self.login() - self.client.post('/admin/page/page/', { - '__cmd': 'move_node', - 'position': 'last-child', - 'cut_item': '1', - 'pasted_on': '5', - }, HTTP_X_REQUESTED_WITH='XMLHttpRequest') - - self.assertEqual(Page.objects.get(pk=1).get_absolute_url(), - '/page5/test-page/') - self.assertEqual(Page.objects.get(pk=5).get_absolute_url(), - '/page5/') - self.assertEqual(Page.objects.get(pk=3).get_absolute_url(), - '/page5/test-page/test-child-page/page3/') + self.client.post( + "/admin/page/page/", + { + "__cmd": "move_node", + "position": "last-child", + "cut_item": "1", + "pasted_on": "5", + }, + HTTP_X_REQUESTED_WITH="XMLHttpRequest", + ) + + self.assertEqual(Page.objects.get(pk=1).get_absolute_url(), "/page5/test-page/") + self.assertEqual(Page.objects.get(pk=5).get_absolute_url(), "/page5/") + self.assertEqual( + Page.objects.get(pk=3).get_absolute_url(), + "/page5/test-page/test-child-page/page3/", + ) def test_07_tree_editor_toggle_boolean(self): self.create_default_page_set() @@ -279,38 +270,46 @@ def test_07_tree_editor_toggle_boolean(self): self.login() self.assertContains( - self.client.post('/admin/page/page/', { - '__cmd': 'toggle_boolean', - 'item_id': 1, - 'attr': 'in_navigation', - }, HTTP_X_REQUESTED_WITH='XMLHttpRequest'), - r'checked=\"checked\"') + self.client.post( + "/admin/page/page/", + {"__cmd": "toggle_boolean", "item_id": 1, "attr": "in_navigation"}, + HTTP_X_REQUESTED_WITH="XMLHttpRequest", + ), + r"checked=\"checked\"", + ) self.assertEqual(Page.objects.get(pk=1).in_navigation, True) self.assertNotContains( - self.client.post('/admin/page/page/', { - '__cmd': 'toggle_boolean', - 'item_id': 1, - 'attr': 'in_navigation', - }, HTTP_X_REQUESTED_WITH='XMLHttpRequest'), - 'checked="checked"') + self.client.post( + "/admin/page/page/", + {"__cmd": "toggle_boolean", "item_id": 1, "attr": "in_navigation"}, + HTTP_X_REQUESTED_WITH="XMLHttpRequest", + ), + 'checked="checked"', + ) self.assertEqual(Page.objects.get(pk=1).in_navigation, False) - self.assertTrue(isinstance( - self.client.post('/admin/page/page/', { - '__cmd': 'toggle_boolean', - 'item_id': 1, - 'attr': 'notexists', - }, HTTP_X_REQUESTED_WITH='XMLHttpRequest'), - HttpResponseBadRequest)) + self.assertTrue( + isinstance( + self.client.post( + "/admin/page/page/", + {"__cmd": "toggle_boolean", "item_id": 1, "attr": "notexists"}, + HTTP_X_REQUESTED_WITH="XMLHttpRequest", + ), + HttpResponseBadRequest, + ) + ) def test_07_tree_editor_invalid_ajax(self): self.login() self.assertContains( - self.client.post('/admin/page/page/', { - '__cmd': 'notexists', - }, HTTP_X_REQUESTED_WITH='XMLHttpRequest'), - 'Oops. AJAX request not understood.', - status_code=400) + self.client.post( + "/admin/page/page/", + {"__cmd": "notexists"}, + HTTP_X_REQUESTED_WITH="XMLHttpRequest", + ), + "Oops. AJAX request not understood.", + status_code=400, + ) def test_08_publishing(self): self.create_default_page_set() @@ -350,47 +349,41 @@ def test_08_publishing(self): def create_page_through_admincontent(self, page, **kwargs): data = { - 'title': page.title, - 'slug': page.slug, + "title": page.title, + "slug": page.slug, # 'parent': page.parent_id, # this field is excluded from the form - 'template_key': page.template_key, - 'publication_date_0': '2009-01-01', - 'publication_date_1': '00:00:00', - 'initial-publication_date_0': '2009-01-01', - 'initial-publication_date_1': '00:00:00', - 'language': 'en', - 'navigation_group': 'default', - 'site': self.site_1.id, - - 'rawcontent_set-TOTAL_FORMS': 1, - 'rawcontent_set-INITIAL_FORMS': 0, - 'rawcontent_set-MAX_NUM_FORMS': 10, - - 'rawcontent_set-0-parent': 1, - 'rawcontent_set-0-region': 'main', - 'rawcontent_set-0-ordering': 0, - 'rawcontent_set-0-text': 'This is some example content', - - 'mediafilecontent_set-TOTAL_FORMS': 1, - 'mediafilecontent_set-INITIAL_FORMS': 0, - 'mediafilecontent_set-MAX_NUM_FORMS': 10, - - 'mediafilecontent_set-0-parent': 1, - 'mediafilecontent_set-0-type': 'default', - - 'templatecontent_set-TOTAL_FORMS': 1, - 'templatecontent_set-INITIAL_FORMS': 0, - 'templatecontent_set-MAX_NUM_FORMS': 10, - - 'applicationcontent_set-TOTAL_FORMS': 1, - 'applicationcontent_set-INITIAL_FORMS': 0, - 'applicationcontent_set-MAX_NUM_FORMS': 10, + "template_key": page.template_key, + "publication_date_0": "2009-01-01", + "publication_date_1": "00:00:00", + "initial-publication_date_0": "2009-01-01", + "initial-publication_date_1": "00:00:00", + "language": "en", + "navigation_group": "default", + "site": self.site_1.id, + "rawcontent_set-TOTAL_FORMS": 1, + "rawcontent_set-INITIAL_FORMS": 0, + "rawcontent_set-MAX_NUM_FORMS": 10, + "rawcontent_set-0-parent": 1, + "rawcontent_set-0-region": "main", + "rawcontent_set-0-ordering": 0, + "rawcontent_set-0-text": "This is some example content", + "mediafilecontent_set-TOTAL_FORMS": 1, + "mediafilecontent_set-INITIAL_FORMS": 0, + "mediafilecontent_set-MAX_NUM_FORMS": 10, + "mediafilecontent_set-0-parent": 1, + "mediafilecontent_set-0-type": "default", + "templatecontent_set-TOTAL_FORMS": 1, + "templatecontent_set-INITIAL_FORMS": 0, + "templatecontent_set-MAX_NUM_FORMS": 10, + "applicationcontent_set-TOTAL_FORMS": 1, + "applicationcontent_set-INITIAL_FORMS": 0, + "applicationcontent_set-MAX_NUM_FORMS": 10, } data.update(kwargs) return self.client.post( - reverse('admin:page_page_change', args=(page.pk,)), - data) + reverse("admin:page_page_change", args=(page.pk,)), data + ) def test_09_pagecontent(self): self.create_default_page_set() @@ -398,8 +391,8 @@ def test_09_pagecontent(self): page = Page.objects.get(pk=1) self.login() response = self.create_page_through_admincontent(page) - self.assertRedirects(response, '/admin/page/page/') - self.assertEqual(page.content.main[0].__class__.__name__, 'RawContent') + self.assertRedirects(response, "/admin/page/page/") + self.assertEqual(page.content.main[0].__class__.__name__, "RawContent") page2 = Page.objects.get(pk=2) page2.symlinked_page = page @@ -408,12 +401,12 @@ def test_09_pagecontent(self): # other content methods self.assertEqual(len(page2.content.all_of_type(RawContent)), 1) - self.assertEqual( - page2.content.main[0].__class__.__name__, 'RawContent') + self.assertEqual(page2.content.main[0].__class__.__name__, "RawContent") self.assertEqual( force_text(page2.content.main[0]), - 'RawContent, region=main,' - ' ordering=0>') + "RawContent, region=main," + " ordering=0>", + ) self.assertEqual(len(page2.content.main), 1) self.assertEqual(len(page2.content.sidebar), 0) @@ -430,73 +423,72 @@ def test_10_mediafile_and_imagecontent(self): page = Page.objects.get(pk=1) self.create_page_through_admincontent(page) - category = Category.objects.create(title='Category', parent=None) - category2 = Category.objects.create(title='Something', parent=category) + category = Category.objects.create(title="Category", parent=None) + category2 = Category.objects.create(title="Something", parent=category) - self.assertEqual(force_text(category2), 'Category - Something') - self.assertEqual(force_text(category), 'Category') + self.assertEqual(force_text(category2), "Category - Something") + self.assertEqual(force_text(category), "Category") - mediafile = MediaFile.objects.create(file='somefile.jpg') + mediafile = MediaFile.objects.create(file="somefile.jpg") if django.VERSION < (2, 0): mediafile.categories = [category] else: mediafile.categories.set([category]) page.mediafilecontent_set.create( - mediafile=mediafile, - region='main', - type='default', - ordering=1) + mediafile=mediafile, region="main", type="default", ordering=1 + ) - self.assertEqual(force_text(mediafile), 'somefile.jpg') + self.assertEqual(force_text(mediafile), "somefile.jpg") mediafile.translations.create( - caption='something', language_code='%s-ha' % short_language_code()) + caption="something", language_code="%s-ha" % short_language_code() + ) mediafile.purge_translation_cache() - self.assertTrue('something' in force_text(mediafile)) + self.assertTrue("something" in force_text(mediafile)) mf = page.content.main[1].mediafile - self.assertEqual(mf.translation.caption, 'something') - self.assertEqual( - mf.translation.short_language_code(), short_language_code()) - self.assertNotEqual(mf.get_absolute_url(), '') - self.assertEqual(force_text(mf), 'something') - self.assertTrue(mf.type == 'image') + self.assertEqual(mf.translation.caption, "something") + self.assertEqual(mf.translation.short_language_code(), short_language_code()) + self.assertNotEqual(mf.get_absolute_url(), "") + self.assertEqual(force_text(mf), "something") + self.assertTrue(mf.type == "image") - self.assertEqual(MediaFile.objects.only_language('de').count(), 0) - self.assertEqual(MediaFile.objects.only_language('en').count(), 0) + self.assertEqual(MediaFile.objects.only_language("de").count(), 0) + self.assertEqual(MediaFile.objects.only_language("en").count(), 0) self.assertEqual( MediaFile.objects.only_language( - lambda: '%s-ha' % short_language_code() + lambda: "%s-ha" % short_language_code() ).count(), 1, ) - self.assertTrue( - '%s-ha' % short_language_code() in mf.available_translations) + self.assertTrue("%s-ha" % short_language_code() in mf.available_translations) # this should not raise - self.client.get(reverse('admin:page_page_change', args=(1,))) + self.client.get(reverse("admin:page_page_change", args=(1,))) page.mediafilecontent_set.update(mediafile=3) # this should not raise - self.client.get('/admin/page/page/1/') + self.client.get("/admin/page/page/1/") - field = MediaFile._meta.get_field('file') + field = MediaFile._meta.get_field("file") old = (field.upload_to, field.storage, field.generate_filename) from django.core.files.storage import FileSystemStorage + MediaFile.reconfigure( - upload_to=lambda: 'anywhere', - storage=FileSystemStorage(location='/wha/', base_url='/whe/')) + upload_to=lambda: "anywhere", + storage=FileSystemStorage(location="/wha/", base_url="/whe/"), + ) mediafile = MediaFile.objects.get(pk=1) - self.assertEqual(mediafile.file.url, '/whe/somefile.jpg') + self.assertEqual(mediafile.file.url, "/whe/somefile.jpg") # restore settings (field.upload_to, field.storage, field.generate_filename) = old mediafile = MediaFile.objects.get(pk=1) - self.assertEqual(mediafile.file.url, '/media/somefile.jpg') + self.assertEqual(mediafile.file.url, "/media/somefile.jpg") def test_11_translations(self): self.create_default_page_set() @@ -511,7 +503,7 @@ def test_11_translations(self): page1.save() page2.active = True - page2.language = 'de' + page2.language = "de" page2.save() self.assertEqual(len(page2.available_translations()), 0) @@ -533,62 +525,50 @@ def test_12_titles(self): self.assertEqual(page.page_title, page.title) self.assertEqual(page.content_title, page.title) - page._content_title = 'Something\nawful' - page._page_title = 'Hello world' + page._content_title = "Something\nawful" + page._page_title = "Hello world" page.save() - self.assertEqual(page.page_title, 'Hello world') - self.assertEqual(page.content_title, 'Something') - self.assertEqual(page.content_subtitle, 'awful') + self.assertEqual(page.page_title, "Hello world") + self.assertEqual(page.content_title, "Something") + self.assertEqual(page.content_subtitle, "awful") - page._content_title = 'Only one line' - self.assertEqual(page.content_title, 'Only one line') - self.assertEqual(page.content_subtitle, '') + page._content_title = "Only one line" + self.assertEqual(page.content_title, "Only one line") + self.assertEqual(page.content_subtitle, "") - page._content_title = '' + page._content_title = "" self.assertEqual(page.content_title, page.title) - self.assertEqual(page.content_subtitle, '') + self.assertEqual(page.content_subtitle, "") def test_13_inheritance_and_ct_tracker(self): self.create_default_page_set() page = Page.objects.get(pk=1) - page.rawcontent_set.create( - region='sidebar', - ordering=0, - text='Something') - page.rawcontent_set.create( - region='main', - ordering=0, - text='Anything') + page.rawcontent_set.create(region="sidebar", ordering=0, text="Something") + page.rawcontent_set.create(region="main", ordering=0, text="Anything") page2 = Page.objects.get(pk=2) - page2.rawcontent_set.create( - region='main', - ordering=0, - text='Something else') - page2.rawcontent_set.create( - region='main', - ordering=1, - text='Whatever') + page2.rawcontent_set.create(region="main", ordering=0, text="Something else") + page2.rawcontent_set.create(region="main", ordering=1, text="Whatever") # Set default, non-caching content proxy page2.content_proxy_class = ContentProxy - if hasattr(self, 'assertNumQueries'): + if hasattr(self, "assertNumQueries"): # 4 queries: Two to get the content types of page and page2, one to # fetch all ancestor PKs of page2 and one to materialize the # RawContent instances belonging to page's sidebar and page2's # main. self.assertNumQueries( - 4, lambda: [page2.content.main, page2.content.sidebar]) - self.assertNumQueries( - 0, lambda: page2.content.sidebar[0].render()) + 4, lambda: [page2.content.main, page2.content.sidebar] + ) + self.assertNumQueries(0, lambda: page2.content.sidebar[0].render()) self.assertEqual( - ''.join(c.render() for c in page2.content.main), - 'Something elseWhatever') - self.assertEqual(page2.content.sidebar[0].render(), 'Something') + "".join(c.render() for c in page2.content.main), "Something elseWhatever" + ) + self.assertEqual(page2.content.sidebar[0].render(), "Something") page2 = Page.objects.get(pk=2) self.assertEqual(page2._ct_inventory, {}) @@ -597,7 +577,7 @@ def test_13_inheritance_and_ct_tracker(self): for ct in Page._feincms_content_types: ContentType.objects.get_for_model(ct) - if hasattr(self, 'assertNumQueries'): + if hasattr(self, "assertNumQueries"): # 5 queries: Two to get the content types of page and page2, one to # fetch all ancestor PKs of page2 and one to materialize the # RawContent instances belonging to page's sidebar and page2's main @@ -606,18 +586,19 @@ def test_13_inheritance_and_ct_tracker(self): # - one update to clobber the _ct_inventory attribute of all # descendants of page2 self.assertNumQueries( - 5, lambda: [page2.content.main, page2.content.sidebar]) - self.assertNumQueries( - 0, lambda: page2.content.sidebar[0].render()) + 5, lambda: [page2.content.main, page2.content.sidebar] + ) + self.assertNumQueries(0, lambda: page2.content.sidebar[0].render()) - self.assertEqual(page2.content.sidebar[0].render(), 'Something') + self.assertEqual(page2.content.sidebar[0].render(), "Something") # Reload, again, to test ct_tracker extension page2 = Page.objects.get(pk=2) - if hasattr(self, 'assertNumQueries'): + if hasattr(self, "assertNumQueries"): self.assertNumQueries( - 1, lambda: [page2.content.main, page2.content.sidebar]) + 1, lambda: [page2.content.main, page2.content.sidebar] + ) self.assertNotEqual(page2._ct_inventory, {}) @@ -625,13 +606,13 @@ def test_14_richtext(self): # only create the content type to test the item editor # customization hooks tmp = Page._feincms_content_types[:] - type = Page.create_content_type( - RichTextContent, regions=('notexists',)) + type = Page.create_content_type(RichTextContent, regions=("notexists",)) Page._feincms_content_types = tmp from django.utils.safestring import SafeData + obj = type() - obj.text = 'Something' + obj.text = "Something" self.assertTrue(isinstance(obj.render(), SafeData)) def test_17_page_template_tags(self): @@ -640,7 +621,7 @@ def test_17_page_template_tags(self): page1 = Page.objects.get(pk=1) page2 = Page.objects.get(pk=2) - page2.language = 'de' + page2.language = "de" page2.translation_of = page1 page2.active = True page2.in_navigation = True @@ -648,9 +629,9 @@ def test_17_page_template_tags(self): page3 = Page.objects.create( parent=page2, - title='page3', - slug='page3', - language='en', + title="page3", + slug="page3", + language="en", active=True, in_navigation=True, publication_date=datetime(2001, 1, 1), @@ -660,51 +641,55 @@ def test_17_page_template_tags(self): page1 = Page.objects.get(pk=1) page2 = Page.objects.get(pk=2) - context = template.Context({'feincms_page': page2, 'page3': page3}) + context = template.Context({"feincms_page": page2, "page3": page3}) t = template.Template( - '{% load feincms_page_tags %}{% feincms_parentlink of feincms_page' - ' level=1 %}') - self.assertEqual(t.render(context), '/test-page/') + "{% load feincms_page_tags %}{% feincms_parentlink of feincms_page" + " level=1 %}" + ) + self.assertEqual(t.render(context), "/test-page/") t = template.Template( - '{% load feincms_page_tags %}{% feincms_languagelinks for' - ' feincms_page as links %}{% for key, name, link in links %}' - '{{ key }}:{{ link }}{% if not forloop.last %},{% endif %}' - '{% endfor %}') + "{% load feincms_page_tags %}{% feincms_languagelinks for" + " feincms_page as links %}{% for key, name, link in links %}" + "{{ key }}:{{ link }}{% if not forloop.last %},{% endif %}" + "{% endfor %}" + ) self.assertEqual( - t.render(context), - 'en:/test-page/,de:/test-page/test-child-page/') + t.render(context), "en:/test-page/,de:/test-page/test-child-page/" + ) t = template.Template( - '{% load feincms_page_tags %}{% feincms_languagelinks for page3' - ' as links %}{% for key, name, link in links %}{{ key }}:' - '{{ link }}{% if not forloop.last %},{% endif %}{% endfor %}') + "{% load feincms_page_tags %}{% feincms_languagelinks for page3" + " as links %}{% for key, name, link in links %}{{ key }}:" + "{{ link }}{% if not forloop.last %},{% endif %}{% endfor %}" + ) self.assertEqual( - t.render(context), - 'en:/test-page/test-child-page/page3/,de:None') + t.render(context), "en:/test-page/test-child-page/page3/,de:None" + ) t = template.Template( - '{% load feincms_page_tags %}{% feincms_languagelinks for page3' - ' as links existing %}{% for key, name, link in links %}{{ key }}:' - '{{ link }}{% if not forloop.last %},{% endif %}{% endfor %}') - self.assertEqual( - t.render(context), - 'en:/test-page/test-child-page/page3/') + "{% load feincms_page_tags %}{% feincms_languagelinks for page3" + " as links existing %}{% for key, name, link in links %}{{ key }}:" + "{{ link }}{% if not forloop.last %},{% endif %}{% endfor %}" + ) + self.assertEqual(t.render(context), "en:/test-page/test-child-page/page3/") t = template.Template( - '{% load feincms_page_tags %}{% feincms_languagelinks for' - ' feincms_page as links excludecurrent=1 %}' - '{% for key, name, link in links %}{{ key }}:{{ link }}' - '{% if not forloop.last %},{% endif %}{% endfor %}') - self.assertEqual(t.render(context), 'en:/test-page/') + "{% load feincms_page_tags %}{% feincms_languagelinks for" + " feincms_page as links excludecurrent=1 %}" + "{% for key, name, link in links %}{{ key }}:{{ link }}" + "{% if not forloop.last %},{% endif %}{% endfor %}" + ) + self.assertEqual(t.render(context), "en:/test-page/") t = template.Template( - '{% load feincms_page_tags %}' - '{% feincms_nav feincms_page level=1 as nav %}' - '{% for p in nav %}{{ p.get_absolute_url }}' - '{% if not forloop.last %},{% endif %}{% endfor %}') - self.assertEqual(t.render(context), '') + "{% load feincms_page_tags %}" + "{% feincms_nav feincms_page level=1 as nav %}" + "{% for p in nav %}{{ p.get_absolute_url }}" + "{% if not forloop.last %},{% endif %}{% endfor %}" + ) + self.assertEqual(t.render(context), "") # XXX should the other template tags not respect the in_navigation # setting too? @@ -712,101 +697,114 @@ def test_17_page_template_tags(self): page1.in_navigation = True page1.save() - self.assertEqual(t.render(context), '/test-page/') + self.assertEqual(t.render(context), "/test-page/") t = template.Template( - '{% load feincms_page_tags %}' - '{% feincms_nav feincms_page level=2 as nav %}' - '{% for p in nav %}{{ p.get_absolute_url }}' - '{% if not forloop.last %},{% endif %}{% endfor %}') - self.assertEqual(t.render(context), '/test-page/test-child-page/') + "{% load feincms_page_tags %}" + "{% feincms_nav feincms_page level=2 as nav %}" + "{% for p in nav %}{{ p.get_absolute_url }}" + "{% if not forloop.last %},{% endif %}{% endfor %}" + ) + self.assertEqual(t.render(context), "/test-page/test-child-page/") t = template.Template( - '{% load feincms_page_tags %}' - '{% feincms_nav request level=2 as nav %}' - '{% for p in nav %}{{ p.get_absolute_url }}' - '{% if not forloop.last %},{% endif %}{% endfor %}') + "{% load feincms_page_tags %}" + "{% feincms_nav request level=2 as nav %}" + "{% for p in nav %}{{ p.get_absolute_url }}" + "{% if not forloop.last %},{% endif %}{% endfor %}" + ) from django.http import HttpRequest + request = HttpRequest() - request.path = '/test-page/' + request.path = "/test-page/" self.assertEqual( - t.render(template.Context({'request': request})), - '/test-page/test-child-page/') + t.render(template.Context({"request": request})), + "/test-page/test-child-page/", + ) t = template.Template( - '{% load feincms_page_tags %}' - '{% feincms_nav feincms_page level=99 as nav %}' - '{% for p in nav %}{{ p.get_absolute_url }}' - '{% if not forloop.last %},{% endif %}{% endfor %}') - self.assertEqual(t.render(context), '') + "{% load feincms_page_tags %}" + "{% feincms_nav feincms_page level=99 as nav %}" + "{% for p in nav %}{{ p.get_absolute_url }}" + "{% if not forloop.last %},{% endif %}{% endfor %}" + ) + self.assertEqual(t.render(context), "") t = template.Template( - '{% load feincms_page_tags %}' - '{% feincms_breadcrumbs feincms_page %}') + "{% load feincms_page_tags %}" "{% feincms_breadcrumbs feincms_page %}" + ) rendered = t.render(context) self.assertTrue("Test child page" in rendered) self.assertTrue( 'href="/test-page/">Test page' in rendered, - msg="The parent page should be a breadcrumb link") + msg="The parent page should be a breadcrumb link", + ) self.assertTrue( 'href="/test-page/test-child-page/"' not in rendered, - msg="The current page should not be a link in the breadcrumbs") + msg="The current page should not be a link in the breadcrumbs", + ) t = template.Template( - '{% load feincms_page_tags %}' - '{% feincms_nav feincms_page level=2 depth=2 as nav %}' - '{% for p in nav %}{{ p.get_absolute_url }}' - '{% if not forloop.last %},{% endif %}{% endfor %}') + "{% load feincms_page_tags %}" + "{% feincms_nav feincms_page level=2 depth=2 as nav %}" + "{% for p in nav %}{{ p.get_absolute_url }}" + "{% if not forloop.last %},{% endif %}{% endfor %}" + ) self.assertEqual( t.render(context), - '/test-page/test-child-page/,/test-page/test-child-page/page3/') + "/test-page/test-child-page/,/test-page/test-child-page/page3/", + ) t = template.Template( - '{% load feincms_page_tags %}' - '{% feincms_nav feincms_page level=1 depth=2 as nav %}' - '{% for p in nav %}{{ p.get_absolute_url }}' - '{% if not forloop.last %},{% endif %}{% endfor %}') - self.assertEqual( - t.render(context), - '/test-page/,/test-page/test-child-page/') + "{% load feincms_page_tags %}" + "{% feincms_nav feincms_page level=1 depth=2 as nav %}" + "{% for p in nav %}{{ p.get_absolute_url }}" + "{% if not forloop.last %},{% endif %}{% endfor %}" + ) + self.assertEqual(t.render(context), "/test-page/,/test-page/test-child-page/") t = template.Template( - '{% load feincms_page_tags %}' - '{% feincms_nav feincms_page level=1 depth=3 as nav %}' - '{% for p in nav %}{{ p.get_absolute_url }}' - '{% if not forloop.last %},{% endif %}{% endfor %}') + "{% load feincms_page_tags %}" + "{% feincms_nav feincms_page level=1 depth=3 as nav %}" + "{% for p in nav %}{{ p.get_absolute_url }}" + "{% if not forloop.last %},{% endif %}{% endfor %}" + ) self.assertEqual( t.render(context), - '/test-page/,/test-page/test-child-page/,/test-page/test-child' - '-page/page3/') + "/test-page/,/test-page/test-child-page/,/test-page/test-child" + "-page/page3/", + ) t = template.Template( - '{% load feincms_page_tags %}' - '{% feincms_nav feincms_page level=3 depth=2 as nav %}' - '{% for p in nav %}{{ p.get_absolute_url }}' - '{% if not forloop.last %},{% endif %}{% endfor %}') - self.assertEqual( - t.render(context), '/test-page/test-child-page/page3/') + "{% load feincms_page_tags %}" + "{% feincms_nav feincms_page level=3 depth=2 as nav %}" + "{% for p in nav %}{{ p.get_absolute_url }}" + "{% if not forloop.last %},{% endif %}{% endfor %}" + ) + self.assertEqual(t.render(context), "/test-page/test-child-page/page3/") t = template.Template( - '{% load feincms_page_tags %}' - '{% if feincms_page|is_parent_of:page3 %}yes{% endif %}|' - '{% if page3|is_parent_of:feincms_page %}yes{% endif %}') - self.assertEqual(t.render(context), 'yes|') + "{% load feincms_page_tags %}" + "{% if feincms_page|is_parent_of:page3 %}yes{% endif %}|" + "{% if page3|is_parent_of:feincms_page %}yes{% endif %}" + ) + self.assertEqual(t.render(context), "yes|") t = template.Template( - '{% load feincms_page_tags %}' - '{% if feincms_page|is_equal_or_parent_of:page3 %}yes{% endif %}|' - '{% if page3|is_equal_or_parent_of:feincms_page %}yes{% endif %}') - self.assertEqual(t.render(context), 'yes|') + "{% load feincms_page_tags %}" + "{% if feincms_page|is_equal_or_parent_of:page3 %}yes{% endif %}|" + "{% if page3|is_equal_or_parent_of:feincms_page %}yes{% endif %}" + ) + self.assertEqual(t.render(context), "yes|") t = template.Template( - '{% load feincms_page_tags %}' - '{% feincms_translatedpage for feincms_page as t1 language=de %}' - '{% feincms_translatedpage for feincms_page as t2 %}' - '{{ t1.id }}|{{ t2.id }}') - self.assertEqual(t.render(context), '2|1') + "{% load feincms_page_tags %}" + "{% feincms_translatedpage for feincms_page as t1 language=de %}" + "{% feincms_translatedpage for feincms_page as t2 %}" + "{{ t1.id }}|{{ t2.id }}" + ) + self.assertEqual(t.render(context), "2|1") def test_17_feincms_nav(self): """ @@ -815,30 +813,30 @@ def test_17_feincms_nav(self): self.login() - self.create_page_through_admin('Page 1') # 1 - self.create_page_through_admin('Page 1.1', 1) - self.create_page_through_admin('Page 1.2', 1) # 3 - self.create_page_through_admin('Page 1.2.1', 3) - self.create_page_through_admin('Page 1.2.2', 3) - self.create_page_through_admin('Page 1.2.3', 3) - self.create_page_through_admin('Page 1.3', 1) - - self.create_page_through_admin('Page 2') # 8 - self.create_page_through_admin('Page 2.1', 8) - self.create_page_through_admin('Page 2.2', 8) - self.create_page_through_admin('Page 2.3', 8) - - self.create_page_through_admin('Page 3') # 12 - self.create_page_through_admin('Page 3.1', 12) - self.create_page_through_admin('Page 3.2', 12) - self.create_page_through_admin('Page 3.3', 12) # 15 - self.create_page_through_admin('Page 3.3.1', 15) # 16 - self.create_page_through_admin('Page 3.3.1.1', 16) - self.create_page_through_admin('Page 3.3.2', 15) - - self.create_page_through_admin('Page 4') # 19 - self.create_page_through_admin('Page 4.1', 19) - self.create_page_through_admin('Page 4.2', 19) + self.create_page_through_admin("Page 1") # 1 + self.create_page_through_admin("Page 1.1", 1) + self.create_page_through_admin("Page 1.2", 1) # 3 + self.create_page_through_admin("Page 1.2.1", 3) + self.create_page_through_admin("Page 1.2.2", 3) + self.create_page_through_admin("Page 1.2.3", 3) + self.create_page_through_admin("Page 1.3", 1) + + self.create_page_through_admin("Page 2") # 8 + self.create_page_through_admin("Page 2.1", 8) + self.create_page_through_admin("Page 2.2", 8) + self.create_page_through_admin("Page 2.3", 8) + + self.create_page_through_admin("Page 3") # 12 + self.create_page_through_admin("Page 3.1", 12) + self.create_page_through_admin("Page 3.2", 12) + self.create_page_through_admin("Page 3.3", 12) # 15 + self.create_page_through_admin("Page 3.3.1", 15) # 16 + self.create_page_through_admin("Page 3.3.1.1", 16) + self.create_page_through_admin("Page 3.3.2", 15) + + self.create_page_through_admin("Page 4") # 19 + self.create_page_through_admin("Page 4.1", 19) + self.create_page_through_admin("Page 4.2", 19) """ Creates the following structure: @@ -866,122 +864,125 @@ def test_17_feincms_nav(self): tests = [ ( - {'feincms_page': Page.objects.get(pk=1)}, - '{% load feincms_page_tags %}' - '{% feincms_nav feincms_page level=1 depth=2 as nav %}' - '{% for p in nav %}{{ p.get_absolute_url }}' - '{% if not forloop.last %},{% endif %}{% endfor %}', - '/page-1/,/page-1/page-11/,/page-1/page-12/,/page-1/page-13/' - ',/page-2/,/page-2/page-22/,/page-2/page-23/,/page-3/,/page-' - '3/page-31/,/page-3/page-32/,/page-3/page-33/', + {"feincms_page": Page.objects.get(pk=1)}, + "{% load feincms_page_tags %}" + "{% feincms_nav feincms_page level=1 depth=2 as nav %}" + "{% for p in nav %}{{ p.get_absolute_url }}" + "{% if not forloop.last %},{% endif %}{% endfor %}", + "/page-1/,/page-1/page-11/,/page-1/page-12/,/page-1/page-13/" + ",/page-2/,/page-2/page-22/,/page-2/page-23/,/page-3/,/page-" + "3/page-31/,/page-3/page-32/,/page-3/page-33/", ), ( - {'feincms_page': Page.objects.get(pk=14)}, - '{% load feincms_page_tags %}' - '{% feincms_nav feincms_page level=2 depth=2 as nav %}' - '{% for p in nav %}{{ p.get_absolute_url }}' - '{% if not forloop.last %},{% endif %}{% endfor %}', - '/page-3/page-31/,/page-3/page-32/,/page-3/page-33/,/page-3/' - 'page-33/page-331/,/page-3/page-33/page-332/', + {"feincms_page": Page.objects.get(pk=14)}, + "{% load feincms_page_tags %}" + "{% feincms_nav feincms_page level=2 depth=2 as nav %}" + "{% for p in nav %}{{ p.get_absolute_url }}" + "{% if not forloop.last %},{% endif %}{% endfor %}", + "/page-3/page-31/,/page-3/page-32/,/page-3/page-33/,/page-3/" + "page-33/page-331/,/page-3/page-33/page-332/", ), ( - {'feincms_page': Page.objects.get(pk=14)}, - '{% load feincms_page_tags %}' - '{% feincms_nav feincms_page level=2 depth=3 as nav %}' - '{% for p in nav %}{{ p.get_absolute_url }}' - '{% if not forloop.last %},{% endif %}{% endfor %}', - '/page-3/page-31/,/page-3/page-32/,/page-3/page-33/,/page-3/' - 'page-33/page-331/,/page-3/page-33/page-331/page-3311/,/page' - '-3/page-33/page-332/', + {"feincms_page": Page.objects.get(pk=14)}, + "{% load feincms_page_tags %}" + "{% feincms_nav feincms_page level=2 depth=3 as nav %}" + "{% for p in nav %}{{ p.get_absolute_url }}" + "{% if not forloop.last %},{% endif %}{% endfor %}", + "/page-3/page-31/,/page-3/page-32/,/page-3/page-33/,/page-3/" + "page-33/page-331/,/page-3/page-33/page-331/page-3311/,/page" + "-3/page-33/page-332/", ), ( - {'feincms_page': Page.objects.get(pk=19)}, - '{% load feincms_page_tags %}' - '{% feincms_nav feincms_page level=1 depth=2 as nav %}' - '{% for p in nav %}{{ p.get_absolute_url }}' - '{% if not forloop.last %},{% endif %}{% endfor %}', - '/page-1/,/page-1/page-11/,/page-1/page-12/,/page-1/page-13' - '/,/page-2/,/page-2/page-22/,/page-2/page-23/,/page-3/,/pag' - 'e-3/page-31/,/page-3/page-32/,/page-3/page-33/', + {"feincms_page": Page.objects.get(pk=19)}, + "{% load feincms_page_tags %}" + "{% feincms_nav feincms_page level=1 depth=2 as nav %}" + "{% for p in nav %}{{ p.get_absolute_url }}" + "{% if not forloop.last %},{% endif %}{% endfor %}", + "/page-1/,/page-1/page-11/,/page-1/page-12/,/page-1/page-13" + "/,/page-2/,/page-2/page-22/,/page-2/page-23/,/page-3/,/pag" + "e-3/page-31/,/page-3/page-32/,/page-3/page-33/", ), ( - {'feincms_page': Page.objects.get(pk=1)}, - '{% load feincms_page_tags %}' - '{% feincms_nav feincms_page level=3 depth=1 as nav %}' - '{% for p in nav %}{{ p.get_absolute_url }}' - '{% if not forloop.last %},{% endif %}{% endfor %}', - '', + {"feincms_page": Page.objects.get(pk=1)}, + "{% load feincms_page_tags %}" + "{% feincms_nav feincms_page level=3 depth=1 as nav %}" + "{% for p in nav %}{{ p.get_absolute_url }}" + "{% if not forloop.last %},{% endif %}{% endfor %}", + "", ), ] for c, t, r in tests: - self.assertEqual( - template.Template(t).render(template.Context(c)), - r) + self.assertEqual(template.Template(t).render(template.Context(c)), r) # Test that navigation entries do not exist several times, even with # navigation extensions. Apply the PassthroughExtension to a page # which does only have direct children, because it does not collect # pages further down the tree. page = Page.objects.get(pk=8) - page.navigation_extension =\ - 'testapp.navigation_extensions.PassthroughExtension' + page.navigation_extension = "testapp.navigation_extensions.PassthroughExtension" page.save() for c, t, r in tests: - self.assertEqual( - template.Template(t).render(template.Context(c)), - r) + self.assertEqual(template.Template(t).render(template.Context(c)), r) # Now check that disabling a page also disables it in Navigation: p = Page.objects.get(pk=15) - tmpl = '''{% load feincms_page_tags %} + tmpl = """{% load feincms_page_tags %} {% feincms_nav feincms_page level=1 depth=3 as nav %} {% for p in nav %}{{ p.pk }}{% if not forloop.last %},{% endif %}{% endfor %} -''' +""" - data = template.Template(tmpl).render( - template.Context({'feincms_page': p}) - ).strip(), + data = ( + template.Template(tmpl) + .render(template.Context({"feincms_page": p})) + .strip(), + ) self.assertEqual( - data, - ('1,2,3,4,6,7,8,10,11,12,13,14,15,16,18',), - "Original navigation") + data, ("1,2,3,4,6,7,8,10,11,12,13,14,15,16,18",), "Original navigation" + ) p.active = False p.save() - data = template.Template(tmpl).render( - template.Context({'feincms_page': p}) - ).strip(), + data = ( + template.Template(tmpl) + .render(template.Context({"feincms_page": p})) + .strip(), + ) self.assertEqual( data, - ('1,2,3,4,6,7,8,10,11,12,13,14',), - "Navigation after disabling intermediate page") + ("1,2,3,4,6,7,8,10,11,12,13,14",), + "Navigation after disabling intermediate page", + ) # Same test with feincms_nav - tmpl = '''{% load feincms_page_tags %} + tmpl = """{% load feincms_page_tags %} {% feincms_nav feincms_page level=1 depth=3 as nav %} {% for p in nav %}{{ p.pk }}{% if not forloop.last %},{% endif %}{% endfor %} -''' +""" - data = template.Template(tmpl).render( - template.Context({'feincms_page': p}) - ).strip(), + data = ( + template.Template(tmpl) + .render(template.Context({"feincms_page": p})) + .strip(), + ) self.assertEqual( data, - ('1,2,3,4,6,7,8,10,11,12,13,14',), - "Navigation after disabling intermediate page") + ("1,2,3,4,6,7,8,10,11,12,13,14",), + "Navigation after disabling intermediate page", + ) p.active = True p.save() - data = template.Template(tmpl).render( - template.Context({'feincms_page': p}) - ).strip(), + data = ( + template.Template(tmpl) + .render(template.Context({"feincms_page": p})) + .strip(), + ) self.assertEqual( - data, - ('1,2,3,4,6,7,8,10,11,12,13,14,15,16,18',), - "Original navigation") + data, ("1,2,3,4,6,7,8,10,11,12,13,14,15,16,18",), "Original navigation" + ) def test_18_default_render_method(self): """ @@ -994,17 +995,17 @@ class Meta: abstract = True def render_main(self): - return 'Hello' + return "Hello" # do not register this model in the internal FeinCMS bookkeeping # structures tmp = Page._feincms_content_types[:] - type = Page.create_content_type(Something, regions=('notexists',)) + type = Page.create_content_type(Something, regions=("notexists",)) Page._feincms_content_types = tmp - s = type(region='main', ordering='1') + s = type(region="main", ordering="1") - self.assertEqual(s.render(), 'Hello') + self.assertEqual(s.render(), "Hello") def test_19_page_manager(self): self.create_default_page_set() @@ -1015,73 +1016,78 @@ def test_19_page_manager(self): self.assertRaises( Page.DoesNotExist, - lambda: Page.objects.page_for_path(page.get_absolute_url())) + lambda: Page.objects.page_for_path(page.get_absolute_url()), + ) self.assertRaises( Page.DoesNotExist, lambda: Page.objects.best_match_for_path( - page.get_absolute_url() + 'something/hello/')) + page.get_absolute_url() + "something/hello/" + ), + ) self.assertRaises( Http404, - lambda: Page.objects.best_match_for_path( - '/blabla/blabla/', raise404=True)) + lambda: Page.objects.best_match_for_path("/blabla/blabla/", raise404=True), + ) self.assertRaises( - Http404, - lambda: Page.objects.page_for_path('/asdf/', raise404=True)) + Http404, lambda: Page.objects.page_for_path("/asdf/", raise404=True) + ) self.assertRaises( Page.DoesNotExist, - lambda: Page.objects.best_match_for_path('/blabla/blabla/')) + lambda: Page.objects.best_match_for_path("/blabla/blabla/"), + ) self.assertRaises( - Page.DoesNotExist, - lambda: Page.objects.page_for_path('/asdf/')) + Page.DoesNotExist, lambda: Page.objects.page_for_path("/asdf/") + ) request = Empty() request.path = request.path_info = page.get_absolute_url() - request.method = 'GET' - request.get_full_path = lambda: '/xyz/' + request.method = "GET" + request.get_full_path = lambda: "/xyz/" request.GET = {} request.META = {} request.user = AnonymousUser() # tadaa from django.utils import translation + translation.activate(page.language) page.active = False page.save() self.assertRaises( - Http404, - lambda: Page.objects.for_request(request, raise404=True)) + Http404, lambda: Page.objects.for_request(request, raise404=True) + ) page.active = True page.save() self.assertRaises( - Http404, - lambda: Page.objects.for_request(request, raise404=True)) + Http404, lambda: Page.objects.for_request(request, raise404=True) + ) page.parent.active = True page.parent.save() self.assertEqual(page, Page.objects.for_request(request)) - self.assertEqual( - page, Page.objects.page_for_path(page.get_absolute_url())) + self.assertEqual(page, Page.objects.page_for_path(page.get_absolute_url())) self.assertEqual( page, Page.objects.best_match_for_path( - page.get_absolute_url() + 'something/hello/')) + page.get_absolute_url() + "something/hello/" + ), + ) old = feincms_settings.FEINCMS_ALLOW_EXTRA_PATH - request.path += 'hello/' + request.path += "hello/" feincms_settings.FEINCMS_ALLOW_EXTRA_PATH = False self.assertEqual(self.client.get(request.path).status_code, 404) feincms_settings.FEINCMS_ALLOW_EXTRA_PATH = True self.assertEqual(self.client.get(request.path).status_code, 200) - self.assertEqual( - page, Page.objects.for_request(request, best_match=True)) + self.assertEqual(page, Page.objects.for_request(request, best_match=True)) feincms_settings.FEINCMS_ALLOW_EXTRA_PATH = old @@ -1096,7 +1102,7 @@ def test_20_redirects(self): page2.active = True page2.publication_date = timezone.now() - timedelta(days=1) - page2.override_url = '/blablabla/' + page2.override_url = "/blablabla/" page2.redirect_to = page1.get_absolute_url() page2.save() @@ -1109,11 +1115,11 @@ def test_20_redirects(self): # page2 has been modified too, but its URL should not have changed try: self.assertRedirects( - self.client.get('/blablabla/'), - page1.get_absolute_url()) + self.client.get("/blablabla/"), page1.get_absolute_url() + ) except TemplateDoesNotExist as e: # catch the error from rendering page1 - if e.args != ('feincms_base.html',): + if e.args != ("feincms_base.html",): raise def test_21_copy_content(self): @@ -1133,8 +1139,7 @@ def test_23_navigation_extension(self): self.assertEqual(len(page.extended_navigation()), 0) - page.navigation_extension =\ - 'testapp.navigation_extensions.PassthroughExtension' + page.navigation_extension = "testapp.navigation_extensions.PassthroughExtension" page2 = Page.objects.get(pk=2) page2.active = True @@ -1143,16 +1148,15 @@ def test_23_navigation_extension(self): self.assertEqual(list(page.extended_navigation()), [page2]) - page.navigation_extension =\ - 'testapp.navigation_extensions.ThisExtensionDoesNotExist' + page.navigation_extension = ( + "testapp.navigation_extensions.ThisExtensionDoesNotExist" + ) self.assertEqual(len(page.extended_navigation()), 1) - page.navigation_extension =\ - 'testapp.navigation_extensions.PretenderExtension' + page.navigation_extension = "testapp.navigation_extensions.PretenderExtension" - self.assertEqual( - page.extended_navigation()[0].get_absolute_url(), '/asdsa/') + self.assertEqual(page.extended_navigation()[0].get_absolute_url(), "/asdsa/") def test_24_admin_redirects(self): self.create_default_page_set() @@ -1160,15 +1164,13 @@ def test_24_admin_redirects(self): page = Page.objects.get(pk=1) response = self.create_page_through_admincontent(page, _continue=1) - self.assertRedirects( - response, - reverse('admin:page_page_change', args=(1,))) + self.assertRedirects(response, reverse("admin:page_page_change", args=(1,))) response = self.create_page_through_admincontent(page, _addanother=1) - self.assertRedirects(response, '/admin/page/page/add/') + self.assertRedirects(response, "/admin/page/page/add/") response = self.create_page_through_admincontent(page) - self.assertRedirects(response, '/admin/page/page/') + self.assertRedirects(response, "/admin/page/page/") def test_25_applicationcontent(self): self.create_default_page_set() @@ -1179,134 +1181,131 @@ def test_25_applicationcontent(self): page = Page.objects.get(pk=2) page.active = True - page.template_key = 'theother' + page.template_key = "theother" page.save() # Should not be published because the page has no application contents # and should therefore not catch anything below it. - self.is_published(page1.get_absolute_url() + 'anything/', False) + self.is_published(page1.get_absolute_url() + "anything/", False) page.applicationcontent_set.create( - region='main', ordering=0, - urlconf_path='testapp.applicationcontent_urls') + region="main", ordering=0, urlconf_path="testapp.applicationcontent_urls" + ) + self.assertContains(self.client.get(page.get_absolute_url()), "module_root") self.assertContains( - self.client.get(page.get_absolute_url()), - 'module_root') - self.assertContains( - self.client.get(page.get_absolute_url() + 'args_test/abc/def/'), - 'abc-def') + self.client.get(page.get_absolute_url() + "args_test/abc/def/"), "abc-def" + ) self.assertContains( - self.client.get(page.get_absolute_url() + 'kwargs_test/abc/def/'), - 'def-abc') + self.client.get(page.get_absolute_url() + "kwargs_test/abc/def/"), "def-abc" + ) - response = self.client.get( - page.get_absolute_url() + 'full_reverse_test/') - self.assertContains(response, 'home:/test-page/test-child-page/') + response = self.client.get(page.get_absolute_url() + "full_reverse_test/") + self.assertContains(response, "home:/test-page/test-child-page/") self.assertContains( - response, - 'args:/test-page/test-child-page/args_test/xy/zzy/') - self.assertContains(response, 'base:/test/') - self.assertContains(response, 'homeas:/test-page/test-child-page/') + response, "args:/test-page/test-child-page/args_test/xy/zzy/" + ) + self.assertContains(response, "base:/test/") + self.assertContains(response, "homeas:/test-page/test-child-page/") self.assertEqual( - app_reverse('ac_module_root', 'testapp.applicationcontent_urls'), - '/test-page/test-child-page/') + app_reverse("ac_module_root", "testapp.applicationcontent_urls"), + "/test-page/test-child-page/", + ) - if hasattr(self, 'assertNumQueries'): + if hasattr(self, "assertNumQueries"): self.assertNumQueries( 0, lambda: app_reverse( - 'ac_module_root', 'testapp.applicationcontent_urls')) + "ac_module_root", "testapp.applicationcontent_urls" + ), + ) # This should not raise self.assertEqual( - self.client.get( - page.get_absolute_url() + 'notexists/' - ).status_code, 404) + self.client.get(page.get_absolute_url() + "notexists/").status_code, 404 + ) self.assertContains( - self.client.get(page.get_absolute_url() + 'fragment/'), - 'some things') + self.client.get(page.get_absolute_url() + "fragment/"), + 'some things', + ) self.assertRedirects( - self.client.get(page.get_absolute_url() + 'redirect/'), - 'http://testserver' + page.get_absolute_url()) + self.client.get(page.get_absolute_url() + "redirect/"), + "http://testserver" + page.get_absolute_url(), + ) self.assertEqual( - app_reverse('ac_module_root', 'testapp.applicationcontent_urls'), - page.get_absolute_url()) + app_reverse("ac_module_root", "testapp.applicationcontent_urls"), + page.get_absolute_url(), + ) - response = self.client.get(page.get_absolute_url() + 'response/') - self.assertContains(response, 'Anything') + response = self.client.get(page.get_absolute_url() + "response/") + self.assertContains(response, "Anything") # Ensure response has been wrapped - self.assertContains(response, '

Main content

') + self.assertContains(response, "

Main content

") # Test standalone behavior self.assertEqual( self.client.get( - page.get_absolute_url() + 'response/', - HTTP_X_REQUESTED_WITH='XMLHttpRequest').content, - self.client.get( - page.get_absolute_url() + 'response_decorated/').content) + page.get_absolute_url() + "response/", + HTTP_X_REQUESTED_WITH="XMLHttpRequest", + ).content, + self.client.get(page.get_absolute_url() + "response_decorated/").content, + ) page1.applicationcontent_set.create( - region='main', - ordering=0, - urlconf_path='whatever') + region="main", ordering=0, urlconf_path="whatever" + ) - response = self.client.get( - page.get_absolute_url() + 'alias_reverse_test/') - self.assertContains(response, 'home:/test-page/') - self.assertContains(response, 'args:/test-page/args_test/xy/zzy/') - self.assertContains(response, 'base:/test/') + response = self.client.get(page.get_absolute_url() + "alias_reverse_test/") + self.assertContains(response, "home:/test-page/") + self.assertContains(response, "args:/test-page/args_test/xy/zzy/") + self.assertContains(response, "base:/test/") self.assertEqual( - app_reverse('ac_module_root', 'testapp.applicationcontent_urls'), - '/test-page/test-child-page/') - self.assertEqual( - app_reverse('ac_module_root', 'whatever'), - '/test-page/') + app_reverse("ac_module_root", "testapp.applicationcontent_urls"), + "/test-page/test-child-page/", + ) + self.assertEqual(app_reverse("ac_module_root", "whatever"), "/test-page/") page.applicationcontent_set.get( - urlconf_path='testapp.applicationcontent_urls').delete() + urlconf_path="testapp.applicationcontent_urls" + ).delete() - self.assertEqual( - app_reverse('ac_module_root', 'whatever'), - '/test-page/') + self.assertEqual(app_reverse("ac_module_root", "whatever"), "/test-page/") # Ensure ApplicationContent's admin_fields support works properly self.login() - response = self.client.get( - reverse('admin:page_page_change', args=(page1.id,)) - ) + response = self.client.get(reverse("admin:page_page_change", args=(page1.id,))) - self.assertContains(response, 'exclusive_subpages') - self.assertContains(response, 'custom_field') + self.assertContains(response, "exclusive_subpages") + self.assertContains(response, "custom_field") # Check if admin_fields get populated correctly app_ct = page1.applicationcontent_set.all()[0] - app_ct.parameters =\ - '{"custom_field":"val42", "exclusive_subpages": false}' + app_ct.parameters = '{"custom_field":"val42", "exclusive_subpages": false}' app_ct.save() - response = self.client.get( - reverse('admin:page_page_change', args=(page1.id,)) - ) - self.assertContains(response, 'val42') + response = self.client.get(reverse("admin:page_page_change", args=(page1.id,))) + self.assertContains(response, "val42") def test_26_page_form_initial(self): self.create_default_page_set() self.login() - self.assertEqual(self.client.get( - '/admin/page/page/add/?translation_of=1&lang=de' - ).status_code, 200) - self.assertEqual(self.client.get( - '/admin/page/page/add/?parent=1' - ).status_code, 200) - self.assertEqual(self.client.get( - '/admin/page/page/add/?parent=2' - ).status_code, 200) + self.assertEqual( + self.client.get( + "/admin/page/page/add/?translation_of=1&lang=de" + ).status_code, + 200, + ) + self.assertEqual( + self.client.get("/admin/page/page/add/?parent=1").status_code, 200 + ) + self.assertEqual( + self.client.get("/admin/page/page/add/?parent=2").status_code, 200 + ) def test_27_cached_url_clash(self): self.create_default_page_set() @@ -1314,15 +1313,15 @@ def test_27_cached_url_clash(self): page1 = Page.objects.get(pk=1) page2 = Page.objects.get(pk=2) - page1.override_url = '/' + page1.override_url = "/" page1.active = True page1.save() self.login() self.assertContains( - self.create_page_through_admincontent( - page2, active=True, override_url='/'), - 'already taken by') + self.create_page_through_admincontent(page2, active=True, override_url="/"), + "already taken by", + ) def test_28_applicationcontent_reverse(self): self.create_default_page_set() @@ -1332,48 +1331,49 @@ def test_28_applicationcontent_reverse(self): page = Page.objects.get(pk=2) page.active = True - page.template_key = 'theother' + page.template_key = "theother" page.save() page.applicationcontent_set.create( - region='main', ordering=0, - urlconf_path='testapp.applicationcontent_urls') + region="main", ordering=0, urlconf_path="testapp.applicationcontent_urls" + ) # test app_reverse self.assertEqual( - app_reverse('ac_module_root', 'testapp.applicationcontent_urls'), - page.get_absolute_url()) + app_reverse("ac_module_root", "testapp.applicationcontent_urls"), + page.get_absolute_url(), + ) # when specific applicationcontent exists more then once reverse should # return the URL of the first (ordered by primary key) page. self.login() + self.create_page_through_admin(title="Home DE", language="de", active=True) + page_de = Page.objects.get(title="Home DE") self.create_page_through_admin( - title='Home DE', language='de', active=True) - page_de = Page.objects.get(title='Home DE') - self.create_page_through_admin( - title='Child 1 DE', language='de', parent=page_de.id, active=True) - page_de_1 = Page.objects.get(title='Child 1 DE') + title="Child 1 DE", language="de", parent=page_de.id, active=True + ) + page_de_1 = Page.objects.get(title="Child 1 DE") page_de_1.applicationcontent_set.create( - region='main', ordering=0, - urlconf_path='testapp.applicationcontent_urls') + region="main", ordering=0, urlconf_path="testapp.applicationcontent_urls" + ) page.active = False page.save() - settings.TEMPLATE_DIRS = ( - os.path.join(os.path.dirname(__file__), 'templates'), - ) + settings.TEMPLATE_DIRS = (os.path.join(os.path.dirname(__file__), "templates"),) self.client.get(page_de_1.get_absolute_url()) self.assertEqual( - app_reverse('ac_module_root', 'testapp.applicationcontent_urls'), - page_de_1.get_absolute_url()) + app_reverse("ac_module_root", "testapp.applicationcontent_urls"), + page_de_1.get_absolute_url(), + ) page.active = True page.save() self.client.get(page1.get_absolute_url()) self.assertEqual( - app_reverse('ac_module_root', 'testapp.applicationcontent_urls'), - page.get_absolute_url()) + app_reverse("ac_module_root", "testapp.applicationcontent_urls"), + page.get_absolute_url(), + ) def test_29_medialibrary_admin(self): self.create_default_page_set() @@ -1381,56 +1381,59 @@ def test_29_medialibrary_admin(self): page = Page.objects.get(pk=1) - mediafile = MediaFile.objects.create(file='somefile.jpg') + mediafile = MediaFile.objects.create(file="somefile.jpg") page.mediafilecontent_set.create( - mediafile=mediafile, - region='main', - type='default', - ordering=1) + mediafile=mediafile, region="main", type="default", ordering=1 + ) self.assertContains( - self.client.get('/admin/medialibrary/mediafile/'), - 'somefile.jpg') + self.client.get("/admin/medialibrary/mediafile/"), "somefile.jpg" + ) import zipfile - zf = zipfile.ZipFile('test.zip', 'w') + + zf = zipfile.ZipFile("test.zip", "w") for i in range(10): - zf.writestr('test%d.txt' % i, 'test%d' % i) + zf.writestr("test%d.txt" % i, "test%d" % i) zf.close() - with open('test.zip', 'rb') as handle: + with open("test.zip", "rb") as handle: response = self.client.post( - '/admin/medialibrary/mediafile/mediafile-bulk-upload/', { - 'data': handle, - }) - self.assertRedirects(response, '/admin/medialibrary/mediafile/') + "/admin/medialibrary/mediafile/mediafile-bulk-upload/", {"data": handle} + ) + self.assertRedirects(response, "/admin/medialibrary/mediafile/") self.assertEqual( MediaFile.objects.count(), 11, - "Upload of media files with ZIP does not work") + "Upload of media files with ZIP does not work", + ) dn = os.path.dirname path = os.path.join( - dn(dn(dn(dn(__file__)))), 'docs', 'images', 'tree_editor.png') + dn(dn(dn(dn(__file__)))), "docs", "images", "tree_editor.png" + ) - with open(path, 'rb') as handle: - response = self.client.post('/admin/medialibrary/mediafile/add/', { - 'file': handle, - 'translations-TOTAL_FORMS': 0, - 'translations-INITIAL_FORMS': 0, - 'translations-MAX_NUM_FORMS': 10, - }) - self.assertRedirects(response, '/admin/medialibrary/mediafile/') + with open(path, "rb") as handle: + response = self.client.post( + "/admin/medialibrary/mediafile/add/", + { + "file": handle, + "translations-TOTAL_FORMS": 0, + "translations-INITIAL_FORMS": 0, + "translations-MAX_NUM_FORMS": 10, + }, + ) + self.assertRedirects(response, "/admin/medialibrary/mediafile/") self.assertContains( - self.client.get('/admin/medialibrary/mediafile/'), - '100x100') + self.client.get("/admin/medialibrary/mediafile/"), "100x100" + ) - stats = list(MediaFile.objects.values_list('type', flat=True)) + stats = list(MediaFile.objects.values_list("type", flat=True)) self.assertEqual(len(stats), 12) - self.assertEqual(stats.count('image'), 2) - self.assertEqual(stats.count('txt'), 10) + self.assertEqual(stats.count("image"), 2) + self.assertEqual(stats.count("txt"), 10) def test_30_context_processors(self): self.create_default_page_set() @@ -1439,20 +1442,20 @@ def test_30_context_processors(self): request = Empty() request.GET = {} request.META = {} - request.method = 'GET' - request.path = request.path_info = '/test-page/test-child-page/abcdef/' - request.get_full_path = lambda: '/test-page/test-child-page/abcdef/' + request.method = "GET" + request.path = request.path_info = "/test-page/test-child-page/abcdef/" + request.get_full_path = lambda: "/test-page/test-child-page/abcdef/" ctx = add_page_if_missing(request) - self.assertEqual(ctx['feincms_page'], request._feincms_page) + self.assertEqual(ctx["feincms_page"], request._feincms_page) def test_31_sites_framework_associating_with_single_site(self): self.login() - site_2 = Site.objects.create(name='site 2', domain='2.example.com') - self.create_page_through_admin( - 'site 1 homepage', override_url='/', active=True) + site_2 = Site.objects.create(name="site 2", domain="2.example.com") + self.create_page_through_admin("site 1 homepage", override_url="/", active=True) self.create_page_through_admin( - 'site 2 homepage', override_url='/', site=site_2.id, active=True) + "site 2 homepage", override_url="/", site=site_2.id, active=True + ) self.assertEqual(Page.objects.count(), 2) self.assertEqual(Page.objects.active().count(), 1) @@ -1465,145 +1468,141 @@ def test_32_applicationcontent_inheritance20(self): page = Page.objects.get(pk=2) page.active = True - page.template_key = 'theother' + page.template_key = "theother" page.save() # Should not be published because the page has no application contents # and should therefore not catch anything below it. - self.is_published(page1.get_absolute_url() + 'anything/', False) + self.is_published(page1.get_absolute_url() + "anything/", False) page.applicationcontent_set.create( - region='main', ordering=0, - urlconf_path='testapp.applicationcontent_urls') + region="main", ordering=0, urlconf_path="testapp.applicationcontent_urls" + ) page.rawcontent_set.create( - region='main', ordering=1, text='some_main_region_text') + region="main", ordering=1, text="some_main_region_text" + ) page.rawcontent_set.create( - region='sidebar', ordering=0, text='some_sidebar_region_text') + region="sidebar", ordering=0, text="some_sidebar_region_text" + ) - self.assertContains(self.client.get(page.get_absolute_url()), - 'module_root') + self.assertContains(self.client.get(page.get_absolute_url()), "module_root") - response = self.client.get(page.get_absolute_url() + 'inheritance20/') - self.assertContains(response, 'a content 42') - self.assertContains(response, 'b content') - self.assertNotContains(response, 'some_main_region_text') - self.assertContains(response, 'some_sidebar_region_text') - self.assertNotContains(response, 'some content outside') + response = self.client.get(page.get_absolute_url() + "inheritance20/") + self.assertContains(response, "a content 42") + self.assertContains(response, "b content") + self.assertNotContains(response, "some_main_region_text") + self.assertContains(response, "some_sidebar_region_text") + self.assertNotContains(response, "some content outside") - response = self.client.get( - page.get_absolute_url() + 'inheritance20_unpack/') - self.assertContains(response, 'a content 43') - self.assertIn('yabba dabba', response['cache-control']) + response = self.client.get(page.get_absolute_url() + "inheritance20_unpack/") + self.assertContains(response, "a content 43") + self.assertIn("yabba dabba", response["cache-control"]) def test_33_preview(self): self.create_default_page_set() page = Page.objects.get(pk=1) - page.template_key = 'theother' + page.template_key = "theother" page.save() - page.rawcontent_set.create( - region='main', - ordering=0, - text='Example content') + page.rawcontent_set.create(region="main", ordering=0, text="Example content") self.login() - self.assertEqual( - self.client.get(page.get_absolute_url()).status_code, 404) + self.assertEqual(self.client.get(page.get_absolute_url()).status_code, 404) self.assertContains( - self.client.get('%s_preview/%s/' % ( - page.get_absolute_url(), - page.pk), - ), - 'Example content') + self.client.get("%s_preview/%s/" % (page.get_absolute_url(), page.pk)), + "Example content", + ) def test_34_access(self): self.create_default_page_set() page = Page.objects.get(pk=1) - page.override_url = '/something/' + page.override_url = "/something/" page.save() Page.objects.update(active=True) self.login() self.create_page_through_admin( - title='redirect page', - override_url='/', + title="redirect page", + override_url="/", redirect_to=page.get_absolute_url(), - active=True) + active=True, + ) # / -> redirect to /something/ - r = self.client.get('/') + r = self.client.get("/") self.assertRedirects(r, page.get_absolute_url()) # /something/ should work r = self.client.get(page.override_url) self.assertEqual(r.status_code, 200) # /foo not existant -> 404 - r = self.client.get('/foo/') + r = self.client.get("/foo/") self.assertEqual(r.status_code, 404) def test_35_access_with_extra_path(self): self.login() self.create_page( - title='redirect again', - override_url='/', - redirect_to='/somewhere/', - active=True) - self.create_page(title='somewhere', active=True) - - r = self.client.get('/') - self.assertRedirects(r, '/somewhere/') - r = self.client.get('/dingdong/') + title="redirect again", + override_url="/", + redirect_to="/somewhere/", + active=True, + ) + self.create_page(title="somewhere", active=True) + + r = self.client.get("/") + self.assertRedirects(r, "/somewhere/") + r = self.client.get("/dingdong/") self.assertEqual(r.status_code, 404) old = feincms_settings.FEINCMS_ALLOW_EXTRA_PATH feincms_settings.FEINCMS_ALLOW_EXTRA_PATH = True - r = self.client.get('/') - self.assertRedirects(r, '/somewhere/') - r = self.client.get('/dingdong/') + r = self.client.get("/") + self.assertRedirects(r, "/somewhere/") + r = self.client.get("/dingdong/") self.assertEqual(r.status_code, 404) feincms_settings.FEINCMS_ALLOW_EXTRA_PATH = old def test_36_sitemaps(self): - response = self.client.get('/sitemap.xml') - self.assertContains(response, '', status_code=200) + response = self.client.get("/sitemap.xml") + self.assertNotContains(response, "", status_code=200) page = Page.objects.get() page.active = True page.in_navigation = True page.save() - response = self.client.get('/sitemap.xml') - self.assertContains(response, '', status_code=200) + response = self.client.get("/sitemap.xml") + self.assertContains(response, "", status_code=200) def test_37_invalid_parent(self): self.create_default_page_set() - page1, page2 = list(Page.objects.order_by('id')) + page1, page2 = list(Page.objects.order_by("id")) page1.parent = page1 self.assertRaises(InvalidMove, page1.save) - self.create_page('Page 3', parent=page2) - page1, page2, page3 = list(Page.objects.order_by('id')) + self.create_page("Page 3", parent=page2) + page1, page2, page3 = list(Page.objects.order_by("id")) page1.parent = page3 self.assertRaises(InvalidMove, page1.save) def test_38_invalid_template(self): page = Page() - page.template_key = 'test' - self.assertEqual(page.template.key, 'base') + page.template_key = "test" + self.assertEqual(page.template.key, "base") def test_39_navigationgroups(self): self.create_default_page_set() - page1, page2 = list(Page.objects.order_by('id')) + page1, page2 = list(Page.objects.order_by("id")) page1.active = True page1.in_navigation = True @@ -1611,84 +1610,77 @@ def test_39_navigationgroups(self): page2.active = True page2.in_navigation = True - page2.navigation_group = 'footer' + page2.navigation_group = "footer" page2.save() t = template.Template( - ''' + """ {% load feincms_page_tags %} {% feincms_nav feincms_page level=1 depth=10 group='default' as nav %} {% for p in nav %}{{ p.get_absolute_url }},{% endfor %} - ''' + """ ) - str = t.render(template.Context({ - 'feincms_page': page1, - })) + str = t.render(template.Context({"feincms_page": page1})) - self.assertEqual(str.strip(), '/test-page/,') + self.assertEqual(str.strip(), "/test-page/,") t = template.Template( - ''' + """ {% load feincms_page_tags %} {% feincms_nav feincms_page level=1 depth=10 group='footer' as nav %} {% for p in nav %}{{ p.get_absolute_url }},{% endfor %} - ''' + """ ) - str = t.render(template.Context({ - 'feincms_page': page1, - })) + str = t.render(template.Context({"feincms_page": page1})) - self.assertEqual(str.strip(), '/test-page/test-child-page/,') + self.assertEqual(str.strip(), "/test-page/test-child-page/,") def test_40_page_is_active(self): self.create_default_page_set() - page1, page2 = list(Page.objects.order_by('id')) + page1, page2 = list(Page.objects.order_by("id")) - self.assertTrue(feincms_page_tags.page_is_active( - {'feincms_page': page1}, page1)) - self.assertTrue(feincms_page_tags.page_is_active( - {'feincms_page': page2}, page1)) - self.assertFalse(feincms_page_tags.page_is_active( - {'feincms_page': page1}, page2)) + self.assertTrue( + feincms_page_tags.page_is_active({"feincms_page": page1}, page1) + ) + self.assertTrue( + feincms_page_tags.page_is_active({"feincms_page": page2}, page1) + ) + self.assertFalse( + feincms_page_tags.page_is_active({"feincms_page": page1}, page2) + ) - p = PagePretender( - title='bla', - slug='bla', - url='/test-page/whatsup/') + p = PagePretender(title="bla", slug="bla", url="/test-page/whatsup/") - self.assertTrue(feincms_page_tags.page_is_active( - {}, p, path='/test-page/whatsup/test/')) - self.assertFalse(feincms_page_tags.page_is_active( - {}, p, path='/test-page/')) + self.assertTrue( + feincms_page_tags.page_is_active({}, p, path="/test-page/whatsup/test/") + ) + self.assertFalse(feincms_page_tags.page_is_active({}, p, path="/test-page/")) - self.assertTrue(feincms_page_tags.page_is_active( - {'feincms_page': page1}, p, path='/test-page/whatsup/test/')) - self.assertFalse(feincms_page_tags.page_is_active( - {'feincms_page': page2}, p, path='/test-page/')) + self.assertTrue( + feincms_page_tags.page_is_active( + {"feincms_page": page1}, p, path="/test-page/whatsup/test/" + ) + ) + self.assertFalse( + feincms_page_tags.page_is_active( + {"feincms_page": page2}, p, path="/test-page/" + ) + ) def test_41_templatecontent(self): page = self.create_page(active=True) page.templatecontent_set.create( - region='main', - ordering=10, - template='templatecontent_1.html', + region="main", ordering=10, template="templatecontent_1.html" ) # The empty form contains the template option. self.login() self.assertContains( - self.client.get( - reverse('admin:page_page_change', args=(page.id,)) - ), - '') + self.client.get(reverse("admin:page_page_change", args=(page.id,))), + '', + ) response = self.client.get(page.get_absolute_url()) - self.assertContains( - response, - 'TemplateContent_1', - ) - self.assertContains( - response, - '#42#', - ) + self.assertContains(response, "TemplateContent_1") + self.assertContains(response, "#42#") diff --git a/tests/testapp/tests/test_stuff.py b/tests/testapp/tests/test_stuff.py index b567daba6..71ac0bd4e 100644 --- a/tests/testapp/tests/test_stuff.py +++ b/tests/testapp/tests/test_stuff.py @@ -39,47 +39,40 @@ class ModelsTest(TestCase): def test_region(self): # Creation should not fail - r = Region('region', 'region title') + r = Region("region", "region title") t = Template( - 'base template', - 'base.html', - ( - ('region', 'region title'), - Region('region2', 'region2 title'), - ), + "base template", + "base.html", + (("region", "region title"), Region("region2", "region2 title")), ) # I'm not sure whether this test tests anything at all self.assertEqual(r.key, t.regions[0].key) - self.assertEqual(force_text(r), 'region title') + self.assertEqual(force_text(r), "region title") class UtilsTest(TestCase): def test_get_object(self): - self.assertRaises( - AttributeError, lambda: get_object('feincms.does_not_exist')) - self.assertRaises( - ImportError, lambda: get_object('feincms.does_not_exist.fn')) + self.assertRaises(AttributeError, lambda: get_object("feincms.does_not_exist")) + self.assertRaises(ImportError, lambda: get_object("feincms.does_not_exist.fn")) - self.assertEqual(get_object, get_object('feincms.utils.get_object')) + self.assertEqual(get_object, get_object("feincms.utils.get_object")) def test_shorten_string(self): string = shorten_string( - "Der Wolf und die Grossmutter assen im Wald zu mittag", - 15, ellipsis="_") - self.assertEqual(string, 'Der Wolf und_ag') + "Der Wolf und die Grossmutter assen im Wald zu mittag", 15, ellipsis="_" + ) + self.assertEqual(string, "Der Wolf und_ag") self.assertEqual(len(string), 15) string = shorten_string( - "Haenschen-Klein, ging allein, in den tiefen Wald hinein", - 15) - self.assertEqual(string, 'Haenschen \u2026 ein') + "Haenschen-Klein, ging allein, in den tiefen Wald hinein", 15 + ) + self.assertEqual(string, "Haenschen \u2026 ein") self.assertEqual(len(string), 15) - string = shorten_string( - 'Badgerbadgerbadgerbadgerbadger', - 10, ellipsis='-') - self.assertEqual(string, 'Badger-ger') + string = shorten_string("Badgerbadgerbadgerbadgerbadger", 10, ellipsis="-") + self.assertEqual(string, "Badger-ger") self.assertEqual(len(string), 10) @@ -88,7 +81,7 @@ class TimezoneTest(TestCase): def test_granular_now_dst_transition(self): # Should not raise an exception d = datetime(2016, 10, 30, 2, 10) - tz = pytz.timezone('Europe/Copenhagen') + tz = pytz.timezone("Europe/Copenhagen") granular_now(d, default_tz=tz) def test_granular_now_rounding(self): @@ -97,4 +90,5 @@ def test_granular_now_rounding(self): self.assertEqual(d.hour, g.hour) self.assertEqual(10, g.minute) + # ------------------------------------------------------------------------ diff --git a/tests/testapp/tests/utils.py b/tests/testapp/tests/utils.py index 31ca0639e..5d52b4f5f 100644 --- a/tests/testapp/tests/utils.py +++ b/tests/testapp/tests/utils.py @@ -8,6 +8,7 @@ class MockDatetime(datetime.datetime): @classmethod def now(cls): return datetime.datetime(2012, 6, 1) + return MockDatetime @@ -16,4 +17,5 @@ class MockDate(datetime.date): @classmethod def today(cls): return datetime.date(2012, 6, 1) + return MockDate diff --git a/tests/testapp/urls.py b/tests/testapp/urls.py index f1dbcc4a9..df1d6791a 100644 --- a/tests/testapp/urls.py +++ b/tests/testapp/urls.py @@ -11,27 +11,20 @@ from feincms.module.page.sitemap import PageSitemap -sitemaps = {'pages': PageSitemap} +sitemaps = {"pages": PageSitemap} admin.autodiscover() urlpatterns = [ - url(r'^admin/', admin.site.urls), - + url(r"^admin/", admin.site.urls), url( - r'^media/(?P.*)$', + r"^media/(?P.*)$", serve, - {'document_root': os.path.join(os.path.dirname(__file__), 'media/')}, + {"document_root": os.path.join(os.path.dirname(__file__), "media/")}, ), - - url( - r'^sitemap\.xml$', - sitemap, - {'sitemaps': sitemaps}, - ), - - url(r'', include('feincms.contrib.preview.urls')), - url(r'', include('feincms.urls')), + url(r"^sitemap\.xml$", sitemap, {"sitemaps": sitemaps}), + url(r"", include("feincms.contrib.preview.urls")), + url(r"", include("feincms.urls")), ] urlpatterns += staticfiles_urlpatterns() diff --git a/tox.ini b/tox.ini index 3f96dd417..cf21779ef 100644 --- a/tox.ini +++ b/tox.ini @@ -3,9 +3,11 @@ basepython = python3 [testenv:style] deps = + black flake8 changedir = {toxinidir} commands = + black feincms tests setup.py flake8 . skip_install = true From 8c94fb0a429d90b5557aa5649e351d3922d92c48 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Fri, 1 Feb 2019 18:28:05 +0100 Subject: [PATCH 018/185] Django@master dropped the staticfiles template tag library --- feincms/module/page/modeladmins.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/feincms/module/page/modeladmins.py b/feincms/module/page/modeladmins.py index 4a6847537..fe2050daf 100644 --- a/feincms/module/page/modeladmins.py +++ b/feincms/module/page/modeladmins.py @@ -9,7 +9,6 @@ from django.conf import settings as django_settings from django.core.exceptions import PermissionDenied from django.contrib.contenttypes.models import ContentType -from django.contrib.staticfiles.templatetags.staticfiles import static from django.contrib import admin from django.contrib import messages from django.http import HttpResponseRedirect @@ -21,6 +20,12 @@ except ImportError: from django.core.urlresolvers import reverse +try: + from django.contrib.staticfiles.templatetags.staticfiles import static +except ImportError: + # Newer Django versions. + from django.templatetags.static import static + from feincms import ensure_completely_loaded from feincms import settings from feincms.admin import item_editor, tree_editor From 54a4a2eb32763c8ed2b9ddb66cb92195564edf86 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Fri, 1 Feb 2019 18:31:53 +0100 Subject: [PATCH 019/185] FeinCMS v1.16.0 --- CHANGELOG.rst | 8 +++++++- feincms/__init__.py | 2 +- setup.py | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5a54bc50d..11a949cb4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,7 +6,12 @@ Change log `Next version`_ ~~~~~~~~~~~~~~~ +`v1.16.0`_ (2019-02-01) +~~~~~~~~~~~~~~~~~~~~~~~ + - Reformatted everything using black. +- Added a fallback import for the ``staticfiles`` template tag library + which will be gone in Django 3.0. `v1.15.0`_ (2018-12-21) @@ -59,4 +64,5 @@ Change log .. _v1.14.0: https://github.com/feincms/feincms/compare/v1.13.0...v1.14.0 .. _v1.15.0: https://github.com/feincms/feincms/compare/v1.14.0...v1.15.0 -.. _Next version: https://github.com/feincms/feincms/compare/v1.15.0...master +.. _v1.16.0: https://github.com/feincms/feincms/compare/v1.15.0...v1.16.0 +.. _Next version: https://github.com/feincms/feincms/compare/v1.16.0...master diff --git a/feincms/__init__.py b/feincms/__init__.py index 308b8f1cd..68ac06ad5 100644 --- a/feincms/__init__.py +++ b/feincms/__init__.py @@ -1,6 +1,6 @@ from __future__ import absolute_import, unicode_literals -VERSION = (1, 15, 0) +VERSION = (1, 16, 0) __version__ = ".".join(map(str, VERSION)) diff --git a/setup.py b/setup.py index a9fdce423..f969f4cd6 100755 --- a/setup.py +++ b/setup.py @@ -49,6 +49,7 @@ def read(filename): "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development", "Topic :: Software Development :: Libraries :: Application Frameworks", From 17de1d0371bd491954bd26c4ddacf555467353e2 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Fri, 1 Feb 2019 18:42:45 +0100 Subject: [PATCH 020/185] Various compatibility fixes --- feincms/admin/tree_editor.py | 7 ++++++- feincms/contrib/fields.py | 2 +- feincms/module/medialibrary/models.py | 2 +- feincms/templates/admin/feincms/_regions_js.html | 2 +- feincms/templates/admin/feincms/item_editor.html | 2 +- feincms/templates/admin/feincms/load-jquery.include | 2 +- feincms/templates/admin/feincms/tree_editor.html | 2 +- tests/testapp/settings.py | 2 +- 8 files changed, 13 insertions(+), 8 deletions(-) diff --git a/feincms/admin/tree_editor.py b/feincms/admin/tree_editor.py index 2eecb1484..9e718b714 100644 --- a/feincms/admin/tree_editor.py +++ b/feincms/admin/tree_editor.py @@ -11,7 +11,6 @@ from django.contrib.admin.views import main from django.contrib.admin.actions import delete_selected from django.contrib.auth import get_permission_codename -from django.contrib.staticfiles.templatetags.staticfiles import static from django.db.models import Q from django.http import ( HttpResponse, @@ -31,6 +30,12 @@ from feincms import settings from feincms.extensions import ExtensionModelAdmin +try: + # Django<3 + from django.contrib.staticfiles.templatetags.staticfiles import static +except ImportError: + from django.templatetags.static import static + logger = logging.getLogger(__name__) diff --git a/feincms/contrib/fields.py b/feincms/contrib/fields.py index bf8b8ed3b..89f0b2639 100644 --- a/feincms/contrib/fields.py +++ b/feincms/contrib/fields.py @@ -69,7 +69,7 @@ def to_python(self, value): assert value is None return {} - def from_db_value(self, value, expression, connection, context): + def from_db_value(self, value, expression, connection, context=None): return self.to_python(value) def get_prep_value(self, value): diff --git a/feincms/module/medialibrary/models.py b/feincms/module/medialibrary/models.py index 424abd327..1a2d2ba08 100644 --- a/feincms/module/medialibrary/models.py +++ b/feincms/module/medialibrary/models.py @@ -143,7 +143,7 @@ def register_filetypes(cls, *types): cls.filetypes[0:0] = types choices = [t[0:2] for t in cls.filetypes] cls.filetypes_dict = dict(choices) - cls._meta.get_field("type").choices[:] = choices + cls._meta.get_field("type").choices = choices def __init__(self, *args, **kwargs): super(MediaFileBase, self).__init__(*args, **kwargs) diff --git a/feincms/templates/admin/feincms/_regions_js.html b/feincms/templates/admin/feincms/_regions_js.html index adebda187..2d39f7eb3 100644 --- a/feincms/templates/admin/feincms/_regions_js.html +++ b/feincms/templates/admin/feincms/_regions_js.html @@ -1,4 +1,4 @@ -{% load staticfiles %} +{% load static %} {% endblock %} From 7687efc7b2362b00a63dbda8c40cbfa5b9875348 Mon Sep 17 00:00:00 2001 From: Arkadiy Korotaev Date: Tue, 16 Mar 2021 20:48:40 +0100 Subject: [PATCH 062/185] fix(JS): Use default form.action for django3.1 (where this attribute is absent) --- feincms/static/feincms/item_editor.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/feincms/static/feincms/item_editor.js b/feincms/static/feincms/item_editor.js index a4d25f93b..2a2c4de7f 100644 --- a/feincms/static/feincms/item_editor.js +++ b/feincms/static/feincms/item_editor.js @@ -496,7 +496,8 @@ if (!Array.prototype.indexOf) { $('form').submit(function(){ give_ordering_to_content_types(); var form = $(this); - form.attr('action', form.attr('action')+window.location.hash); + var action = form.attr("action") || ""; + form.attr('action', action + window.location.hash); return true; }); From 576b252bff6fdc808be9a0eb70768c72febf42ac Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Mon, 22 Mar 2021 20:19:16 +0100 Subject: [PATCH 063/185] Format the code to fix line length errors --- feincms/content/filer/models.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/feincms/content/filer/models.py b/feincms/content/filer/models.py index e2eaf1ea4..2feba4578 100644 --- a/feincms/content/filer/models.py +++ b/feincms/content/filer/models.py @@ -50,7 +50,9 @@ def render(self, **kwargs): ) class FilerFileContent(ContentWithFilerFile): - mediafile = FilerFileField(verbose_name=_("file"), related_name="+", on_delete=models.CASCADE) + mediafile = FilerFileField( + verbose_name=_("file"), related_name="+", on_delete=models.CASCADE + ) file_type = "file" type = "download" @@ -86,7 +88,9 @@ class FilerImageContent(ContentWithFilerFile): must_always_publish_copyright, date_taken, file, id, is_public, url """ - mediafile = FilerImageField(verbose_name=_("image"), related_name="+", on_delete=models.CASCADE) + mediafile = FilerImageField( + verbose_name=_("image"), related_name="+", on_delete=models.CASCADE + ) caption = models.CharField(_("caption"), max_length=1000, blank=True) url = models.CharField(_("URL"), max_length=1000, blank=True) From 31712d8e54a9af46b655ae51701771bc8dcfb5c1 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Mon, 22 Mar 2021 20:19:50 +0100 Subject: [PATCH 064/185] No need to run flake8 checks inside each job --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index f09bcd3ad..4e3cab945 100644 --- a/.travis.yml +++ b/.travis.yml @@ -59,6 +59,6 @@ matrix: - env: REQ="https://github.com/django/django/archive/master.zip django-mptt" install: - pip install -U pip wheel setuptools - - pip install $REQ Pillow flake8 pytz six + - pip install $REQ Pillow pytz six - python setup.py install -script: "cd tests && ./manage.py test testapp && cd .. && flake8 ." +script: "cd tests && ./manage.py test testapp" From 8f7cb92c4118e9bead092b35200c338fb2926f3c Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Mon, 22 Mar 2021 20:56:46 +0100 Subject: [PATCH 065/185] Reformat everything, add GitHub action workflows for testing --- .editorconfig | 12 + .eslintrc.js | 19 + .github/workflows/tests.yml | 50 + .gitignore | 1 + .prettierignore | 3 + CHANGELOG.rst | 6 + README.rst | 6 +- docs/conf.py | 99 +- feincms/__init__.py | 7 +- feincms/_internal.py | 1 + feincms/admin/__init__.py | 3 +- feincms/admin/filters.py | 2 +- feincms/admin/tree_editor.py | 10 +- feincms/content/application/models.py | 9 +- feincms/content/contactform/__init__.py | 1 + feincms/content/filer/models.py | 3 +- feincms/content/medialibrary/models.py | 1 + feincms/contrib/fields.py | 7 +- feincms/contrib/tagging.py | 5 +- feincms/default_settings.py | 1 + feincms/extensions/__init__.py | 3 +- feincms/extensions/base.py | 4 +- feincms/extensions/changedate.py | 2 +- feincms/extensions/datepublisher.py | 2 +- feincms/extensions/translations.py | 2 +- .../commands/medialibrary_to_filer.py | 5 +- feincms/management/commands/rebuild_mptt.py | 1 + feincms/models.py | 8 +- feincms/module/extensions/changedate.py | 1 + feincms/module/extensions/ct_tracker.py | 1 + feincms/module/extensions/datepublisher.py | 1 + feincms/module/extensions/featured.py | 1 + feincms/module/extensions/seo.py | 1 + feincms/module/extensions/translations.py | 1 + feincms/module/medialibrary/__init__.py | 1 + feincms/module/medialibrary/admin.py | 3 +- feincms/module/medialibrary/fields.py | 5 +- feincms/module/medialibrary/forms.py | 2 +- feincms/module/medialibrary/modeladmins.py | 8 +- feincms/module/medialibrary/models.py | 4 +- feincms/module/medialibrary/zip.py | 2 +- feincms/module/page/admin.py | 4 +- feincms/module/page/extensions/navigation.py | 6 +- feincms/module/page/forms.py | 3 +- feincms/module/page/modeladmins.py | 9 +- feincms/module/page/models.py | 4 +- feincms/module/page/sitemap.py | 2 +- feincms/signals.py | 1 + feincms/static/feincms/item_editor.css | 304 +++-- feincms/static/feincms/item_editor.js | 1160 +++++++++-------- feincms/static/feincms/tree_editor.css | 33 +- feincms/static/feincms/tree_editor.js | 765 ++++++----- .../templatetags/applicationcontent_tags.py | 1 + feincms/templatetags/feincms_thumbnail.py | 10 +- feincms/translations.py | 1 - feincms/urls.py | 12 +- feincms/utils/__init__.py | 3 +- feincms/views/decorators.py | 1 + package.json | 16 + setup.cfg | 63 +- setup.py | 60 +- tests/manage.py | 1 + tests/testapp/applicationcontent_urls.py | 28 +- .../migrate/medialibrary/0001_initial.py | 3 +- tests/testapp/migrate/page/0001_initial.py | 3 +- tests/testapp/migrations/0001_initial.py | 3 +- tests/testapp/models.py | 7 +- tests/testapp/settings.py | 4 +- tests/testapp/tests/__init__.py | 3 +- tests/testapp/tests/test_cms.py | 3 +- tests/testapp/tests/test_extensions.py | 10 +- tests/testapp/tests/test_page.py | 6 +- tests/testapp/tests/test_stuff.py | 5 +- tests/testapp/urls.py | 16 +- tox.ini | 62 +- yarn.lock | 762 +++++++++++ 76 files changed, 2385 insertions(+), 1293 deletions(-) create mode 100644 .editorconfig create mode 100644 .eslintrc.js create mode 100644 .github/workflows/tests.yml create mode 100644 .prettierignore create mode 100644 package.json create mode 100644 yarn.lock diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..fd4bd13c7 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +[*.py] +indent_size = 4 diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..8e3d952be --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,19 @@ +module.exports = { + env: { + browser: true, + es6: true, + }, + extends: "eslint:recommended", + parserOptions: { + ecmaVersion: 2018, + }, + plugins: ["prettier"], + rules: { + indent: ["error", 2], + "linebreak-style": ["error", "unix"], + semi: ["error", "always"], + "no-unused-vars": ["error", { argsIgnorePattern: "^_" }], + "prettier/prettier": "error", + quotes: 0, + }, +}; diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 000000000..4ef9a6c36 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,50 @@ +name: Tests + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + tests: + name: Python ${{ matrix.python-version }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: + - 2.7 + - 3.6 + - 3.7 + - 3.8 + - 3.9 + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip wheel setuptools tox + - name: Run tox targets for ${{ matrix.python-version }} + run: | + ENV_PREFIX=$(tr -C -d "0-9" <<< "${{ matrix.python-version }}") + TOXENV=$(tox --listenvs | grep "^py$ENV_PREFIX" | tr '\n' ',') python -m tox + + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Install dependencies + run: | + python -m pip install --upgrade pip tox + - name: Run lint + run: tox -e style diff --git a/.gitignore b/.gitignore index 5dd000736..4cdbee3c0 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ venv .coverage htmlcov test.zip +node_modules diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..1c0ca47e9 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +feincms/static/feincms/jquery-1.11.3.min.js +feincms/static/feincms/jquery-ui-1.10.3.custom.min.js +feincms/static/feincms/js.cookie.js diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b327491c3..4f12ac9b8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,12 @@ Change log `Next version`_ ~~~~~~~~~~~~~~~ +- Renamed the main branch to main. +- Switched to a declarative setup. +- Switched to GitHub actions. +- Sorted imports. +- Reformated the JavaScript code using prettier. + `v1.19.0`_ (2021-03-04) ~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/README.rst b/README.rst index a3fb80618..cf9cf1901 100644 --- a/README.rst +++ b/README.rst @@ -2,10 +2,8 @@ FeinCMS - An extensible Django-based CMS ======================================== -.. image:: https://travis-ci.org/feincms/feincms.svg?branch=next - :target: https://travis-ci.org/feincms/feincms -.. image:: https://travis-ci.org/feincms/feincms.svg?branch=master - :target: https://travis-ci.org/feincms/feincms +.. image:: https://github.com/feincms/feincms/workflows/Tests/badge.svg + :target: https://github.com/feincms/feincms When was the last time, that a pre-built software package you wanted to use got many things right, but in the end, you still needed to modify diff --git a/docs/conf.py b/docs/conf.py index 488cb87be..ff00bfac2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -14,6 +14,7 @@ import os import sys + # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. @@ -25,20 +26,20 @@ extensions = [] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. -#source_encoding = 'utf-8' +# source_encoding = 'utf-8' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'FeinCMS' -copyright = u'2009-2010, Feinheit GmbH and contributors' +project = u"FeinCMS" +copyright = u"2009-2010, Feinheit GmbH and contributors" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -48,46 +49,47 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) import feincms -version = '.'.join(map(str, feincms.VERSION)) + +version = ".".join(map(str, feincms.VERSION)) # The full version, including alpha/beta/rc tags. release = feincms.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. -#unused_docs = [] +# unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. -exclude_trees = ['_build'] +exclude_trees = ["_build"] # The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- @@ -95,104 +97,109 @@ # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. # html_theme_path = ['_theme'] -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_use_modindex = True +# html_use_modindex = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = '' +# html_file_suffix = '' # Output file base name for HTML help builder. -htmlhelp_basename = 'FeinCMSdoc' +htmlhelp_basename = "FeinCMSdoc" # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). -latex_paper_size = 'a4' +latex_paper_size = "a4" # The font size ('10pt', '11pt' or '12pt'). -latex_font_size = '10pt' +latex_font_size = "10pt" # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). -latex_documents = [( - 'index', 'FeinCMS.tex', u'FeinCMS Documentation', - u'Feinheit GmbH and contributors', 'manual'), +latex_documents = [ + ( + "index", + "FeinCMS.tex", + u"FeinCMS Documentation", + u"Feinheit GmbH and contributors", + "manual", + ), ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # Additional stuff for the LaTeX preamble. -#latex_preamble = '' +# latex_preamble = '' # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_use_modindex = True +# latex_use_modindex = True diff --git a/feincms/__init__.py b/feincms/__init__.py index 392b5aed7..1ec248461 100644 --- a/feincms/__init__.py +++ b/feincms/__init__.py @@ -1,14 +1,16 @@ from __future__ import absolute_import, unicode_literals + VERSION = (1, 19, 0) __version__ = ".".join(map(str, VERSION)) class LazySettings(object): def _load_settings(self): - from feincms import default_settings from django.conf import settings as django_settings + from feincms import default_settings + for key in dir(default_settings): if not key.startswith("FEINCMS_"): continue @@ -59,9 +61,10 @@ def ensure_completely_loaded(force=False): # Here we flush the caches rather than actually _filling them so # that relations defined after all content types registrations # don't miss out. - import django from distutils.version import LooseVersion + import django + if LooseVersion(django.get_version()) < LooseVersion("1.8"): for model in apps.get_models(): diff --git a/feincms/_internal.py b/feincms/_internal.py index 44a669748..f0034dfee 100644 --- a/feincms/_internal.py +++ b/feincms/_internal.py @@ -7,6 +7,7 @@ from __future__ import absolute_import, unicode_literals from distutils.version import LooseVersion + from django import get_version from django.template.loader import render_to_string diff --git a/feincms/admin/__init__.py b/feincms/admin/__init__.py index 9008a9982..9bd00dfcb 100644 --- a/feincms/admin/__init__.py +++ b/feincms/admin/__init__.py @@ -1,7 +1,8 @@ from __future__ import absolute_import from django.contrib.admin.filters import FieldListFilter -from .filters import ParentFieldListFilter, CategoryFieldListFilter + +from .filters import CategoryFieldListFilter, ParentFieldListFilter FieldListFilter.register( diff --git a/feincms/admin/filters.py b/feincms/admin/filters.py index 2d8bcbf0c..bb269dd05 100644 --- a/feincms/admin/filters.py +++ b/feincms/admin/filters.py @@ -6,12 +6,12 @@ from __future__ import absolute_import, unicode_literals +from django import VERSION as DJANGO_VERSION from django.contrib.admin.filters import ChoicesFieldListFilter from django.db.models import Count from django.utils.encoding import smart_text from django.utils.safestring import mark_safe from django.utils.translation import gettext as _ -from django import VERSION as DJANGO_VERSION from feincms.utils import shorten_string diff --git a/feincms/admin/tree_editor.py b/feincms/admin/tree_editor.py index 6c22aa627..01ba0f2c6 100644 --- a/feincms/admin/tree_editor.py +++ b/feincms/admin/tree_editor.py @@ -4,12 +4,12 @@ from __future__ import absolute_import, unicode_literals -from functools import reduce import json import logging +from functools import reduce -from django.contrib.admin.views import main from django.contrib.admin.actions import delete_selected +from django.contrib.admin.views import main from django.contrib.auth import get_permission_codename from django.db.models import Q from django.http import ( @@ -19,17 +19,17 @@ HttpResponseNotFound, HttpResponseServerError, ) +from django.utils.encoding import force_text from django.utils.html import escape from django.utils.safestring import mark_safe -from django.utils.translation import gettext_lazy as _, gettext -from django.utils.encoding import force_text - +from django.utils.translation import gettext, gettext_lazy as _ from mptt.exceptions import InvalidMove from mptt.forms import MPTTAdminForm from feincms import settings from feincms.extensions import ExtensionModelAdmin + try: # Django<3 from django.contrib.staticfiles.templatetags.staticfiles import static diff --git a/feincms/content/application/models.py b/feincms/content/application/models.py index 9a5568b55..6eb4735db 100644 --- a/feincms/content/application/models.py +++ b/feincms/content/application/models.py @@ -1,10 +1,10 @@ from __future__ import absolute_import +import warnings from collections import OrderedDict from email.utils import parsedate from functools import partial, wraps from time import mktime -import warnings from django.conf import settings from django.core.cache import cache @@ -16,14 +16,15 @@ from django.utils.safestring import mark_safe from django.utils.translation import get_language, gettext_lazy as _ + try: from django.urls import ( NoReverseMatch, - reverse, - get_script_prefix, - set_script_prefix, Resolver404, + get_script_prefix, resolve, + reverse, + set_script_prefix, ) except ImportError: from django.core.urlresolvers import ( diff --git a/feincms/content/contactform/__init__.py b/feincms/content/contactform/__init__.py index c25396137..5d3fb54d8 100644 --- a/feincms/content/contactform/__init__.py +++ b/feincms/content/contactform/__init__.py @@ -3,6 +3,7 @@ import warnings + warnings.warn( "The contactform content has been deprecated. Use form-designer instead.", DeprecationWarning, diff --git a/feincms/content/filer/models.py b/feincms/content/filer/models.py index 2feba4578..5213ead70 100644 --- a/feincms/content/filer/models.py +++ b/feincms/content/filer/models.py @@ -5,8 +5,9 @@ from django.db import models from django.utils.translation import gettext_lazy as _ -from feincms.admin.item_editor import FeinCMSInline from feincms._internal import ct_render_to_string +from feincms.admin.item_editor import FeinCMSInline + try: from filer.fields.file import FilerFileField diff --git a/feincms/content/medialibrary/models.py b/feincms/content/medialibrary/models.py index bc7d2f474..0e0a10ed3 100644 --- a/feincms/content/medialibrary/models.py +++ b/feincms/content/medialibrary/models.py @@ -5,6 +5,7 @@ from feincms.module.medialibrary.contents import MediaFileContent + warnings.warn( "Import MediaFileContent from feincms.module.medialibrary.contents.", DeprecationWarning, diff --git a/feincms/contrib/fields.py b/feincms/contrib/fields.py index 6d359e5dd..4a03fe972 100644 --- a/feincms/contrib/fields.py +++ b/feincms/contrib/fields.py @@ -2,13 +2,12 @@ import json import logging -import six from distutils.version import LooseVersion -from django import get_version -from django import forms -from django.db import models +import six +from django import forms, get_version from django.core.serializers.json import DjangoJSONEncoder +from django.db import models class JSONFormField(forms.fields.CharField): diff --git a/feincms/contrib/tagging.py b/feincms/contrib/tagging.py index f5652ea03..2b496a678 100644 --- a/feincms/contrib/tagging.py +++ b/feincms/contrib/tagging.py @@ -11,16 +11,15 @@ from __future__ import absolute_import, unicode_literals import six - -from django import forms, VERSION +from django import VERSION, forms from django.contrib.admin.widgets import FilteredSelectMultiple from django.db.models.signals import pre_save from django.utils.translation import gettext_lazy as _ - from tagging.fields import TagField from tagging.models import Tag from tagging.utils import parse_tag_input + try: from tagging.registry import AlreadyRegistered except ImportError: diff --git a/feincms/default_settings.py b/feincms/default_settings.py index a0d20be5c..93cbc88be 100644 --- a/feincms/default_settings.py +++ b/feincms/default_settings.py @@ -12,6 +12,7 @@ from django.conf import settings + # e.g. 'uploads' if you would prefer /uploads/imagecontent/test.jpg # to /imagecontent/test.jpg. FEINCMS_UPLOAD_PREFIX = getattr(settings, "FEINCMS_UPLOAD_PREFIX", "") diff --git a/feincms/extensions/__init__.py b/feincms/extensions/__init__.py index ad37b177f..74a10a8a6 100644 --- a/feincms/extensions/__init__.py +++ b/feincms/extensions/__init__.py @@ -1,12 +1,13 @@ from __future__ import absolute_import from .base import ( - ExtensionsMixin, Extension, ExtensionModelAdmin, + ExtensionsMixin, prefetch_modeladmin_get_queryset, ) + __all__ = ( "ExtensionsMixin", "Extension", diff --git a/feincms/extensions/base.py b/feincms/extensions/base.py index e2701090a..375bf045b 100644 --- a/feincms/extensions/base.py +++ b/feincms/extensions/base.py @@ -4,10 +4,10 @@ from __future__ import absolute_import, unicode_literals -from functools import wraps -import six import inspect +from functools import wraps +import six from django.contrib import admin from django.core.exceptions import ImproperlyConfigured diff --git a/feincms/extensions/changedate.py b/feincms/extensions/changedate.py index 880641fba..cf07cbb58 100644 --- a/feincms/extensions/changedate.py +++ b/feincms/extensions/changedate.py @@ -7,7 +7,7 @@ from __future__ import absolute_import, unicode_literals -from email.utils import parsedate_tz, mktime_tz +from email.utils import mktime_tz, parsedate_tz from time import mktime from django.db import models diff --git a/feincms/extensions/datepublisher.py b/feincms/extensions/datepublisher.py index 695d09585..14f5dda93 100644 --- a/feincms/extensions/datepublisher.py +++ b/feincms/extensions/datepublisher.py @@ -11,7 +11,6 @@ from __future__ import absolute_import, unicode_literals from datetime import datetime -from pytz.exceptions import AmbiguousTimeError from django.db import models from django.db.models import Q @@ -19,6 +18,7 @@ from django.utils.cache import patch_response_headers from django.utils.html import mark_safe from django.utils.translation import gettext_lazy as _ +from pytz.exceptions import AmbiguousTimeError from feincms import extensions diff --git a/feincms/extensions/translations.py b/feincms/extensions/translations.py index 3893808a2..662065017 100644 --- a/feincms/extensions/translations.py +++ b/feincms/extensions/translations.py @@ -28,8 +28,8 @@ from django.utils.translation import gettext_lazy as _ from feincms import extensions, settings -from feincms.translations import is_primary_language from feincms._internal import monkeypatch_method, monkeypatch_property +from feincms.translations import is_primary_language # ------------------------------------------------------------------------ diff --git a/feincms/management/commands/medialibrary_to_filer.py b/feincms/management/commands/medialibrary_to_filer.py index 2ac3fefec..cf1f08688 100644 --- a/feincms/management/commands/medialibrary_to_filer.py +++ b/feincms/management/commands/medialibrary_to_filer.py @@ -1,16 +1,15 @@ from __future__ import absolute_import, unicode_literals +from django.contrib.auth.models import User from django.core.files import File as DjangoFile from django.core.management.base import NoArgsCommand -from django.contrib.auth.models import User +from filer.models import File, Image from feincms.contents import FilerFileContent, FilerImageContent from feincms.module.medialibrary.contents import MediaFileContent from feincms.module.medialibrary.models import MediaFile from feincms.module.page.models import Page -from filer.models import File, Image - PageMediaFileContent = Page.content_type_for(MediaFileContent) PageFilerFileContent = Page.content_type_for(FilerFileContent) diff --git a/feincms/management/commands/rebuild_mptt.py b/feincms/management/commands/rebuild_mptt.py index f290daa50..6a77ab3c1 100644 --- a/feincms/management/commands/rebuild_mptt.py +++ b/feincms/management/commands/rebuild_mptt.py @@ -10,6 +10,7 @@ from __future__ import absolute_import, unicode_literals + try: from django.core.management.base import NoArgsCommand as BaseCommand except ImportError: diff --git a/feincms/models.py b/feincms/models.py index 461c31df1..5b93fd4e6 100644 --- a/feincms/models.py +++ b/feincms/models.py @@ -7,13 +7,13 @@ from __future__ import absolute_import, unicode_literals -from collections import OrderedDict -from functools import reduce -import six -import sys import operator +import sys import warnings +from collections import OrderedDict +from functools import reduce +import six from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ImproperlyConfigured from django.db import connections, models diff --git a/feincms/module/extensions/changedate.py b/feincms/module/extensions/changedate.py index fdfd93475..865cdb062 100644 --- a/feincms/module/extensions/changedate.py +++ b/feincms/module/extensions/changedate.py @@ -5,6 +5,7 @@ from feincms.extensions.changedate import * + warnings.warn( "Import %(name)s from feincms.extensions.%(name)s" % {"name": __name__.split(".")[-1]}, diff --git a/feincms/module/extensions/ct_tracker.py b/feincms/module/extensions/ct_tracker.py index f810b346e..6ea11bd32 100644 --- a/feincms/module/extensions/ct_tracker.py +++ b/feincms/module/extensions/ct_tracker.py @@ -5,6 +5,7 @@ from feincms.extensions.ct_tracker import * + warnings.warn( "Import %(name)s from feincms.extensions.%(name)s" % {"name": __name__.split(".")[-1]}, diff --git a/feincms/module/extensions/datepublisher.py b/feincms/module/extensions/datepublisher.py index 313568d74..b95b01633 100644 --- a/feincms/module/extensions/datepublisher.py +++ b/feincms/module/extensions/datepublisher.py @@ -5,6 +5,7 @@ from feincms.extensions.datepublisher import * + warnings.warn( "Import %(name)s from feincms.extensions.%(name)s" % {"name": __name__.split(".")[-1]}, diff --git a/feincms/module/extensions/featured.py b/feincms/module/extensions/featured.py index 7713d026b..5b947c84f 100644 --- a/feincms/module/extensions/featured.py +++ b/feincms/module/extensions/featured.py @@ -5,6 +5,7 @@ from feincms.extensions.featured import * + warnings.warn( "Import %(name)s from feincms.extensions.%(name)s" % {"name": __name__.split(".")[-1]}, diff --git a/feincms/module/extensions/seo.py b/feincms/module/extensions/seo.py index aa386950f..d81fe2b69 100644 --- a/feincms/module/extensions/seo.py +++ b/feincms/module/extensions/seo.py @@ -5,6 +5,7 @@ from feincms.extensions.seo import * + warnings.warn( "Import %(name)s from feincms.extensions.%(name)s" % {"name": __name__.split(".")[-1]}, diff --git a/feincms/module/extensions/translations.py b/feincms/module/extensions/translations.py index c89c8d538..68e27512a 100644 --- a/feincms/module/extensions/translations.py +++ b/feincms/module/extensions/translations.py @@ -5,6 +5,7 @@ from feincms.extensions.translations import * + warnings.warn( "Import %(name)s from feincms.extensions.%(name)s" % {"name": __name__.split(".")[-1]}, diff --git a/feincms/module/medialibrary/__init__.py b/feincms/module/medialibrary/__init__.py index 98a59597d..b2d495235 100644 --- a/feincms/module/medialibrary/__init__.py +++ b/feincms/module/medialibrary/__init__.py @@ -6,6 +6,7 @@ import logging + # ------------------------------------------------------------------------ logger = logging.getLogger("feincms.medialibrary") diff --git a/feincms/module/medialibrary/admin.py b/feincms/module/medialibrary/admin.py index 922450af9..91103eb38 100644 --- a/feincms/module/medialibrary/admin.py +++ b/feincms/module/medialibrary/admin.py @@ -6,8 +6,9 @@ from django.contrib import admin -from .models import Category, MediaFile from .modeladmins import CategoryAdmin, MediaFileAdmin +from .models import Category, MediaFile + # ------------------------------------------------------------------------ admin.site.register(Category, CategoryAdmin) diff --git a/feincms/module/medialibrary/fields.py b/feincms/module/medialibrary/fields.py index 9682bd6b9..3d7417efa 100644 --- a/feincms/module/medialibrary/fields.py +++ b/feincms/module/medialibrary/fields.py @@ -5,9 +5,7 @@ from __future__ import absolute_import, unicode_literals import six - -from django.contrib.admin.widgets import AdminFileWidget -from django.contrib.admin.widgets import ForeignKeyRawIdWidget +from django.contrib.admin.widgets import AdminFileWidget, ForeignKeyRawIdWidget from django.db import models from django.utils.html import escape from django.utils.safestring import mark_safe @@ -15,6 +13,7 @@ from feincms.admin.item_editor import FeinCMSInline from feincms.utils import shorten_string + from .models import MediaFile from .thumbnail import admin_thumbnail diff --git a/feincms/module/medialibrary/forms.py b/feincms/module/medialibrary/forms.py index 38df3ab8f..e48f73539 100644 --- a/feincms/module/medialibrary/forms.py +++ b/feincms/module/medialibrary/forms.py @@ -12,8 +12,8 @@ from feincms import settings from . import logger -from .models import Category, MediaFile from .fields import AdminFileWithPreviewWidget +from .models import Category, MediaFile # ------------------------------------------------------------------------ diff --git a/feincms/module/medialibrary/modeladmins.py b/feincms/module/medialibrary/modeladmins.py index adc217430..7cc72e36c 100644 --- a/feincms/module/medialibrary/modeladmins.py +++ b/feincms/module/medialibrary/modeladmins.py @@ -8,8 +8,7 @@ from django import forms from django.conf import settings as django_settings -from django.contrib import admin -from django.contrib import messages +from django.contrib import admin, messages from django.contrib.auth.decorators import permission_required from django.contrib.sites.shortcuts import get_current_site from django.core.files.images import get_image_dimensions @@ -17,9 +16,10 @@ from django.shortcuts import render from django.template.defaultfilters import filesizeformat from django.utils.safestring import mark_safe -from django.utils.translation import ungettext, gettext_lazy as _ +from django.utils.translation import gettext_lazy as _, ungettext from django.views.decorators.csrf import csrf_protect + try: from django.urls import reverse except ImportError: @@ -29,8 +29,8 @@ from feincms.translations import admin_translationinline, lookup_translations from feincms.utils import shorten_string -from .models import Category, MediaFileTranslation from .forms import MediaCategoryAdminForm, MediaFileAdminForm +from .models import Category, MediaFileTranslation from .thumbnail import admin_thumbnail from .zip import import_zipfile diff --git a/feincms/module/medialibrary/models.py b/feincms/module/medialibrary/models.py index 738beef14..beca82997 100644 --- a/feincms/module/medialibrary/models.py +++ b/feincms/module/medialibrary/models.py @@ -6,9 +6,9 @@ import os import re -import six import django +import six from django.db import models from django.db.models.signals import post_delete from django.dispatch.dispatcher import receiver @@ -19,9 +19,9 @@ from feincms import settings from feincms.models import ExtensionsMixin from feincms.translations import ( + TranslatedObjectManager, TranslatedObjectMixin, Translation, - TranslatedObjectManager, ) from . import logger diff --git a/feincms/module/medialibrary/zip.py b/feincms/module/medialibrary/zip.py index 2e91376f6..3f16b7641 100644 --- a/feincms/module/medialibrary/zip.py +++ b/feincms/module/medialibrary/zip.py @@ -10,9 +10,9 @@ from __future__ import absolute_import, unicode_literals import json -import zipfile import os import time +import zipfile from django.conf import settings as django_settings from django.core.files.base import ContentFile diff --git a/feincms/module/page/admin.py b/feincms/module/page/admin.py index e4c61602e..8a396aa88 100644 --- a/feincms/module/page/admin.py +++ b/feincms/module/page/admin.py @@ -8,8 +8,10 @@ from django.core.exceptions import ImproperlyConfigured from feincms import ensure_completely_loaded, settings -from .models import Page + from .modeladmins import PageAdmin +from .models import Page + try: from django.core.exceptions import FieldDoesNotExist diff --git a/feincms/module/page/extensions/navigation.py b/feincms/module/page/extensions/navigation.py index 9f03311a0..85bab288e 100644 --- a/feincms/module/page/extensions/navigation.py +++ b/feincms/module/page/extensions/navigation.py @@ -9,17 +9,17 @@ from __future__ import absolute_import, unicode_literals -from collections import OrderedDict -import six import types +from collections import OrderedDict +import six from django.db import models from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ from feincms import extensions -from feincms.utils import get_object, shorten_string from feincms._internal import monkeypatch_method +from feincms.utils import get_object, shorten_string class TypeRegistryMetaClass(type): diff --git a/feincms/module/page/forms.py b/feincms/module/page/forms.py index 114a97378..c86c4841b 100644 --- a/feincms/module/page/forms.py +++ b/feincms/module/page/forms.py @@ -11,11 +11,10 @@ from django.forms.models import model_to_dict from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ +from mptt.forms import MPTTAdminForm from feincms import ensure_completely_loaded -from mptt.forms import MPTTAdminForm - class RedirectToWidget(ForeignKeyRawIdWidget): def label_for_value(self, value): diff --git a/feincms/module/page/modeladmins.py b/feincms/module/page/modeladmins.py index 5f0b94fbe..070c2a735 100644 --- a/feincms/module/page/modeladmins.py +++ b/feincms/module/page/modeladmins.py @@ -8,13 +8,13 @@ from threading import local from django.conf import settings as django_settings -from django.core.exceptions import PermissionDenied +from django.contrib import admin, messages from django.contrib.contenttypes.models import ContentType -from django.contrib import admin -from django.contrib import messages +from django.core.exceptions import PermissionDenied from django.http import HttpResponseRedirect from django.utils.translation import gettext_lazy as _ + try: from django.urls import reverse except ImportError: @@ -26,8 +26,7 @@ # Newer Django versions. from django.templatetags.static import static -from feincms import ensure_completely_loaded -from feincms import settings +from feincms import ensure_completely_loaded, settings from feincms.admin import item_editor, tree_editor # ------------------------------------------------------------------------ diff --git a/feincms/module/page/models.py b/feincms/module/page/models.py index 281d7364b..e642ce7f2 100644 --- a/feincms/module/page/models.py +++ b/feincms/module/page/models.py @@ -5,13 +5,13 @@ from __future__ import absolute_import, unicode_literals import six - from django.core.exceptions import PermissionDenied from django.db import models from django.db.models import Q from django.http import Http404 from django.utils.translation import gettext_lazy as _ + try: from django.urls import reverse except ImportError: @@ -23,8 +23,8 @@ from feincms.models import create_base_model from feincms.module.mixins import ContentModelMixin from feincms.module.page import processors +from feincms.utils import get_model_instance, match_model_string, shorten_string from feincms.utils.managers import ActiveAwareContentManagerMixin -from feincms.utils import shorten_string, match_model_string, get_model_instance # ------------------------------------------------------------------------ diff --git a/feincms/module/page/sitemap.py b/feincms/module/page/sitemap.py index c5cbd5850..46667b7b5 100644 --- a/feincms/module/page/sitemap.py +++ b/feincms/module/page/sitemap.py @@ -5,8 +5,8 @@ from __future__ import absolute_import, unicode_literals from django.apps import apps -from django.db.models import Max from django.contrib.sitemaps import Sitemap +from django.db.models import Max from feincms import settings diff --git a/feincms/signals.py b/feincms/signals.py index 2d4fca135..0655b3ec4 100644 --- a/feincms/signals.py +++ b/feincms/signals.py @@ -11,6 +11,7 @@ from django.dispatch import Signal + # ------------------------------------------------------------------------ # This signal is sent when an item editor managed object is completely # saved, especially including all foreign or manytomany dependencies. diff --git a/feincms/static/feincms/item_editor.css b/feincms/static/feincms/item_editor.css index 69ba18640..3f80d1539 100644 --- a/feincms/static/feincms/item_editor.css +++ b/feincms/static/feincms/item_editor.css @@ -1,278 +1,312 @@ .navi_tab { - float:left; - padding: 8px 10px; - cursor:pointer; - margin-top:3px; - font-weight: bold; - font-size: 11px; - color: #666; - background: #f6f6f6; - border: 1px solid #eee; - text-transform: uppercase; + float: left; + padding: 8px 10px; + cursor: pointer; + margin-top: 3px; + font-weight: bold; + font-size: 11px; + color: #666; + background: #f6f6f6; + border: 1px solid #eee; + text-transform: uppercase; } .tab_active { - background: #79aec8; - color: white; - border-color: #79aec8; + background: #79aec8; + color: white; + border-color: #79aec8; } #feincmsmain { - clear:both; - padding: 10px 10px 10px 10px; - border: 1px solid #eee; - margin: 0 0 10px 0; + clear: both; + padding: 10px 10px 10px 10px; + border: 1px solid #eee; + margin: 0 0 10px 0; } .panel { - display:none; - position:relative; - padding-bottom: 39px; + display: none; + position: relative; + padding-bottom: 39px; } .order-item { - margin: 0 0 10px 0; - position:relative; - + margin: 0 0 10px 0; + position: relative; } .order-item h2 { - background-image: url('img/arrow-move.png'); - background-repeat: no-repeat; - background-position: 6px 9px; + background-image: url("img/arrow-move.png"); + background-repeat: no-repeat; + background-position: 6px 9px; } .order-item .handle { - display: inline-block; - height: 14px; - width: 15px; - cursor: move; + display: inline-block; + height: 14px; + width: 15px; + cursor: move; } .order-item .collapse { - cursor: pointer; - /*color: #444;*/ - font-weight: normal; + cursor: pointer; + /*color: #444;*/ + font-weight: normal; - border-bottom: 1px solid rgba(255, 255, 255, 0.25); + border-bottom: 1px solid rgba(255, 255, 255, 0.25); } .order-item .collapse:hover { - opacity: 0.7; + opacity: 0.7; } .item-delete { - cursor:pointer; - float:right; - margin: 4px 3px 0px 0; + cursor: pointer; + float: right; + margin: 4px 3px 0px 0; } -.highlight, .helper { - height: 34px; - margin: 0 0 10px 0; - border: none; - opacity: 0.3; - background: #79aec8; +.highlight, +.helper { + height: 34px; + margin: 0 0 10px 0; + border: none; + opacity: 0.3; + background: #79aec8; } -.helper{ - height: 25px !important; - opacity: 1; +.helper { + height: 25px !important; + opacity: 1; } .button { - margin:5px; padding:5px; - font-weight: bold; - cursor:pointer; - border: 1px solid #678; + margin: 5px; + padding: 5px; + font-weight: bold; + cursor: pointer; + border: 1px solid #678; } select { - max-width: 580px; + max-width: 580px; } #feincmsmain_wrapper { - margin: 10px 0 30px 0; + margin: 10px 0 30px 0; } -.clearfix { *zoom:1; } -.clearfix:before, .clearfix:after { content: " "; display: table; } -.clearfix:after { clear: both; } +.clearfix { + *zoom: 1; +} +.clearfix:before, +.clearfix:after { + content: " "; + display: table; +} +.clearfix:after { + clear: both; +} textarea { - width: 580px; - margin-top:5px; - margin-bottom:5px; + width: 580px; + margin-top: 5px; + margin-bottom: 5px; } .inline-group .tabular textarea { - width: auto; + width: auto; } .item-controls { - position: absolute; - top: 2px; - right: 32px; + position: absolute; + top: 2px; + right: 32px; } .item-control-unit { - float:left; + float: left; } .item-controls select { - margin-left: 7px; + margin-left: 7px; } .machine-control { - padding: 5px 10px 5px 10px; - border: 1px solid #ccc; - background-color: #edf3fe; - position:absolute; - left:-11px; - bottom:-30px; - width: 100%; + padding: 5px 10px 5px 10px; + border: 1px solid #ccc; + background-color: #edf3fe; + position: absolute; + left: -11px; + bottom: -30px; + width: 100%; - background: #f8f8f8; - border: 1px solid #eee; - height: 55px; + background: #f8f8f8; + border: 1px solid #eee; + height: 55px; } .machine-control .button { - padding-top: 6px; - padding-bottom: 6px; + padding-top: 6px; + padding-bottom: 6px; } .control-unit { - float:left; - padding: 0 20px 0 5px; - border-left: 1px solid #eee; + float: left; + padding: 0 20px 0 5px; + border-left: 1px solid #eee; } .control-unit:first-child { - border-left: none; + border-left: none; } .control-unit span { - font-weight:bold; + font-weight: bold; } a.actionbutton { - display: block; - background-repeat: no-repeat; - width:50px; - height:50px; - float:left; - margin: 5px 0 0 20px; - text-indent:-7000px; + display: block; + background-repeat: no-repeat; + width: 50px; + height: 50px; + float: left; + margin: 5px 0 0 20px; + text-indent: -7000px; } -a.richtextcontent { background: url(img/contenttypes.png) no-repeat 0 0; } -a.richtextcontent:hover { background-position: 0 -70px; } +a.richtextcontent { + background: url(img/contenttypes.png) no-repeat 0 0; +} +a.richtextcontent:hover { + background-position: 0 -70px; +} -a.imagecontent { background: url(img/contenttypes.png) no-repeat -70px 0; } -a.imagecontent:hover { background-position: -70px -70px; } +a.imagecontent { + background: url(img/contenttypes.png) no-repeat -70px 0; +} +a.imagecontent:hover { + background-position: -70px -70px; +} -a.gallerycontent { background: url(img/contenttypes.png) no-repeat -140px 0; } -a.gallerycontent:hover { background-position: -140px -70px; } +a.gallerycontent { + background: url(img/contenttypes.png) no-repeat -140px 0; +} +a.gallerycontent:hover { + background-position: -140px -70px; +} -a.oembedcontent { background: url(img/contenttypes.png) no-repeat -280px 0; } -a.oembedcontent:hover { background-position: -280px -70px; } +a.oembedcontent { + background: url(img/contenttypes.png) no-repeat -280px 0; +} +a.oembedcontent:hover { + background-position: -280px -70px; +} -a.pdfcontent { background: url(img/contenttypes.png) no-repeat -210px 0; } -a.pdfcontent:hover { background-position: -210px -70px; } +a.pdfcontent { + background: url(img/contenttypes.png) no-repeat -210px 0; +} +a.pdfcontent:hover { + background-position: -210px -70px; +} -a.audiocontent { background: url(img/contenttypes.png) no-repeat -350px 0; } -a.audiocontent:hover { background-position: -350px -70px; } +a.audiocontent { + background: url(img/contenttypes.png) no-repeat -350px 0; +} +a.audiocontent:hover { + background-position: -350px -70px; +} .control-unit select { - float: left; - position: relative; - top: 13px; + float: left; + position: relative; + top: 13px; } .empty-machine-msg { - margin:10px 0px 20px 20px; - font-size:14px; + margin: 10px 0px 20px 20px; + font-size: 14px; } td span select { - width:600px; + width: 600px; } - .change-template-button { - margin-left: 7em; - padding-left: 30px; + margin-left: 7em; + padding-left: 30px; } /* Allow nested lists in error items */ ul.errorlist ul { - margin-left: 1em; - padding-left: 0; - list-style-type: square; + margin-left: 1em; + padding-left: 0; + list-style-type: square; } ul.errorlist li li { - /* Avoid repeating the warning image every time*/ - background-image:none; - padding: 0; + /* Avoid repeating the warning image every time*/ + background-image: none; + padding: 0; } -div.order-machine div.inline-related > h3{ - display: none; +div.order-machine div.inline-related > h3 { + display: none; } .hidden-form-row { - display: none; + display: none; } #extension_options_wrapper { border-bottom: 1px solid #eee; } -#extension_options>.module.aligned { +#extension_options > .module.aligned { border-top: 1px solid #eee; margin-bottom: -1px; } - /* various overrides */ -#id_redirect_to { width: 20em; } /* raw_id_fields act-a-like for redirect_to */ +#id_redirect_to { + width: 20em; +} /* raw_id_fields act-a-like for redirect_to */ /* overwrite flat theme default label width because of problems with the CKEditor */ -.aligned .text label { width: auto; } - +.aligned .text label { + width: auto; +} /* django suit hacks */ /*********************/ #suit-center #feincmsmain { - clear:none; + clear: none; } #suit-center .panel { - padding-top: 15px; + padding-top: 15px; } #suit-center .form-horizontal .inline-related fieldset { - margin-top: 10px; + margin-top: 10px; } #suit-center .panel h2 { - color: white; - font-size: 13px; - line-height: 12px; - margin-left: 0; - text-shadow: none; + color: white; + font-size: 13px; + line-height: 12px; + margin-left: 0; + text-shadow: none; } #suit-center .item-delete { - margin: 3px 5px 0 0; + margin: 3px 5px 0 0; } #suit-center .order-item .handle { - height: 36px; + height: 36px; } #suit-center .order-machine .order-item { - margin-top: 10px; + margin-top: 10px; } diff --git a/feincms/static/feincms/item_editor.js b/feincms/static/feincms/item_editor.js index 2a2c4de7f..a64f9a57f 100644 --- a/feincms/static/feincms/item_editor.js +++ b/feincms/static/feincms/item_editor.js @@ -1,603 +1,687 @@ +/* global Downcoder, django, feincms */ +/* global IMG_DELETELINK_PATH, REGION_MAP, REGION_NAMES, ACTIVE_REGION, CONTENT_NAMES, FEINCMS_ITEM_EDITOR_GETTEXT, CONTENT_TYPE_BUTTONS */ +/* global contentblock_init_handlers, contentblock_move_handlers */ +/* global id_to_windowname */ +/* global template_regions */ + // IE<9 lacks Array.prototype.indexOf if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function(needle) { - for (i=0, l=this.length; i").addClass("module aligned order-item item-wrapper-" + modvar); - var original_id_id = '#id_' + form.attr('id') + '-id'; - - var wrp = ['

']; - // If original has delete checkbox or this is a freshly added CT? Add delete link! - if($('.delete', form).length || !$(original_id_id, form).val()) { - wrp.push(''); - } - wrp.push(' '+modname+'

'); - wrp.push('
'); - fieldset.append(wrp.join("")); - - fieldset.children(".item-content").append(form); //relocates, not clone - - $("
").addClass("item-controls").appendTo(fieldset); - - return fieldset; - } - - - SELECTS = {}; - function save_content_type_selects() { - $('#feincmsmain>.panel').each(function() { - SELECTS[this.id.replace(/_body$/, '')] = $("select[name=order-machine-add-select]", this).clone().removeAttr("name"); - }); +(function ($) { + // Patch up urlify maps to generate nicer slugs in german + if (typeof Downcoder != "undefined") { + Downcoder.Initialize(); + Downcoder.map["ö"] = Downcoder.map["Ö"] = "oe"; + Downcoder.map["ä"] = Downcoder.map["Ä"] = "ae"; + Downcoder.map["ü"] = Downcoder.map["Ü"] = "ue"; + } + + function feincms_gettext(s) { + // Unfortunately, we cannot use Django's jsi18n view for this + // because it only sends translations from the package + // "django.conf" -- our own djangojs domain strings won't be + // picked up + + if (FEINCMS_ITEM_EDITOR_GETTEXT[s]) return FEINCMS_ITEM_EDITOR_GETTEXT[s]; + return s; + } + + function create_new_item_from_form(form, modname, modvar) { + let fieldset = $("
").addClass( + "module aligned order-item item-wrapper-" + modvar + ); + let original_id_id = "#id_" + form.attr("id") + "-id"; + + let wrp = ["

"]; + // If original has delete checkbox or this is a freshly added CT? Add delete link! + if ($(".delete", form).length || !$(original_id_id, form).val()) { + wrp.push(''); } - - function update_item_controls(item, target_region_id){ - var item_controls = item.find(".item-controls"); - item_controls.empty(); - - // Insert control unit - var insert_control = $("
").addClass("item-control-unit"); - var select_content = SELECTS[REGION_MAP[target_region_id]].clone(); - - select_content.change(function() { - var modvar = select_content.val(); - var modname = select_content.find("option:selected").html(); - var new_fieldset = create_new_fieldset_from_module(modvar, modname); - add_fieldset(target_region_id, new_fieldset, {where:'insertBefore', relative_to:item, animate:true}); - update_item_controls(new_fieldset, target_region_id); - - select_content.val(''); - }); - insert_control.append(select_content); - item_controls.append(insert_control); - - // Move control unit - if (REGION_MAP.length > 1) { - var wrp = []; - wrp.push('
'); - - var move_control = $(wrp.join("")); - move_control.find("select").change(function(){ - var move_to = $(this).val(); - move_item(REGION_MAP.indexOf(move_to), item); - }); - item_controls.append(move_control); // Add new one + wrp.push( + ' ' + + modname + + "

" + ); + wrp.push('
'); + fieldset.append(wrp.join("")); + + fieldset.children(".item-content").append(form); //relocates, not clone + + $("
").addClass("item-controls").appendTo(fieldset); + + return fieldset; + } + + const SELECTS = {}; + function save_content_type_selects() { + $("#feincmsmain>.panel").each(function () { + SELECTS[this.id.replace(/_body$/, "")] = $( + "select[name=order-machine-add-select]", + this + ) + .clone() + .removeAttr("name"); + }); + } + + function update_item_controls(item, target_region_id) { + let item_controls = item.find(".item-controls"); + item_controls.empty(); + + // Insert control unit + let insert_control = $("
").addClass("item-control-unit"); + let select_content = SELECTS[REGION_MAP[target_region_id]].clone(); + + select_content.change(function () { + let modvar = select_content.val(); + let modname = select_content.find("option:selected").html(); + let new_fieldset = create_new_fieldset_from_module(modvar, modname); + add_fieldset(target_region_id, new_fieldset, { + where: "insertBefore", + relative_to: item, + animate: true, + }); + update_item_controls(new_fieldset, target_region_id); + + select_content.val(""); + }); + insert_control.append(select_content); + item_controls.append(insert_control); + + // Move control unit + if (REGION_MAP.length > 1) { + let wrp = []; + wrp.push( + '
"); + + let move_control = $(wrp.join("")); + move_control.find("select").change(function () { + let move_to = $(this).val(); + move_item(REGION_MAP.indexOf(move_to), item); + }); + item_controls.append(move_control); // Add new one } + } + function create_new_fieldset_from_module(modvar, modname) { + let new_form = create_new_spare_form(modvar); + return create_new_item_from_form(new_form, modname, modvar); + } - function create_new_fieldset_from_module(modvar, modname) { - var new_form = create_new_spare_form(modvar); - return create_new_item_from_form(new_form, modname, modvar); - } - - function add_fieldset(region_id, item, how){ - /* `how` should be an object. + function add_fieldset(region_id, item, how) { + /* `how` should be an object. `how.where` should be one of: - 'append' -- last region - 'prepend' -- first region - 'insertBefore' -- insert before relative_to - 'insertAfter' -- insert after relative_to */ - // Default parameters - if (how) $.extend({ - where: 'append', - relative_to: undefined, - animate: false - }, how); - - item.hide(); - if(how.where == 'append' || how.where == 'prepend'){ - $("#"+ REGION_MAP[region_id] +"_body").children("div.order-machine")[how.where](item); - } - else if(how.where == 'insertBefore' || how.where == 'insertAfter'){ - if(how.relative_to){ - item[how.where](how.relative_to); - } - else{ - window.alert('DEBUG: invalid add_fieldset usage'); - return; - } - } - else{ - window.alert('DEBUG: invalid add_fieldset usage'); - return; - } - set_item_field_value(item, "region-choice-field", region_id); - init_contentblocks(); - - if (how.animate) { - item.fadeIn(800); - } - else { - item.show(); - } - } - - function create_new_spare_form(modvar) { - var old_form_count = parseInt($('#id_'+modvar+'_set-TOTAL_FORMS').val(), 10); - // **** UGLY CODE WARNING, avert your gaze! **** - // for some unknown reason, the add-button click handler function - // fails on the first triggerHandler call in some rare cases; - // we can detect this here and retry: - for(var i = 0; i < 2; i++){ - // Use Django's built-in inline spawing mechanism (Django 1.2+) - // must use django.jQuery since the bound function lives there: - django.jQuery('#'+modvar+'_set-group').find( - '.add-row a').triggerHandler('click'); - var new_form_count = parseInt($('#id_'+modvar+'_set-TOTAL_FORMS').val(), 10); - if(new_form_count > old_form_count){ - return $('#'+modvar+'_set-'+(new_form_count-1)); - } - } - } - - function set_item_field_value(item, field, value) { - // item: DOM object for the item's fieldset. - // field: "order-field" | "delete-field" | "region-choice-field" - if (field=="delete-field") - item.find("."+field).attr("checked",value); - else if (field=="region-choice-field") { - var old_region_id = REGION_MAP.indexOf(item.find("."+field).val()); - item.find("."+field).val(REGION_MAP[value]); - - // show/hide the empty machine message in the source and - // target region. - old_region_item = $("#"+REGION_MAP[old_region_id]+"_body"); - if (old_region_item.children("div.order-machine").children().length == 0) - old_region_item.children("div.empty-machine-msg").show(); - else - old_region_item.children("div.empty-machine-msg").hide(); - - new_region_item = $("#"+REGION_MAP[value]+"_body"); - new_region_item.children("div.empty-machine-msg").hide(); - } - else - item.find("."+field).val(value); - } - - function move_item(region_id, item) { - poorify_rich(item); - item.fadeOut(800, function() { - add_fieldset(region_id, item, {where:'append'}); - richify_poor(item); - update_item_controls(item, region_id); - item.show(); - }); - } - - function poorify_rich(item){ - item.children(".item-content").hide(); - - for (var i=0; i v2 ? 1 : -1; + } + + function create_new_spare_form(modvar) { + let old_form_count = parseInt( + $("#id_" + modvar + "_set-TOTAL_FORMS").val(), + 10 + ); + // **** UGLY CODE WARNING, avert your gaze! **** + // for some unknown reason, the add-button click handler function + // fails on the first triggerHandler call in some rare cases; + // we can detect this here and retry: + for (let i = 0; i < 2; i++) { + // Use Django's built-in inline spawing mechanism (Django 1.2+) + // must use django.jQuery since the bound function lives there: + django + .jQuery("#" + modvar + "_set-group") + .find(".add-row a") + .triggerHandler("click"); + let new_form_count = parseInt( + $("#id_" + modvar + "_set-TOTAL_FORMS").val(), + 10 + ); + if (new_form_count > old_form_count) { + return $("#" + modvar + "_set-" + (new_form_count - 1)); + } } - - function give_ordering_to_content_types() { - for (var i=0; i v2 ? 1 : -1; + } + + function give_ordering_to_content_types() { + for (let i = 0; i < REGION_MAP.length; i++) { + let container = $("#" + REGION_MAP[i] + "_body div.order-machine"); + for (let j = 0; j < container.children().length; j++) { + set_item_field_value( + container.find("fieldset.order-item:eq(" + j + ")"), + "order-field", + j + ); } } - - function order_content_types_in_regions() { - for (var i=0; i