Projet

Général

Profil

0001-general-move-asset-management-to-its-own-app-24450.patch

Frédéric Péters, 12 juin 2018 08:39

Télécharger (20,1 ko)

Voir les différences:

Subject: [PATCH] general: move asset management to its own app (#24450)

 combo/apps/assets/__init__.py                 |  34 +++++
 combo/apps/assets/forms.py                    |  22 +++
 .../combo/manager_asset_confirm_delete.html   |   0
 .../combo/manager_asset_overwrite.html        |   0
 .../templates/combo/manager_asset_upload.html |   0
 .../templates/combo/manager_assets.html       |   0
 combo/apps/assets/urls.py                     |  33 ++++
 combo/apps/assets/views.py                    | 144 ++++++++++++++++++
 combo/manager/forms.py                        |   4 -
 .../manager/templates/combo/manager_home.html |   1 -
 combo/manager/urls.py                         |   4 -
 combo/manager/views.py                        | 123 +--------------
 combo/settings.py                             |   1 +
 13 files changed, 236 insertions(+), 130 deletions(-)
 create mode 100644 combo/apps/assets/__init__.py
 create mode 100644 combo/apps/assets/forms.py
 rename combo/{manager => apps/assets}/templates/combo/manager_asset_confirm_delete.html (100%)
 rename combo/{manager => apps/assets}/templates/combo/manager_asset_overwrite.html (100%)
 rename combo/{manager => apps/assets}/templates/combo/manager_asset_upload.html (100%)
 rename combo/{manager => apps/assets}/templates/combo/manager_assets.html (100%)
 create mode 100644 combo/apps/assets/urls.py
 create mode 100644 combo/apps/assets/views.py
combo/apps/assets/__init__.py
1
# combo - content management system
2
# Copyright (C) 2017-2018  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
import django.apps
18
from django.core.urlresolvers import reverse
19
from django.utils.translation import ugettext_lazy as _
20

  
21

  
22
class AppConfig(django.apps.AppConfig):
23
    name = 'combo.apps.assets'
24
    verbose_name = _('Assets')
25

  
26
    def get_before_urls(self):
27
        from . import urls
28
        return urls.urlpatterns
29

  
30
    def get_extra_manager_actions(self):
31
        return [{'href': reverse('combo-manager-assets'),
32
                'text': _('Assets')}]
33

  
34
default_app_config = 'combo.apps.assets.AppConfig'
combo/apps/assets/forms.py
1
# combo - content management system
2
# Copyright (C) 2017-2018  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
from django import forms
18
from django.utils.translation import ugettext_lazy as _
19

  
20

  
21
class AssetUploadForm(forms.Form):
22
    upload = forms.FileField(label=_('File'))
combo/apps/assets/urls.py
1
# combo - content management system
2
# Copyright (C) 2017-2018  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
from django.conf.urls import url, include
18

  
19
from combo.urls_utils import decorated_includes, manager_required
20

  
21
from . import views
22

  
23
assets_manager_urls = [
24
    url(r'^$', views.assets, name='combo-manager-assets'),
25
    url(r'^delete$', views.asset_delete, name='combo-manager-asset-delete'),
26
    url(r'^overwrite/$', views.asset_overwrite, name='combo-manager-asset-overwrite'),
27
    url(r'^upload/$', views.asset_upload, name='combo-manager-asset-upload'),
28
]
29

  
30
urlpatterns = [
31
    url(r'^manage/assets/', decorated_includes(manager_required,
32
        include(assets_manager_urls))),
33
]
combo/apps/assets/views.py
1
# combo - content management system
2
# Copyright (C) 2017-2018  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
import os
18

  
19
from django.conf import settings
20
from django.core.exceptions import PermissionDenied
21
from django.core.files.storage import default_storage
22
from django.core.urlresolvers import reverse, reverse_lazy
23
from django.shortcuts import redirect
24
from django.views.generic import TemplateView, ListView, FormView
25

  
26
import ckeditor
27

  
28
from .forms import AssetUploadForm
29

  
30

  
31
class Asset(object):
32
    def __init__(self, filepath):
33
        self.filepath = filepath
34
        self.filename = os.path.basename(filepath)
35
        self.src = ckeditor.utils.get_media_url(filepath)
36

  
37
    def css_classes(self):
38
        extension = os.path.splitext(self.filepath)[-1].strip('.')
39
        if extension:
40
            return 'asset-ext-%s' % extension
41
        return ''
42

  
43
    def size(self):
44
        return os.stat(default_storage.path(self.filepath)).st_size
45

  
46
    def thumb(self):
47
        if getattr(settings, 'CKEDITOR_IMAGE_BACKEND', None):
48
            thumb = ckeditor.utils.get_media_url(
49
                    ckeditor.utils.get_thumb_filename(self.filepath))
50
        else:
51
            thumb = self.src
52
        return thumb
53

  
54
    def is_image(self):
55
        return ckeditor.views.is_image(self.src)
56

  
57

  
58
class Assets(ListView):
59
    template_name = 'combo/manager_assets.html'
60
    paginate_by = 10
61

  
62
    def get_queryset(self):
63
        files = [Asset(x) for x in ckeditor.views.get_image_files(self.request.user)]
64
        q = self.request.GET.get('q')
65
        if q:
66
            files = [x for x in files if q.lower() in x.filename.lower()]
67
        files.sort(key=lambda x: getattr(x, 'filename'))
68
        return files
69

  
70
    def get_context_data(self, **kwargs):
71
        context = super(Assets, self).get_context_data(**kwargs)
72
        context['query'] = self.request.GET.get('q') or ''
73
        return context
74

  
75
assets = Assets.as_view()
76

  
77

  
78
class AssetUpload(FormView):
79
    form_class = AssetUploadForm
80
    template_name = 'combo/manager_asset_upload.html'
81
    success_url = reverse_lazy('combo-manager-assets')
82

  
83
    def form_valid(self, form):
84
        # use native ckeditor view so it's available from ckeditor file/image
85
        # dialogs.
86
        ckeditor_upload_view = ckeditor.views.ImageUploadView()
87
        self.request.GET = {'CKEditorFuncNum': '-'} # hack
88
        ckeditor_upload_view.post(self.request)
89
        return super(AssetUpload, self).form_valid(form)
90

  
91
asset_upload = AssetUpload.as_view()
92

  
93

  
94
class AssetOverwrite(FormView):
95
    form_class = AssetUploadForm
96
    template_name = 'combo/manager_asset_overwrite.html'
97
    success_url = reverse_lazy('combo-manager-assets')
98

  
99
    def form_valid(self, form):
100
        img_orig = self.request.GET['img']
101
        if '..' in img_orig:
102
            raise PermissionDenied() # better safe than sorry
103
        base_path = settings.CKEDITOR_UPLOAD_PATH
104
        if getattr(settings, 'CKEDITOR_RESTRICT_BY_USER', False):
105
            base_path = os.path.join(base_path, self.request.user.username)
106
        if not img_orig.startswith(base_path):
107
            raise PermissionDenied()
108

  
109
        upload = self.request.FILES['upload']
110
        default_storage.delete(img_orig)
111
        if getattr(settings, 'CKEDITOR_IMAGE_BACKEND', None):
112
            thumb = ckeditor.utils.get_thumb_filename(img_orig)
113
            default_storage.delete(thumb)
114
        saved_path = default_storage.save(img_orig, upload)
115
        backend = ckeditor.image_processing.get_backend()
116
        upload.seek(0) # rewind file to be sure
117
        try:
118
            backend.image_verify(upload)
119
        except ckeditor.utils.NotAnImageException:
120
            pass
121
        else:
122
            if backend.should_create_thumbnail(saved_path):
123
                backend.create_thumbnail(saved_path)
124
        return super(AssetOverwrite, self).form_valid(form)
125

  
126
asset_overwrite = AssetOverwrite.as_view()
127

  
128

  
129
class AssetDelete(TemplateView):
130
    template_name = 'combo/manager_asset_confirm_delete.html'
131

  
132
    def post(self, request):
133
        img_orig = request.GET['img']
134
        if '..' in img_orig:
135
            raise PermissionDenied() # better safe than sorry
136
        base_path = settings.CKEDITOR_UPLOAD_PATH
137
        if getattr(settings, 'CKEDITOR_RESTRICT_BY_USER', False):
138
            base_path = os.path.join(base_path, request.user.username)
139
        if not img_orig.startswith(base_path):
140
            raise PermissionDenied()
141
        default_storage.delete(img_orig)
142
        return redirect(reverse('combo-manager-assets'))
143

  
144
asset_delete = AssetDelete.as_view()
combo/manager/forms.py
101 101

  
102 102
class SiteImportForm(forms.Form):
103 103
    site_json = forms.FileField(label=_('Site Export File'))
104

  
105

  
106
class AssetUploadForm(forms.Form):
107
    upload = forms.FileField(label=_('File'))
combo/manager/templates/combo/manager_home.html
6 6
<a class="extra-actions-menu-opener"></a>
7 7
<a rel="popup" href="{% url 'combo-manager-page-add' %}">{% trans 'New' %}</a>
8 8
<ul class="extra-actions-menu">
9
 <li><a href="{% url 'combo-manager-assets' %}">{% trans 'Assets' %}</a></li>
10 9
 <li><a href="{% url 'combo-manager-site-export' %}">{% trans 'Export Site' %}</a></li>
11 10
 <li><a href="{% url 'combo-manager-site-import' %}">{% trans 'Import Site' %}</a></li>
12 11
 {% for extra_action in extra_actions %}
combo/manager/urls.py
76 76
            name='combo-manager-cell-order'),
