From e92f3207efa20b1479f537f2ce0eba9a9deed9d0 Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Tue, 18 Feb 2020 21:43:21 +0100 Subject: [PATCH 01/69] wip clipboard_group --- cmsplugin_cascade/clipboard/cms_plugins.py | 56 +++++++++++++++++----- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/cmsplugin_cascade/clipboard/cms_plugins.py b/cmsplugin_cascade/clipboard/cms_plugins.py index 826678999..929fe95b1 100644 --- a/cmsplugin_cascade/clipboard/cms_plugins.py +++ b/cmsplugin_cascade/clipboard/cms_plugins.py @@ -1,10 +1,9 @@ import json - from django.conf.urls import url from django.contrib.admin import site as default_admin_site from django.contrib.admin.helpers import AdminForm from django.core.exceptions import PermissionDenied -from django.forms import CharField, ModelChoiceField +from django.forms import CharField, ModelChoiceField, ModelMultipleChoiceField from django.shortcuts import render from django.template.response import TemplateResponse from django.urls import reverse @@ -17,7 +16,14 @@ from cms.utils import get_language_from_request from cmsplugin_cascade.clipboard.forms import ClipboardBaseForm from cmsplugin_cascade.clipboard.utils import deserialize_to_clipboard, serialize_from_placeholder -from cmsplugin_cascade.models import CascadeClipboard +from cmsplugin_cascade.models import CascadeClipboard, CascadeClipboardGroup +from cmsplugin_cascade.clipboard.forms import ClipboardBaseForm +from django.forms import widgets +from django.contrib.admin.widgets import RelatedFieldWidgetWrapper + +class ClipboardWidget(widgets.Select): + #template_name = 'django/forms/widgets/select.html' + template_name = 'cascade/admin/widgets/clipboard.html' class CascadeClipboardPlugin(CMSPluginBase): @@ -25,6 +31,14 @@ class CascadeClipboardPlugin(CMSPluginBase): render_plugin = False change_form_template = 'admin/cms/page/plugin/change_form.html' + + def __init__(self, *args,**kwargs): + # conditional model depend model gived in view + if 'model' in kwargs: + self.model = kwargs['model'] + super().__init__(*args, **kwargs) + + def get_plugin_urls(self): urlpatterns = [ url(r'^export-plugins/$', self.export_plugins_view, name='export_clipboard_plugins'), @@ -40,12 +54,12 @@ def get_extra_placeholder_menu_items(cls, request, placeholder): }) return [ PluginMenuItem( - _("Export to Clipboard"), + _("Export from Clipboard"), reverse('admin:export_clipboard_plugins') + '?' + data, data={}, action='modal', attributes={ - 'icon': 'export', + 'icon': 'import', }, ), PluginMenuItem( @@ -56,31 +70,39 @@ def get_extra_placeholder_menu_items(cls, request, placeholder): attributes={ 'icon': 'import', }, - ) + ), ] + def render_modal_window(self, request, form): """ Render a modal popup window with a select box to edit the form """ + form.fields['group'].widget = RelatedFieldWidgetWrapper( + form.fields['group'].widget,CascadeClipboard.group.rel, + default_admin_site, can_change_related=True) + opts = self.model._meta fieldsets = [(None, {'fields': list(form.fields)})] adminForm = AdminForm(form, fieldsets, {}, []) + context = { **default_admin_site.each_context(request), 'title': form.title, 'adminform': adminForm, - 'add': False, + 'add': True, 'change': True, - 'save_as': False, - 'has_add_permission': False, + 'save_as': True, + 'has_add_permission': True, 'has_change_permission': True, + 'can_change_related':True, + 'can_add_related':True, 'opts': opts, 'root_path': reverse('admin:index'), 'is_popup': True, - 'app_label': opts.app_label, 'media': self.media + form.media, } + return TemplateResponse(request, self.change_form_template, context) def import_plugins_view(self, request): @@ -93,6 +115,7 @@ def import_plugins_view(self, request): queryset=CascadeClipboard.objects.all(), label=_("Select Clipboard Content"), required=False, + widget=ClipboardWidget, ), 'title': title, }) @@ -103,6 +126,7 @@ def import_plugins_view(self, request): 'clipboard': ModelChoiceField( queryset=CascadeClipboard.objects.all(), label=_("Select Clipboard Content"), + widget=ClipboardWidget, ), 'title': title, }) @@ -151,6 +175,10 @@ def export_plugins_view(self, request): Form = type('ClipboardExportForm', (ClipboardBaseForm,), { 'identifier': CharField(required=False), 'title': title, + 'group' : ModelMultipleChoiceField( + queryset=CascadeClipboardGroup.objects.all(), + required=False, + ), }) form = Form(request.GET) assert form.is_valid() @@ -158,6 +186,10 @@ def export_plugins_view(self, request): Form = type('ClipboardExportForm', (ClipboardBaseForm,), { 'identifier': CharField(), 'title': title, + 'group' : ModelMultipleChoiceField( + queryset=CascadeClipboardGroup.objects.all(), + required=False, + ), }) form = Form(request.POST) if form.is_valid(): @@ -168,11 +200,13 @@ def add_to_clipboard(self, request, form): placeholder = form.cleaned_data['placeholder'] language = form.cleaned_data['language'] identifier = form.cleaned_data['identifier'] + group = form.cleaned_data['group'] data = serialize_from_placeholder(placeholder) - CascadeClipboard.objects.create( + cascade_clipboard = CascadeClipboard.objects.create( identifier=identifier, data=data, ) + cascade_clipboard.group.set(group) return render(request, 'cascade/admin/clipboard_close_frame.html', {}) plugin_pool.register_plugin(CascadeClipboardPlugin) From d3c2f2d9380d0bb8ca08a98a49bd475e3586f49a Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Tue, 18 Feb 2020 21:45:27 +0100 Subject: [PATCH 02/69] remove not needed code --- cmsplugin_cascade/clipboard/cms_plugins.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/cmsplugin_cascade/clipboard/cms_plugins.py b/cmsplugin_cascade/clipboard/cms_plugins.py index 929fe95b1..b963c7545 100644 --- a/cmsplugin_cascade/clipboard/cms_plugins.py +++ b/cmsplugin_cascade/clipboard/cms_plugins.py @@ -31,14 +31,6 @@ class CascadeClipboardPlugin(CMSPluginBase): render_plugin = False change_form_template = 'admin/cms/page/plugin/change_form.html' - - def __init__(self, *args,**kwargs): - # conditional model depend model gived in view - if 'model' in kwargs: - self.model = kwargs['model'] - super().__init__(*args, **kwargs) - - def get_plugin_urls(self): urlpatterns = [ url(r'^export-plugins/$', self.export_plugins_view, name='export_clipboard_plugins'), From 82cc88e8c9cc299b849290b87dcfb4f2c779c1d3 Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Tue, 18 Feb 2020 21:58:52 +0100 Subject: [PATCH 03/69] wip clipboard_group (optimize, clean code) --- cmsplugin_cascade/clipboard/cms_plugins.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/cmsplugin_cascade/clipboard/cms_plugins.py b/cmsplugin_cascade/clipboard/cms_plugins.py index b963c7545..4ddadefa7 100644 --- a/cmsplugin_cascade/clipboard/cms_plugins.py +++ b/cmsplugin_cascade/clipboard/cms_plugins.py @@ -70,10 +70,6 @@ def render_modal_window(self, request, form): """ Render a modal popup window with a select box to edit the form """ - form.fields['group'].widget = RelatedFieldWidgetWrapper( - form.fields['group'].widget,CascadeClipboard.group.rel, - default_admin_site, can_change_related=True) - opts = self.model._meta fieldsets = [(None, {'fields': list(form.fields)})] adminForm = AdminForm(form, fieldsets, {}, []) @@ -173,6 +169,10 @@ def export_plugins_view(self, request): ), }) form = Form(request.GET) + form.fields['group'].widget = RelatedFieldWidgetWrapper( + form.fields['group'].widget,CascadeClipboard.group.rel, + default_admin_site, can_change_related=True) + assert form.is_valid() elif request.method == 'POST': Form = type('ClipboardExportForm', (ClipboardBaseForm,), { @@ -184,6 +184,10 @@ def export_plugins_view(self, request): ), }) form = Form(request.POST) + form.fields['group'].widget = RelatedFieldWidgetWrapper( + form.fields['group'].widget,CascadeClipboard.group.rel, + default_admin_site, can_change_related=True) + if form.is_valid(): return self.add_to_clipboard(request, form) return self.render_modal_window(request, form) From 8372d4fb0c0bb55a8e1e9b094892392c92750b94 Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Tue, 18 Feb 2020 22:07:00 +0100 Subject: [PATCH 04/69] wip clipboard_group (add model ManyToManyField) --- cmsplugin_cascade/models.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cmsplugin_cascade/models.py b/cmsplugin_cascade/models.py index f33fb7a64..07534a783 100644 --- a/cmsplugin_cascade/models.py +++ b/cmsplugin_cascade/models.py @@ -225,6 +225,12 @@ class Meta: managed = False # it's a dummy model db_table = None +class CascadeClipboardGroup(models.Model): + name = models.CharField(max_length=50) + + def __str__(self): + return str(self.name) + class CascadeClipboard(models.Model): """ @@ -236,6 +242,8 @@ class CascadeClipboard(models.Model): unique=True, ) + group = models.ManyToManyField(CascadeClipboardGroup) + data = JSONField( null=True, blank=True, From d8d15431ea623ec5786ae5656a86289f2160a4fb Mon Sep 17 00:00:00 2001 From: Haricot Date: Tue, 18 Feb 2020 22:15:29 +0100 Subject: [PATCH 05/69] wip clipboard_group (add migrations) --- .../migrations/0028_auto_20200218_1514.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 cmsplugin_cascade/migrations/0028_auto_20200218_1514.py diff --git a/cmsplugin_cascade/migrations/0028_auto_20200218_1514.py b/cmsplugin_cascade/migrations/0028_auto_20200218_1514.py new file mode 100644 index 000000000..a44f37f69 --- /dev/null +++ b/cmsplugin_cascade/migrations/0028_auto_20200218_1514.py @@ -0,0 +1,25 @@ +# Generated by Django 2.2.10 on 2020-02-18 21:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cmsplugin_cascade', '0027_version_1'), + ] + + operations = [ + migrations.CreateModel( + name='CascadeClipboardGroup', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=50)), + ], + ), + migrations.AddField( + model_name='cascadeclipboard', + name='group', + field=models.ManyToManyField(to='cmsplugin_cascade.CascadeClipboardGroup'), + ), + ] From 8027e99b95f39812d9ddfcac857fc1567fc07e18 Mon Sep 17 00:00:00 2001 From: Haricot Date: Tue, 18 Feb 2020 22:36:35 +0100 Subject: [PATCH 06/69] wip clipboard_group (admin group) --- cmsplugin_cascade/clipboard/admin.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/cmsplugin_cascade/clipboard/admin.py b/cmsplugin_cascade/clipboard/admin.py index 45ef44ba3..580c20ee7 100644 --- a/cmsplugin_cascade/clipboard/admin.py +++ b/cmsplugin_cascade/clipboard/admin.py @@ -9,9 +9,9 @@ from jsonfield.fields import JSONField from cms.models.placeholderpluginmodel import PlaceholderReference +from cms.admin.placeholderadmin import PlaceholderAdminMixin from cmsplugin_cascade.clipboard.utils import deserialize_to_clipboard, serialize_from_placeholder -from cmsplugin_cascade.models import CascadeClipboard - +from cmsplugin_cascade.models import CascadeClipboard, CascadeClipboardGroup class JSONAdminWidget(widgets.Textarea): def __init__(self): @@ -35,10 +35,13 @@ def render(self, name, value, attrs=None, renderer=None): _("Successfully pasted JSON data"), _("Successfully copied JSON data")) +@admin.register(CascadeClipboardGroup) +class GroupModelAdmin(PlaceholderAdminMixin, admin.ModelAdmin): + pass @admin.register(CascadeClipboard) class CascadeClipboardAdmin(admin.ModelAdmin): - fields = ('identifier', 'save_clipboard', 'restore_clipboard', 'data',) + fields = ('identifier', 'group', 'save_clipboard', 'restore_clipboard', 'data',) readonly_fields = ('save_clipboard', 'restore_clipboard',) formfield_overrides = { JSONField: {'widget': JSONAdminWidget}, From 22d87a40277493fa51ae7ba242b98d7ac210a469 Mon Sep 17 00:00:00 2001 From: Haricot Date: Tue, 18 Feb 2020 22:53:34 +0100 Subject: [PATCH 07/69] wip clipboard_group (finished) --- cmsplugin_cascade/clipboard/cms_plugins.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmsplugin_cascade/clipboard/cms_plugins.py b/cmsplugin_cascade/clipboard/cms_plugins.py index 4ddadefa7..2a6cd8e17 100644 --- a/cmsplugin_cascade/clipboard/cms_plugins.py +++ b/cmsplugin_cascade/clipboard/cms_plugins.py @@ -22,8 +22,8 @@ from django.contrib.admin.widgets import RelatedFieldWidgetWrapper class ClipboardWidget(widgets.Select): - #template_name = 'django/forms/widgets/select.html' - template_name = 'cascade/admin/widgets/clipboard.html' + template_name = 'django/forms/widgets/select.html' + #template_name = 'cascade/admin/widgets/clipboard.html' class CascadeClipboardPlugin(CMSPluginBase): From 6e5ebb07ac421ac50853fb13cce775b4a3403a37 Mon Sep 17 00:00:00 2001 From: Haricot Date: Sat, 22 Feb 2020 22:44:11 +0100 Subject: [PATCH 08/69] wip view_groups clipboard --- cmsplugin_cascade/clipboard/cms_plugins.py | 119 +++++++++++++++--- cmsplugin_cascade/models.py | 3 +- .../static/cascade/css/admin/clipboard.css | 35 +++++- cmsplugin_cascade/strides.py | 8 +- .../templatetags/cascade_tags.py | 55 +++++--- 5 files changed, 183 insertions(+), 37 deletions(-) diff --git a/cmsplugin_cascade/clipboard/cms_plugins.py b/cmsplugin_cascade/clipboard/cms_plugins.py index 2a6cd8e17..77a80008d 100644 --- a/cmsplugin_cascade/clipboard/cms_plugins.py +++ b/cmsplugin_cascade/clipboard/cms_plugins.py @@ -3,7 +3,7 @@ from django.contrib.admin import site as default_admin_site from django.contrib.admin.helpers import AdminForm from django.core.exceptions import PermissionDenied -from django.forms import CharField, ModelChoiceField, ModelMultipleChoiceField +from django.forms import CharField, ModelChoiceField, ModelMultipleChoiceField, ChoiceField, MultipleChoiceField from django.shortcuts import render from django.template.response import TemplateResponse from django.urls import reverse @@ -21,9 +21,18 @@ from django.forms import widgets from django.contrib.admin.widgets import RelatedFieldWidgetWrapper + class ClipboardWidget(widgets.Select): - template_name = 'django/forms/widgets/select.html' - #template_name = 'cascade/admin/widgets/clipboard.html' + #template_name = 'django/forms/widgets/select.html' + template_name = 'cascade/admin/widgets/clipboard.html' + + def get_context(self, name, value, attrs): + context = super(ClipboardWidget, self).get_context(name, value, attrs) + context['widget']['optgroups'] = self.optgroups(name, context['widget']['value'], attrs) + groups=list(CascadeClipboardGroup.objects.all().exclude( name='Clipboard Home').values_list('name',flat=True)) + context['groups_exclude_home'] = groups + context['qs_clipboards'] = CascadeClipboard.objects.all() + return context class CascadeClipboardPlugin(CMSPluginBase): @@ -88,33 +97,87 @@ def render_modal_window(self, request, form): 'opts': opts, 'root_path': reverse('admin:index'), 'is_popup': True, + 'app_label': opts.app_label, 'media': self.media + form.media, } return TemplateResponse(request, self.change_form_template, context) - def import_plugins_view(self, request): + + def import_plugins_view(self, request, *args, **kwargs): # TODO: check for permissions - title = _("Import from Clipboard") + placeholder_ref_id = None + if request.GET.get('placeholder'): + placeholder_ref_id = request.GET.get('placeholder') + queryset=CascadeClipboard.objects.all().prefetch_related('group') + clipboards_groupby={} + + def treegroup( groups, index2): + groups_clipboard=list(groups.group.values_list('name', flat=True)) + if len(groups_clipboard) >= 1: + for index, key in enumerate(groups_clipboard, start=1): + clipboards_groupby.setdefault(key, []) + clipboards_groupby[key].append(( groups.identifier ,groups.identifier,)) + else: + clipboards_groupby.setdefault('ungroup', []) + clipboards_groupby['ungroup'].append(( groups.identifier ,groups.identifier,)) + + [treegroup( groups, index) for index, groups in enumerate(queryset , start=1)] + + if not 'Clipboard Home' in clipboards_groupby: + clipboards_groupby[ 'Clipboard Home'] = [('demo', 'demo')] + clipboard_home = CascadeClipboardGroup.objects.create(name="Clipboard Home") + data_demo = self.populate_static_json("demo_carousel-plugin.json") + print(data_demo) + group_clipboard_home = clipboard_home + cascade_clipboard = CascadeClipboard.objects.create( + identifier="demo", + data=data_demo, + ) + cascade_clipboard.group.set([group_clipboard_home]) + + CHOICES=(list(clipboards_groupby.items(),)) + ff=_("Import from Clipboard") + + if request.GET.get('group'): + req_parameter_group = request.GET.get('group') + title = f": {req_parameter_group}" + else: + req_parameter_group = "Clipboard Home" + title = _("Import to Clipboard") + + # if empty clipboards but has group do empty + if not req_parameter_group in clipboards_groupby: + clipboards_groupby[req_parameter_group] = '' + + if 'ungroup' in clipboards_groupby : + len_ungroup = len(clipboards_groupby[req_parameter_group]) + else: + len_ungroup = 0 + + CHOICES=clipboards_groupby[req_parameter_group] + language= get_language_from_request(request) if request.method == 'GET': Form = type('ClipboardImportForm', (ClipboardBaseForm,), { - 'clipboard': ModelChoiceField( - queryset=CascadeClipboard.objects.all(), - label=_("Select Clipboard Content"), + 'clipboard':ChoiceField( + choices=CHOICES, + label=_("Select Clipboard"), required=False, - widget=ClipboardWidget, + widget=ClipboardWidget(attrs={"placeholder_ref_id": placeholder_ref_id, "language": language, 'count_target':len_ungroup }), ), 'title': title, }) + + Form.Media =type("Media",(), {'css' : { 'all': ['cascade/css/admin/clipboard.css'] }}) form = Form(request.GET) assert form.is_valid() elif request.method == 'POST': Form = type('ClipboardImportForm', (ClipboardBaseForm,), { - 'clipboard': ModelChoiceField( - queryset=CascadeClipboard.objects.all(), - label=_("Select Clipboard Content"), - widget=ClipboardWidget, + 'clipboard': ChoiceField( + choices=CHOICES, + label=_("Select Clipboard"), + widget=ClipboardWidget(), ), 'title': title, }) @@ -127,13 +190,21 @@ def paste_from_clipboard(self, request, form): placeholder = form.cleaned_data['placeholder'] language = form.cleaned_data['language'] cascade_clipboard = form.cleaned_data['clipboard'] + tree_order = placeholder.get_plugin_tree_order(language) - deserialize_to_clipboard(request, cascade_clipboard.data) + if not hasattr(cascade_clipboard, 'data'): + deserialize_to_clipboard(request, CascadeClipboard.objects.get(identifier=cascade_clipboard).data) + else: + deserialize_to_clipboard(request,cascade_clipboard.data) # detach plugins from clipboard and reattach them to current placeholder cb_placeholder_plugin = request.toolbar.clipboard.cmsplugin_set.first() cb_placeholder_instance, _ = cb_placeholder_plugin.get_plugin_instance() + + # bug if the Clipboard Placeholder has alias 'AliasPluginModel', object, it has no attribute 'placeholder_ref', + # possible need request.toolbar.clipboard.clear() , add .placeholder_ref new_plugins = cb_placeholder_instance.placeholder_ref.get_plugins() + new_plugins.update(placeholder=placeholder) # reorder root plugins in placeholder @@ -158,14 +229,18 @@ def export_plugins_view(self, request): if not request.user.is_staff: raise PermissionDenied + qs_clipboards=CascadeClipboardGroup.objects.all() + if not'Clipboard Home' in list(qs_clipboards.values_list( 'name' , flat=True)): + qs_clipboards.get_or_create(name="Clipboard Home") + title = _("Export to Clipboard") if request.method == 'GET': Form = type('ClipboardExportForm', (ClipboardBaseForm,), { 'identifier': CharField(required=False), 'title': title, 'group' : ModelMultipleChoiceField( - queryset=CascadeClipboardGroup.objects.all(), - required=False, + queryset=qs_clipboards, + required=False, ), }) form = Form(request.GET) @@ -179,7 +254,7 @@ def export_plugins_view(self, request): 'identifier': CharField(), 'title': title, 'group' : ModelMultipleChoiceField( - queryset=CascadeClipboardGroup.objects.all(), + queryset=qs_clipboards, required=False, ), }) @@ -205,4 +280,14 @@ def add_to_clipboard(self, request, form): cascade_clipboard.group.set(group) return render(request, 'cascade/admin/clipboard_close_frame.html', {}) + + def populate_static_json(self, filename): + import os, io, json + from django.contrib.staticfiles import finders + clipboard_folder ='cascade/admin/clipboard/' + path = finders.find(clipboard_folder) + with io.open(os.path.join(path, filename), 'r') as fh: + config_data = json.load(fh) + return config_data + plugin_pool.register_plugin(CascadeClipboardPlugin) diff --git a/cmsplugin_cascade/models.py b/cmsplugin_cascade/models.py index 07534a783..65f362183 100644 --- a/cmsplugin_cascade/models.py +++ b/cmsplugin_cascade/models.py @@ -231,7 +231,6 @@ class CascadeClipboardGroup(models.Model): def __str__(self): return str(self.name) - class CascadeClipboard(models.Model): """ A model class to persist, export and re-import the clipboard's content. @@ -242,7 +241,7 @@ class CascadeClipboard(models.Model): unique=True, ) - group = models.ManyToManyField(CascadeClipboardGroup) + group = models.ManyToManyField(CascadeClipboardGroup,blank=True) data = JSONField( null=True, diff --git a/cmsplugin_cascade/static/cascade/css/admin/clipboard.css b/cmsplugin_cascade/static/cascade/css/admin/clipboard.css index aaf2dec94..27cebec30 100644 --- a/cmsplugin_cascade/static/cascade/css/admin/clipboard.css +++ b/cmsplugin_cascade/static/cascade/css/admin/clipboard.css @@ -40,4 +40,37 @@ div.cms .cms-structure .cms-submenu-item a[data-icon=import]:focus:before { div.cms .cms-structure.cms-structure-condensed .cms-submenu-item a[data-icon=export]:before, div.cms .cms-structure.cms-structure-condensed .cms-submenu-item a[data-icon=import]:before { top: 12px !important; -} \ No newline at end of file +} + +.container_clipboard { + display: flex; + justify-content: space-between; +} + +.cascade_clipboard_admin { + padding: 1rem; +} + + +.button_clipboard { + background-color:#ffffff; + -webkit-border-radius:6px; + -moz-border-radius:6px; + border-radius:6px; + border:1px solid #dcdcdc; + cursor:pointer; + color:#666666; + font-family:Arial; + font-size:15px; + font-weight:bold; + padding:6px 24px; + text-decoration:none; +} +.button_clipboard:hover { + background-color:#f6f6f6; +} +.button_clipboard:active { + position:relative; + top:1px; +} + diff --git a/cmsplugin_cascade/strides.py b/cmsplugin_cascade/strides.py index e0c11991b..8906dcd97 100644 --- a/cmsplugin_cascade/strides.py +++ b/cmsplugin_cascade/strides.py @@ -183,8 +183,9 @@ def render(self, context, instance, placeholder): class StrideContentRenderer(object): def __init__(self, request): - self.request = request - self.language = get_language_from_request(request) + if request: + self.request = request + self.language = get_language_from_request(request) self._cached_templates = {} def render_cascade(self, context, tree_data): @@ -205,6 +206,9 @@ def render_plugin(self, instance, context, placeholder=None, editable=False): from sekizai.helpers import get_varname as get_sekizai_context_key sekizai_context_key = get_sekizai_context_key() + print("sekizai_context_kesssssssssy") + print(sekizai_context_key) + print( placeholder) if app_settings.CMSPLUGIN_CASCADE['cache_strides'] and getattr(instance.plugin, 'cache', not editable): cache = caches['default'] key = 'cascade_element-{}'.format(instance.pk) diff --git a/cmsplugin_cascade/templatetags/cascade_tags.py b/cmsplugin_cascade/templatetags/cascade_tags.py index b42839f80..bb2f003b3 100644 --- a/cmsplugin_cascade/templatetags/cascade_tags.py +++ b/cmsplugin_cascade/templatetags/cascade_tags.py @@ -26,25 +26,39 @@ class StrideRenderer(Tag): """ name = 'render_cascade' options = Options( - Argument('datafile'), + Argument('data_clipboard'), + Argument('identifier'), ) - def render_tag(self, context, datafile): + def render_tag(self, context, data_clipboard, identifier=None): from sekizai.helpers import get_varname as get_sekizai_context_key from cmsplugin_cascade.strides import StrideContentRenderer + if isinstance(data_clipboard, dict): + identifier = identifier + datafile = False + else : + datafile = data_clipboard + identifier = datafile + tree_data_key = 'cascade-strides:' + identifier cache = caches['default'] - tree_data_key = 'cascade-strides:' + datafile tree_data = cache.get(tree_data_key) if cache else None if tree_data is None: - jsonfile = finders.find(datafile) - if not jsonfile: - raise IOError("Unable to find file: {}".format(datafile)) - - with io.open(jsonfile) as fp: - tree_data = json.load(fp) - - content_renderer = StrideContentRenderer(context['request']) + if datafile: + jsonfile = finders.find(datafile) + if not jsonfile: + raise IOError("Unable to find file: {}".format(datafile)) + with io.open(jsonfile) as fp: + tree_data = json.load(fp) + else: + tree_data = json.load(data_clipboard) + else: + tree_data = data_clipboard + if 'request' in context: + data_req = context['request'] + else: + data_req = None + content_renderer = StrideContentRenderer(data_req) with context.push(cms_content_renderer=content_renderer): content = content_renderer.render_cascade(context, tree_data) @@ -55,6 +69,7 @@ def render_tag(self, context, datafile): SEKIZAI_CONTENT_HOLDER = cache.get_or_set(sekizai_context_key, context.get(sekizai_context_key)) if SEKIZAI_CONTENT_HOLDER: for name in SEKIZAI_CONTENT_HOLDER: + context[sekizai_context_key] = {'name':''} context[sekizai_context_key][name] = SEKIZAI_CONTENT_HOLDER[name] return content @@ -74,9 +89,6 @@ class RenderPlugin(Tag): def render_tag(self, context, plugin): if not plugin: return '' - - request = context['request'] - toolbar = get_toolbar_from_request(request) if 'cms_content_renderer' in context and isinstance(context['cms_content_renderer'], StrideContentRenderer): content_renderer = context['cms_content_renderer'] elif 'cms_renderer' in context: @@ -84,11 +96,17 @@ def render_tag(self, context, plugin): elif 'cms_content_renderer' in context: content_renderer = context['cms_content_renderer'] else: + request = context['request'] + toolbar = get_toolbar_from_request(request) content_renderer = toolbar.content_renderer + try: + toolbar_edit_mode = toolbar.edit_mode_active + except UnboundLocalError: + toolbar_edit_mode = True content = content_renderer.render_plugin( instance=plugin, context=context, - editable=toolbar.edit_mode_active, + editable=toolbar_edit_mode ) return content @@ -110,3 +128,10 @@ def sphinx_docs_include(path): raise TemplateDoesNotExist("'{path}' does not exist".format(path=path)) with io.open(filename) as fh: return mark_safe(fh.read()) + + + +@register.simple_tag +def cascadeclipboard_data_by_identifier(queryset, identifier ): + qs_identifier=queryset.filter(identifier=identifier) + return qs_identifier[0].data From 911ef9d29de5a9994ce336cf35192ae5aa423652 Mon Sep 17 00:00:00 2001 From: Haricot Date: Sat, 22 Feb 2020 22:46:48 +0100 Subject: [PATCH 09/69] wip view_groups clipboard --- .../admin/clipboards/carousel-plugin.json | 79 +++++++++++++++++++ .../demo_carousel-plugin (copie).json | 79 +++++++++++++++++++ .../clipboards/demo_carousel-plugin.json | 79 +++++++++++++++++++ .../cascade/admin/widgets/clipboard.html | 44 +++++++++++ .../admin/widgets/clipboard_stride.html | 11 +++ 5 files changed, 292 insertions(+) create mode 100644 cmsplugin_cascade/static/cascade/admin/clipboards/carousel-plugin.json create mode 100644 cmsplugin_cascade/static/cascade/admin/clipboards/demo_carousel-plugin (copie).json create mode 100644 cmsplugin_cascade/static/cascade/admin/clipboards/demo_carousel-plugin.json create mode 100644 cmsplugin_cascade/templates/cascade/admin/widgets/clipboard.html create mode 100644 cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_stride.html diff --git a/cmsplugin_cascade/static/cascade/admin/clipboards/carousel-plugin.json b/cmsplugin_cascade/static/cascade/admin/clipboards/carousel-plugin.json new file mode 100644 index 000000000..b5350d77d --- /dev/null +++ b/cmsplugin_cascade/static/cascade/admin/clipboards/carousel-plugin.json @@ -0,0 +1,79 @@ +{ + "plugins": [ + [ + "BootstrapCarouselPlugin", + { + "glossary":{ + "hide_plugin":false, + "margins_xs":"", + "margins_sm":"", + "margins_md":"", + "margins_lg":"", + "interval":5, + "options":[ + "slide", + "pause", + "wrap" + ], + "container_max_heights":{ + "xs":"9rem", + "sm":"9rem", + "md":"9rem", + "lg":"9rem", + "xl":"9rem" + }, + "resize_options":[ + "upscale", + "crop", + "subject_location", + "high_resolution" + ] + }, + "pk":229 + }, + [ + [ + "BootstrapCarouselSlidePlugin", + { + "glossary":{ + "resize_options":[ + "upscale", + "crop", + "subject_location", + "high_resolution" + ], + "image":{ + "pk":4, + "model":"filer.Image" + }, + "media_queries":{ + "xs":{ + "width":572, + "media":"(max-width: 575.98px)" + }, + "sm":{ + "width":540, + "media":"(min-width: 576px) and (max-width: 767.98px)" + }, + "md":{ + "width":720, + "media":"(min-width: 768px) and (max-width: 991.98px)" + }, + "lg":{ + "width":960, + "media":"(min-width: 992px) and (max-width: 1199.98px)" + }, + "xl":{ + "width":1140, + "media":"(min-width: 1200px)" + } + } + }, + "pk":1526 + }, + [] + ] + ] + ] + ] +} diff --git a/cmsplugin_cascade/static/cascade/admin/clipboards/demo_carousel-plugin (copie).json b/cmsplugin_cascade/static/cascade/admin/clipboards/demo_carousel-plugin (copie).json new file mode 100644 index 000000000..b5350d77d --- /dev/null +++ b/cmsplugin_cascade/static/cascade/admin/clipboards/demo_carousel-plugin (copie).json @@ -0,0 +1,79 @@ +{ + "plugins": [ + [ + "BootstrapCarouselPlugin", + { + "glossary":{ + "hide_plugin":false, + "margins_xs":"", + "margins_sm":"", + "margins_md":"", + "margins_lg":"", + "interval":5, + "options":[ + "slide", + "pause", + "wrap" + ], + "container_max_heights":{ + "xs":"9rem", + "sm":"9rem", + "md":"9rem", + "lg":"9rem", + "xl":"9rem" + }, + "resize_options":[ + "upscale", + "crop", + "subject_location", + "high_resolution" + ] + }, + "pk":229 + }, + [ + [ + "BootstrapCarouselSlidePlugin", + { + "glossary":{ + "resize_options":[ + "upscale", + "crop", + "subject_location", + "high_resolution" + ], + "image":{ + "pk":4, + "model":"filer.Image" + }, + "media_queries":{ + "xs":{ + "width":572, + "media":"(max-width: 575.98px)" + }, + "sm":{ + "width":540, + "media":"(min-width: 576px) and (max-width: 767.98px)" + }, + "md":{ + "width":720, + "media":"(min-width: 768px) and (max-width: 991.98px)" + }, + "lg":{ + "width":960, + "media":"(min-width: 992px) and (max-width: 1199.98px)" + }, + "xl":{ + "width":1140, + "media":"(min-width: 1200px)" + } + } + }, + "pk":1526 + }, + [] + ] + ] + ] + ] +} diff --git a/cmsplugin_cascade/static/cascade/admin/clipboards/demo_carousel-plugin.json b/cmsplugin_cascade/static/cascade/admin/clipboards/demo_carousel-plugin.json new file mode 100644 index 000000000..b5350d77d --- /dev/null +++ b/cmsplugin_cascade/static/cascade/admin/clipboards/demo_carousel-plugin.json @@ -0,0 +1,79 @@ +{ + "plugins": [ + [ + "BootstrapCarouselPlugin", + { + "glossary":{ + "hide_plugin":false, + "margins_xs":"", + "margins_sm":"", + "margins_md":"", + "margins_lg":"", + "interval":5, + "options":[ + "slide", + "pause", + "wrap" + ], + "container_max_heights":{ + "xs":"9rem", + "sm":"9rem", + "md":"9rem", + "lg":"9rem", + "xl":"9rem" + }, + "resize_options":[ + "upscale", + "crop", + "subject_location", + "high_resolution" + ] + }, + "pk":229 + }, + [ + [ + "BootstrapCarouselSlidePlugin", + { + "glossary":{ + "resize_options":[ + "upscale", + "crop", + "subject_location", + "high_resolution" + ], + "image":{ + "pk":4, + "model":"filer.Image" + }, + "media_queries":{ + "xs":{ + "width":572, + "media":"(max-width: 575.98px)" + }, + "sm":{ + "width":540, + "media":"(min-width: 576px) and (max-width: 767.98px)" + }, + "md":{ + "width":720, + "media":"(min-width: 768px) and (max-width: 991.98px)" + }, + "lg":{ + "width":960, + "media":"(min-width: 992px) and (max-width: 1199.98px)" + }, + "xl":{ + "width":1140, + "media":"(min-width: 1200px)" + } + } + }, + "pk":1526 + }, + [] + ] + ] + ] + ] +} diff --git a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard.html b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard.html new file mode 100644 index 000000000..21352d59e --- /dev/null +++ b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard.html @@ -0,0 +1,44 @@ +{% load static cascade_tags %} +{% spaceless %} +{% include "cascade/admin/widgets/clipboard_menu_group.html" %} + +{% with id=widget.attrs.id %} + + +
+
+ {% for group, options, index in widget.optgroups %} +
+ {% for option in options %} + +

