From 2a98578a9a83fe13126ef4aa2fc06ab43d1a841f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Wed, 13 Jun 2018 11:43:41 +0200 Subject: [PATCH] general: add support for slot assets (#24453) --- combo/apps/assets/migrations/0001_initial.py | 24 +++++++ combo/apps/assets/migrations/__init__.py | 0 combo/apps/assets/models.py | 21 ++++++ .../templates/combo/manager_assets.html | 15 +++- combo/apps/assets/templatetags/__init__.py | 0 combo/apps/assets/templatetags/assets.py | 47 +++++++++++++ combo/apps/assets/urls.py | 2 + combo/apps/assets/views.py | 70 +++++++++++++++++-- combo/manager/static/css/combo.manager.css | 1 + combo/settings.py | 3 + tests/test_manager.py | 37 ++++++++++ tests/test_public_templatetags.py | 28 ++++++++ 12 files changed, 240 insertions(+), 8 deletions(-) create mode 100644 combo/apps/assets/migrations/0001_initial.py create mode 100644 combo/apps/assets/migrations/__init__.py create mode 100644 combo/apps/assets/models.py create mode 100644 combo/apps/assets/templatetags/__init__.py create mode 100644 combo/apps/assets/templatetags/assets.py diff --git a/combo/apps/assets/migrations/0001_initial.py b/combo/apps/assets/migrations/0001_initial.py new file mode 100644 index 0000000..6c37a3d --- /dev/null +++ b/combo/apps/assets/migrations/0001_initial.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.12 on 2018-06-12 11:42 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Asset', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('key', models.CharField(max_length=128, unique=True)), + ('asset', models.FileField(upload_to=b'assets')), + ], + ), + ] diff --git a/combo/apps/assets/migrations/__init__.py b/combo/apps/assets/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/combo/apps/assets/models.py b/combo/apps/assets/models.py new file mode 100644 index 0000000..7915b13 --- /dev/null +++ b/combo/apps/assets/models.py @@ -0,0 +1,21 @@ +# combo - content management system +# Copyright (C) 2017-2018 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from django.db import models + +class Asset(models.Model): + key = models.CharField(max_length=128, unique=True) + asset = models.FileField(upload_to='assets') diff --git a/combo/apps/assets/templates/combo/manager_assets.html b/combo/apps/assets/templates/combo/manager_assets.html index 5c81528..804a8b9 100644 --- a/combo/apps/assets/templates/combo/manager_assets.html +++ b/combo/apps/assets/templates/combo/manager_assets.html @@ -34,7 +34,7 @@ - + @@ -43,14 +43,23 @@ {% for asset in object_list %} - - + + diff --git a/combo/apps/assets/templatetags/__init__.py b/combo/apps/assets/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/combo/apps/assets/templatetags/assets.py b/combo/apps/assets/templatetags/assets.py new file mode 100644 index 0000000..8065d71 --- /dev/null +++ b/combo/apps/assets/templatetags/assets.py @@ -0,0 +1,47 @@ +# combo - content management system +# Copyright (C) 2017-2018 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from django import template +from django.db.models.fields.files import ImageFieldFile + +from ..models import Asset + +register = template.Library() + + +@register.simple_tag +def asset_url(*args): + for asset in args: + if isinstance(asset, ImageFieldFile): + try: + return asset.url + except ValueError: # no associated file + continue + if isinstance(asset, basestring): + try: + asset = Asset.objects.get(key=asset) + except Asset.DoesNotExist: + continue + return asset.asset.url + return '' + + +@register.assignment_tag +def get_asset(key): + try: + return Asset.objects.get(key=key) + except Asset.DoesNotExist: + return None diff --git a/combo/apps/assets/urls.py b/combo/apps/assets/urls.py index 8936c87..4d5c9d8 100644 --- a/combo/apps/assets/urls.py +++ b/combo/apps/assets/urls.py @@ -25,6 +25,8 @@ assets_manager_urls = [ url(r'^delete$', views.asset_delete, name='combo-manager-asset-delete'), url(r'^overwrite/$', views.asset_overwrite, name='combo-manager-asset-overwrite'), url(r'^upload/$', views.asset_upload, name='combo-manager-asset-upload'), + url(r'^upload/(?P[\w_:-]+)/$', views.slot_asset_upload, name='combo-manager-slot-asset-upload'), + url(r'^delete/(?P[\w_:-]+)/$', views.slot_asset_delete, name='combo-manager-slot-asset-delete'), ] urlpatterns = [ diff --git a/combo/apps/assets/views.py b/combo/apps/assets/views.py index 5748f42..5b5ea33 100644 --- a/combo/apps/assets/views.py +++ b/combo/apps/assets/views.py @@ -24,16 +24,22 @@ from django.shortcuts import redirect from django.views.generic import TemplateView, ListView, FormView import ckeditor +from sorl.thumbnail.shortcuts import get_thumbnail from .forms import AssetUploadForm +from .models import Asset -class Asset(object): +class CkEditorAsset(object): def __init__(self, filepath): self.filepath = filepath - self.filename = os.path.basename(filepath) + self.name = os.path.basename(filepath) self.src = ckeditor.utils.get_media_url(filepath) + @classmethod + def get_assets(cls, request): + return [cls(x) for x in ckeditor.views.get_image_files(request.user)] + def css_classes(self): extension = os.path.splitext(self.filepath)[-1].strip('.') if extension: @@ -55,16 +61,43 @@ class Asset(object): return ckeditor.views.is_image(self.src) +class SlotAsset(object): + def __init__(self, key=None, asset=None): + self.key = key + self.name = settings.COMBO_ASSET_SLOTS[key]['label'] + self.asset = asset + + def is_image(self): + return bool(self.asset) + + def size(self): + if self.asset: + return os.stat(self.asset.asset.path).st_size + return None + + def src(self): + return self.asset.asset.url if self.asset else '' + + def thumb(self): + return get_thumbnail(self.asset.asset, '75x75').url + + @classmethod + def get_assets(cls): + assets = dict([(x.key, x) for x in Asset.objects.all() if x.key in settings.COMBO_ASSET_SLOTS]) + for key, value in settings.COMBO_ASSET_SLOTS.items(): + yield cls(key, asset=assets.get(key)) + + class Assets(ListView): template_name = 'combo/manager_assets.html' paginate_by = 10 def get_queryset(self): - files = [Asset(x) for x in ckeditor.views.get_image_files(self.request.user)] + files = list(SlotAsset.get_assets()) + CkEditorAsset.get_assets(self.request) q = self.request.GET.get('q') if q: - files = [x for x in files if q.lower() in x.filename.lower()] - files.sort(key=lambda x: getattr(x, 'filename')) + files = [x for x in files if q.lower() in x.name.lower()] + files.sort(key=lambda x: getattr(x, 'name')) return files def get_context_data(self, **kwargs): @@ -142,3 +175,30 @@ class AssetDelete(TemplateView): return redirect(reverse('combo-manager-assets')) asset_delete = AssetDelete.as_view() + + +class SlotAssetUpload(FormView): + form_class = AssetUploadForm + template_name = 'combo/manager_asset_upload.html' + success_url = reverse_lazy('combo-manager-assets') + + def form_valid(self, form): + try: + asset = Asset.objects.get(key=self.kwargs['key']) + except Asset.DoesNotExist: + asset = Asset(key=self.kwargs['key']) + asset.asset = self.request.FILES['upload'] + asset.save() + return super(SlotAssetUpload, self).form_valid(form) + +slot_asset_upload = SlotAssetUpload.as_view() + + +class SlotAssetDelete(TemplateView): + template_name = 'combo/manager_asset_confirm_delete.html' + + def post(self, request, *args, **kwargs): + Asset.objects.filter(key=kwargs['key']).delete() + return redirect(reverse('combo-manager-assets')) + +slot_asset_delete = SlotAssetDelete.as_view() diff --git a/combo/manager/static/css/combo.manager.css b/combo/manager/static/css/combo.manager.css index b497f4d..9de3b7f 100644 --- a/combo/manager/static/css/combo.manager.css +++ b/combo/manager/static/css/combo.manager.css @@ -199,6 +199,7 @@ p#redirection { #assets-browser #assets-listing table td.image { padding: 0; + text-align: center; } #assets-browser #assets-listing table td.actions { diff --git a/combo/settings.py b/combo/settings.py index ed07675..8ddba6b 100644 --- a/combo/settings.py +++ b/combo/settings.py @@ -303,6 +303,9 @@ REQUESTS_TIMEOUT = 28 # default duration of notifications (in days) COMBO_DEFAULT_NOTIFICATION_DURATION = 3 +# predefined slots for assets +COMBO_ASSET_SLOTS = {} + # hide work-in-progress/experimental/whatever cells for now BOOKING_CALENDAR_CELL_ENABLED = False NEWSLETTERS_CELL_ENABLED = False diff --git a/tests/test_manager.py b/tests/test_manager.py index a4a6a49..f2686de 100644 --- a/tests/test_manager.py +++ b/tests/test_manager.py @@ -20,6 +20,7 @@ from webtest import Upload from combo.wsgi import application from combo.data.models import Page, CellBase, TextCell, LinkCell, ConfigJsonCell, JsonCell, PageSnapshot +from combo.apps.assets.models import Asset from combo.apps.family.models import FamilyInfosCell from combo.apps.search.models import SearchCell @@ -652,6 +653,42 @@ def test_asset_management_search(app, admin_user): resp = resp.form.submit() assert resp.body.count('
{% trans "Filename" %}{% trans "Name" %} {% trans "Size" %}
{{ asset.filename }}{{ asset.size|filesizeformat }}{{ asset.name }}{% if asset.size %}{{ asset.size|filesizeformat }}{% else %}-{% endif %} {% if asset.is_image %}{% endif %} + {% if asset.key %}{# theme asset #} + {% trans 'Overwrite' %} + {% if asset.asset %} + {% trans 'Delete' %} + {% endif %} + {% else %} {% trans 'Overwrite' %} {% trans 'Delete' %} + {% endif %}