77 77
        url(r'^pages/order$', views.page_order,
78 78
            name='combo-manager-page-order'),
79
        url(r'^assets/$', views.assets, name='combo-manager-assets'),
80
        url(r'^assets/delete$', views.asset_delete, name='combo-manager-asset-delete'),
81
        url(r'^assets/overwrite/$', views.asset_overwrite, name='combo-manager-asset-overwrite'),
82
        url(r'^assets/upload/$', views.asset_upload, name='combo-manager-asset-upload'),
83 79
        url(r'^ckeditor/upload/', staff_member_required(ckeditor_views.upload),
84 80
            name='ckeditor_upload'),
85 81
        url(r'^ckeditor/browse/', never_cache(staff_member_required(ckeditor_views.browse)),
combo/manager/views.py
17 17
import json
18 18
import os
19 19

  
20
import ckeditor
21

  
22 20
from django.conf import settings
23 21
from django.contrib import messages
24
from django.core.files.storage import default_storage
25 22
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
26 23
from django.core.urlresolvers import reverse, reverse_lazy
27 24
from django.http import HttpResponse, HttpResponseRedirect, Http404
......
31 28
from django.utils.formats import date_format
32 29
from django.utils.timezone import localtime
33 30
from django.views.decorators.csrf import requires_csrf_token
34
from django.views.generic import (TemplateView, RedirectView, DetailView,
31
from django.views.generic import (RedirectView, DetailView,
35 32
        CreateView, UpdateView, ListView, DeleteView, FormView)
36 33

  
37 34
from combo.data.models import Page, CellBase, ParentContentCell, PageSnapshot
......
42 39
from .forms import (PageEditTitleForm, PageVisibilityForm, SiteImportForm,
43 40
        PageEditRedirectionForm, PageSelectTemplateForm, PageEditSlugForm,
44 41
        PageEditPictureForm, PageEditExcludeFromNavigationForm,
45
        AssetUploadForm, PageEditDescriptionForm)
42
        PageEditDescriptionForm)
46 43

  
47 44

  
48 45
class HomepageView(ListView):
......
473 470
    return response
474 471

  
475 472

  
476
class Asset(object):
477
    def __init__(self, filepath):
478
        self.filepath = filepath
479
        self.filename = os.path.basename(filepath)
480
        self.src = ckeditor.utils.get_media_url(filepath)
481

  
482
    def css_classes(self):
483
        extension = os.path.splitext(self.filepath)[-1].strip('.')
484
        if extension:
485
            return 'asset-ext-%s' % extension
486
        return ''
487

  
488
    def size(self):
489
        return os.stat(default_storage.path(self.filepath)).st_size
490

  
491
    def thumb(self):
492
        if getattr(settings, 'CKEDITOR_IMAGE_BACKEND', None):
493
            thumb = ckeditor.utils.get_media_url(
494
                    ckeditor.utils.get_thumb_filename(self.filepath))
495
        else:
496
            thumb = self.src
497
        return thumb
498

  
499
    def is_image(self):
500
        return ckeditor.views.is_image(self.src)
501

  
502

  
503
class Assets(ListView):
504
    template_name = 'combo/manager_assets.html'
505
    paginate_by = 10
506

  
507
    def get_queryset(self):
508
        files = [Asset(x) for x in ckeditor.views.get_image_files(self.request.user)]
509
        q = self.request.GET.get('q')
510
        if q:
511
            files = [x for x in files if q.lower() in x.filename.lower()]
512
        files.sort(key=lambda x: getattr(x, 'filename'))
513
        return files
514

  
515
    def get_context_data(self, **kwargs):
516
        context = super(Assets, self).get_context_data(**kwargs)
517
        context['query'] = self.request.GET.get('q') or ''
518
        return context
519

  
520
assets = Assets.as_view()
521

  
522

  
523
class AssetUpload(FormView):
524
    form_class = AssetUploadForm
525
    template_name = 'combo/manager_asset_upload.html'
526
    success_url = reverse_lazy('combo-manager-assets')
527

  
528
    def form_valid(self, form):
529
        # use native ckeditor view so it's available from ckeditor file/image
530
        # dialogs.
531
        ckeditor_upload_view = ckeditor.views.ImageUploadView()
532
        self.request.GET = {'CKEditorFuncNum': '-'} # hack
533
        ckeditor_upload_view.post(self.request)
534
        return super(AssetUpload, self).form_valid(form)
535

  
536
asset_upload = AssetUpload.as_view()
537

  
538

  
539
class AssetOverwrite(FormView):
540
    form_class = AssetUploadForm
541
    template_name = 'combo/manager_asset_overwrite.html'
542
    success_url = reverse_lazy('combo-manager-assets')
543

  
544
    def form_valid(self, form):
545
        img_orig = self.request.GET['img']
546
        if '..' in img_orig:
547
            raise PermissionDenied() # better safe than sorry
548
        base_path = settings.CKEDITOR_UPLOAD_PATH
549
        if getattr(settings, 'CKEDITOR_RESTRICT_BY_USER', False):
550
            base_path = os.path.join(base_path, self.request.user.username)
551
        if not img_orig.startswith(base_path):
552
            raise PermissionDenied()
553

  
554
        upload = self.request.FILES['upload']
555
        default_storage.delete(img_orig)
556
        if getattr(settings, 'CKEDITOR_IMAGE_BACKEND', None):
557
            thumb = ckeditor.utils.get_thumb_filename(img_orig)
558
            default_storage.delete(thumb)
559
        saved_path = default_storage.save(img_orig, upload)
560
        backend = ckeditor.image_processing.get_backend()
561
        upload.seek(0) # rewind file to be sure
562
        try:
563
            backend.image_verify(upload)
564
        except ckeditor.utils.NotAnImageException:
565
            pass
566
        else:
567
            if backend.should_create_thumbnail(saved_path):
568
                backend.create_thumbnail(saved_path)
569
        return super(AssetOverwrite, self).form_valid(form)
570

  
571
asset_overwrite = AssetOverwrite.as_view()
572

  
573

  
574
class AssetDelete(TemplateView):
575
    template_name = 'combo/manager_asset_confirm_delete.html'
576

  
577
    def post(self, request):
578
        img_orig = request.GET['img']
579
        if '..' in img_orig:
580
            raise PermissionDenied() # better safe than sorry
581
        base_path = settings.CKEDITOR_UPLOAD_PATH
582
        if getattr(settings, 'CKEDITOR_RESTRICT_BY_USER', False):
583
            base_path = os.path.join(base_path, request.user.username)
584
        if not img_orig.startswith(base_path):
585
            raise PermissionDenied()
586
        default_storage.delete(img_orig)
587
        return redirect(reverse('combo-manager-assets'))
588

  
589
asset_delete = AssetDelete.as_view()
590

  
591

  
592 473
def menu_json(request):
593 474
    if settings.TEMPLATE_VARS.get('site_title'):
594 475
        label = _('Editing "%(site_title)s"') % settings.TEMPLATE_VARS
combo/settings.py
62 62
    'combo.profile',
63 63
    'combo.manager',
64 64
    'combo.public',
65
    'combo.apps.assets',
65 66
    'combo.apps.dashboard',
66 67
    'combo.apps.wcs',
67 68
    'combo.apps.publik',
68
-