+

{{ option.label }}

+ {% include "cascade/admin/widgets/clipboard_stride.html" %} +

+ {% endfor %} +
+ {% endfor %} +
+ + +
+
    + {% for group, options, index in widget.optgroups %} + {% for option in options %} +
  • + +
  • + {% endfor %} + {% endfor %} +
+
+
+
+
+ +{% endwith %} +{% endspaceless %} diff --git a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_stride.html b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_stride.html new file mode 100644 index 000000000..0014cc461 --- /dev/null +++ b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_stride.html @@ -0,0 +1,11 @@ +{% load i18n cms_admin cascade_tags %} +
+
+ {% cascadeclipboard_data_by_identifier qs_clipboards option.label as data %} + + + {% render_cascade data option.label %} + + +
+
From 5c1c9bff797bf11d0a991ce9e5af2aeaf8973a2b Mon Sep 17 00:00:00 2001 From: Haricot Date: Sat, 22 Feb 2020 22:47:34 +0100 Subject: [PATCH 10/69] wip view_groups clipboard --- .../demo_carousel-plugin (copie).json | 79 ------------------- 1 file changed, 79 deletions(-) delete mode 100644 cmsplugin_cascade/static/cascade/admin/clipboards/demo_carousel-plugin (copie).json diff --git a/cmsplugin_cascade/static/cascade/admin/clipboards/demo_carousel-plugin (copie).json b/cmsplugin_cascade/static/cascade/admin/clipboards/demo_carousel-plugin (copie).json deleted file mode 100644 index b5350d77d..000000000 --- a/cmsplugin_cascade/static/cascade/admin/clipboards/demo_carousel-plugin (copie).json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "plugins": [ - [ - "BootstrapCarouselPlugin", - { - "glossary":{ - "hide_plugin":false, - "margins_xs":"", - "margins_sm":"", - "margins_md":"", - "margins_lg":"", - "interval":5, - "options":[ - "slide", - "pause", - "wrap" - ], - "container_max_heights":{ - "xs":"9rem", - "sm":"9rem", - "md":"9rem", - "lg":"9rem", - "xl":"9rem" - }, - "resize_options":[ - "upscale", - "crop", - "subject_location", - "high_resolution" - ] - }, - "pk":229 - }, - [ - [ - "BootstrapCarouselSlidePlugin", - { - "glossary":{ - "resize_options":[ - "upscale", - "crop", - "subject_location", - "high_resolution" - ], - "image":{ - "pk":4, - "model":"filer.Image" - }, - "media_queries":{ - "xs":{ - "width":572, - "media":"(max-width: 575.98px)" - }, - "sm":{ - "width":540, - "media":"(min-width: 576px) and (max-width: 767.98px)" - }, - "md":{ - "width":720, - "media":"(min-width: 768px) and (max-width: 991.98px)" - }, - "lg":{ - "width":960, - "media":"(min-width: 992px) and (max-width: 1199.98px)" - }, - "xl":{ - "width":1140, - "media":"(min-width: 1200px)" - } - } - }, - "pk":1526 - }, - [] - ] - ] - ] - ] -} From e7e6d6b4ca3cf12596b73e58e70d9cf080ec08b2 Mon Sep 17 00:00:00 2001 From: Haricot Date: Sat, 22 Feb 2020 22:48:20 +0100 Subject: [PATCH 11/69] wip view_groups clipboard --- cmsplugin_cascade/clipboard/cms_plugins.py | 14 +- .../admin/widgets/clipboard_menu_group.html | 21 + examples/bs4demo/settings.py | 1 + examples/poetry.lock | 656 ++++++++++++++++++ 4 files changed, 685 insertions(+), 7 deletions(-) create mode 100644 cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_menu_group.html create mode 100644 examples/poetry.lock diff --git a/cmsplugin_cascade/clipboard/cms_plugins.py b/cmsplugin_cascade/clipboard/cms_plugins.py index 77a80008d..2ecdd1c9d 100644 --- a/cmsplugin_cascade/clipboard/cms_plugins.py +++ b/cmsplugin_cascade/clipboard/cms_plugins.py @@ -123,14 +123,14 @@ def treegroup( groups, index2): clipboards_groupby.setdefault('ungroup', []) clipboards_groupby['ungroup'].append(( groups.identifier ,groups.identifier,)) - [treegroup( groups, index) for index, groups in enumerate(queryset , start=1)] - + [treegroup( groups, index) for index, groups in enumerate(queryset , start=1)] + if not 'Clipboard Home' in clipboards_groupby: clipboards_groupby[ 'Clipboard Home'] = [('demo', 'demo')] clipboard_home = CascadeClipboardGroup.objects.create(name="Clipboard Home") data_demo = self.populate_static_json("demo_carousel-plugin.json") print(data_demo) - group_clipboard_home = clipboard_home + group_clipboard_home = clipboard_home cascade_clipboard = CascadeClipboard.objects.create( identifier="demo", data=data_demo, @@ -138,11 +138,11 @@ def treegroup( groups, index2): cascade_clipboard.group.set([group_clipboard_home]) CHOICES=(list(clipboards_groupby.items(),)) - ff=_("Import from Clipboard") + ff=_("Import from Clipboard") if request.GET.get('group'): req_parameter_group = request.GET.get('group') - title = f": {req_parameter_group}" + title = f": {req_parameter_group}" else: req_parameter_group = "Clipboard Home" title = _("Import to Clipboard") @@ -151,7 +151,7 @@ def treegroup( groups, index2): if not req_parameter_group in clipboards_groupby: clipboards_groupby[req_parameter_group] = '' - if 'ungroup' in clipboards_groupby : + if 'ungroup' in clipboards_groupby : len_ungroup = len(clipboards_groupby[req_parameter_group]) else: len_ungroup = 0 @@ -284,7 +284,7 @@ def add_to_clipboard(self, request, form): def populate_static_json(self, filename): import os, io, json from django.contrib.staticfiles import finders - clipboard_folder ='cascade/admin/clipboard/' + clipboard_folder ='cascade/admin/clipboards/' path = finders.find(clipboard_folder) with io.open(os.path.join(path, filename), 'r') as fh: config_data = json.load(fh) diff --git a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_menu_group.html b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_menu_group.html new file mode 100644 index 000000000..563072fd7 --- /dev/null +++ b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_menu_group.html @@ -0,0 +1,21 @@ + +
+ {% for group in groups_exclude_home %} + {{ group }} + {% endfor %} + {% if widget.attrs.count_target %} + Ungroup + {% endif %} +
diff --git a/examples/bs4demo/settings.py b/examples/bs4demo/settings.py index 8e8e3f130..2ef5ea32e 100644 --- a/examples/bs4demo/settings.py +++ b/examples/bs4demo/settings.py @@ -62,6 +62,7 @@ 'sass_processor', 'sekizai', 'bs4demo', + 'django_extensions', ] diff --git a/examples/poetry.lock b/examples/poetry.lock new file mode 100644 index 000000000..52f905dde --- /dev/null +++ b/examples/poetry.lock @@ -0,0 +1,656 @@ +[[package]] +category = "dev" +description = "Python package for providing Mozilla's CA Bundle." +name = "certifi" +optional = false +python-versions = "*" +version = "2019.11.28" + +[[package]] +category = "dev" +description = "Universal encoding detector for Python 2 and 3" +name = "chardet" +optional = false +python-versions = "*" +version = "3.0.4" + +[[package]] +category = "dev" +description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design." +name = "django" +optional = false +python-versions = ">=3.5" +version = "2.2.10" + +[package.dependencies] +pytz = "*" +sqlparse = "*" + +[package.extras] +argon2 = ["argon2-cffi (>=16.1.0)"] +bcrypt = ["bcrypt"] + +[[package]] +category = "dev" +description = "Generic drag-and-drop sorting for the List, the Stacked- and the Tabular-Inlines Views in the Django Admin" +name = "django-admin-sortable2" +optional = false +python-versions = "*" +version = "0.7.5" + +[package.dependencies] +Django = ">=1.8,<3.1" + +[[package]] +category = "dev" +description = "A helper class for handling configuration defaults of packaged apps gracefully." +name = "django-appconf" +optional = false +python-versions = "*" +version = "1.0.3" + +[package.dependencies] +django = "*" +six = "*" + +[[package]] +category = "dev" +description = "Class based template tags for Django" +name = "django-classy-tags" +optional = false +python-versions = "*" +version = "1.0.0" + +[package.dependencies] +django = ">=1.11" +six = "*" + +[[package]] +category = "dev" +description = "An Advanced Django CMS" +name = "django-cms" +optional = false +python-versions = "*" +version = "3.7.1" + +[package.dependencies] +Django = ">=1.11,<3.0" +django-classy-tags = ">=0.7.2" +django-formtools = ">=2.1" +django-sekizai = ">=0.7" +django-treebeard = ">=4.3" +djangocms-admin-style = ">=1.2" + +[[package]] +category = "dev" +description = "Compresses linked and inline JavaScript or CSS into single cached files." +name = "django-compressor" +optional = false +python-versions = "*" +version = "2.4" + +[package.dependencies] +django-appconf = ">=1.0.3" +rcssmin = "1.0.6" +rjsmin = "1.1.0" +six = ">=1.12.0" + +[[package]] +category = "dev" +description = "Dynamic global and instance settings for your django project" +name = "django-dynamic-preferences" +optional = false +python-versions = "*" +version = "1.8.1" + +[package.dependencies] +django = ">=1.11" +persisting-theory = ">=0.2.1" +six = "*" + +[[package]] +category = "dev" +description = "Edit JSON field using Django Model Form" +name = "django-entangled" +optional = false +python-versions = "*" +version = "0.3" + +[[package]] +category = "dev" +description = "A file management application for django that makes handling of files and images a breeze." +name = "django-filer" +optional = false +python-versions = "*" +version = "1.6.0" + +[package.dependencies] +Unidecode = ">=0.04,<1.1" +django = ">=1.11,<3.0" +django-mptt = ">=0.6,<1.0" +django_polymorphic = ">=0.7,<2.1" +easy-thumbnails = ">=2,<3.0" + +[[package]] +category = "dev" +description = "A set of high-level abstractions for Django forms" +name = "django-formtools" +optional = false +python-versions = "*" +version = "2.2" + +[package.dependencies] +Django = ">=1.11" + +[[package]] +category = "dev" +description = "script tag with additional attributes for django.forms.Media" +name = "django-js-asset" +optional = false +python-versions = "*" +version = "1.2.2" + +[[package]] +category = "dev" +description = "Utilities for implementing Modified Preorder Tree Traversal with your Django Models and working with trees of Model instances." +name = "django-mptt" +optional = false +python-versions = ">=3.5" +version = "0.11.0" + +[package.dependencies] +Django = ">=1.11" +django-js-asset = "*" + +[[package]] +category = "dev" +description = "Seamless polymorphic inheritance for Django models" +name = "django-polymorphic" +optional = false +python-versions = "*" +version = "2.0.3" + +[package.dependencies] +Django = ">=1.11" + +[[package]] +category = "dev" +description = "SASS processor to compile SCSS files into *.css, while rendering, or offline." +name = "django-sass-processor" +optional = false +python-versions = "*" +version = "0.8" + +[package.extras] +dev = ["libsass (>=0.13)"] +management-command = ["django-compressor (>=2.4)"] + +[[package]] +category = "dev" +description = "Django Sekizai" +name = "django-sekizai" +optional = false +python-versions = "*" +version = "1.1.0" + +[package.dependencies] +django = ">=1.11" +django-classy-tags = ">=0.9.0" +six = "*" + +[[package]] +category = "dev" +description = "Select2 option fields for Django" +name = "django-select2" +optional = false +python-versions = "*" +version = "7.2.0" + +[package.dependencies] +django = ">=2.2" +django-appconf = ">=0.6.0" + +[[package]] +category = "dev" +description = "Efficient tree implementations for Django" +name = "django-treebeard" +optional = false +python-versions = "*" +version = "4.3.1" + +[package.dependencies] +Django = ">=1.8" + +[[package]] +category = "dev" +description = "Adds pretty CSS styles for the django CMS admin interface." +name = "djangocms-admin-style" +optional = false +python-versions = "*" +version = "1.5.0" + +[[package]] +category = "dev" +description = "Templates and templatetags to be used with django-CMS with Bootstrap3 or Bootstrap4." +name = "djangocms-bootstrap" +optional = false +python-versions = "*" +version = "1.1.2" + +[package.dependencies] +django-cms = ">3.4" + +[[package]] +category = "dev" +description = "Build Single Page Applications using the Django-CMS plugin system" +name = "djangocms-cascade" +optional = false +python-versions = "*" +version = "1.2.3" + +[package.dependencies] +django = ">=1.11,<3.0" +django-classy-tags = ">=0.8" +django-cms = ">=3.5,<4" +django-entangled = "*" +djangocms-text-ckeditor = ">=3.7" +jsonfield = "*" +requests = "*" + +[[package]] +category = "dev" +description = "Adds undo/redo functionality to django CMS" +name = "djangocms-history" +optional = false +python-versions = "*" +version = "1.1.0" + +[package.dependencies] +django-cms = ">=3.4.5" + +[[package]] +category = "dev" +description = "Text Plugin for django CMS with CKEditor support" +name = "djangocms-text-ckeditor" +optional = false +python-versions = "*" +version = "3.8.0" + +[package.dependencies] +Pillow = "*" +django-cms = ">=3.4.5" +html5lib = ">=0.999999999" + +[[package]] +category = "dev" +description = "Adds import and export of plugin data." +name = "djangocms-transfer" +optional = false +python-versions = "*" +version = "0.2.0" + +[package.dependencies] +django-cms = ">=3.4.5" + +[[package]] +category = "dev" +description = "Easy thumbnails for Django" +name = "easy-thumbnails" +optional = false +python-versions = ">=3.5" +version = "2.7" + +[package.dependencies] +django = ">=1.11,<4.0" +pillow = "*" + +[[package]] +category = "dev" +description = "HTML parser based on the WHATWG HTML specification" +name = "html5lib" +optional = false +python-versions = "*" +version = "1.0.1" + +[package.dependencies] +six = ">=1.9" +webencodings = "*" + +[package.extras] +all = ["genshi", "chardet (>=2.2)", "datrie", "lxml"] +chardet = ["chardet (>=2.2)"] +datrie = ["datrie"] +genshi = ["genshi"] +lxml = ["lxml"] + +[[package]] +category = "dev" +description = "Internationalized Domain Names in Applications (IDNA)" +name = "idna" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.8" + +[[package]] +category = "dev" +description = "A reusable Django field that allows you to store validated JSON in your model." +name = "jsonfield" +optional = false +python-versions = "*" +version = "3.0.0" + +[package.dependencies] +Django = ">=2.2" + +[[package]] +category = "dev" +description = "Sass for Python: A straightforward binding of libsass for Python." +name = "libsass" +optional = false +python-versions = "*" +version = "0.19.4" + +[package.dependencies] +six = "*" + +[[package]] +category = "dev" +description = "Registries that can autodiscover values accross your project apps" +name = "persisting-theory" +optional = false +python-versions = "*" +version = "0.2.1" + +[[package]] +category = "dev" +description = "Python Imaging Library (Fork)" +name = "pillow" +optional = false +python-versions = ">=3.5" +version = "7.0.0" + +[[package]] +category = "dev" +description = "World timezone definitions, modern and historical" +name = "pytz" +optional = false +python-versions = "*" +version = "2019.3" + +[[package]] +category = "dev" +description = "CSS Minifier" +name = "rcssmin" +optional = false +python-versions = "*" +version = "1.0.6" + +[[package]] +category = "dev" +description = "Python HTTP for Humans." +name = "requests" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "2.22.0" + +[package.dependencies] +certifi = ">=2017.4.17" +chardet = ">=3.0.2,<3.1.0" +idna = ">=2.5,<2.9" +urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" + +[package.extras] +security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)"] +socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] + +[[package]] +category = "dev" +description = "Javascript Minifier" +name = "rjsmin" +optional = false +python-versions = "*" +version = "1.1.0" + +[[package]] +category = "dev" +description = "Python 2 and 3 compatibility utilities" +name = "six" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +version = "1.14.0" + +[[package]] +category = "dev" +description = "Non-validating SQL parser" +name = "sqlparse" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.3.0" + +[[package]] +category = "dev" +description = "ASCII transliterations of Unicode text" +name = "unidecode" +optional = false +python-versions = "*" +version = "1.0.23" + +[[package]] +category = "dev" +description = "HTTP library with thread-safe connection pooling, file post, and more." +name = "urllib3" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" +version = "1.25.8" + +[package.extras] +brotli = ["brotlipy (>=0.6.0)"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] + +[[package]] +category = "dev" +description = "Character encoding aliases for legacy web content" +name = "webencodings" +optional = false +python-versions = "*" +version = "0.5.1" + +[metadata] +content-hash = "2115171a195bcabffe11d3f5f9b97eb118bbc02c9c77c45ed3c7c68db1550ff6" +python-versions = "^3.6" + +[metadata.files] +certifi = [ + {file = "certifi-2019.11.28-py2.py3-none-any.whl", hash = "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3"}, + {file = "certifi-2019.11.28.tar.gz", hash = "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"}, +] +chardet = [ + {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, + {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, +] +django = [ + {file = "Django-2.2.10-py3-none-any.whl", hash = "sha256:9a4635813e2d498a3c01b10c701fe4a515d76dd290aaa792ccb65ca4ccb6b038"}, + {file = "Django-2.2.10.tar.gz", hash = "sha256:1226168be1b1c7efd0e66ee79b0e0b58b2caa7ed87717909cd8a57bb13a7079a"}, +] +django-admin-sortable2 = [ + {file = "django-admin-sortable2-0.7.5.tar.gz", hash = "sha256:1366cb159439aa28472639dc1f33d6370a56d4c3efbe3def854d953aaa6bbd24"}, +] +django-appconf = [ + {file = "django-appconf-1.0.3.tar.gz", hash = "sha256:35f13ca4d567f132b960e2cd4c832c2d03cb6543452d34e29b7ba10371ba80e3"}, + {file = "django_appconf-1.0.3-py2.py3-none-any.whl", hash = "sha256:c98a7af40062e996b921f5962a1c4f3f0c979fa7885f7be4710cceb90ebe13a6"}, +] +django-classy-tags = [ + {file = "django-classy-tags-1.0.0.tar.gz", hash = "sha256:ad6a25fc2b58a098f00d86bd5e5dad47922f5ca4e744bc3cccb7b4be5bc35eb1"}, +] +django-cms = [ + {file = "django-cms-3.7.1.tar.gz", hash = "sha256:05ea915d490562413428e04acb9ecae604c63b8dc5ed9250d0e9437b1314c996"}, + {file = "django_cms-3.7.1-py2.py3-none-any.whl", hash = "sha256:89d1e3bebd732a6bd00b0cfaf8128bfb0924429e83f1297e0b34dd9343e55b7a"}, +] +django-compressor = [ + {file = "django_compressor-2.4-py2.py3-none-any.whl", hash = "sha256:57ac0a696d061e5fc6fbc55381d2050f353b973fb97eee5593f39247bc0f30af"}, + {file = "django_compressor-2.4.tar.gz", hash = "sha256:d2ed1c6137ddaac5536233ec0a819e14009553fee0a869bea65d03e5285ba74f"}, +] +django-dynamic-preferences = [ + {file = "django-dynamic-preferences-1.8.1.tar.gz", hash = "sha256:727f71f865ff29df93c01a0a1686af0dca19776ee786f876ea0ae7e062719b24"}, + {file = "django_dynamic_preferences-1.8.1-py2.py3-none-any.whl", hash = "sha256:1d600c2f88baa9e0098e856f8a14e320a4a479305deea1afbc8722d953c44c65"}, +] +django-entangled = [ + {file = "django-entangled-0.3.tar.gz", hash = "sha256:e51cb1748f30d3dc474fd32d195f051307507a3dbbe789f0fed29b80ce2e6a35"}, +] +django-filer = [ + {file = "django-filer-1.6.0.tar.gz", hash = "sha256:3f2045cfd9e53c1a29cd8a71747e984faead630ee72baab29d6b3b45584d52e0"}, +] +django-formtools = [ + {file = "django-formtools-2.2.tar.gz", hash = "sha256:c5272c03c1cd51b2375abf7397a199a3148a9fbbf2f100e186467a84025d13b2"}, + {file = "django_formtools-2.2-py2.py3-none-any.whl", hash = "sha256:304fa777b8ef9e0693ce7833f885cb89ba46b0e46fc23b01176900a93f46742f"}, +] +django-js-asset = [ + {file = "django-js-asset-1.2.2.tar.gz", hash = "sha256:c163ae80d2e0b22d8fb598047cd0dcef31f81830e127cfecae278ad574167260"}, + {file = "django_js_asset-1.2.2-py2.py3-none-any.whl", hash = "sha256:8ec12017f26eec524cab436c64ae73033368a372970af4cf42d9354fcb166bdd"}, +] +django-mptt = [ + {file = "django-mptt-0.11.0.tar.gz", hash = "sha256:dfdb3af75ad27cdd4458b0544ec8574174f2b90f99bc2cafab6a15b4bc1895a8"}, + {file = "django_mptt-0.11.0-py2.py3-none-any.whl", hash = "sha256:90eb236eb4f1a92124bd7c37852bbe09c0d21158477cc237556d59842a91c509"}, +] +django-polymorphic = [ + {file = "django-polymorphic-2.0.3.tar.gz", hash = "sha256:1fb5505537bcaf71cfc951ff94c4e3ba83c761eaca04b7b2ce9cb63937634ea5"}, + {file = "django_polymorphic-2.0.3-py2.py3-none-any.whl", hash = "sha256:79e7df455fdc8c3d28d38b7ab8323fc21d109a162b8ca282119e0e9ce8db7bdb"}, +] +django-sass-processor = [ + {file = "django-sass-processor-0.8.tar.gz", hash = "sha256:e039551994feaaba6fcf880412b25a772dd313162a34cbb4289814988cfae340"}, +] +django-sekizai = [ + {file = "django-sekizai-1.1.0.tar.gz", hash = "sha256:e2f6e666d4dd9d3ecc27284acb85ef709e198014f5d5af8c6d54ed04c2d684d9"}, +] +django-select2 = [ + {file = "django-select2-7.2.0.tar.gz", hash = "sha256:4c531cb7e9eb4152c7e5f8ab83be386f46978b3d80e91e55ad1fb46382222a0b"}, + {file = "django_select2-7.2.0-py2.py3-none-any.whl", hash = "sha256:d17bb0e64503a7e52ba405f73a187664906cefda5f1c33971c67ab0b3891e91c"}, +] +django-treebeard = [ + {file = "django-treebeard-4.3.1.tar.gz", hash = "sha256:83aebc34a9f06de7daaec330d858d1c47887e81be3da77e3541fe7368196dd8a"}, +] +djangocms-admin-style = [ + {file = "djangocms-admin-style-1.5.0.tar.gz", hash = "sha256:47d9868aa448e593ce90059d6542a4d907c120d357cd0561c586f5557e3ba316"}, +] +djangocms-bootstrap = [ + {file = "djangocms-bootstrap-1.1.2.tar.gz", hash = "sha256:60e0802111df17298933104fdc73fc7bb74924e435d5c025b3a12a29a10e6761"}, +] +djangocms-cascade = [ + {file = "djangocms-cascade-1.2.3.tar.gz", hash = "sha256:5fb0e6519d36577469bbbd6645c41220db0abade147fe6e0dc530c02b845755a"}, +] +djangocms-history = [ + {file = "djangocms-history-1.1.0.tar.gz", hash = "sha256:783c0aea25671e14df634838660e53023d6559c66697c9d8d8cfa771842e9ff2"}, +] +djangocms-text-ckeditor = [ + {file = "djangocms-text-ckeditor-3.8.0.tar.gz", hash = "sha256:0f0291cdf305c469741a639d89c71ee77f29dfc5aada4f7a453d6dc2926ceca9"}, +] +djangocms-transfer = [ + {file = "djangocms-transfer-0.2.0.tar.gz", hash = "sha256:9e7d251a82fa13beb6bf541a157a63b8306a8ae29a90349c783f0077e439f325"}, +] +easy-thumbnails = [ + {file = "easy-thumbnails-2.7.tar.gz", hash = "sha256:e4e7a0dd4001f56bfd4058428f2c91eafe27d33ef3b8b33ac4e013b159b9ff91"}, +] +html5lib = [ + {file = "html5lib-1.0.1-py2.py3-none-any.whl", hash = "sha256:20b159aa3badc9d5ee8f5c647e5efd02ed2a66ab8d354930bd9ff139fc1dc0a3"}, + {file = "html5lib-1.0.1.tar.gz", hash = "sha256:66cb0dcfdbbc4f9c3ba1a63fdb511ffdbd4f513b2b6d81b80cd26ce6b3fb3736"}, +] +idna = [ + {file = "idna-2.8-py2.py3-none-any.whl", hash = "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"}, + {file = "idna-2.8.tar.gz", hash = "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407"}, +] +jsonfield = [ + {file = "jsonfield-3.0.0-py3-none-any.whl", hash = "sha256:a6616f38db42542d1f2434f5eae1a91550e5cc15dff27edd87b4358493ef2a92"}, + {file = "jsonfield-3.0.0.tar.gz", hash = "sha256:1a17efe9a26850e9175900cb37cf30c6182fc2251989f8af44ef47fbbf48eaff"}, +] +libsass = [ + {file = "libsass-0.19.4-cp27-cp27m-macosx_10_14_intel.whl", hash = "sha256:74acd9adf506142699dfa292f0e569fdccbd9e7cf619e8226f7117de73566e32"}, + {file = "libsass-0.19.4-cp27-cp27m-win32.whl", hash = "sha256:50778d4be269a021ba2bf42b5b8f6ff3704ab96a82175a052680bddf3ba7cc9f"}, + {file = "libsass-0.19.4-cp27-cp27m-win_amd64.whl", hash = "sha256:4dcfd561fb100250b89496e1362b96f2cc804f689a59731eb0f94f9a9e144f4a"}, + {file = "libsass-0.19.4-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:845a9573b25c141164972d498855f4ad29367c09e6d76fad12955ad0e1c83013"}, + {file = "libsass-0.19.4-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:81a013a4c2a614927fd1ef7a386eddabbba695cbb02defe8f31cf495106e974c"}, + {file = "libsass-0.19.4-cp35-cp35m-win32.whl", hash = "sha256:fcb7ab4dc81889e5fc99cafbc2017bc76996f9992fc6b175f7a80edac61d71df"}, + {file = "libsass-0.19.4-cp35-cp35m-win_amd64.whl", hash = "sha256:fc5f8336750f76f1bfae82f7e9e89ae71438d26fc4597e3ab4c05ca8fcd41d8a"}, + {file = "libsass-0.19.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:9b59afa0d755089c4165516400a39a289b796b5612eeef5736ab7a1ebf96a67c"}, + {file = "libsass-0.19.4-cp36-cp36m-win32.whl", hash = "sha256:c93df526eeef90b1ea4799c1d33b6cd5aea3e9f4633738fb95c1287c13e6b404"}, + {file = "libsass-0.19.4-cp36-cp36m-win_amd64.whl", hash = "sha256:0fd8b4337b3b101c6e6afda9112cc0dc4bacb9133b59d75d65968c7317aa3272"}, + {file = "libsass-0.19.4-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:003a65b4facb4c5dbace53fb0f70f61c5aae056a04b4d112a198c3c9674b31f2"}, + {file = "libsass-0.19.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:338e9ae066bf1fde874e335324d5355c52d2081d978b4f74fc59536564b35b08"}, + {file = "libsass-0.19.4-cp37-cp37m-win32.whl", hash = "sha256:e318f06f06847ff49b1f8d086ac9ebce1e63404f7ea329adab92f4f16ba0e00e"}, + {file = "libsass-0.19.4-cp37-cp37m-win_amd64.whl", hash = "sha256:a7e685466448c9b1bf98243339793978f654a1151eb5c975f09b83c7a226f4c1"}, + {file = "libsass-0.19.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6a51393d75f6e3c812785b0fa0b7d67c54258c28011921f204643b55f7355ec0"}, + {file = "libsass-0.19.4.tar.gz", hash = "sha256:8b5b6d1a7c4ea1d954e0982b04474cc076286493f6af2d0a13c2e950fbe0be95"}, +] +persisting-theory = [ + {file = "persisting-theory-0.2.1.tar.gz", hash = "sha256:00ff7dcc8f481ff75c770ca5797d968e8725b6df1f77fe0cf7d20fa1e5790c0a"}, +] +pillow = [ + {file = "Pillow-7.0.0-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:5f3546ceb08089cedb9e8ff7e3f6a7042bb5b37c2a95d392fb027c3e53a2da00"}, + {file = "Pillow-7.0.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:9d2ba4ed13af381233e2d810ff3bab84ef9f18430a9b336ab69eaf3cd24299ff"}, + {file = "Pillow-7.0.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:ff3797f2f16bf9d17d53257612da84dd0758db33935777149b3334c01ff68865"}, + {file = "Pillow-7.0.0-cp35-cp35m-win32.whl", hash = "sha256:c18f70dc27cc5d236f10e7834236aff60aadc71346a5bc1f4f83a4b3abee6386"}, + {file = "Pillow-7.0.0-cp35-cp35m-win_amd64.whl", hash = "sha256:875358310ed7abd5320f21dd97351d62de4929b0426cdb1eaa904b64ac36b435"}, + {file = "Pillow-7.0.0-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:ab76e5580b0ed647a8d8d2d2daee170e8e9f8aad225ede314f684e297e3643c2"}, + {file = "Pillow-7.0.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a62ec5e13e227399be73303ff301f2865bf68657d15ea50b038d25fc41097317"}, + {file = "Pillow-7.0.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8ac6ce7ff3892e5deaab7abaec763538ffd011f74dc1801d93d3c5fc541feee2"}, + {file = "Pillow-7.0.0-cp36-cp36m-win32.whl", hash = "sha256:91b710e3353aea6fc758cdb7136d9bbdcb26b53cefe43e2cba953ac3ee1d3313"}, + {file = "Pillow-7.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:bf598d2e37cf8edb1a2f26ed3fb255191f5232badea4003c16301cb94ac5bdd0"}, + {file = "Pillow-7.0.0-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:5bfef0b1cdde9f33881c913af14e43db69815c7e8df429ceda4c70a5e529210f"}, + {file = "Pillow-7.0.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:dc058b7833184970d1248135b8b0ab702e6daa833be14035179f2acb78ff5636"}, + {file = "Pillow-7.0.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:c5ed816632204a2fc9486d784d8e0d0ae754347aba99c811458d69fcdfd2a2f9"}, + {file = "Pillow-7.0.0-cp37-cp37m-win32.whl", hash = "sha256:54ebae163e8412aff0b9df1e88adab65788f5f5b58e625dc5c7f51eaf14a6837"}, + {file = "Pillow-7.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:87269cc6ce1e3dee11f23fa515e4249ae678dbbe2704598a51cee76c52e19cda"}, + {file = "Pillow-7.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0a628977ac2e01ca96aaae247ec2bd38e729631ddf2221b4b715446fd45505be"}, + {file = "Pillow-7.0.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:62a889aeb0a79e50ecf5af272e9e3c164148f4bd9636cc6bcfa182a52c8b0533"}, + {file = "Pillow-7.0.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:bf4003aa538af3f4205c5fac56eacaa67a6dd81e454ffd9e9f055fff9f1bc614"}, + {file = "Pillow-7.0.0-cp38-cp38-win32.whl", hash = "sha256:7406f5a9b2fd966e79e6abdaf700585a4522e98d6559ce37fc52e5c955fade0a"}, + {file = "Pillow-7.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:5f7ae9126d16194f114435ebb79cc536b5682002a4fa57fa7bb2cbcde65f2f4d"}, + {file = "Pillow-7.0.0-pp373-pypy36_pp73-win32.whl", hash = "sha256:8453f914f4e5a3d828281a6628cf517832abfa13ff50679a4848926dac7c0358"}, + {file = "Pillow-7.0.0.tar.gz", hash = "sha256:4d9ed9a64095e031435af120d3c910148067087541131e82b3e8db302f4c8946"}, +] +pytz = [ + {file = "pytz-2019.3-py2.py3-none-any.whl", hash = "sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d"}, + {file = "pytz-2019.3.tar.gz", hash = "sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"}, +] +rcssmin = [ + {file = "rcssmin-1.0.6.tar.gz", hash = "sha256:ca87b695d3d7864157773a61263e5abb96006e9ff0e021eff90cbe0e1ba18270"}, +] +requests = [ + {file = "requests-2.22.0-py2.py3-none-any.whl", hash = "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"}, + {file = "requests-2.22.0.tar.gz", hash = "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4"}, +] +rjsmin = [ + {file = "rjsmin-1.1.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:799890bd07a048892d8d3deb9042dbc20b7f5d0eb7da91e9483c561033b23ce2"}, + {file = "rjsmin-1.1.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:211c2fe8298951663bbc02acdffbf714f6793df54bfc50e1c6c9e71b3f2559a3"}, + {file = "rjsmin-1.1.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:585e75a84d9199b68056fd4a083d9a61e2a92dfd10ff6d4ce5bdb04bc3bdbfaf"}, + {file = "rjsmin-1.1.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e487a7783ac4339e79ec610b98228eb9ac72178973e3dee16eba0e3feef25924"}, + {file = "rjsmin-1.1.0-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:0ab825839125eaca57cc59581d72e596e58a7a56fbc0839996b7528f0343a0a8"}, + {file = "rjsmin-1.1.0-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:6044ca86e917cd5bb2f95e6679a4192cef812122f28ee08c677513de019629b3"}, + {file = "rjsmin-1.1.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:ecd29f1b3e66a4c0753105baec262b331bcbceefc22fbe6f7e8bcd2067bcb4d7"}, + {file = "rjsmin-1.1.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:975b69754d6a76be47c0bead12367a1ca9220d08e5393f80bab0230d4625d1f4"}, + {file = "rjsmin-1.1.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:466fe70cc5647c7c51b3260c7e2e323a98b2b173564247f9c89e977720a0645f"}, + {file = "rjsmin-1.1.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e3908b21ebb584ce74a6ac233bdb5f29485752c9d3be5e50c5484ed74169232c"}, + {file = "rjsmin-1.1.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:714329db774a90947e0e2086cdddb80d5e8c4ac1c70c9f92436378dedb8ae345"}, + {file = "rjsmin-1.1.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:dd0f4819df4243ffe4c964995794c79ca43943b5b756de84be92b445a652fb86"}, + {file = "rjsmin-1.1.0.tar.gz", hash = "sha256:b15dc75c71f65d9493a8c7fa233fdcec823e3f1b88ad84a843ffef49b338ac32"}, +] +six = [ + {file = "six-1.14.0-py2.py3-none-any.whl", hash = "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"}, + {file = "six-1.14.0.tar.gz", hash = "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a"}, +] +sqlparse = [ + {file = "sqlparse-0.3.0-py2.py3-none-any.whl", hash = "sha256:40afe6b8d4b1117e7dff5504d7a8ce07d9a1b15aeeade8a2d10f130a834f8177"}, + {file = "sqlparse-0.3.0.tar.gz", hash = "sha256:7c3dca29c022744e95b547e867cee89f4fce4373f3549ccd8797d8eb52cdb873"}, +] +unidecode = [ + {file = "Unidecode-1.0.23-py2.py3-none-any.whl", hash = "sha256:092cdf7ad9d1052c50313426a625b717dab52f7ac58f859e09ea020953b1ad8f"}, + {file = "Unidecode-1.0.23.tar.gz", hash = "sha256:8b85354be8fd0c0e10adbf0675f6dc2310e56fda43fa8fe049123b6c475e52fb"}, +] +urllib3 = [ + {file = "urllib3-1.25.8-py2.py3-none-any.whl", hash = "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc"}, + {file = "urllib3-1.25.8.tar.gz", hash = "sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"}, +] +webencodings = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] From f69c58b31dc9ec41da28e9c972ded73bd2f64bb2 Mon Sep 17 00:00:00 2001 From: Haricot Date: Sat, 22 Feb 2020 22:56:20 +0100 Subject: [PATCH 12/69] update migrations --- cmsplugin_cascade/migrations/0028_auto_20200218_1514.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmsplugin_cascade/migrations/0028_auto_20200218_1514.py b/cmsplugin_cascade/migrations/0028_auto_20200218_1514.py index a44f37f69..42ff05426 100644 --- a/cmsplugin_cascade/migrations/0028_auto_20200218_1514.py +++ b/cmsplugin_cascade/migrations/0028_auto_20200218_1514.py @@ -20,6 +20,6 @@ class Migration(migrations.Migration): migrations.AddField( model_name='cascadeclipboard', name='group', - field=models.ManyToManyField(to='cmsplugin_cascade.CascadeClipboardGroup'), + field=models.ManyToManyField(blank=True, to='cmsplugin_cascade.CascadeClipboardGroup'), ), ] From 3f1cdddc2555157a6e9b898a2bdd38471014e013 Mon Sep 17 00:00:00 2001 From: Haricot Date: Sat, 22 Feb 2020 23:01:02 +0100 Subject: [PATCH 13/69] wip view_group clipboard --- cmsplugin_cascade/clipboard/cms_plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmsplugin_cascade/clipboard/cms_plugins.py b/cmsplugin_cascade/clipboard/cms_plugins.py index 2ecdd1c9d..e7fa25470 100644 --- a/cmsplugin_cascade/clipboard/cms_plugins.py +++ b/cmsplugin_cascade/clipboard/cms_plugins.py @@ -60,7 +60,7 @@ def get_extra_placeholder_menu_items(cls, request, placeholder): data={}, action='modal', attributes={ - 'icon': 'import', + 'icon': 'export', }, ), PluginMenuItem( From be5f98cb614d09cf86c8aef6783a443625f3af80 Mon Sep 17 00:00:00 2001 From: Haricot Date: Sat, 22 Feb 2020 23:04:15 +0100 Subject: [PATCH 14/69] fix py35 test --- cmsplugin_cascade/clipboard/cms_plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmsplugin_cascade/clipboard/cms_plugins.py b/cmsplugin_cascade/clipboard/cms_plugins.py index e7fa25470..437d7e04a 100644 --- a/cmsplugin_cascade/clipboard/cms_plugins.py +++ b/cmsplugin_cascade/clipboard/cms_plugins.py @@ -142,7 +142,7 @@ def treegroup( groups, index2): if request.GET.get('group'): req_parameter_group = request.GET.get('group') - title = f": {req_parameter_group}" + title = ": {}".format(req_parameter_group) else: req_parameter_group = "Clipboard Home" title = _("Import to Clipboard") From 6170748f617c0754e22bc96e99c747d3e4d2c580 Mon Sep 17 00:00:00 2001 From: Haricot Date: Sat, 22 Feb 2020 23:10:48 +0100 Subject: [PATCH 15/69] wip groups clipboard --- cmsplugin_cascade/strides.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/cmsplugin_cascade/strides.py b/cmsplugin_cascade/strides.py index 8906dcd97..0598e7f90 100644 --- a/cmsplugin_cascade/strides.py +++ b/cmsplugin_cascade/strides.py @@ -206,9 +206,6 @@ def render_plugin(self, instance, context, placeholder=None, editable=False): from sekizai.helpers import get_varname as get_sekizai_context_key sekizai_context_key = get_sekizai_context_key() - print("sekizai_context_kesssssssssy") - print(sekizai_context_key) - print( placeholder) if app_settings.CMSPLUGIN_CASCADE['cache_strides'] and getattr(instance.plugin, 'cache', not editable): cache = caches['default'] key = 'cascade_element-{}'.format(instance.pk) From a85252b77067982803fa858a457bf062acd31804 Mon Sep 17 00:00:00 2001 From: Haricot Date: Sat, 22 Feb 2020 23:15:17 +0100 Subject: [PATCH 16/69] wip groups clipboard --- cmsplugin_cascade/clipboard/cms_plugins.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cmsplugin_cascade/clipboard/cms_plugins.py b/cmsplugin_cascade/clipboard/cms_plugins.py index 437d7e04a..10a5fe4fe 100644 --- a/cmsplugin_cascade/clipboard/cms_plugins.py +++ b/cmsplugin_cascade/clipboard/cms_plugins.py @@ -129,7 +129,6 @@ def treegroup( groups, index2): clipboards_groupby[ 'Clipboard Home'] = [('demo', 'demo')] clipboard_home = CascadeClipboardGroup.objects.create(name="Clipboard Home") data_demo = self.populate_static_json("demo_carousel-plugin.json") - print(data_demo) group_clipboard_home = clipboard_home cascade_clipboard = CascadeClipboard.objects.create( identifier="demo", From 25a3855e9d1fcbd4241c02590c78c282dc840d47 Mon Sep 17 00:00:00 2001 From: Haricot Date: Sat, 22 Feb 2020 23:18:13 +0100 Subject: [PATCH 17/69] remove unneeded file --- .../admin/clipboards/carousel-plugin.json | 79 ------------------- 1 file changed, 79 deletions(-) delete mode 100644 cmsplugin_cascade/static/cascade/admin/clipboards/carousel-plugin.json diff --git a/cmsplugin_cascade/static/cascade/admin/clipboards/carousel-plugin.json b/cmsplugin_cascade/static/cascade/admin/clipboards/carousel-plugin.json deleted file mode 100644 index b5350d77d..000000000 --- a/cmsplugin_cascade/static/cascade/admin/clipboards/carousel-plugin.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "plugins": [ - [ - "BootstrapCarouselPlugin", - { - "glossary":{ - "hide_plugin":false, - "margins_xs":"", - "margins_sm":"", - "margins_md":"", - "margins_lg":"", - "interval":5, - "options":[ - "slide", - "pause", - "wrap" - ], - "container_max_heights":{ - "xs":"9rem", - "sm":"9rem", - "md":"9rem", - "lg":"9rem", - "xl":"9rem" - }, - "resize_options":[ - "upscale", - "crop", - "subject_location", - "high_resolution" - ] - }, - "pk":229 - }, - [ - [ - "BootstrapCarouselSlidePlugin", - { - "glossary":{ - "resize_options":[ - "upscale", - "crop", - "subject_location", - "high_resolution" - ], - "image":{ - "pk":4, - "model":"filer.Image" - }, - "media_queries":{ - "xs":{ - "width":572, - "media":"(max-width: 575.98px)" - }, - "sm":{ - "width":540, - "media":"(min-width: 576px) and (max-width: 767.98px)" - }, - "md":{ - "width":720, - "media":"(min-width: 768px) and (max-width: 991.98px)" - }, - "lg":{ - "width":960, - "media":"(min-width: 992px) and (max-width: 1199.98px)" - }, - "xl":{ - "width":1140, - "media":"(min-width: 1200px)" - } - } - }, - "pk":1526 - }, - [] - ] - ] - ] - ] -} From 6950f080934ede2b23b9c6e9a8bfe937a46a3e1c Mon Sep 17 00:00:00 2001 From: Haricot Date: Sat, 22 Feb 2020 23:23:44 +0100 Subject: [PATCH 18/69] fix stride test required --- cmsplugin_cascade/templatetags/cascade_tags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmsplugin_cascade/templatetags/cascade_tags.py b/cmsplugin_cascade/templatetags/cascade_tags.py index bb2f003b3..e0a21e8b7 100644 --- a/cmsplugin_cascade/templatetags/cascade_tags.py +++ b/cmsplugin_cascade/templatetags/cascade_tags.py @@ -27,7 +27,7 @@ class StrideRenderer(Tag): name = 'render_cascade' options = Options( Argument('data_clipboard'), - Argument('identifier'), + Argument('identifier', required=True), ) def render_tag(self, context, data_clipboard, identifier=None): From 4921bb612d647a6957eeb1004248288677563f9c Mon Sep 17 00:00:00 2001 From: Haricot Date: Sat, 22 Feb 2020 23:41:49 +0100 Subject: [PATCH 19/69] wip groups clipboard --- cmsplugin_cascade/templatetags/cascade_tags.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cmsplugin_cascade/templatetags/cascade_tags.py b/cmsplugin_cascade/templatetags/cascade_tags.py index e0a21e8b7..3dc81d48a 100644 --- a/cmsplugin_cascade/templatetags/cascade_tags.py +++ b/cmsplugin_cascade/templatetags/cascade_tags.py @@ -69,7 +69,6 @@ def render_tag(self, context, data_clipboard, identifier=None): SEKIZAI_CONTENT_HOLDER = cache.get_or_set(sekizai_context_key, context.get(sekizai_context_key)) if SEKIZAI_CONTENT_HOLDER: for name in SEKIZAI_CONTENT_HOLDER: - context[sekizai_context_key] = {'name':''} context[sekizai_context_key][name] = SEKIZAI_CONTENT_HOLDER[name] return content From d67f68f54fad8dc98d1577916d037a59539e4fbd Mon Sep 17 00:00:00 2001 From: Haricot Date: Sat, 22 Feb 2020 23:45:28 +0100 Subject: [PATCH 20/69] wip groups clipboard (fix test) --- cmsplugin_cascade/templatetags/cascade_tags.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmsplugin_cascade/templatetags/cascade_tags.py b/cmsplugin_cascade/templatetags/cascade_tags.py index 3dc81d48a..37ce638e2 100644 --- a/cmsplugin_cascade/templatetags/cascade_tags.py +++ b/cmsplugin_cascade/templatetags/cascade_tags.py @@ -27,7 +27,7 @@ class StrideRenderer(Tag): name = 'render_cascade' options = Options( Argument('data_clipboard'), - Argument('identifier', required=True), + Argument('identifier', required=False), ) def render_tag(self, context, data_clipboard, identifier=None): @@ -129,7 +129,6 @@ def sphinx_docs_include(path): return mark_safe(fh.read()) - @register.simple_tag def cascadeclipboard_data_by_identifier(queryset, identifier ): qs_identifier=queryset.filter(identifier=identifier) From 1675e1d178158427fdfc407d66ee79c6cc6f86c8 Mon Sep 17 00:00:00 2001 From: Haricot Date: Sun, 23 Feb 2020 00:00:47 +0100 Subject: [PATCH 21/69] wip groups clipboard (fix test) --- cmsplugin_cascade/clipboard/cms_plugins.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmsplugin_cascade/clipboard/cms_plugins.py b/cmsplugin_cascade/clipboard/cms_plugins.py index 10a5fe4fe..77435a295 100644 --- a/cmsplugin_cascade/clipboard/cms_plugins.py +++ b/cmsplugin_cascade/clipboard/cms_plugins.py @@ -286,7 +286,11 @@ def populate_static_json(self, filename): clipboard_folder ='cascade/admin/clipboards/' path = finders.find(clipboard_folder) with io.open(os.path.join(path, filename), 'r') as fh: - config_data = json.load(fh) + if hasattr(fh,'read'): + config_data = json.load(fh) + else: + #needed for test + config_data = {} return config_data plugin_pool.register_plugin(CascadeClipboardPlugin) From 98c693f03a314f75acbe2e34005755b9084e7ac8 Mon Sep 17 00:00:00 2001 From: Haricot Date: Sun, 23 Feb 2020 00:56:39 +0100 Subject: [PATCH 22/69] wip groups clipboard (fix test) --- cmsplugin_cascade/clipboard/cms_plugins.py | 7 ++----- cmsplugin_cascade/templatetags/cascade_tags.py | 2 -- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/cmsplugin_cascade/clipboard/cms_plugins.py b/cmsplugin_cascade/clipboard/cms_plugins.py index 77435a295..3a654cdb1 100644 --- a/cmsplugin_cascade/clipboard/cms_plugins.py +++ b/cmsplugin_cascade/clipboard/cms_plugins.py @@ -285,12 +285,9 @@ def populate_static_json(self, filename): from django.contrib.staticfiles import finders clipboard_folder ='cascade/admin/clipboards/' path = finders.find(clipboard_folder) + os.path.join(path, filename) with io.open(os.path.join(path, filename), 'r') as fh: - if hasattr(fh,'read'): - config_data = json.load(fh) - else: - #needed for test - config_data = {} + config_data = json.load(fh) return config_data plugin_pool.register_plugin(CascadeClipboardPlugin) diff --git a/cmsplugin_cascade/templatetags/cascade_tags.py b/cmsplugin_cascade/templatetags/cascade_tags.py index 37ce638e2..c93b9c8c1 100644 --- a/cmsplugin_cascade/templatetags/cascade_tags.py +++ b/cmsplugin_cascade/templatetags/cascade_tags.py @@ -50,8 +50,6 @@ def render_tag(self, context, data_clipboard, identifier=None): raise IOError("Unable to find file: {}".format(datafile)) with io.open(jsonfile) as fp: tree_data = json.load(fp) - else: - tree_data = json.load(data_clipboard) else: tree_data = data_clipboard if 'request' in context: From 115b8c0023ed409963064a0d37ae7c85c306528d Mon Sep 17 00:00:00 2001 From: Haricot Date: Sun, 23 Feb 2020 11:12:02 +0100 Subject: [PATCH 23/69] wip groups clipboard (fix .json data) --- cmsplugin_cascade/clipboard/cms_plugins.py | 18 ++++-------------- .../admin/widgets/clipboard_menu_group.html | 2 +- cmsplugin_cascade/templatetags/cascade_tags.py | 5 ++--- 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/cmsplugin_cascade/clipboard/cms_plugins.py b/cmsplugin_cascade/clipboard/cms_plugins.py index 3a654cdb1..a0a66c4b8 100644 --- a/cmsplugin_cascade/clipboard/cms_plugins.py +++ b/cmsplugin_cascade/clipboard/cms_plugins.py @@ -126,12 +126,13 @@ def treegroup( groups, index2): [treegroup( groups, index) for index, groups in enumerate(queryset , start=1)] if not 'Clipboard Home' in clipboards_groupby: - clipboards_groupby[ 'Clipboard Home'] = [('demo', 'demo')] + identifier = 'demo' + clipboards_groupby[ 'Clipboard Home'] = [( identifier, identifier)] clipboard_home = CascadeClipboardGroup.objects.create(name="Clipboard Home") - data_demo = self.populate_static_json("demo_carousel-plugin.json") + data_demo = "cascade/admin/clipboards/demo_carousel-plugin.json" group_clipboard_home = clipboard_home cascade_clipboard = CascadeClipboard.objects.create( - identifier="demo", + identifier=identifier, data=data_demo, ) cascade_clipboard.group.set([group_clipboard_home]) @@ -279,15 +280,4 @@ def add_to_clipboard(self, request, form): cascade_clipboard.group.set(group) return render(request, 'cascade/admin/clipboard_close_frame.html', {}) - - def populate_static_json(self, filename): - import os, io, json - from django.contrib.staticfiles import finders - clipboard_folder ='cascade/admin/clipboards/' - path = finders.find(clipboard_folder) - os.path.join(path, filename) - with io.open(os.path.join(path, filename), 'r') as fh: - config_data = json.load(fh) - return config_data - plugin_pool.register_plugin(CascadeClipboardPlugin) diff --git a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_menu_group.html b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_menu_group.html index 563072fd7..32ee70dee 100644 --- a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_menu_group.html +++ b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_menu_group.html @@ -7,7 +7,7 @@ diff --git a/cmsplugin_cascade/templatetags/cascade_tags.py b/cmsplugin_cascade/templatetags/cascade_tags.py index c93b9c8c1..bcfb25f75 100644 --- a/cmsplugin_cascade/templatetags/cascade_tags.py +++ b/cmsplugin_cascade/templatetags/cascade_tags.py @@ -33,7 +33,6 @@ class StrideRenderer(Tag): def render_tag(self, context, data_clipboard, identifier=None): from sekizai.helpers import get_varname as get_sekizai_context_key from cmsplugin_cascade.strides import StrideContentRenderer - if isinstance(data_clipboard, dict): identifier = identifier datafile = False @@ -48,8 +47,8 @@ def render_tag(self, context, data_clipboard, identifier=None): jsonfile = finders.find(datafile) if not jsonfile: raise IOError("Unable to find file: {}".format(datafile)) - with io.open(jsonfile) as fp: - tree_data = json.load(fp) + with io.open(jsonfile) as fp: + tree_data = json.load(fp) else: tree_data = data_clipboard if 'request' in context: From 46b74ce2e5e6a1b28146871cccd46087fa4f1ef4 Mon Sep 17 00:00:00 2001 From: Haricot Date: Sun, 23 Feb 2020 12:12:38 +0100 Subject: [PATCH 24/69] wip groups clipboard (fix test) --- cmsplugin_cascade/clipboard/cms_plugins.py | 11 ++++++++++- cmsplugin_cascade/templatetags/cascade_tags.py | 9 +++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/cmsplugin_cascade/clipboard/cms_plugins.py b/cmsplugin_cascade/clipboard/cms_plugins.py index a0a66c4b8..fe6e53529 100644 --- a/cmsplugin_cascade/clipboard/cms_plugins.py +++ b/cmsplugin_cascade/clipboard/cms_plugins.py @@ -129,7 +129,7 @@ def treegroup( groups, index2): identifier = 'demo' clipboards_groupby[ 'Clipboard Home'] = [( identifier, identifier)] clipboard_home = CascadeClipboardGroup.objects.create(name="Clipboard Home") - data_demo = "cascade/admin/clipboards/demo_carousel-plugin.json" + data_demo = self.populate_static_json("cascade/admin/clipboards/demo_carousel-plugin.json") group_clipboard_home = clipboard_home cascade_clipboard = CascadeClipboard.objects.create( identifier=identifier, @@ -280,4 +280,13 @@ def add_to_clipboard(self, request, form): cascade_clipboard.group.set(group) return render(request, 'cascade/admin/clipboard_close_frame.html', {}) + + def populate_static_json(self, relative_path_filename): + import os, io, json + from django.contrib.staticfiles import finders + path = finders.find(relative_path_filename) + with io.open(path, 'r') as fh: + config_data = json.load(fh) + return config_data + plugin_pool.register_plugin(CascadeClipboardPlugin) diff --git a/cmsplugin_cascade/templatetags/cascade_tags.py b/cmsplugin_cascade/templatetags/cascade_tags.py index bcfb25f75..76e0c60bb 100644 --- a/cmsplugin_cascade/templatetags/cascade_tags.py +++ b/cmsplugin_cascade/templatetags/cascade_tags.py @@ -33,17 +33,22 @@ class StrideRenderer(Tag): def render_tag(self, context, data_clipboard, identifier=None): from sekizai.helpers import get_varname as get_sekizai_context_key from cmsplugin_cascade.strides import StrideContentRenderer + tr=False if isinstance(data_clipboard, dict): + # qs_clipboards identifier = identifier datafile = False - else : + elif isinstance(data_clipboard, str): + # relative path datafile = data_clipboard identifier = datafile + tree_data_key = 'cascade-strides:' + identifier cache = caches['default'] tree_data = cache.get(tree_data_key) if cache else None + if tree_data is None: - if datafile: + if datafile : jsonfile = finders.find(datafile) if not jsonfile: raise IOError("Unable to find file: {}".format(datafile)) From 082ac0fcf1dae08d94ea274ee9857decdb446e6a Mon Sep 17 00:00:00 2001 From: Haricot Date: Sun, 23 Feb 2020 17:43:38 +0100 Subject: [PATCH 25/69] wip clipboards group and fallback --- cmsplugin_cascade/app_settings.py | 8 ++ cmsplugin_cascade/bootstrap4/utils.py | 14 +++- cmsplugin_cascade/image.py | 6 +- .../migrations/0027_version_1.py | 2 +- .../templates/cascade/bootstrap4/image.html | 8 +- .../cascade/bootstrap4/jumbotron.html | 5 ++ .../templates/cascade/bootstrap4/picture.html | 6 +- .../cascade/generic/fallback_image.html | 27 +++++++ .../cascade/generic/fallback_picture.html | 8 ++ .../templatetags/cascade_tags.py | 80 +++++++++++++++++++ cmsplugin_cascade/utils.py | 18 +++-- 11 files changed, 164 insertions(+), 18 deletions(-) create mode 100644 cmsplugin_cascade/templates/cascade/generic/fallback_image.html create mode 100644 cmsplugin_cascade/templates/cascade/generic/fallback_picture.html diff --git a/cmsplugin_cascade/app_settings.py b/cmsplugin_cascade/app_settings.py index 6618a3600..700f752e6 100644 --- a/cmsplugin_cascade/app_settings.py +++ b/cmsplugin_cascade/app_settings.py @@ -132,6 +132,14 @@ def CMSPLUGIN_CASCADE(self): config.setdefault('register_page_editor', True) + + config.setdefault('fallback',{ + 'image':{'color':'hsl(196, 71%, 93%, 0.8)', 'svg':''}, + 'picture':{'color':'hsl(150, 86%,94%, 0.8)', 'svg':'' }, + 'jumbotron':{'color':'hsl(62, 90%, 90%, 0.8)', 'svg':''}, + }) + + for module_name in self.CASCADE_PLUGINS: try: settings_module = import_module('{}.settings'.format(module_name)) diff --git a/cmsplugin_cascade/bootstrap4/utils.py b/cmsplugin_cascade/bootstrap4/utils.py index eee350938..af2637dac 100644 --- a/cmsplugin_cascade/bootstrap4/utils.py +++ b/cmsplugin_cascade/bootstrap4/utils.py @@ -31,6 +31,9 @@ def get_image_tags(instance): aspect_ratio = compute_aspect_ratio(instance.image) elif 'image' in instance.glossary and 'width' in instance.glossary['image']: aspect_ratio = compute_aspect_ratio_with_glossary(instance.glossary) + # fallback logic + elif 'image_properties' in instance.glossary and 'width' in instance.glossary['image_properties']: + aspect_ratio = compute_aspect_ratio_with_glossary(instance.glossary) else: # if accessing the image file fails or fake image fails, abort here raise FileNotFoundError("Unable to compute aspect ratio of image") @@ -52,7 +55,11 @@ def get_image_tags(instance): else: image_width = parse_responsive_length(instance.glossary['image_width_fixed']) if not image_width[0]: - image_width = (instance.image.width, image_width[1]) + if hasattr(instance,'image' ) and hasattr(instance.image,'width' ) : + image_width = (instance.image.width, image_width[1]) + # logic fallback + else: + image_width = (instance.glossary['image_properties']['width'],image_width[1] ) try: image_height = parse_responsive_length(instance.glossary['image_height']) except KeyError: @@ -99,7 +106,10 @@ def get_picture_elements(instance): if hasattr(instance, 'image') and hasattr(instance.image, 'exif'): aspect_ratio = compute_aspect_ratio(instance.image) - elif 'image' in instance.glossary and 'width' in instance.glossary['image']: + # fallback logic picture + elif 'image' in instance.glossary and 'width' in instance.glossary['image']: + aspect_ratio = compute_aspect_ratio_with_glossary(instance.glossary) + elif 'image_properties' in instance.glossary and 'width' in instance.glossary['image_properties']: aspect_ratio = compute_aspect_ratio_with_glossary(instance.glossary) else: # if accessing the image file fails or fake image fails, abort here diff --git a/cmsplugin_cascade/image.py b/cmsplugin_cascade/image.py index 6142511d8..15a3e0314 100644 --- a/cmsplugin_cascade/image.py +++ b/cmsplugin_cascade/image.py @@ -20,10 +20,10 @@ class ImageFormMixin(EntangledModelFormMixin): help_text=_("Textual description of the image added to the 'alt' tag of the element."), ) - _image_properties = EntangledField() + image_properties = EntangledField() class Meta: - entangled_fields = {'glossary': ['image_file', 'image_title', 'alt_tag', '_image_properties']} + entangled_fields = {'glossary': ['image_file', 'image_title', 'alt_tag', 'image_properties']} def clean(self): cleaned_data = super().clean() @@ -31,7 +31,7 @@ def clean(self): if not image_file: raise ValidationError(_("No image has been selected.")) # _image_properties are just a cached representation, maybe useless - cleaned_data['_image_properties'] = { + cleaned_data['image_properties'] = { 'width': image_file._width, 'height': image_file._height, 'exif_orientation': image_file.exif.get('Orientation', 1), diff --git a/cmsplugin_cascade/migrations/0027_version_1.py b/cmsplugin_cascade/migrations/0027_version_1.py index a21f06754..31d38fc58 100644 --- a/cmsplugin_cascade/migrations/0027_version_1.py +++ b/cmsplugin_cascade/migrations/0027_version_1.py @@ -58,7 +58,7 @@ def migrate_image(glossary): }) if 'width' in image and 'height' in image and 'exif_orientation' in image: glossary.update({ - '_image_properties': {'width': image['width'], 'height': image['height'], + 'image_properties': {'width': image['width'], 'height': image['height'], 'exif_orientation': image['exif_orientation']}, }) return True diff --git a/cmsplugin_cascade/templates/cascade/bootstrap4/image.html b/cmsplugin_cascade/templates/cascade/bootstrap4/image.html index ab4e8c093..fb9492dfd 100644 --- a/cmsplugin_cascade/templates/cascade/bootstrap4/image.html +++ b/cmsplugin_cascade/templates/cascade/bootstrap4/image.html @@ -1,15 +1,15 @@ -{% load l10n static cascade_tags thumbnail %} +{% load l10n static cascade_tags thumbnail %} {% localize off %}{% spaceless %}{% with css_classes=instance.css_classes inline_styles=instance.inline_styles %} - {% else %} - src="{% static 'cascade/fallback.svg' %}" +{% include "cascade/generic/fallback_image.html" %} {% endif %} -/> {% endwith %}{% endspaceless %}{% endlocalize %} diff --git a/cmsplugin_cascade/templates/cascade/bootstrap4/jumbotron.html b/cmsplugin_cascade/templates/cascade/bootstrap4/jumbotron.html index fdffc3a0d..cb448c7ca 100644 --- a/cmsplugin_cascade/templates/cascade/bootstrap4/jumbotron.html +++ b/cmsplugin_cascade/templates/cascade/bootstrap4/jumbotron.html @@ -28,6 +28,10 @@ } } {% endfor %} +{% elif 'pk' in instance.glossary.image_file %} + #cascadeelement_id-{{ instance.pk }} { + {% fallback instance %} + } {% endif %} {% endlocalize %}{% endaddtoblock %} @@ -39,3 +43,4 @@ {% endfor %} {% endspaceless %}{% endlocalize %} + diff --git a/cmsplugin_cascade/templates/cascade/bootstrap4/picture.html b/cmsplugin_cascade/templates/cascade/bootstrap4/picture.html index 0db1a5cb7..eee56dec4 100644 --- a/cmsplugin_cascade/templates/cascade/bootstrap4/picture.html +++ b/cmsplugin_cascade/templates/cascade/bootstrap4/picture.html @@ -1,7 +1,7 @@ {% load l10n static cascade_tags thumbnail %} {% localize off %}{% spaceless %} - {% if instance.image|is_valid_image %} + {% for elem in elements %} {% thumbnail instance.image elem.size zoom=elem.zoom crop=elem.crop upscale=elem.upscale subject_location=elem.subject_location as thumb %} {% if elem.size2 %} @@ -13,8 +13,8 @@ {% endif %} {% endfor %} + {% else %} - +
{% fallback instance %}
{% endif %} - {% endspaceless %}{% endlocalize %} diff --git a/cmsplugin_cascade/templates/cascade/generic/fallback_image.html b/cmsplugin_cascade/templates/cascade/generic/fallback_image.html new file mode 100644 index 000000000..518509e27 --- /dev/null +++ b/cmsplugin_cascade/templates/cascade/generic/fallback_image.html @@ -0,0 +1,27 @@ +{% load l10n static thumbnail %} +{% if instance.glossary.image_properties.width != src.size.0 or instance.glossary.image_properties.height != src.size.1 %} +{% if not 'img-fluid' in instance.glossary.image_shapes %} +
+ +
+{% else %} +
+ +
+{% endif %} +{% else %} +
+ +
+{% endif %} diff --git a/cmsplugin_cascade/templates/cascade/generic/fallback_picture.html b/cmsplugin_cascade/templates/cascade/generic/fallback_picture.html new file mode 100644 index 000000000..0a19f39b0 --- /dev/null +++ b/cmsplugin_cascade/templates/cascade/generic/fallback_picture.html @@ -0,0 +1,8 @@ +{% load l10n cascade_tags %} +{% with tag_type=instance.tag_type css_classes=instance.css_classes inline_styles=instance.inline_styles element_id=instance.element_id %} +<{{ tag_type }}{{ instance.html_tag_attributes }}{% if element_id %} id="{{ element_id|unlocalize }}"{% endif %}{% if css_classes %} class="{{ css_classes }}"{% endif %}{% if inline_styles %} style="{{ inline_styles }}"{% endif %}> +{% for plugin in instance.child_plugin_instances %} + {% render_plugin plugin %} +{% endfor %} + +{% endwith %} diff --git a/cmsplugin_cascade/templatetags/cascade_tags.py b/cmsplugin_cascade/templatetags/cascade_tags.py index 76e0c60bb..4eaa473c5 100644 --- a/cmsplugin_cascade/templatetags/cascade_tags.py +++ b/cmsplugin_cascade/templatetags/cascade_tags.py @@ -11,6 +11,7 @@ from classytags.arguments import Argument from classytags.core import Options, Tag from cmsplugin_cascade.strides import StrideContentRenderer +from django.templatetags.static import static register = template.Library() @@ -114,6 +115,85 @@ def render_tag(self, context, plugin): register.tag('render_plugin', RenderPlugin) +class FallBack(Tag): + name = 'fallback' + + options = Options( + Argument('plugin',required=False) + ) + + def render_tag(self, context, plugin): + for context_ in context: + if 'instance'in context_ : + glossary = context_['instance'].glossary + instance = context_['instance'] + fallback_plugin_type = plugin.plugin_class.__name__ + css_classes = glossary.get('css_classes','') + width = 0; height = 0; exif_orientation = 0; x = 0; y = 0; + inline_styles = glossary.get('inline_styles','') + html_tag_attributes = glossary.get('html_tag_attributes','') + + if 'image' in glossary: + image_fallback = 'image' + img = settings.CMSPLUGIN_CASCADE["fallback"]["image"]['svg'] + color = settings.CMSPLUGIN_CASCADE["fallback"]["image"]['color'] + static_fallback_svg = static(img) + elif fallback_plugin_type == 'BootstrapJumbotronPlugin': + image_fallback=None + img = settings.CMSPLUGIN_CASCADE["fallback"]["jumbotron"]['svg'] + color = settings.CMSPLUGIN_CASCADE["fallback"]["jumbotron"]['color'] + static_fallback_svg = static(img) + elif 'image_properties' in glossary: + image_fallback='image_properties' + img = settings.CMSPLUGIN_CASCADE["fallback"]["picture"]['svg'] + color = settings.CMSPLUGIN_CASCADE["fallback"]["picture"]['color'] + static_fallback_svg = static(img) + else: + image_fallback=None + img = settings.CMSPLUGIN_CASCADE["fallback"]["picture"]['svg'] + color = settings.CMSPLUGIN_CASCADE["fallback"]["picture"]['color'] + static_fallback_svg = static(img) + if image_fallback : + width = glossary[image_fallback].get('width',0) + height = glossary[image_fallback].get('height',0) + exif_orientation = glossary[image_fallback].get('exif_orientation',0) + + x = 50 + y = 50 + + if fallback_plugin_type == 'BootstrapJumbotronPlugin': + style=''' + background: url({static_fallback_svg}); + background-size: auto; + background-position-y: 20%; + background-size: 50%; + background-repeat: no-repeat; + background-position-x: 50%; + background-attachment: fixed; + background-color: {color}; + border: white solid; + '''.format( color=color, static_fallback_svg=static_fallback_svg) + + return style + else: + svg=' \ + \ + '.format( + width=width, + height=height, + color=color, + css_classes=css_classes, + inline_styles=inline_styles, + html_tag_attributes=html_tag_attributes, + static_fallback_svg=static_fallback_svg, + x=x, + y=y) + return svg + +register.tag('fallback', FallBack) + @register.filter def is_valid_image(image): try: diff --git a/cmsplugin_cascade/utils.py b/cmsplugin_cascade/utils.py index 3142da131..0e8e77b3c 100644 --- a/cmsplugin_cascade/utils.py +++ b/cmsplugin_cascade/utils.py @@ -48,11 +48,19 @@ def compute_aspect_ratio(image): def compute_aspect_ratio_with_glossary(glossary): - if glossary['image']['exif_orientation'] > 4: - # image is rotated by 90 degrees, while keeping width and height - return float(glossary['image']['width']) / float(glossary['image']['height']) - else: - return float(glossary['image']['height']) / float(glossary['image']['width']) + if 'image' in glossary : + if glossary['image']['exif_orientation'] > 4: + # image is rotated by 90 degrees, while keeping width and height + return float(glossary['image']['width']) / float(glossary['image']['height']) + else: + return float(glossary['image']['height']) / float(glossary['image']['width']) + # fallogic logic + elif 'image_properties' in glossary: + if glossary['image_properties']['exif_orientation'] > 4: + # image is rotated by 90 degrees, while keeping width and height + return float(glossary['image_properties']['width']) / float(glossary['image_properties']['height']) + else: + return float(glossary['image_properties']['height']) / float(glossary['image_properties']['width']) def get_image_size(width, image_height, aspect_ratio): From 9e2acb4e7f5e1a9978431f38fc1ac4fa804005e9 Mon Sep 17 00:00:00 2001 From: Haricot Date: Sun, 23 Feb 2020 18:14:37 +0100 Subject: [PATCH 26/69] fix image fallback --- cmsplugin_cascade/templates/cascade/bootstrap4/image.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmsplugin_cascade/templates/cascade/bootstrap4/image.html b/cmsplugin_cascade/templates/cascade/bootstrap4/image.html index fb9492dfd..f8191072b 100644 --- a/cmsplugin_cascade/templates/cascade/bootstrap4/image.html +++ b/cmsplugin_cascade/templates/cascade/bootstrap4/image.html @@ -10,6 +10,6 @@ src="{{ thumb.url }}"{% if not sizes %} width="{{ thumb.width }}" height="{{ thumb.height }}"{% endif %} /> {% else %} -{% include "cascade/generic/fallback_image.html" %} +
{% fallback instance %}
{% endif %} {% endwith %}{% endspaceless %}{% endlocalize %} From 1c0d49ca1be5ecd0641fe7192ff0cd3a375263e1 Mon Sep 17 00:00:00 2001 From: Haricot Date: Mon, 24 Feb 2020 17:32:10 +0100 Subject: [PATCH 27/69] wip fallback clipboards group --- cmsplugin_cascade/bootstrap4/jumbotron.py | 2 ++ cmsplugin_cascade/clipboard/cms_plugins.py | 7 ++-- cmsplugin_cascade/strides.py | 35 +++++++++++++++---- .../cascade/admin/widgets/clipboard.html | 12 +++++-- .../admin/widgets/clipboard_stride.html | 3 +- .../templates/cascade/bootstrap4/image.html | 2 +- .../cascade/bootstrap4/jumbotron.html | 1 - .../cascade/generic/fallback_image.html | 4 +-- .../cascade/generic/fallback_jumbotron.html | 3 ++ .../templatetags/cascade_tags.py | 11 ++++-- examples/bs4demo/settings.py | 1 + 11 files changed, 60 insertions(+), 21 deletions(-) create mode 100644 cmsplugin_cascade/templates/cascade/generic/fallback_jumbotron.html diff --git a/cmsplugin_cascade/bootstrap4/jumbotron.py b/cmsplugin_cascade/bootstrap4/jumbotron.py index ae3eb213f..b1d28c5cd 100644 --- a/cmsplugin_cascade/bootstrap4/jumbotron.py +++ b/cmsplugin_cascade/bootstrap4/jumbotron.py @@ -195,11 +195,13 @@ class BootstrapJumbotronPlugin(BootstrapPluginBase): form = JumbotronFormMixin raw_id_fields = ['image_file'] render_template = 'cascade/bootstrap4/jumbotron.html' + render_template_fallback = "cascade/generic/fallback_jumbotron.html" # mode stride gallery ring_plugin = 'JumbotronPlugin' footnote_html = """

For more information about the Jumbotron please read the Bootstrap documentation.

""" + class Media: js = ['admin/js/jquery.init.js', 'cascade/js/admin/jumbotronplugin.js'] diff --git a/cmsplugin_cascade/clipboard/cms_plugins.py b/cmsplugin_cascade/clipboard/cms_plugins.py index fe6e53529..233040caf 100644 --- a/cmsplugin_cascade/clipboard/cms_plugins.py +++ b/cmsplugin_cascade/clipboard/cms_plugins.py @@ -20,7 +20,7 @@ from cmsplugin_cascade.clipboard.forms import ClipboardBaseForm from django.forms import widgets from django.contrib.admin.widgets import RelatedFieldWidgetWrapper - +from django.shortcuts import render class ClipboardWidget(widgets.Select): #template_name = 'django/forms/widgets/select.html' @@ -82,7 +82,6 @@ def render_modal_window(self, request, form): opts = self.model._meta fieldsets = [(None, {'fields': list(form.fields)})] adminForm = AdminForm(form, fieldsets, {}, []) - context = { **default_admin_site.each_context(request), 'title': form.title, @@ -99,11 +98,11 @@ def render_modal_window(self, request, form): 'is_popup': True, 'app_label': opts.app_label, 'media': self.media + form.media, + } return TemplateResponse(request, self.change_form_template, context) - def import_plugins_view(self, request, *args, **kwargs): # TODO: check for permissions @@ -169,7 +168,7 @@ def treegroup( groups, index2): 'title': title, }) - Form.Media =type("Media",(), {'css' : { 'all': ['cascade/css/admin/clipboard.css'] }}) + Form.Media = type("Media",(), {'css' : { 'all': ['cascade/css/admin/clipboard.css'] }}) form = Form(request.GET) assert form.is_valid() elif request.method == 'POST': diff --git a/cmsplugin_cascade/strides.py b/cmsplugin_cascade/strides.py index 0598e7f90..f5a6346bd 100644 --- a/cmsplugin_cascade/strides.py +++ b/cmsplugin_cascade/strides.py @@ -30,6 +30,9 @@ class StrideElementBase(object): """ def __init__(self, plugin, data, children_data, parent=None): self.plugin = plugin + from random import randint + #pass args fake id + data['pk'] = randint(0, 20000) self.id = data.get('pk') self.glossary = data.get('glossary', {}) self.sortinline_elements = self.inline_elements = EmulateQuerySet(data.get('inlines', [])) @@ -38,6 +41,7 @@ def __init__(self, plugin, data, children_data, parent=None): @property def pk(self): + from random import randint #self.id+1 return self.id @property @@ -151,6 +155,10 @@ def _get_render_template(self, context, instance, placeholder): if not template: raise TemplateDoesNotExist("plugin {} has no render_template".format(self.__class__)) + + if hasattr(instance.plugin, 'render_template_fallback'): + template = instance.plugin.render_template_fallback + return template def in_edit_mode(self, request, placeholder): @@ -183,12 +191,14 @@ def render(self, context, instance, placeholder): class StrideContentRenderer(object): def __init__(self, request): + if request: self.request = request self.language = get_language_from_request(request) + self._cached_templates = {} - def render_cascade(self, context, tree_data): + def render_cascade(self, context, tree_data, **kwargs): contents = [] # create temporary copy of context to prevent pollution for other CMS placeholders context = make_context(flatten_context(context)) @@ -199,6 +209,8 @@ def render_cascade(self, context, tree_data): # create a temporary object to store the plugins cache status cms_cachable_plugins = type(str('CachablePlugins'), (object,), {'value': True}) context.push(cms_cachable_plugins=cms_cachable_plugins) + + contents.append(self.render_plugin(plugin_instance, context)) return mark_safe(''.join(contents)) @@ -206,26 +218,37 @@ def render_plugin(self, instance, context, placeholder=None, editable=False): from sekizai.helpers import get_varname as get_sekizai_context_key sekizai_context_key = get_sekizai_context_key() + + from collections import defaultdict + + from sekizai.data import UniqueSequence + # from sekizai.helpers import get_varname + context[sekizai_context_key] = defaultdict(UniqueSequence) + if app_settings.CMSPLUGIN_CASCADE['cache_strides'] and getattr(instance.plugin, 'cache', not editable): + cache = caches['default'] key = 'cascade_element-{}'.format(instance.pk) content = cache.get(key) if content: - context[sekizai_context_key]['css'].extend(cache.get(key + ':css_list', [])) - context[sekizai_context_key]['js'].extend(cache.get(key + ':js_list', [])) + if sekizai_context_key in context: + context[sekizai_context_key]['css'].extend(cache.get(key + ':css_list', [])) + context[sekizai_context_key]['js'].extend(cache.get(key + ':js_list', [])) return content else: context['cms_cachable_plugins'].value = False context = instance.plugin.render(context, instance, placeholder) context = flatten_context(context) - template = instance.plugin._get_render_template(context, instance, placeholder) + template = instance.plugin._get_render_template( context, instance, placeholder) template = self.get_cached_template(template) + content = template.render(context) if context['cms_cachable_plugins'].value: cache.set(key, content) - cache.set(key + ':css_list', context[sekizai_context_key]['css'].data) - cache.set(key + ':js_list', context[sekizai_context_key]['js'].data) + if sekizai_context_key in context: + cache.set(key + ':css_list', context[sekizai_context_key]['css'].data) + cache.set(key + ':js_list', context[sekizai_context_key]['js'].data) return content def user_is_on_edit_mode(self): diff --git a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard.html b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard.html index 21352d59e..d65a82594 100644 --- a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard.html +++ b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard.html @@ -1,7 +1,13 @@ -{% load static cascade_tags %} +{% load static cascade_tags sekizai_tags %} + {% spaceless %} {% include "cascade/admin/widgets/clipboard_menu_group.html" %} + +{% block css %} + +{% endblock %} + {% with id=widget.attrs.id %} @@ -13,7 +19,9 @@

{{ option.label }}

- {% include "cascade/admin/widgets/clipboard_stride.html" %} + + {% cascadeclipboard_data_by_identifier qs_clipboards option.label as data %} + {% include "cascade/admin/widgets/clipboard_stride.html" with data=data %}

{% endfor %} diff --git a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_stride.html b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_stride.html index 0014cc461..80f635dd8 100644 --- a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_stride.html +++ b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_stride.html @@ -1,7 +1,6 @@ -{% load i18n cms_admin cascade_tags %} +{% load i18n cms_admin cascade_tags sekizai_tags %}
- {% cascadeclipboard_data_by_identifier qs_clipboards option.label as data %} {% render_cascade data option.label %} diff --git a/cmsplugin_cascade/templates/cascade/bootstrap4/image.html b/cmsplugin_cascade/templates/cascade/bootstrap4/image.html index f8191072b..fb9492dfd 100644 --- a/cmsplugin_cascade/templates/cascade/bootstrap4/image.html +++ b/cmsplugin_cascade/templates/cascade/bootstrap4/image.html @@ -10,6 +10,6 @@ src="{{ thumb.url }}"{% if not sizes %} width="{{ thumb.width }}" height="{{ thumb.height }}"{% endif %} /> {% else %} -
{% fallback instance %}
+{% include "cascade/generic/fallback_image.html" %} {% endif %} {% endwith %}{% endspaceless %}{% endlocalize %} diff --git a/cmsplugin_cascade/templates/cascade/bootstrap4/jumbotron.html b/cmsplugin_cascade/templates/cascade/bootstrap4/jumbotron.html index cb448c7ca..db0997df1 100644 --- a/cmsplugin_cascade/templates/cascade/bootstrap4/jumbotron.html +++ b/cmsplugin_cascade/templates/cascade/bootstrap4/jumbotron.html @@ -43,4 +43,3 @@ {% endfor %} {% endspaceless %}{% endlocalize %} - diff --git a/cmsplugin_cascade/templates/cascade/generic/fallback_image.html b/cmsplugin_cascade/templates/cascade/generic/fallback_image.html index 518509e27..19a7bf2bc 100644 --- a/cmsplugin_cascade/templates/cascade/generic/fallback_image.html +++ b/cmsplugin_cascade/templates/cascade/generic/fallback_image.html @@ -1,7 +1,7 @@ {% load l10n static thumbnail %} {% if instance.glossary.image_properties.width != src.size.0 or instance.glossary.image_properties.height != src.size.1 %} {% if not 'img-fluid' in instance.glossary.image_shapes %} -
@@ -9,7 +9,7 @@
{% else %}
Date: Mon, 24 Feb 2020 17:45:14 +0100 Subject: [PATCH 28/69] wip fallback clipboards group --- cmsplugin_cascade/bootstrap4/jumbotron.py | 1 - cmsplugin_cascade/strides.py | 13 +++---------- .../templates/cascade/admin/widgets/clipboard.html | 8 +------- .../cascade/admin/widgets/clipboard_stride.html | 2 +- cmsplugin_cascade/templatetags/cascade_tags.py | 6 +----- examples/bs4demo/settings.py | 1 - 6 files changed, 6 insertions(+), 25 deletions(-) diff --git a/cmsplugin_cascade/bootstrap4/jumbotron.py b/cmsplugin_cascade/bootstrap4/jumbotron.py index b1d28c5cd..b9ca66ca3 100644 --- a/cmsplugin_cascade/bootstrap4/jumbotron.py +++ b/cmsplugin_cascade/bootstrap4/jumbotron.py @@ -201,7 +201,6 @@ class BootstrapJumbotronPlugin(BootstrapPluginBase): For more information about the Jumbotron please read the Bootstrap documentation.

""" - class Media: js = ['admin/js/jquery.init.js', 'cascade/js/admin/jumbotronplugin.js'] diff --git a/cmsplugin_cascade/strides.py b/cmsplugin_cascade/strides.py index f5a6346bd..a04f211d5 100644 --- a/cmsplugin_cascade/strides.py +++ b/cmsplugin_cascade/strides.py @@ -11,6 +11,8 @@ from cmsplugin_cascade import app_settings from cmsplugin_cascade.mixins import CascadePluginMixin +from collections import defaultdict +from sekizai.data import UniqueSequence __all__ = ['register_stride', 'StrideContentRenderer'] @@ -191,14 +193,12 @@ def render(self, context, instance, placeholder): class StrideContentRenderer(object): def __init__(self, request): - if request: self.request = request self.language = get_language_from_request(request) - self._cached_templates = {} - def render_cascade(self, context, tree_data, **kwargs): + def render_cascade(self, context, tree_data): contents = [] # create temporary copy of context to prevent pollution for other CMS placeholders context = make_context(flatten_context(context)) @@ -209,8 +209,6 @@ def render_cascade(self, context, tree_data, **kwargs): # create a temporary object to store the plugins cache status cms_cachable_plugins = type(str('CachablePlugins'), (object,), {'value': True}) context.push(cms_cachable_plugins=cms_cachable_plugins) - - contents.append(self.render_plugin(plugin_instance, context)) return mark_safe(''.join(contents)) @@ -218,11 +216,6 @@ def render_plugin(self, instance, context, placeholder=None, editable=False): from sekizai.helpers import get_varname as get_sekizai_context_key sekizai_context_key = get_sekizai_context_key() - - from collections import defaultdict - - from sekizai.data import UniqueSequence - # from sekizai.helpers import get_varname context[sekizai_context_key] = defaultdict(UniqueSequence) if app_settings.CMSPLUGIN_CASCADE['cache_strides'] and getattr(instance.plugin, 'cache', not editable): diff --git a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard.html b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard.html index d65a82594..57712aa56 100644 --- a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard.html +++ b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard.html @@ -1,15 +1,9 @@ {% load static cascade_tags sekizai_tags %} - {% spaceless %} {% include "cascade/admin/widgets/clipboard_menu_group.html" %} - -{% block css %} - -{% endblock %} - {% with id=widget.attrs.id %} -
diff --git a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_stride.html b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_stride.html index 80f635dd8..0a0435861 100644 --- a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_stride.html +++ b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_stride.html @@ -1,4 +1,4 @@ -{% load i18n cms_admin cascade_tags sekizai_tags %} +{% load i18n cms_admin cascade_tags %}
diff --git a/cmsplugin_cascade/templatetags/cascade_tags.py b/cmsplugin_cascade/templatetags/cascade_tags.py index c9b0feaab..f45d5406d 100644 --- a/cmsplugin_cascade/templatetags/cascade_tags.py +++ b/cmsplugin_cascade/templatetags/cascade_tags.py @@ -75,7 +75,7 @@ def render_tag(self, context, data_clipboard, identifier=None): for name in SEKIZAI_CONTENT_HOLDER: context[sekizai_context_key][name] = SEKIZAI_CONTENT_HOLDER[name] return content - + register.tag('render_cascade', StrideRenderer) @@ -99,9 +99,6 @@ def render_tag(self, context, plugin, request=None): elif 'cms_content_renderer' in context: content_renderer = context['cms_content_renderer'] else: - #print('request') - #print(request) - print(context['cms_content_renderer']) request = context['request'] toolbar = get_toolbar_from_request(request) content_renderer = toolbar.content_renderer @@ -215,7 +212,6 @@ def sphinx_docs_include(path): return mark_safe(fh.read()) - @register.simple_tag def cascadeclipboard_data_by_identifier(queryset, identifier ): qs_identifier=queryset.filter(identifier=identifier) diff --git a/examples/bs4demo/settings.py b/examples/bs4demo/settings.py index b82b69c63..2ef5ea32e 100644 --- a/examples/bs4demo/settings.py +++ b/examples/bs4demo/settings.py @@ -34,7 +34,6 @@ }, } - INSTALLED_APPS = [ 'django.contrib.auth', 'django.contrib.contenttypes', From 3cd8092fc0c74950be95fe88d5984efb1b9b524b Mon Sep 17 00:00:00 2001 From: Haricot Date: Mon, 24 Feb 2020 17:46:48 +0100 Subject: [PATCH 29/69] wip fallback clipboards group --- cmsplugin_cascade/clipboard/cms_plugins.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmsplugin_cascade/clipboard/cms_plugins.py b/cmsplugin_cascade/clipboard/cms_plugins.py index 233040caf..592c8dd4d 100644 --- a/cmsplugin_cascade/clipboard/cms_plugins.py +++ b/cmsplugin_cascade/clipboard/cms_plugins.py @@ -20,7 +20,7 @@ from cmsplugin_cascade.clipboard.forms import ClipboardBaseForm from django.forms import widgets from django.contrib.admin.widgets import RelatedFieldWidgetWrapper -from django.shortcuts import render + class ClipboardWidget(widgets.Select): #template_name = 'django/forms/widgets/select.html' @@ -98,7 +98,6 @@ def render_modal_window(self, request, form): 'is_popup': True, 'app_label': opts.app_label, 'media': self.media + form.media, - } return TemplateResponse(request, self.change_form_template, context) From 265be16f3f9eb41dca04ce9510b8ef73827253f6 Mon Sep 17 00:00:00 2001 From: Haricot Date: Mon, 24 Feb 2020 17:51:56 +0100 Subject: [PATCH 30/69] wip fallback clipboards group (clean code) --- cmsplugin_cascade/strides.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cmsplugin_cascade/strides.py b/cmsplugin_cascade/strides.py index a04f211d5..96ca1454f 100644 --- a/cmsplugin_cascade/strides.py +++ b/cmsplugin_cascade/strides.py @@ -13,7 +13,7 @@ from cmsplugin_cascade.mixins import CascadePluginMixin from collections import defaultdict from sekizai.data import UniqueSequence - +from random import randint __all__ = ['register_stride', 'StrideContentRenderer'] @@ -32,8 +32,7 @@ class StrideElementBase(object): """ def __init__(self, plugin, data, children_data, parent=None): self.plugin = plugin - from random import randint - #pass args fake id + #pass args radom id data['pk'] = randint(0, 20000) self.id = data.get('pk') self.glossary = data.get('glossary', {}) @@ -43,7 +42,6 @@ def __init__(self, plugin, data, children_data, parent=None): @property def pk(self): - from random import randint #self.id+1 return self.id @property From 462f010d54349cdaa385e0cf3b7631f859f65fbc Mon Sep 17 00:00:00 2001 From: Haricot Date: Mon, 24 Feb 2020 21:09:12 +0100 Subject: [PATCH 31/69] readd accurate fallback --- cmsplugin_cascade/app_settings.py | 4 +- .../admin/widgets/clipboard_stride.html | 4 +- .../templates/cascade/bootstrap4/picture.html | 8 ++-- .../cascade/generic/fallback_image.html | 8 ++-- .../cascade/generic/fallback_picture.html | 38 +++++++++++++++---- .../templatetags/cascade_tags.py | 4 ++ 6 files changed, 49 insertions(+), 17 deletions(-) diff --git a/cmsplugin_cascade/app_settings.py b/cmsplugin_cascade/app_settings.py index 700f752e6..49fe1e486 100644 --- a/cmsplugin_cascade/app_settings.py +++ b/cmsplugin_cascade/app_settings.py @@ -134,8 +134,8 @@ def CMSPLUGIN_CASCADE(self): config.setdefault('fallback',{ - 'image':{'color':'hsl(196, 71%, 93%, 0.8)', 'svg':''}, - 'picture':{'color':'hsl(150, 86%,94%, 0.8)', 'svg':'' }, + 'image':{'color':'hsla(221.7, 57.5%, 84.3%, 0.8)', 'svg':''}, + 'picture':{'color':'hsla(0, 40%, 80.4%, 0.8)', 'svg':'' }, 'jumbotron':{'color':'hsl(62, 90%, 90%, 0.8)', 'svg':''}, }) diff --git a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_stride.html b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_stride.html index 0a0435861..650907caa 100644 --- a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_stride.html +++ b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_stride.html @@ -1,5 +1,5 @@ -{% load i18n cms_admin cascade_tags %} -
+{% load i18n cms_admin cascade_tags %} +
diff --git a/cmsplugin_cascade/templates/cascade/bootstrap4/picture.html b/cmsplugin_cascade/templates/cascade/bootstrap4/picture.html index eee56dec4..a902e9e85 100644 --- a/cmsplugin_cascade/templates/cascade/bootstrap4/picture.html +++ b/cmsplugin_cascade/templates/cascade/bootstrap4/picture.html @@ -1,7 +1,8 @@ {% load l10n static cascade_tags thumbnail %} {% localize off %}{% spaceless %} -{% if instance.image|is_valid_image %} +{% if instance.image|is_valid_image %} + {% for elem in elements %} {% thumbnail instance.image elem.size zoom=elem.zoom crop=elem.crop upscale=elem.upscale subject_location=elem.subject_location as thumb %} {% if elem.size2 %} @@ -13,8 +14,9 @@ {% endif %} {% endfor %} - {% else %} -
{% fallback instance %}
+
{# fallback instance #}
+ {% include "cascade/generic/fallback_picture.html" %} {% endif %} + {% endspaceless %}{% endlocalize %} diff --git a/cmsplugin_cascade/templates/cascade/generic/fallback_image.html b/cmsplugin_cascade/templates/cascade/generic/fallback_image.html index 19a7bf2bc..f07d033c8 100644 --- a/cmsplugin_cascade/templates/cascade/generic/fallback_image.html +++ b/cmsplugin_cascade/templates/cascade/generic/fallback_image.html @@ -1,7 +1,9 @@ -{% load l10n static thumbnail %} +{% load l10n static thumbnail cascade_tags %} + {% if instance.glossary.image_properties.width != src.size.0 or instance.glossary.image_properties.height != src.size.1 %} +{% fallback_config 'image' as fallback_image %} {% if not 'img-fluid' in instance.glossary.image_shapes %} -
@@ -9,7 +11,7 @@
{% else %}
-{% for plugin in instance.child_plugin_instances %} - {% render_plugin plugin %} +{% load l10n thumbnail cascade_tags %} +{% fallback_config 'picture' as fallback_picture %} + +
+ +
diff --git a/cmsplugin_cascade/templatetags/cascade_tags.py b/cmsplugin_cascade/templatetags/cascade_tags.py index f45d5406d..490994a66 100644 --- a/cmsplugin_cascade/templatetags/cascade_tags.py +++ b/cmsplugin_cascade/templatetags/cascade_tags.py @@ -216,3 +216,7 @@ def sphinx_docs_include(path): def cascadeclipboard_data_by_identifier(queryset, identifier ): qs_identifier=queryset.filter(identifier=identifier) return qs_identifier[0].data + +@register.simple_tag +def fallback_config(type_fallback): + return settings.CMSPLUGIN_CASCADE["fallback"][type_fallback] From 7b30db09023d9bcca3aa938298964793fb3a7f08 Mon Sep 17 00:00:00 2001 From: Haricot Date: Mon, 24 Feb 2020 21:19:22 +0100 Subject: [PATCH 32/69] fix picture edit bs4demo/settings.py --- examples/bs4demo/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/bs4demo/settings.py b/examples/bs4demo/settings.py index 2ef5ea32e..e50b0ae21 100644 --- a/examples/bs4demo/settings.py +++ b/examples/bs4demo/settings.py @@ -218,7 +218,7 @@ 'plugins_with_sharables': { 'BootstrapImagePlugin': ('image_shapes', 'image_width_responsive', 'image_width_fixed', 'image_height', 'resize_options',), - 'BootstrapPicturePlugin': ('image_shapes', 'responsive_heights', 'image_size', 'resize_options',), + 'BootstrapPicturePlugin': ('image_shapes', 'responsive_heights','resize_options',), }, 'exclude_hiding_plugin': ('SegmentPlugin', 'Badge'), 'allow_plugin_hiding': True, From b797c4c0e23845dc57c1a2a8163c3a00c1e8eddd Mon Sep 17 00:00:00 2001 From: Haricot Date: Thu, 27 Feb 2020 11:27:29 +0100 Subject: [PATCH 33/69] wip clipboard import --- cmsplugin_cascade/app_settings.py | 4 +- cmsplugin_cascade/bootstrap4/buttons.py | 2 + cmsplugin_cascade/clipboard/cms_plugins.py | 10 +- .../static/cascade/css/admin/clipboard.css | 67 ++++++++++- .../cascade/admin/widgets/clipboard.html | 106 +++++++++++++----- .../admin/widgets/clipboard_menu_group.html | 28 +++++ .../admin/widgets/clipboard_stride.html | 15 ++- 7 files changed, 192 insertions(+), 40 deletions(-) diff --git a/cmsplugin_cascade/app_settings.py b/cmsplugin_cascade/app_settings.py index 49fe1e486..cfefb88a1 100644 --- a/cmsplugin_cascade/app_settings.py +++ b/cmsplugin_cascade/app_settings.py @@ -132,8 +132,10 @@ def CMSPLUGIN_CASCADE(self): config.setdefault('register_page_editor', True) - + config.setdefault('fallback',{ + # default + 'path_main_scss': 'bs4demo/css/main.scss', 'image':{'color':'hsla(221.7, 57.5%, 84.3%, 0.8)', 'svg':''}, 'picture':{'color':'hsla(0, 40%, 80.4%, 0.8)', 'svg':'' }, 'jumbotron':{'color':'hsl(62, 90%, 90%, 0.8)', 'svg':''}, diff --git a/cmsplugin_cascade/bootstrap4/buttons.py b/cmsplugin_cascade/bootstrap4/buttons.py index 282d74cb6..8919eac4c 100644 --- a/cmsplugin_cascade/bootstrap4/buttons.py +++ b/cmsplugin_cascade/bootstrap4/buttons.py @@ -118,6 +118,7 @@ class BootstrapButtonMixin(IconPluginMixin): default_css_attributes = ['button_type', 'button_size', 'button_options', 'stretched_link'] ring_plugin = 'ButtonMixin' + class Media: css = {'all': ['cascade/css/admin/bootstrap4-buttons.css', 'cascade/css/admin/iconplugin.css']} js = ['admin/js/jquery.init.js', 'cascade/js/admin/buttonmixin.js'] @@ -148,6 +149,7 @@ class BootstrapButtonPlugin(BootstrapButtonMixin, LinkPluginBase): form = BootstrapButtonFormMixin ring_plugin = 'ButtonPlugin' DEFAULT_BUTTON_ATTRIBUTES = {'role': 'button'} +# render_template_fallback = "cascade/generic/fallback_button.html" # mode stride gallery class Media: js = ['admin/js/jquery.init.js', 'cascade/js/admin/buttonplugin.js'] diff --git a/cmsplugin_cascade/clipboard/cms_plugins.py b/cmsplugin_cascade/clipboard/cms_plugins.py index 592c8dd4d..f2edd9acc 100644 --- a/cmsplugin_cascade/clipboard/cms_plugins.py +++ b/cmsplugin_cascade/clipboard/cms_plugins.py @@ -20,6 +20,7 @@ from cmsplugin_cascade.clipboard.forms import ClipboardBaseForm from django.forms import widgets from django.contrib.admin.widgets import RelatedFieldWidgetWrapper +from django.conf import settings class ClipboardWidget(widgets.Select): @@ -32,6 +33,7 @@ def get_context(self, name, value, attrs): groups=list(CascadeClipboardGroup.objects.all().exclude( name='Clipboard Home').values_list('name',flat=True)) context['groups_exclude_home'] = groups context['qs_clipboards'] = CascadeClipboard.objects.all() + context['main_scss'] = settings.CMSPLUGIN_CASCADE['fallback']['path_main_scss'] return context @@ -102,9 +104,13 @@ def render_modal_window(self, request, form): return TemplateResponse(request, self.change_form_template, context) + def import_plugins_view(self, request, *args, **kwargs): # TODO: check for permissions + + view_breakdown = request.session.get('view_breakdown', "lg") + placeholder_ref_id = None if request.GET.get('placeholder'): placeholder_ref_id = request.GET.get('placeholder') @@ -162,12 +168,12 @@ def treegroup( groups, index2): choices=CHOICES, label=_("Select Clipboard"), required=False, - widget=ClipboardWidget(attrs={"placeholder_ref_id": placeholder_ref_id, "language": language, 'count_target':len_ungroup }), + widget=ClipboardWidget(attrs={"placeholder_ref_id": placeholder_ref_id, "language": language, 'count_target':len_ungroup ,'view_breakdown':view_breakdown }), ), 'title': title, }) - Form.Media = type("Media",(), {'css' : { 'all': ['cascade/css/admin/clipboard.css'] }}) + Form.Media = type("Media",(), {'css' : { 'all': [ ''] }}) form = Form(request.GET) assert form.is_valid() elif request.method == 'POST': diff --git a/cmsplugin_cascade/static/cascade/css/admin/clipboard.css b/cmsplugin_cascade/static/cascade/css/admin/clipboard.css index 27cebec30..f7621250f 100644 --- a/cmsplugin_cascade/static/cascade/css/admin/clipboard.css +++ b/cmsplugin_cascade/static/cascade/css/admin/clipboard.css @@ -42,6 +42,8 @@ div.cms .cms-structure.cms-structure-condensed .cms-submenu-item a[data-icon=imp top: 12px !important; } + + .container_clipboard { display: flex; justify-content: space-between; @@ -51,7 +53,6 @@ div.cms .cms-structure.cms-structure-condensed .cms-submenu-item a[data-icon=imp padding: 1rem; } - .button_clipboard { background-color:#ffffff; -webkit-border-radius:6px; @@ -74,3 +75,67 @@ div.cms .cms-structure.cms-structure-condensed .cms-submenu-item a[data-icon=imp top:1px; } +.cms-admin-modal{ +background-color: white; +} +.field-placeholder, .field-language, #footer { + display:none; +} + +.field-clipboard { + border-bottom: none; +} + +.select-box__current { + display: inline-flex; + flex-wrap: wrap; + position:absolute; +} + +.cms-cascade-clipboard { + width:400px; + margin-right:1rem; + margin-bottom:1rem; +} + +.clipboard-base .clipboard-fullscreen:checked ~ .cms-cascade-clipboard { + width:100%; +} + +.clipboard-fullscreen { + display: inline-block; + -webkit-appearance: none; + -moz-appearance: none; + -o-appearance: none; + appearance: none; + position: relative; + right: 3Opx; + left: 50%; + top: 26px; +} + +.clipboard-fullscreen:after{ + content:'fullscreen'; + cursor: pointer; +} + +.clipboard-fullscreen:checked:after { + content:'back'; + cursor: pointer; +} + +.clipboard-base .clipboard-fullscreen:checked { + display: inline-block; + -webkit-appearance: none; + -moz-appearance: none; + -o-appearance: none; + appearance: none; +} + +.clipboard-base .clipboard-fullscreen:not(:checked), .clipboard-base .clipboard-fullscreen:not(:checked) ~ .cms-cascade-clipboard { + display:none; +} + +.clipboard-base .select-box__current { + display:inherit; +} diff --git a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard.html b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard.html index 57712aa56..4c5b317cd 100644 --- a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard.html +++ b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard.html @@ -1,46 +1,90 @@ -{% load static cascade_tags sekizai_tags %} +{% load static cascade_tags sekizai_tags sass_tags %} {% spaceless %} + +{% block extrastyle %} + + +{% endblock %} + {% include "cascade/admin/widgets/clipboard_menu_group.html" %} {% with id=widget.attrs.id %} - + + + +{% block after_field_sets %} + +{% endblock %} + + + + +{% block footer%} + +{% endblock %} + + {% endwith %} -{% endspaceless %} +{% endspaceless %} diff --git a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_menu_group.html b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_menu_group.html index 32ee70dee..690556269 100644 --- a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_menu_group.html +++ b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_menu_group.html @@ -1,9 +1,37 @@ +{% load static %}
diff --git a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_stride.html b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_stride.html index 650907caa..d96f40536 100644 --- a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_stride.html +++ b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_stride.html @@ -1,9 +1,14 @@ -{% load i18n cms_admin cascade_tags %} -
-
- - +{% load i18n cms_admin cascade_tags %} +
+ + +{{ option.label }} +
+ + +
{% render_cascade data option.label %} +
From ce5dd109942107377ef6f55f1a6b8eb4e2f2e894 Mon Sep 17 00:00:00 2001 From: Haricot Date: Thu, 27 Feb 2020 11:39:07 +0100 Subject: [PATCH 34/69] clipboard.html: js now in clipboard_gallery.js --- .../cascade/js/admin/clipboard_gallery.js | 45 +++++++++++++++ .../cascade/admin/widgets/clipboard.html | 55 +------------------ 2 files changed, 46 insertions(+), 54 deletions(-) create mode 100644 cmsplugin_cascade/static/cascade/js/admin/clipboard_gallery.js diff --git a/cmsplugin_cascade/static/cascade/js/admin/clipboard_gallery.js b/cmsplugin_cascade/static/cascade/js/admin/clipboard_gallery.js new file mode 100644 index 000000000..f97c13427 --- /dev/null +++ b/cmsplugin_cascade/static/cascade/js/admin/clipboard_gallery.js @@ -0,0 +1,45 @@ +const div_select_box = document.querySelector(".select-box"); +const svg_stride = document.querySelectorAll(".cms-cascade-svg-viewer"); +const input_fullscreen = document.querySelectorAll(".clipboard-fullscreen"); +const xs = document.getElementById("js-cascade-btn-xs"); +const sm = document.getElementById("js-cascade-btn-sm"); +const md = document.getElementById("js-cascade-btn-md"); +const lg = document.getElementById("js-cascade-btn-lg"); +const xl = document.getElementById("js-cascade-btn-xl"); +const max = document.getElementById("js-cascade-btn-max"); + +input_fullscreen.forEach(element => { + element.addEventListener('click', function(event) { + if ( div_select_box.classList.contains('clipboard-base')){ + div_select_box.classList.remove('clipboard-base'); + } + else { + div_select_box.classList.add('clipboard-base'); + } + }); +}); + +function parser_clipboards(elements, viewbox){ + elements.forEach(element => { + element.setAttribute("viewBox", viewbox); + }); +} + +xs.addEventListener('click', function(event) { + parser_clipboards(svg_stride ,'0 0 475 600'); + }); +sm.addEventListener('click', function(event) { + parser_clipboards(svg_stride ,'0 0 767 800'); + }); +md.addEventListener('click', function(event) { + parser_clipboards(svg_stride ,'0 0 991 900'); + }); +lg.addEventListener('click', function(event) { + parser_clipboards(svg_stride ,'0 0 1199 2000'); + }); +xl.addEventListener('click', function(event) { + parser_clipboards(svg_stride ,'0 0 2000 4000'); + }); +max.addEventListener('click', function(event) { + parser_clipboards(svg_stride ,'0 0 5000 10000'); + }); diff --git a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard.html b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard.html index 4c5b317cd..ad77e2fe0 100644 --- a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard.html +++ b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard.html @@ -26,65 +26,12 @@
- {% block after_field_sets %} + {% endblock %} - - - {% block footer%} - {% endblock %} - - {% endwith %} {% endspaceless %} From bf14f8f2b0dad92006d1481a7e81398b7fcf6e81 Mon Sep 17 00:00:00 2001 From: Haricot Date: Thu, 27 Feb 2020 15:30:28 +0100 Subject: [PATCH 35/69] add folders import to group --- cmsplugin_cascade/clipboard/cms_plugins.py | 69 +++++++++++++++++----- 1 file changed, 54 insertions(+), 15 deletions(-) diff --git a/cmsplugin_cascade/clipboard/cms_plugins.py b/cmsplugin_cascade/clipboard/cms_plugins.py index f2edd9acc..ab511c7bf 100644 --- a/cmsplugin_cascade/clipboard/cms_plugins.py +++ b/cmsplugin_cascade/clipboard/cms_plugins.py @@ -130,16 +130,18 @@ def treegroup( groups, index2): [treegroup( groups, index) for index, groups in enumerate(queryset , start=1)] if not 'Clipboard Home' in clipboards_groupby: - identifier = 'demo' - clipboards_groupby[ 'Clipboard Home'] = [( identifier, identifier)] - clipboard_home = CascadeClipboardGroup.objects.create(name="Clipboard Home") - data_demo = self.populate_static_json("cascade/admin/clipboards/demo_carousel-plugin.json") - group_clipboard_home = clipboard_home - cascade_clipboard = CascadeClipboard.objects.create( - identifier=identifier, - data=data_demo, - ) - cascade_clipboard.group.set([group_clipboard_home]) + identifier = 'Demo' + group ='Clipboard Home' + # For only one fiel demo. + # data_demo = self.populate_static_json("cascade/admin/clipboards/demo_carousel-plugin.json") + # self.populate_db_group_clipboards( clipboards_groupby, identifier, group, data_demo) + + # folder to group and file to group. + data_folders = self.populate_static_folderGroup_json('cascade/admin/clipboards/') + if data_folders: + self.populate_db_data_clipboards( data_folders, identifier, group) + + CHOICES=(list(clipboards_groupby.items(),)) ff=_("Import from Clipboard") @@ -283,14 +285,51 @@ def add_to_clipboard(self, request, form): ) cascade_clipboard.group.set(group) return render(request, 'cascade/admin/clipboard_close_frame.html', {}) + + def populate_db_group_clipboards(self, clipboards_groupby, identifier, group, data_clipboard): + clipboards_groupby[ group] = [( identifier, identifier)] + clipboard_home = CascadeClipboardGroup.objects.get_or_create(name=group) + cascade_clipboard = CascadeClipboard.objects.get_or_create( + identifier=identifier, + data=data_clipboard, + ) + cascade_clipboard[0].group.set([clipboard_home[0]]) def populate_static_json(self, relative_path_filename): - import os, io, json - from django.contrib.staticfiles import finders - path = finders.find(relative_path_filename) - with io.open(path, 'r') as fh: + import os, io, json + from django.contrib.staticfiles import finders + path = finders.find(relative_path_filename) + with io.open(path, 'r') as fh: config_data = json.load(fh) - return config_data + return config_data + + def populate_db_data_clipboards(self,data, identifier, group_name ): + for group_name , values in data.items(): + if len(values) >= 1: + for value in values: + identifier = value.split('/')[-1].replace('.json','') + data_clipboard = self.populate_static_json(value) + self.populate_db_group_clipboards(data, identifier, group_name, data_clipboard) + + + def populate_static_folderGroup_json(self, relative_path_folder): + import os, io, json + import pathlib + from django.contrib.staticfiles import finders + input_path = finders.find(relative_path_folder) + data = {} + if input_path: + list_folders_top=next(os.walk(input_path))[1] + for n, group_folder in enumerate(list_folders_top, 1): + clipboards_folder=[] + list_subfolder_path=os.path.join(input_path, group_folder) + files_path=list(pathlib.Path(list_subfolder_path).glob('**/*.json')) + for path in files_path: + clipboards_folder.append( str(pathlib.Path(relative_path_folder).joinpath(path.relative_to(input_path)))) + data.update({ group_folder : clipboards_folder}) + return data + + plugin_pool.register_plugin(CascadeClipboardPlugin) From 66a78719216e95ed38f7fcc49268c373cb400cb7 Mon Sep 17 00:00:00 2001 From: Haricot Date: Thu, 27 Feb 2020 15:31:43 +0100 Subject: [PATCH 36/69] add demo in demo folder --- .../cascade/admin/clipboards/{ => demo}/demo_carousel-plugin.json | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cmsplugin_cascade/static/cascade/admin/clipboards/{ => demo}/demo_carousel-plugin.json (100%) diff --git a/cmsplugin_cascade/static/cascade/admin/clipboards/demo_carousel-plugin.json b/cmsplugin_cascade/static/cascade/admin/clipboards/demo/demo_carousel-plugin.json similarity index 100% rename from cmsplugin_cascade/static/cascade/admin/clipboards/demo_carousel-plugin.json rename to cmsplugin_cascade/static/cascade/admin/clipboards/demo/demo_carousel-plugin.json From f20d1cf4849997ed9cb9f284bfb1650702c53ad2 Mon Sep 17 00:00:00 2001 From: Haricot Date: Thu, 27 Feb 2020 16:09:57 +0100 Subject: [PATCH 37/69] optimize ui --- cmsplugin_cascade/clipboard/cms_plugins.py | 7 ++- .../static/cascade/css/admin/clipboard.css | 5 ++ .../cascade/js/admin/clipboard_gallery.js | 4 +- .../admin/widgets/clipboard_menu_group.html | 56 +++++++++---------- 4 files changed, 40 insertions(+), 32 deletions(-) diff --git a/cmsplugin_cascade/clipboard/cms_plugins.py b/cmsplugin_cascade/clipboard/cms_plugins.py index ab511c7bf..ac29ec88d 100644 --- a/cmsplugin_cascade/clipboard/cms_plugins.py +++ b/cmsplugin_cascade/clipboard/cms_plugins.py @@ -132,7 +132,7 @@ def treegroup( groups, index2): if not 'Clipboard Home' in clipboards_groupby: identifier = 'Demo' group ='Clipboard Home' - # For only one fiel demo. + # data_demo = self.populate_static_json("cascade/admin/clipboards/demo_carousel-plugin.json") # self.populate_db_group_clipboards( clipboards_groupby, identifier, group, data_demo) @@ -141,7 +141,10 @@ def treegroup( groups, index2): if data_folders: self.populate_db_data_clipboards( data_folders, identifier, group) - + # Clipboard home + data_demo = self.populate_static_json("cascade/admin/clipboards/demo/demo_carousel-plugin.json") + self.populate_db_group_clipboards( clipboards_groupby, identifier, group, data_demo) + CHOICES=(list(clipboards_groupby.items(),)) ff=_("Import from Clipboard") diff --git a/cmsplugin_cascade/static/cascade/css/admin/clipboard.css b/cmsplugin_cascade/static/cascade/css/admin/clipboard.css index f7621250f..abde920ab 100644 --- a/cmsplugin_cascade/static/cascade/css/admin/clipboard.css +++ b/cmsplugin_cascade/static/cascade/css/admin/clipboard.css @@ -139,3 +139,8 @@ background-color: white; .clipboard-base .select-box__current { display:inherit; } + +.shortcuts-admin{ +display:none; +} + diff --git a/cmsplugin_cascade/static/cascade/js/admin/clipboard_gallery.js b/cmsplugin_cascade/static/cascade/js/admin/clipboard_gallery.js index f97c13427..62684d180 100644 --- a/cmsplugin_cascade/static/cascade/js/admin/clipboard_gallery.js +++ b/cmsplugin_cascade/static/cascade/js/admin/clipboard_gallery.js @@ -26,7 +26,7 @@ function parser_clipboards(elements, viewbox){ } xs.addEventListener('click', function(event) { - parser_clipboards(svg_stride ,'0 0 475 600'); + parser_clipboards(svg_stride ,'0 0 475 500'); }); sm.addEventListener('click', function(event) { parser_clipboards(svg_stride ,'0 0 767 800'); @@ -35,7 +35,7 @@ md.addEventListener('click', function(event) { parser_clipboards(svg_stride ,'0 0 991 900'); }); lg.addEventListener('click', function(event) { - parser_clipboards(svg_stride ,'0 0 1199 2000'); + parser_clipboards(svg_stride ,'0 0 1199 1400'); }); xl.addEventListener('click', function(event) { parser_clipboards(svg_stride ,'0 0 2000 4000'); diff --git a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_menu_group.html b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_menu_group.html index 690556269..3fe950d69 100644 --- a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_menu_group.html +++ b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_menu_group.html @@ -3,40 +3,40 @@ -
From f105e8152b50357208a6962d3defbd30a8d3ed28 Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Thu, 5 Mar 2020 14:01:18 +0100 Subject: [PATCH 38/69] Fix travis python3.7 lxml test/requirements.txt --- tests/requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/requirements.txt b/tests/requirements.txt index 9f03e2295..46824e4a4 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,4 +1,5 @@ -lxml==3.5.0 +lxml +#lxml==3.5.0 pluggy==0.12.0 py==1.8.0 pytest==4.5.0 From ce2ed484994c647da9571960d0b483f576fe3b66 Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Thu, 5 Mar 2020 15:01:31 +0100 Subject: [PATCH 39/69] Fix travis python3.7 lxml in tox.ini --- tox.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tox.ini b/tox.ini index 631d264a7..d08dd207e 100644 --- a/tox.ini +++ b/tox.ini @@ -14,6 +14,8 @@ deps = django22: Django<3.0 django111: Django-Select2<6 django{20,21,22}: Django-Select2 + py{35,36}: lxml==3.5.0 + py{37}: lxml==4.5.0 -r requirements/base.txt -r tests/requirements.txt beautifulsoup4==4.8.1 From e04e1b11760badebe6c58b70178eb9e7b56d19cb Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Thu, 5 Mar 2020 15:06:33 +0100 Subject: [PATCH 40/69] Update test/requirements.txt fix travis lxml --- tests/requirements.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/requirements.txt b/tests/requirements.txt index 46824e4a4..748e05382 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,5 +1,3 @@ -lxml -#lxml==3.5.0 pluggy==0.12.0 py==1.8.0 pytest==4.5.0 From 4182f39bf6be3fdcbfa2c791a804717eb9537a27 Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Sat, 7 Mar 2020 16:12:55 +0100 Subject: [PATCH 41/69] Restore context['request'] fot stride --- cmsplugin_cascade/clipboard/cms_plugins.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/cmsplugin_cascade/clipboard/cms_plugins.py b/cmsplugin_cascade/clipboard/cms_plugins.py index ac29ec88d..3b5c321e5 100644 --- a/cmsplugin_cascade/clipboard/cms_plugins.py +++ b/cmsplugin_cascade/clipboard/cms_plugins.py @@ -27,8 +27,13 @@ class ClipboardWidget(widgets.Select): #template_name = 'django/forms/widgets/select.html' template_name = 'cascade/admin/widgets/clipboard.html' + def __init__(self, *args, **kwargs): + self.req= None + super().__init__(*args, **kwargs) + def get_context(self, name, value, attrs): context = super(ClipboardWidget, self).get_context(name, value, attrs) + context['request'] = self.req context['widget']['optgroups'] = self.optgroups(name, context['widget']['value'], attrs) groups=list(CascadeClipboardGroup.objects.all().exclude( name='Clipboard Home').values_list('name',flat=True)) context['groups_exclude_home'] = groups @@ -101,16 +106,13 @@ def render_modal_window(self, request, form): 'app_label': opts.app_label, 'media': self.media + form.media, } - return TemplateResponse(request, self.change_form_template, context) def import_plugins_view(self, request, *args, **kwargs): # TODO: check for permissions - view_breakdown = request.session.get('view_breakdown', "lg") - placeholder_ref_id = None if request.GET.get('placeholder'): placeholder_ref_id = request.GET.get('placeholder') @@ -144,7 +146,6 @@ def treegroup( groups, index2): # Clipboard home data_demo = self.populate_static_json("cascade/admin/clipboards/demo/demo_carousel-plugin.json") self.populate_db_group_clipboards( clipboards_groupby, identifier, group, data_demo) - CHOICES=(list(clipboards_groupby.items(),)) ff=_("Import from Clipboard") @@ -178,7 +179,8 @@ def treegroup( groups, index2): 'title': title, }) - Form.Media = type("Media",(), {'css' : { 'all': [ ''] }}) + Form.Media = type("Media",(), {'css' : { 'all': [ ''] }}) + Form.base_fields['clipboard'].widget.req = request form = Form(request.GET) assert form.is_valid() elif request.method == 'POST': @@ -210,7 +212,7 @@ def paste_from_clipboard(self, request, form): cb_placeholder_plugin = request.toolbar.clipboard.cmsplugin_set.first() cb_placeholder_instance, _ = cb_placeholder_plugin.get_plugin_instance() - # bug if the Clipboard Placeholder has alias 'AliasPluginModel', object, it has no attribute 'placeholder_ref', + # bug if cb_placeholder_instance.plugin_type != 'AliasPluginModel', it has no attribute 'placeholder_ref', # possible need request.toolbar.clipboard.clear() , add .placeholder_ref new_plugins = cb_placeholder_instance.placeholder_ref.get_plugins() @@ -220,8 +222,8 @@ def paste_from_clipboard(self, request, form): root_plugins = placeholder.get_plugins(language).filter(parent__isnull=True).order_by('changed_date') for position, plugin in enumerate(root_plugins.iterator()): plugin.update(position=position) - placeholder.mark_as_dirty(language, clear_cache=False) + placeholder.mark_as_dirty(language, clear_cache=False) # create a list of pasted plugins to be added to the structure view all_plugins = placeholder.get_plugins(language) if all_plugins.exists(): From 33560b1b30138e3a2bd1b82c6d6925aea2c6e2e0 Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Sat, 7 Mar 2020 16:15:29 +0100 Subject: [PATCH 42/69] Restore context['request'] for stride --- cmsplugin_cascade/templatetags/cascade_tags.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/cmsplugin_cascade/templatetags/cascade_tags.py b/cmsplugin_cascade/templatetags/cascade_tags.py index 490994a66..89a1cde40 100644 --- a/cmsplugin_cascade/templatetags/cascade_tags.py +++ b/cmsplugin_cascade/templatetags/cascade_tags.py @@ -35,7 +35,7 @@ class StrideRenderer(Tag): def render_tag(self, context, data_clipboard, identifier=None): from sekizai.helpers import get_varname as get_sekizai_context_key from cmsplugin_cascade.strides import StrideContentRenderer - tr=False + if isinstance(data_clipboard, dict): # qs_clipboards identifier = identifier @@ -58,11 +58,8 @@ def render_tag(self, context, data_clipboard, identifier=None): tree_data = json.load(fp) else: tree_data = data_clipboard - if 'request' in context: - data_req = context['request'] - else: - data_req = None - content_renderer = StrideContentRenderer(data_req) + request = context['request'] + content_renderer = StrideContentRenderer(request) with context.push(cms_content_renderer=content_renderer): content = content_renderer.render_cascade(context, tree_data) @@ -89,7 +86,7 @@ class RenderPlugin(Tag): Argument('plugin') ) - def render_tag(self, context, plugin, request=None): + def render_tag(self, context, plugin): if not plugin: return '' if 'cms_content_renderer' in context and isinstance(context['cms_content_renderer'], StrideContentRenderer): From bfa9ae27aa2de39e40cab43b9931f004a58ac2dd Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Sat, 7 Mar 2020 16:17:03 +0100 Subject: [PATCH 43/69] Restore context['request'] for stride and cache sekizai_context --- cmsplugin_cascade/strides.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/cmsplugin_cascade/strides.py b/cmsplugin_cascade/strides.py index 96ca1454f..d6d9c6d70 100644 --- a/cmsplugin_cascade/strides.py +++ b/cmsplugin_cascade/strides.py @@ -191,9 +191,8 @@ def render(self, context, instance, placeholder): class StrideContentRenderer(object): def __init__(self, request): - if request: - self.request = request - self.language = get_language_from_request(request) + self.request = request + self.language = get_language_from_request(request) self._cached_templates = {} def render_cascade(self, context, tree_data): @@ -214,7 +213,6 @@ def render_plugin(self, instance, context, placeholder=None, editable=False): from sekizai.helpers import get_varname as get_sekizai_context_key sekizai_context_key = get_sekizai_context_key() - context[sekizai_context_key] = defaultdict(UniqueSequence) if app_settings.CMSPLUGIN_CASCADE['cache_strides'] and getattr(instance.plugin, 'cache', not editable): @@ -222,9 +220,8 @@ def render_plugin(self, instance, context, placeholder=None, editable=False): key = 'cascade_element-{}'.format(instance.pk) content = cache.get(key) if content: - if sekizai_context_key in context: - context[sekizai_context_key]['css'].extend(cache.get(key + ':css_list', [])) - context[sekizai_context_key]['js'].extend(cache.get(key + ':js_list', [])) + context[sekizai_context_key]['css'].extend(cache.get(key + ':css_list', [])) + context[sekizai_context_key]['js'].extend(cache.get(key + ':js_list', [])) return content else: context['cms_cachable_plugins'].value = False @@ -237,9 +234,8 @@ def render_plugin(self, instance, context, placeholder=None, editable=False): content = template.render(context) if context['cms_cachable_plugins'].value: cache.set(key, content) - if sekizai_context_key in context: - cache.set(key + ':css_list', context[sekizai_context_key]['css'].data) - cache.set(key + ':js_list', context[sekizai_context_key]['js'].data) + cache.set(key + ':css_list', context[sekizai_context_key]['css'].data) + cache.set(key + ':js_list', context[sekizai_context_key]['js'].data) return content def user_is_on_edit_mode(self): From 82bc3fc9645c07089dfaa9641d82adc07d04c0bf Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Sat, 7 Mar 2020 16:38:09 +0100 Subject: [PATCH 44/69] add css delimiter border before stride svg --- .../templates/cascade/admin/widgets/clipboard_stride.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_stride.html b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_stride.html index d96f40536..25d1ce5af 100644 --- a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_stride.html +++ b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard_stride.html @@ -3,7 +3,7 @@ {{ option.label }} -
+
From fb66ee56788ac1c634439937f39b749b3ef16069 Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Wed, 11 Mar 2020 11:21:46 +0100 Subject: [PATCH 45/69] serialize_from_placeholder() filter language serialize_from_placeholder () without language gives all the placeholders corresponding to all active languages. --- cmsplugin_cascade/clipboard/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmsplugin_cascade/clipboard/utils.py b/cmsplugin_cascade/clipboard/utils.py index d74783050..65b7f3200 100644 --- a/cmsplugin_cascade/clipboard/utils.py +++ b/cmsplugin_cascade/clipboard/utils.py @@ -12,7 +12,7 @@ from cmsplugin_cascade.models import CascadeElement -def serialize_from_placeholder(placeholder, admin_site=default_admin_site): +def serialize_from_placeholder(placeholder, admin_site=default_admin_site, language=None): """ Create a serialized representation of all the plugins belonging to the clipboard. """ @@ -31,7 +31,7 @@ def populate_data(parent, data): populate_data(child, entry[2]) data = {'plugins': []} - plugin_qs = placeholder.cmsplugin_set.all() + plugin_qs = placeholder.cmsplugin_set.filter(language=language) populate_data(None, data['plugins']) return data From 65b067f1e0b0e1659573235ee646b66c1b1c76ee Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Wed, 11 Mar 2020 11:25:14 +0100 Subject: [PATCH 46/69] add_to_clipboard() filter language --- cmsplugin_cascade/clipboard/cms_plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmsplugin_cascade/clipboard/cms_plugins.py b/cmsplugin_cascade/clipboard/cms_plugins.py index 4ff9d7488..646f43a2d 100644 --- a/cmsplugin_cascade/clipboard/cms_plugins.py +++ b/cmsplugin_cascade/clipboard/cms_plugins.py @@ -170,7 +170,7 @@ def add_to_clipboard(self, request, form): placeholder = form.cleaned_data['placeholder'] language = form.cleaned_data['language'] identifier = form.cleaned_data['identifier'] - data = serialize_from_placeholder(placeholder) + data = serialize_from_placeholder(placeholder,language=language) CascadeClipboard.objects.create( identifier=identifier, data=data, From 15ed366eb50c5c17158951970bb673d26e087f1f Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Wed, 11 Mar 2020 17:16:14 +0100 Subject: [PATCH 47/69] Update 0028_cascade_clipboard.py --- .../migrations/0028_cascade_clipboard.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cmsplugin_cascade/migrations/0028_cascade_clipboard.py b/cmsplugin_cascade/migrations/0028_cascade_clipboard.py index d19fdea29..dfb74e100 100644 --- a/cmsplugin_cascade/migrations/0028_cascade_clipboard.py +++ b/cmsplugin_cascade/migrations/0028_cascade_clipboard.py @@ -32,4 +32,16 @@ class Migration(migrations.Migration): name='last_accessed_at', field=models.DateTimeField(default=None, editable=False, null=True, verbose_name='Last accessed at'), ), + migrations.CreateModel( + name='CascadeClipboardGroup', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=50)), + ], + ), + migrations.AddField( + model_name='cascadeclipboard', + name='group', + field=models.ManyToManyField(blank=True, to='cmsplugin_cascade.CascadeClipboardGroup'), + ), ] From eb8ecd1660839365a457aa4f90dd3910cb0f0aa2 Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Wed, 11 Mar 2020 17:16:30 +0100 Subject: [PATCH 48/69] Delete 0028_auto_20200218_1514.py --- .../migrations/0028_auto_20200218_1514.py | 25 ------------------- 1 file changed, 25 deletions(-) delete mode 100644 cmsplugin_cascade/migrations/0028_auto_20200218_1514.py diff --git a/cmsplugin_cascade/migrations/0028_auto_20200218_1514.py b/cmsplugin_cascade/migrations/0028_auto_20200218_1514.py deleted file mode 100644 index 42ff05426..000000000 --- a/cmsplugin_cascade/migrations/0028_auto_20200218_1514.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 2.2.10 on 2020-02-18 21:14 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('cmsplugin_cascade', '0027_version_1'), - ] - - operations = [ - migrations.CreateModel( - name='CascadeClipboardGroup', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=50)), - ], - ), - migrations.AddField( - model_name='cascadeclipboard', - name='group', - field=models.ManyToManyField(blank=True, to='cmsplugin_cascade.CascadeClipboardGroup'), - ), - ] From c7a328f33bccc274a3aa5629de13f35b091e0635 Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Thu, 12 Mar 2020 01:20:00 +0100 Subject: [PATCH 49/69] update logic if not cascade_clipboard.data --- cmsplugin_cascade/clipboard/cms_plugins.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cmsplugin_cascade/clipboard/cms_plugins.py b/cmsplugin_cascade/clipboard/cms_plugins.py index 361b68096..61a961eb7 100644 --- a/cmsplugin_cascade/clipboard/cms_plugins.py +++ b/cmsplugin_cascade/clipboard/cms_plugins.py @@ -147,9 +147,6 @@ def treegroup( groups, index2): data_demo = self.populate_static_json("cascade/admin/clipboards/demo/demo_carousel-plugin.json") self.populate_db_group_clipboards( clipboards_groupby, identifier, group, data_demo) - CHOICES=(list(clipboards_groupby.items(),)) - ff=_("Import from Clipboard") - if request.GET.get('group'): req_parameter_group = request.GET.get('group') title = ": {}".format(req_parameter_group) @@ -203,6 +200,8 @@ def paste_from_clipboard(self, request, form): cascade_clipboard = form.cleaned_data['clipboard'] tree_order = placeholder.get_plugin_tree_order(language) + if not hasattr(cascade_clipboard, 'data'): + cascade_clipboard = CascadeClipboard.objects.get(identifier=cascade_clipboard) deserialize_to_clipboard(request, cascade_clipboard.data) cascade_clipboard.last_accessed_at = now() cascade_clipboard.save(update_fields=['last_accessed_at']) From e17077be590c080db0e5799078554e19c390b100 Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Sun, 12 Apr 2020 16:05:29 +0200 Subject: [PATCH 50/69] Correct:Export form Clipboard=>Export to Clipboard --- cmsplugin_cascade/clipboard/cms_plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmsplugin_cascade/clipboard/cms_plugins.py b/cmsplugin_cascade/clipboard/cms_plugins.py index 61a961eb7..ce1aec70e 100644 --- a/cmsplugin_cascade/clipboard/cms_plugins.py +++ b/cmsplugin_cascade/clipboard/cms_plugins.py @@ -62,7 +62,7 @@ def get_extra_placeholder_menu_items(cls, request, placeholder): }) return [ PluginMenuItem( - _("Export from Clipboard"), + _("Export to Clipboard"), reverse('admin:export_clipboard_plugins') + '?' + data, data={}, action='modal', From 466b9b6d601718a3bcacf041cf8d0eb0159b4d78 Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Mon, 13 Apr 2020 15:03:16 +0200 Subject: [PATCH 51/69] fix _image_properties to image_properties because attribute with underscore in django templates are not available --- cmsplugin_cascade/image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmsplugin_cascade/image.py b/cmsplugin_cascade/image.py index 5744237af..c8caa2426 100644 --- a/cmsplugin_cascade/image.py +++ b/cmsplugin_cascade/image.py @@ -34,7 +34,7 @@ def clean_image_file(self): image_file = self.cleaned_data['image_file'] # _image_properties are just a cached representation, maybe useless if image_file: - self.cleaned_data['_image_properties'] = { + self.cleaned_data['image_properties'] = { 'width': image_file._width, 'height': image_file._height, 'exif_orientation': image_file.exif.get('Orientation', 1), From 12af39945750e512ee0cc002fa02044ec6498b4b Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Mon, 13 Apr 2020 17:22:02 +0200 Subject: [PATCH 52/69] fix fallback if plugin has require_image False --- cmsplugin_cascade/image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmsplugin_cascade/image.py b/cmsplugin_cascade/image.py index c8caa2426..f4dabf9af 100644 --- a/cmsplugin_cascade/image.py +++ b/cmsplugin_cascade/image.py @@ -26,7 +26,7 @@ class Meta: entangled_fields = {'glossary': ['image_file', 'image_title', 'alt_tag', 'image_properties']} def __init__(self, *args, **kwargs): - if not getattr(self, 'require_image', True): + if not getattr(self, 'require_image', True) and not 'image_file' in kwargs['instance'].glossary: self.base_fields['image_file'].required = False super().__init__(*args, **kwargs) From 0fb4094d793524a840fb1d95dae8ddc5b6ef4b3d Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Mon, 13 Apr 2020 18:04:36 +0200 Subject: [PATCH 53/69] refix fallback if plugin has require_image False --- cmsplugin_cascade/image.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmsplugin_cascade/image.py b/cmsplugin_cascade/image.py index f4dabf9af..216f16978 100644 --- a/cmsplugin_cascade/image.py +++ b/cmsplugin_cascade/image.py @@ -26,9 +26,9 @@ class Meta: entangled_fields = {'glossary': ['image_file', 'image_title', 'alt_tag', 'image_properties']} def __init__(self, *args, **kwargs): - if not getattr(self, 'require_image', True) and not 'image_file' in kwargs['instance'].glossary: - self.base_fields['image_file'].required = False super().__init__(*args, **kwargs) + if not getattr(self, 'require_image', True) and not 'image_properties' in kwargs['instance'].glossary: + self.base_fields['image_file'].required = False def clean_image_file(self): image_file = self.cleaned_data['image_file'] From cd4690a2fd638b427bf8312a26e89abb9b18c363 Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Mon, 13 Apr 2020 18:06:15 +0200 Subject: [PATCH 54/69] fix clipboard demo did not have 'image_properties' --- .../clipboards/demo/demo_carousel-plugin.json | 295 +++++++++++++----- 1 file changed, 224 insertions(+), 71 deletions(-) diff --git a/cmsplugin_cascade/static/cascade/admin/clipboards/demo/demo_carousel-plugin.json b/cmsplugin_cascade/static/cascade/admin/clipboards/demo/demo_carousel-plugin.json index b5350d77d..54a948d7c 100644 --- a/cmsplugin_cascade/static/cascade/admin/clipboards/demo/demo_carousel-plugin.json +++ b/cmsplugin_cascade/static/cascade/admin/clipboards/demo/demo_carousel-plugin.json @@ -1,79 +1,232 @@ { - "plugins": [ + "plugins": [ + [ + "BootstrapContainerPlugin", + { + "glossary": { + "hide_plugin": false, + "breakpoints": [ + "xs", + "sm", + "md", + "lg", + "xl" + ], + "fluid": false + }, + "pk": 1295 + }, + [ [ - "BootstrapCarouselPlugin", - { - "glossary":{ - "hide_plugin":false, - "margins_xs":"", - "margins_sm":"", - "margins_md":"", - "margins_lg":"", - "interval":5, - "options":[ - "slide", - "pause", - "wrap" - ], - "container_max_heights":{ - "xs":"9rem", - "sm":"9rem", - "md":"9rem", - "lg":"9rem", - "xl":"9rem" - }, - "resize_options":[ - "upscale", - "crop", - "subject_location", - "high_resolution" - ] + "BootstrapRowPlugin", + { + "glossary": { + "hide_plugin": false, + "padding_xs": "", + "padding_sm": "", + "padding_md": "", + "padding_lg": "", + "padding_xl": "" + }, + "pk": 1296 }, - "pk":229 - }, - [ [ - "BootstrapCarouselSlidePlugin", - { - "glossary":{ - "resize_options":[ - "upscale", - "crop", - "subject_location", - "high_resolution" - ], - "image":{ - "pk":4, - "model":"filer.Image" - }, - "media_queries":{ - "xs":{ - "width":572, - "media":"(max-width: 575.98px)" - }, - "sm":{ - "width":540, - "media":"(min-width: 576px) and (max-width: 767.98px)" - }, - "md":{ - "width":720, - "media":"(min-width: 768px) and (max-width: 991.98px)" + [ + "BootstrapColumnPlugin", + { + "glossary": { + "xs-column-width": "col" + }, + "pk": 1297 }, - "lg":{ - "width":960, - "media":"(min-width: 992px) and (max-width: 1199.98px)" - }, - "xl":{ - "width":1140, - "media":"(min-width: 1200px)" - } - } - }, - "pk":1526 - }, - [] + [ + [ + "BootstrapCarouselPlugin", + { + "glossary": { + "hide_plugin": false, + "margins_xs": "", + "margins_sm": "", + "margins_md": "", + "margins_lg": "", + "margins_xl": "", + "interval": 5, + "options": [ + "slide", + "pause", + "wrap" + ], + "container_max_heights": { + "xs": "100px", + "sm": "150px", + "md": "200px", + "lg": "250px", + "xl": "300px" + }, + "resize_options": [ + "upscale", + "crop", + "subject_location", + "high_resolution" + ] + }, + "pk": 1316 + }, + [ + [ + "BootstrapCarouselSlidePlugin", + { + "glossary": { + "hide_plugin": false, + "image_file": { + "model": "filer.image", + "pk": 234 + }, + "image_title": "", + "alt_tag": "", + "image_properties": { + "width": 1368, + "height": 768, + "exif_orientation": 1 + }, + "resize_options": [ + "upscale", + "crop", + "subject_location", + "high_resolution" + ], + "media_queries": { + "xs": { + "width": 572, + "media": "(max-width: 575.98px)" + }, + "sm": { + "width": 540, + "media": "(min-width: 576px) and (max-width: 767.98px)" + }, + "md": { + "width": 720, + "media": "(min-width: 768px) and (max-width: 991.98px)" + }, + "lg": { + "width": 960, + "media": "(min-width: 992px) and (max-width: 1199.98px)" + }, + "xl": { + "width": 1140, + "media": "(min-width: 1200px)" + } + } + }, + "pk": 1317 + }, + [] + ], + [ + "BootstrapCarouselSlidePlugin", + { + "glossary": { + "hide_plugin": false, + "image_file": { + "model": "filer.image", + "pk": 235 + }, + "image_title": "", + "alt_tag": "", + "image_properties": { + "width": 1280, + "height": 720, + "exif_orientation": 1 + }, + "resize_options": [ + "upscale", + "crop", + "subject_location", + "high_resolution" + ], + "media_queries": { + "xs": { + "width": 572, + "media": "(max-width: 575.98px)" + }, + "sm": { + "width": 540, + "media": "(min-width: 576px) and (max-width: 767.98px)" + }, + "md": { + "width": 720, + "media": "(min-width: 768px) and (max-width: 991.98px)" + }, + "lg": { + "width": 960, + "media": "(min-width: 992px) and (max-width: 1199.98px)" + }, + "xl": { + "width": 1140, + "media": "(min-width: 1200px)" + } + } + }, + "pk": 1318 + }, + [] + ], + [ + "BootstrapCarouselSlidePlugin", + { + "glossary": { + "hide_plugin": false, + "image_file": { + "model": "filer.image", + "pk": 236 + }, + "image_title": "", + "alt_tag": "", + "image_properties": { + "width": 1367, + "height": 344, + "exif_orientation": 1 + }, + "resize_options": [ + "upscale", + "crop", + "subject_location", + "high_resolution" + ], + "media_queries": { + "xs": { + "width": 572, + "media": "(max-width: 575.98px)" + }, + "sm": { + "width": 540, + "media": "(min-width: 576px) and (max-width: 767.98px)" + }, + "md": { + "width": 720, + "media": "(min-width: 768px) and (max-width: 991.98px)" + }, + "lg": { + "width": 960, + "media": "(min-width: 992px) and (max-width: 1199.98px)" + }, + "xl": { + "width": 1140, + "media": "(min-width: 1200px)" + } + } + }, + "pk": 1319 + }, + [] + ] + ] + ] + ] + ] ] - ] ] - ] + ] + ] + ] } From a743f3af4dabd17d18a646a8fd44e5fd067162cb Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Mon, 13 Apr 2020 18:33:13 +0200 Subject: [PATCH 55/69] refix fallback --- cmsplugin_cascade/image.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmsplugin_cascade/image.py b/cmsplugin_cascade/image.py index 216f16978..2ee15b09a 100644 --- a/cmsplugin_cascade/image.py +++ b/cmsplugin_cascade/image.py @@ -27,8 +27,8 @@ class Meta: def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - if not getattr(self, 'require_image', True) and not 'image_properties' in kwargs['instance'].glossary: - self.base_fields['image_file'].required = False + #if not getattr(self, 'require_image', True): + # self.base_fields['image_file'].required = False def clean_image_file(self): image_file = self.cleaned_data['image_file'] From 893185659a20188f5dc7c4d8a90d0c8520d240b6 Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Thu, 16 Apr 2020 22:12:56 +0200 Subject: [PATCH 56/69] fix redis_cache "Must pass in a callable" --- cmsplugin_cascade/templatetags/cascade_tags.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cmsplugin_cascade/templatetags/cascade_tags.py b/cmsplugin_cascade/templatetags/cascade_tags.py index 89a1cde40..a6b9ae2a3 100644 --- a/cmsplugin_cascade/templatetags/cascade_tags.py +++ b/cmsplugin_cascade/templatetags/cascade_tags.py @@ -12,6 +12,7 @@ from classytags.core import Options, Tag from cmsplugin_cascade.strides import StrideContentRenderer from django.templatetags.static import static +from sekizai.data import UniqueSequence register = template.Library() @@ -67,7 +68,12 @@ def render_tag(self, context, data_clipboard, identifier=None): cache = caches['default'] if cache: sekizai_context_key = get_sekizai_context_key() - SEKIZAI_CONTENT_HOLDER = cache.get_or_set(sekizai_context_key, context.get(sekizai_context_key)) + if context.get(sekizai_context_key): + def context_sekizai(): + return context.get(sekizai_context_key) + else: + context_sekizai = UniqueSequence + SEKIZAI_CONTENT_HOLDER = cache.get_or_set(sekizai_context_key, context_sekizai) if SEKIZAI_CONTENT_HOLDER: for name in SEKIZAI_CONTENT_HOLDER: context[sekizai_context_key][name] = SEKIZAI_CONTENT_HOLDER[name] From 89ba45a985bf494d1d7c3314bf8b18486eddfe53 Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Tue, 21 Apr 2020 18:33:11 +0200 Subject: [PATCH 57/69] Corrected fallback picture --- .../cascade/generic/fallback_picture.html | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/cmsplugin_cascade/templates/cascade/generic/fallback_picture.html b/cmsplugin_cascade/templates/cascade/generic/fallback_picture.html index 44fcf9bd0..0cb0de245 100644 --- a/cmsplugin_cascade/templates/cascade/generic/fallback_picture.html +++ b/cmsplugin_cascade/templates/cascade/generic/fallback_picture.html @@ -6,15 +6,15 @@ @media only screen and {{ elem.media }} { .fallback-picture-{{instance.pk}} { {% if "img-fluid" in instance.glossary.image_shapes %} -{% if elem.crop %} - width: {{ elem.size.0 }}px; +{% if elem.crop %} + max-width: {{ elem.size.0 }}px; {% endif %} {% else %} {% if elem.crop %} - width: {{ elem.size.0 }}px; + max-width: {{ elem.size.0 }}px; {% endif %} {% endif %} - height: {{ elem.size.1 }}px; + max-height: {{ elem.size.1 }}px; } } {% endif %} @@ -22,11 +22,9 @@
From 56688b2ab73b6e523d78f19aa1d4412bfbce4b72 Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Tue, 21 Apr 2020 19:03:51 +0200 Subject: [PATCH 58/69] image.py:Correct cleaned_data image_properties otherwise it is missing (image_propreties) --- cmsplugin_cascade/image.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/cmsplugin_cascade/image.py b/cmsplugin_cascade/image.py index 2ee15b09a..c2b3db12f 100644 --- a/cmsplugin_cascade/image.py +++ b/cmsplugin_cascade/image.py @@ -26,20 +26,25 @@ class Meta: entangled_fields = {'glossary': ['image_file', 'image_title', 'alt_tag', 'image_properties']} def __init__(self, *args, **kwargs): + if not getattr(self, 'require_image', True): + self.base_fields['image_file'].required = False super().__init__(*args, **kwargs) - #if not getattr(self, 'require_image', True): - # self.base_fields['image_file'].required = False - def clean_image_file(self): - image_file = self.cleaned_data['image_file'] - # _image_properties are just a cached representation, maybe useless - if image_file: - self.cleaned_data['image_properties'] = { + + def clean(self): + cleaned_data = super().clean() + if 'image_file' in self.cleaned_data: + image_file = self.cleaned_data['image_file'] + if image_file.mime_type == 'image/svg+xml': + image_file_orientation = 1 + else: + image_file_orientation = image_file.exif.get('Orientation', 1) + cleaned_data['image_properties'] = { 'width': image_file._width, 'height': image_file._height, - 'exif_orientation': image_file.exif.get('Orientation', 1), + 'exif_orientation': image_file_orientation, } - return image_file + return cleaned_data class ImagePropertyMixin: From d95108c8b0ef0bf773375b774c0883befb8c0353 Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Tue, 21 Apr 2020 19:16:16 +0200 Subject: [PATCH 59/69] Corrected Image fallback --- .../templates/cascade/generic/fallback_image.html | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/cmsplugin_cascade/templates/cascade/generic/fallback_image.html b/cmsplugin_cascade/templates/cascade/generic/fallback_image.html index f07d033c8..1351c52c0 100644 --- a/cmsplugin_cascade/templates/cascade/generic/fallback_image.html +++ b/cmsplugin_cascade/templates/cascade/generic/fallback_image.html @@ -1,7 +1,6 @@ {% load l10n static thumbnail cascade_tags %} - -{% if instance.glossary.image_properties.width != src.size.0 or instance.glossary.image_properties.height != src.size.1 %} {% fallback_config 'image' as fallback_image %} +{% if instance.glossary.image_properties.width != src.size.0 or instance.glossary.image_properties.height != src.size.1 %} {% if not 'img-fluid' in instance.glossary.image_shapes %}
{% else %} -
{% endif %} {% else %} -
From 8a16da5f61c456a8daf1547adcea327ecfd6913a Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Tue, 21 Apr 2020 19:37:44 +0200 Subject: [PATCH 60/69] if CMSPLUGIN_CASCADE['fallback']['img_or_pic_lost_pk'] --- cmsplugin_cascade/templatetags/cascade_tags.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmsplugin_cascade/templatetags/cascade_tags.py b/cmsplugin_cascade/templatetags/cascade_tags.py index a6b9ae2a3..d237f84a1 100644 --- a/cmsplugin_cascade/templatetags/cascade_tags.py +++ b/cmsplugin_cascade/templatetags/cascade_tags.py @@ -1,6 +1,7 @@ import io import json import os +from ast import literal_eval from cms.toolbar.utils import get_toolbar_from_request from django import template from django.conf import settings @@ -59,6 +60,9 @@ def render_tag(self, context, data_clipboard, identifier=None): tree_data = json.load(fp) else: tree_data = data_clipboard + if settings.CMSPLUGIN_CASCADE['fallback']['img_or_pic_lost_pk']: + tree_data_lost_ref_img = str(tree_data).replace("'image_file': {'model': 'filer.image', 'pk': ", "'image_file': {'model': 'filer.image', 'pk': 10000") + tree_data= literal_eval(tree_data_lost_ref_img) request = context['request'] content_renderer = StrideContentRenderer(request) with context.push(cms_content_renderer=content_renderer): From 0e8d68e55642a6b38e8c1ec1e48e2fb635c3149d Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Tue, 21 Apr 2020 19:40:16 +0200 Subject: [PATCH 61/69] if settings.CMSPLUGIN_CASCADE.get('fallback', None ).get('img_or_pic_lost_pk', None) --- cmsplugin_cascade/templatetags/cascade_tags.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmsplugin_cascade/templatetags/cascade_tags.py b/cmsplugin_cascade/templatetags/cascade_tags.py index d237f84a1..d98d85d2d 100644 --- a/cmsplugin_cascade/templatetags/cascade_tags.py +++ b/cmsplugin_cascade/templatetags/cascade_tags.py @@ -59,8 +59,8 @@ def render_tag(self, context, data_clipboard, identifier=None): with io.open(jsonfile) as fp: tree_data = json.load(fp) else: - tree_data = data_clipboard - if settings.CMSPLUGIN_CASCADE['fallback']['img_or_pic_lost_pk']: + tree_data = data_clipboard + if settings.CMSPLUGIN_CASCADE.get('fallback', None ).get('img_or_pic_lost_pk', None): tree_data_lost_ref_img = str(tree_data).replace("'image_file': {'model': 'filer.image', 'pk': ", "'image_file': {'model': 'filer.image', 'pk': 10000") tree_data= literal_eval(tree_data_lost_ref_img) request = context['request'] From 3c2ab71b518b971effeea983a88fa7c5ccf16f00 Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Wed, 22 Apr 2020 07:41:29 +0200 Subject: [PATCH 62/69] Update clipboard.css --- .../static/cascade/css/admin/clipboard.css | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/cmsplugin_cascade/static/cascade/css/admin/clipboard.css b/cmsplugin_cascade/static/cascade/css/admin/clipboard.css index abde920ab..e799adccc 100644 --- a/cmsplugin_cascade/static/cascade/css/admin/clipboard.css +++ b/cmsplugin_cascade/static/cascade/css/admin/clipboard.css @@ -1,3 +1,6 @@ +body { +padding-top:0px !important!; +} .pull-left { float: left !important; } @@ -51,6 +54,7 @@ div.cms .cms-structure.cms-structure-condensed .cms-submenu-item a[data-icon=imp .cascade_clipboard_admin { padding: 1rem; + z-index: 4; } .button_clipboard { @@ -90,6 +94,8 @@ background-color: white; display: inline-flex; flex-wrap: wrap; position:absolute; + justify-content: center; + width: 100%; } .cms-cascade-clipboard { @@ -103,7 +109,6 @@ background-color: white; } .clipboard-fullscreen { - display: inline-block; -webkit-appearance: none; -moz-appearance: none; -o-appearance: none; @@ -114,6 +119,12 @@ background-color: white; top: 26px; } +.form-clipboard { +position: absolute; +width: 100%; +top: 3rem; +} + .clipboard-fullscreen:after{ content:'fullscreen'; cursor: pointer; @@ -143,4 +154,3 @@ background-color: white; .shortcuts-admin{ display:none; } - From a9cb2737c2673c463dda46271dbe6f1ebc010c66 Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Wed, 22 Apr 2020 14:48:39 +0200 Subject: [PATCH 63/69] Functionality refactoring --- cmsplugin_cascade/clipboard/cms_plugins.py | 198 ++++++++++----------- 1 file changed, 94 insertions(+), 104 deletions(-) diff --git a/cmsplugin_cascade/clipboard/cms_plugins.py b/cmsplugin_cascade/clipboard/cms_plugins.py index ce1aec70e..4e788c927 100644 --- a/cmsplugin_cascade/clipboard/cms_plugins.py +++ b/cmsplugin_cascade/clipboard/cms_plugins.py @@ -1,9 +1,11 @@ import json +from ast import literal_eval from django.conf.urls import url from django.contrib.admin import site as default_admin_site from django.contrib.admin.helpers import AdminForm from django.core.exceptions import PermissionDenied from django.forms import CharField, ModelChoiceField, ModelMultipleChoiceField, ChoiceField, MultipleChoiceField +from django.http import HttpResponse from django.shortcuts import render from django.template.response import TemplateResponse from django.urls import reverse @@ -24,22 +26,73 @@ from django.conf import settings +def FormViewClipboard(request, form, context): + group_selected=request.GET.get('group', 'Clipboard Home') + widget = form['clipboard'].field.widget + widget.attrs['id']= '1' + widget.attrs['pk']= '1' + # widget.choices, len_ungroup = merder() + form['clipboard'].field.widget.get_context('clipboards', '', widget.attrs) + widget.optgroups = form['clipboard'].field.widget.optgroups_result + tpl_basedir = settings.CMSPLUGIN_CASCADE['bootstrap4'].get('template_basedir', None) + context.update( { + 'img_or_pic_lost_pk': settings.CMSPLUGIN_CASCADE['fallback']['img_or_pic_lost_pk'], + 'tpl_basedir' :tpl_basedir, + 'len_ungroup' :form.len_ungroup, + 'group_selected' : group_selected, + 'widget': widget , + 'placeholder_ref_id': request.GET['placeholder'], + 'language_ref': request.GET['language'], + 'main_scss': settings.CMSPLUGIN_CASCADE['fallback']['path_main_scss'], + 'qs_clipboards': CascadeClipboard.objects.all(), + 'groups_exclude_home':list(CascadeClipboardGroup.objects.all().exclude( name='Clipboard Home').values_list('name',flat=True,)), + 'widget_optgroups': form['clipboard'].field.widget.optgroups_result, + 'form': form }) + return render(request, "cascade/admin/widgets/clipboard_sav.html", context) + + +def tree_group_clipboards(): + queryset=CascadeClipboard.objects.all().prefetch_related('group') + clipboards_groupby={} + + def treegroup( groups, index2): + groups_clipboard=list(groups.group.values_list('name', flat=True)) + if len(groups_clipboard) >= 1: + for index, key in enumerate(groups_clipboard, start=1): + clipboards_groupby.setdefault(key, []) + clipboards_groupby[key].append(( groups.identifier ,groups.identifier,)) + else: + clipboards_groupby.setdefault('ungroup', []) + clipboards_groupby['ungroup'].append(( groups.identifier ,groups.identifier,)) + + [treegroup( groups, index) for index, groups in enumerate(queryset , start=1)] + if 'ungroup' in clipboards_groupby : + len_ungroup = len(clipboards_groupby['ungroup']) + else: + len_ungroup = 0 + if not 'Clipboard Home' in clipboards_groupby: + group ='Clipboard Home' + clipboard_home = CascadeClipboardGroup.objects.get_or_create(name=group) + CHOICES = (list(clipboards_groupby.items(),)) + return CHOICES, len_ungroup + + class ClipboardWidget(widgets.Select): - #template_name = 'django/forms/widgets/select.html' - template_name = 'cascade/admin/widgets/clipboard.html' def __init__(self, *args, **kwargs): self.req= None + self.optgroups_result= None super().__init__(*args, **kwargs) def get_context(self, name, value, attrs): context = super(ClipboardWidget, self).get_context(name, value, attrs) context['request'] = self.req - context['widget']['optgroups'] = self.optgroups(name, context['widget']['value'], attrs) + self.optgroups_result = self.optgroups(name, context['widget']['value'], attrs) groups=list(CascadeClipboardGroup.objects.all().exclude( name='Clipboard Home').values_list('name',flat=True)) context['groups_exclude_home'] = groups context['qs_clipboards'] = CascadeClipboard.objects.all() context['main_scss'] = settings.CMSPLUGIN_CASCADE['fallback']['path_main_scss'] + return context @@ -62,7 +115,7 @@ def get_extra_placeholder_menu_items(cls, request, placeholder): }) return [ PluginMenuItem( - _("Export to Clipboard"), + _("Export from Clipboard"), reverse('admin:export_clipboard_plugins') + '?' + data, data={}, action='modal', @@ -73,7 +126,6 @@ def get_extra_placeholder_menu_items(cls, request, placeholder): PluginMenuItem( _("Import from Clipboard"), reverse('admin:import_clipboard_plugins') + '?' + data, - data={}, action='modal', attributes={ 'icon': 'import', @@ -81,8 +133,7 @@ def get_extra_placeholder_menu_items(cls, request, placeholder): ), ] - - def render_modal_window(self, request, form): + def render_modal_window(self, request,form): """ Render a modal popup window with a select box to edit the form """ @@ -106,65 +157,37 @@ def render_modal_window(self, request, form): 'app_label': opts.app_label, 'media': self.media + form.media, } - return TemplateResponse(request, self.change_form_template, context) + if not 'ClipboardExportForm' in str(type(form)): + context.update({ + 'main_scss': settings.CMSPLUGIN_CASCADE['fallback']['path_main_scss'], + 'qs_clipboards': CascadeClipboard.objects.all(), + 'groups_exclude_home':list(CascadeClipboardGroup.objects.all().exclude( name='Clipboard Home').values_list('name',flat=True)), + 'group_selected' : 'Clipboard Home', + }) + response = FormViewClipboard(request, form , context ) + return response + else: + return TemplateResponse(request, self.change_form_template, context ) + + return TemplateResponse(request, self.change_form_template, context ) def import_plugins_view(self, request, *args, **kwargs): # TODO: check for permissions - view_breakdown = request.session.get('view_breakdown', "lg") placeholder_ref_id = None if request.GET.get('placeholder'): placeholder_ref_id = request.GET.get('placeholder') - queryset=CascadeClipboard.objects.all().prefetch_related('group') - clipboards_groupby={} - - def treegroup( groups, index2): - groups_clipboard=list(groups.group.values_list('name', flat=True)) - if len(groups_clipboard) >= 1: - for index, key in enumerate(groups_clipboard, start=1): - clipboards_groupby.setdefault(key, []) - clipboards_groupby[key].append(( groups.identifier ,groups.identifier,)) - else: - clipboards_groupby.setdefault('ungroup', []) - clipboards_groupby['ungroup'].append(( groups.identifier ,groups.identifier,)) - - [treegroup( groups, index) for index, groups in enumerate(queryset , start=1)] - - if not 'Clipboard Home' in clipboards_groupby: - identifier = 'Demo' - group ='Clipboard Home' - - # data_demo = self.populate_static_json("cascade/admin/clipboards/demo_carousel-plugin.json") - # self.populate_db_group_clipboards( clipboards_groupby, identifier, group, data_demo) - - # folder to group and file to group. - data_folders = self.populate_static_folderGroup_json('cascade/admin/clipboards/') - if data_folders: - self.populate_db_data_clipboards( data_folders, identifier, group) - - # Clipboard home - data_demo = self.populate_static_json("cascade/admin/clipboards/demo/demo_carousel-plugin.json") - self.populate_db_group_clipboards( clipboards_groupby, identifier, group, data_demo) - if request.GET.get('group'): req_parameter_group = request.GET.get('group') title = ": {}".format(req_parameter_group) else: req_parameter_group = "Clipboard Home" title = _("Import to Clipboard") + CHOICES, len_ungroup = tree_group_clipboards() - # if empty clipboards but has group do empty - if not req_parameter_group in clipboards_groupby: - clipboards_groupby[req_parameter_group] = '' - - if 'ungroup' in clipboards_groupby : - len_ungroup = len(clipboards_groupby[req_parameter_group]) - else: - len_ungroup = 0 - - CHOICES=clipboards_groupby[req_parameter_group] language= get_language_from_request(request) + if request.method == 'GET': Form = type('ClipboardImportForm', (ClipboardBaseForm,), { 'clipboard':ChoiceField( @@ -179,7 +202,9 @@ def treegroup( groups, index2): Form.Media = type("Media",(), {'css' : { 'all': [ ''] }}) Form.base_fields['clipboard'].widget.req = request form = Form(request.GET) + form.len_ungroup = len_ungroup assert form.is_valid() + elif request.method == 'POST': Form = type('ClipboardImportForm', (ClipboardBaseForm,), { 'clipboard': ChoiceField( @@ -189,12 +214,19 @@ def treegroup( groups, index2): ), 'title': title, }) - form = Form(request.POST) + + complete_form_dict = {'placeholder':request.GET['placeholder'], 'language':request.GET['language'] } + request_post_dict = request.POST.dict() + request_post_dict.update(complete_form_dict) + form = Form(request_post_dict) + + form.len_ungroup = len_ungroup if form.is_valid(): return self.paste_from_clipboard(request, form) return self.render_modal_window(request, form) def paste_from_clipboard(self, request, form): + # request.toolbar.clipboard.clear() placeholder = form.cleaned_data['placeholder'] language = form.cleaned_data['language'] cascade_clipboard = form.cleaned_data['clipboard'] @@ -202,6 +234,9 @@ def paste_from_clipboard(self, request, form): tree_order = placeholder.get_plugin_tree_order(language) if not hasattr(cascade_clipboard, 'data'): cascade_clipboard = CascadeClipboard.objects.get(identifier=cascade_clipboard) + if settings.CMSPLUGIN_CASCADE.get('fallback', None ).get('img_or_pic_lost_pk', None): + tree_data_lost_ref_img = str(cascade_clipboard.data).replace("'image_file': {'model': 'filer.image', 'pk': ", "'image_file': {'model': 'filer.image', 'pk': 10000") + cascade_clipboard.data= literal_eval(tree_data_lost_ref_img) deserialize_to_clipboard(request, cascade_clipboard.data) cascade_clipboard.last_accessed_at = now() cascade_clipboard.save(update_fields=['last_accessed_at']) @@ -210,10 +245,12 @@ def paste_from_clipboard(self, request, form): cb_placeholder_plugin = request.toolbar.clipboard.cmsplugin_set.first() cb_placeholder_instance, _ = cb_placeholder_plugin.get_plugin_instance() - # bug if cb_placeholder_instance.plugin_type != 'AliasPluginModel', it has no attribute 'placeholder_ref', - # possible need request.toolbar.clipboard.clear() , add .placeholder_ref - new_plugins = cb_placeholder_instance.placeholder_ref.get_plugins() - + # bug if cb_placeholder_instance.plugin_type == 'Alias or Text, + # they don't have a 'placeholder ref' attribute. + if cb_placeholder_instance.plugin_type == 'AliasPlugin' or cb_placeholder_instance.plugin_type == 'AliasPluginModel' or cb_placeholder_instance.plugin_type == 'TextPlugin': + return HttpResponse('Clipboard has AliasPlugin or TextPlugin, clear Clipboard Before') + else: + new_plugins = cb_placeholder_instance.placeholder_ref.get_plugins() new_plugins.update(placeholder=placeholder) # reorder root plugins in placeholder @@ -222,7 +259,7 @@ def paste_from_clipboard(self, request, form): plugin.update(position=position) placeholder.mark_as_dirty(language, clear_cache=False) - # create a list of pasted plugins to be added to the structure view + # create a list of pasted plugins to be added to the soptgroups_resultture view all_plugins = placeholder.get_plugins(language) if all_plugins.exists(): new_plugins = placeholder.get_plugins(language).exclude(pk__in=tree_order) @@ -231,7 +268,7 @@ def paste_from_clipboard(self, request, form): else: return render(request, 'cascade/admin/clipboard_reload_page.html') data['target_placeholder_id'] = placeholder.pk - context = {'structure_data': json.dumps(data)} + context = {'soptgroups_resultture_data': json.dumps(data)} return render(request, 'cascade/admin/clipboard_paste_plugins.html', context) def export_plugins_view(self, request): @@ -256,7 +293,6 @@ def export_plugins_view(self, request): form.fields['group'].widget = RelatedFieldWidgetWrapper( form.fields['group'].widget,CascadeClipboard.group.rel, default_admin_site, can_change_related=True) - assert form.is_valid() elif request.method == 'POST': Form = type('ClipboardExportForm', (ClipboardBaseForm,), { @@ -288,51 +324,5 @@ def add_to_clipboard(self, request, form): ) cascade_clipboard.group.set(group) return render(request, 'cascade/admin/clipboard_close_frame.html', {}) - - - def populate_db_group_clipboards(self, clipboards_groupby, identifier, group, data_clipboard): - clipboards_groupby[ group] = [( identifier, identifier)] - clipboard_home = CascadeClipboardGroup.objects.get_or_create(name=group) - cascade_clipboard = CascadeClipboard.objects.get_or_create( - identifier=identifier, - data=data_clipboard, - ) - cascade_clipboard[0].group.set([clipboard_home[0]]) - - def populate_static_json(self, relative_path_filename): - import os, io, json - from django.contrib.staticfiles import finders - path = finders.find(relative_path_filename) - with io.open(path, 'r') as fh: - config_data = json.load(fh) - return config_data - - def populate_db_data_clipboards(self,data, identifier, group_name ): - for group_name , values in data.items(): - if len(values) >= 1: - for value in values: - identifier = value.split('/')[-1].replace('.json','') - data_clipboard = self.populate_static_json(value) - self.populate_db_group_clipboards(data, identifier, group_name, data_clipboard) - - - def populate_static_folderGroup_json(self, relative_path_folder): - import os, io, json - import pathlib - from django.contrib.staticfiles import finders - input_path = finders.find(relative_path_folder) - data = {} - if input_path: - list_folders_top=next(os.walk(input_path))[1] - for n, group_folder in enumerate(list_folders_top, 1): - clipboards_folder=[] - list_subfolder_path=os.path.join(input_path, group_folder) - files_path=list(pathlib.Path(list_subfolder_path).glob('**/*.json')) - for path in files_path: - clipboards_folder.append( str(pathlib.Path(relative_path_folder).joinpath(path.relative_to(input_path)))) - data.update({ group_folder : clipboards_folder}) - return data - - plugin_pool.register_plugin(CascadeClipboardPlugin) From b71d8d3adfe18a59ae57a8651b8c86d2ac0c7eb2 Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Wed, 22 Apr 2020 14:49:06 +0200 Subject: [PATCH 64/69] Update cms_plugins.py --- cmsplugin_cascade/clipboard/cms_plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmsplugin_cascade/clipboard/cms_plugins.py b/cmsplugin_cascade/clipboard/cms_plugins.py index 4e788c927..f61853ffe 100644 --- a/cmsplugin_cascade/clipboard/cms_plugins.py +++ b/cmsplugin_cascade/clipboard/cms_plugins.py @@ -48,7 +48,7 @@ def FormViewClipboard(request, form, context): 'groups_exclude_home':list(CascadeClipboardGroup.objects.all().exclude( name='Clipboard Home').values_list('name',flat=True,)), 'widget_optgroups': form['clipboard'].field.widget.optgroups_result, 'form': form }) - return render(request, "cascade/admin/widgets/clipboard_sav.html", context) + return render(request, "cascade/admin/widgets/clipboard_import.html", context) def tree_group_clipboards(): From 641b5aecf6f727785cd98373428532c32769e0ab Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Wed, 22 Apr 2020 14:49:25 +0200 Subject: [PATCH 65/69] Update cms_plugins.py --- cmsplugin_cascade/clipboard/cms_plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmsplugin_cascade/clipboard/cms_plugins.py b/cmsplugin_cascade/clipboard/cms_plugins.py index f61853ffe..6136c30f3 100644 --- a/cmsplugin_cascade/clipboard/cms_plugins.py +++ b/cmsplugin_cascade/clipboard/cms_plugins.py @@ -48,7 +48,7 @@ def FormViewClipboard(request, form, context): 'groups_exclude_home':list(CascadeClipboardGroup.objects.all().exclude( name='Clipboard Home').values_list('name',flat=True,)), 'widget_optgroups': form['clipboard'].field.widget.optgroups_result, 'form': form }) - return render(request, "cascade/admin/widgets/clipboard_import.html", context) + return render(request, "cascade/admin/clipboard_import.html", context) def tree_group_clipboards(): From eeff3bf8f63d1ea1b8d16d10bc4b987d7d99c468 Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Wed, 22 Apr 2020 14:50:55 +0200 Subject: [PATCH 66/69] Create clipboard_import.html --- .../cascade/admin/clipboard_import.html | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 cmsplugin_cascade/templates/cascade/admin/clipboard_import.html diff --git a/cmsplugin_cascade/templates/cascade/admin/clipboard_import.html b/cmsplugin_cascade/templates/cascade/admin/clipboard_import.html new file mode 100644 index 000000000..de1f38d72 --- /dev/null +++ b/cmsplugin_cascade/templates/cascade/admin/clipboard_import.html @@ -0,0 +1,102 @@ +{% load static cms_tags cascade_tags sekizai_tags djng_tags i18n sass_tags %} + + + + {% block title %}Djangocms-cascade Clipboard{% endblock %} + + + + + + {% block head %} + {% addtoblock "css" %}{% endaddtoblock %} + {% addtoblock "css" %} {% endaddtoblock %} + +{% addtoblock "js" %}{% endaddtoblock %} +{% addtoblock "js" %}{% endaddtoblock %} +{% addtoblock "js" %}{% endaddtoblock %} +{% addtoblock "js" %}{% endaddtoblock %} +{% addtoblock "js" %}{% endaddtoblock %} + {% add_data "ng-requires" "ui.bootstrap" %} + + + + + {% endblock head %} + {% render_block "css" %} + + + + + +
+ {% block header %}{% endblock %} +
+ {% block toast-messages %}{% include "shop/messages.html" %}{% endblock %} + + +{% spaceless %} + +{% include "cascade/admin/widgets/clipboard_menu_group.html" %} + + +
+ {% csrf_token %} + + +{% with id=widget.attrs.id %} + + + +{% endwith %} +
+ + +
+
+ +{% endspaceless %} +{# to do logic for jquery #} +{# #} +{% addtoblock "js" %} {% endaddtoblock %} + +{% render_block 'js' %} + + +{% block footer%} +{% endblock %} + From 5ea1b2a62592551a72db40fd06e79c230c393749 Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Wed, 22 Apr 2020 15:04:58 +0200 Subject: [PATCH 67/69] Update app_settings.py --- cmsplugin_cascade/app_settings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmsplugin_cascade/app_settings.py b/cmsplugin_cascade/app_settings.py index d49bc082b..d060cf742 100644 --- a/cmsplugin_cascade/app_settings.py +++ b/cmsplugin_cascade/app_settings.py @@ -128,9 +128,11 @@ def CMSPLUGIN_CASCADE(self): config.setdefault('fallback',{ # default 'path_main_scss': 'bs4demo/css/main.scss', + 'path_main_scss': 'myshop/css/default.scss', 'image':{'color':'hsla(221.7, 57.5%, 84.3%, 0.8)', 'svg':''}, 'picture':{'color':'hsla(0, 40%, 80.4%, 0.8)', 'svg':'' }, 'jumbotron':{'color':'hsl(62, 90%, 90%, 0.8)', 'svg':''}, + 'img_or_pic_lost_pk': True, }) From c04cd3312a21d175fce3f1599bbfa1be7fddf39b Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Wed, 22 Apr 2020 15:05:37 +0200 Subject: [PATCH 68/69] Delete clipboard.html --- .../cascade/admin/widgets/clipboard.html | 37 ------------------- 1 file changed, 37 deletions(-) delete mode 100644 cmsplugin_cascade/templates/cascade/admin/widgets/clipboard.html diff --git a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard.html b/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard.html deleted file mode 100644 index ad77e2fe0..000000000 --- a/cmsplugin_cascade/templates/cascade/admin/widgets/clipboard.html +++ /dev/null @@ -1,37 +0,0 @@ -{% load static cascade_tags sekizai_tags sass_tags %} -{% spaceless %} - -{% block extrastyle %} - - -{% endblock %} - -{% include "cascade/admin/widgets/clipboard_menu_group.html" %} - -{% with id=widget.attrs.id %} - - - -{% block after_field_sets %} - - -{% endblock %} - -{% block footer%} -{% endblock %} -{% endwith %} -{% endspaceless %} From c5ca6f15ebf5b855de5d0d810b0b225b9832ddd8 Mon Sep 17 00:00:00 2001 From: Nicolas PASCAL Date: Thu, 23 Apr 2020 16:59:47 +0200 Subject: [PATCH 69/69] remove uneeded attribute input --- cmsplugin_cascade/templates/cascade/admin/clipboard_import.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmsplugin_cascade/templates/cascade/admin/clipboard_import.html b/cmsplugin_cascade/templates/cascade/admin/clipboard_import.html index de1f38d72..04e8c7d9b 100644 --- a/cmsplugin_cascade/templates/cascade/admin/clipboard_import.html +++ b/cmsplugin_cascade/templates/cascade/admin/clipboard_import.html @@ -69,7 +69,7 @@ {% endwith %}
- +