diff options
-rw-r--r-- | archaeological_operations/models.py | 7 | ||||
-rw-r--r-- | ishtar_common/models.py | 12 | ||||
-rw-r--r-- | ishtar_common/models_common.py | 16 | ||||
-rw-r--r-- | ishtar_common/models_imports.py | 47 | ||||
-rw-r--r-- | ishtar_common/templates/ishtar/blocks/window_nav.html | 4 | ||||
-rw-r--r-- | ishtar_common/urls.py | 5 | ||||
-rw-r--r-- | ishtar_common/views.py | 54 |
7 files changed, 129 insertions, 16 deletions
diff --git a/archaeological_operations/models.py b/archaeological_operations/models.py index 72838e815..cadb806dc 100644 --- a/archaeological_operations/models.py +++ b/archaeological_operations/models.py @@ -3302,10 +3302,13 @@ class AdministrativeAct(DocumentItem, BaseHistorizedItem, OwnPerms, ValueGetter, def get_extra_templates(self, request): urlname = "generatedoc-administrativeactop" - return [ - (template.name, reverse(urlname, args=[self.pk, template.pk])) + templates = [ + (template.name, reverse(urlname, args=[self.pk, template.pk]), + template.get_icon()) for template in self.act_type.associated_template.all() ] + templates += self.get_media_exporters(request) + return templates def get_filename(self): filename = self.related_item.associated_filename diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 579c3c644..11ac75876 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -2132,6 +2132,13 @@ if settings.USE_LIBREOFFICE: ("xlsx", _("XLSX")), ] +EXPORT_FORMATS_ICONS = { + "docx": "fa fa-file-word-o", + "html": "fa fa-code", + "pdf": "fa fa-file-pdf-o", + "xlsx": "fa fa-file-excel-o", +} + EXPORT_FORMATS_DICT = dict(EXPORT_FORMATS) @@ -2187,6 +2194,11 @@ class DocumentTemplate(models.Model): def __str__(self): return self.name + def get_icon(self): + if not self.export_format or self.export_format not in EXPORT_FORMATS_ICONS: + return "fa fa-file-text-o" + return EXPORT_FORMATS_ICONS[self.export_format] + def natural_key(self): return (self.slug,) diff --git a/ishtar_common/models_common.py b/ishtar_common/models_common.py index 0c22b8e7f..e749580f4 100644 --- a/ishtar_common/models_common.py +++ b/ishtar_common/models_common.py @@ -67,7 +67,7 @@ from simple_history.signals import ( from ishtar_common.data_importer import post_importer_action, ImporterError from ishtar_common.model_managers import TypeManager from ishtar_common.model_merging import merge_model_objects -from ishtar_common.models_imports import Import +from ishtar_common.models_imports import Import, MediaExporter from ishtar_common.templatetags.link_to_window import simple_link_to_window from ishtar_common.utils import ( cached_label_changed, @@ -854,7 +854,19 @@ class TemplateItem: for template in q.all(): urlname = "generate-document" templates.append( - (template.name, reverse(urlname, args=[template.slug, self.pk])) + (template.name, reverse(urlname, args=[template.slug, self.pk]), + template.get_icon()) + ) + templates += self.get_media_exporters(request) + return templates + + def get_media_exporters(self, request): + templates = [] + for media_exporter in MediaExporter.get_available(self.__class__, request): + templates.append( + (media_exporter.name, + reverse("export-media", args=[media_exporter.slug, self.pk]), + "fa fa-file-archive-o") ) return templates diff --git a/ishtar_common/models_imports.py b/ishtar_common/models_imports.py index 3d4bf632b..175d2f25b 100644 --- a/ishtar_common/models_imports.py +++ b/ishtar_common/models_imports.py @@ -3042,6 +3042,8 @@ class MediaExporter(models.Model): ) files_to_export = models.CharField(_("Files to export"), max_length=1, choices=MEDIA_EXPORT_TYPES, default="I") + thumbnail_for_images = models.BooleanField(_("Use thumbnails for images"), + default=False) cascade = models.BooleanField( _("Cascade export"), default=False, @@ -3112,3 +3114,48 @@ class MediaExporter(models.Model): user_profiles__person__ishtaruser__pk=user_id).exists(): return True return False + + def export(self, obj, tmpdir=None): + if not tmpdir: + tmpdir = tempfile.mkdtemp() + if not hasattr(obj, "documents"): + # database inconstency - should not occur + return + q = None + media_attrs = [] + if self.files_to_export in ("A", "I"): + q = Q(image__isnull=False) + if self.thumbnail_for_images: + media_attrs = ["thumbnail"] + else: + media_attrs = ["image"] + if self.files_to_export in ("A", "F"): + q2 = Q(associated_file__isnull=False) + if q: + q |= q2 + else: + q = q2 + media_attrs.append("associated_file") + archive_path = os.path.join(tmpdir, "archive") + os.mkdir(archive_path) + for idx, document in enumerate(obj.documents.filter(q).all()): + for media_attr in media_attrs: + media = getattr(document, media_attr) + if not media or not media.path or not os.path.exists(media.path): + continue + base_name = media.path.split(os.path.sep)[-1] + ext = base_name.split(".")[-1] + if self.naming: + # TODO: naming + name = base_name + else: + if media_attr == "associated_file": + name = "file" + else: + name = "image" + name += f"_{idx + 1:05d}.{ext}" + shutil.copy(media.path, os.path.join(archive_path, name)) + now = datetime.datetime.now() + archive_name = os.path.join(tmpdir, f"media-{now.strftime('%Y-%m-%d-%H%M%S')}") + shutil.make_archive(archive_name, "zip", archive_path) + return archive_name + ".zip" diff --git a/ishtar_common/templates/ishtar/blocks/window_nav.html b/ishtar_common/templates/ishtar/blocks/window_nav.html index d318fe264..5517cc9bb 100644 --- a/ishtar_common/templates/ishtar/blocks/window_nav.html +++ b/ishtar_common/templates/ishtar/blocks/window_nav.html @@ -101,9 +101,9 @@ <a class="dropdown-item" href='{% url show_url item.pk "pdf" %}' title='{% trans "Export as PDF file"%}'> <i class="fa fa-file-pdf-o" aria-hidden="true"></i> PDF - </a>{% endif %}{% for template_name, template_url in extra_templates %} + </a>{% endif %}{% for template_name, template_url, icon in extra_templates %} <a class="dropdown-item" href='{{template_url}}'> - <i class="fa fa-file-word-o" aria-hidden="true"></i> {{template_name}} + <i class="{{icon}}" aria-hidden="true"></i> {{template_name}} </a>{% endfor %} {% if item.HAS_QR_CODE %}<a class="dropdown-item" href='{% url "qrcode-item" item.APP item.MODEL item.pk %}' target="_blank"> <i class="fa fa-qrcode" aria-hidden="true"></i> {% trans "QR Code" %} diff --git a/ishtar_common/urls.py b/ishtar_common/urls.py index d2c954e58..482e647f7 100644 --- a/ishtar_common/urls.py +++ b/ishtar_common/urls.py @@ -67,6 +67,11 @@ urlpatterns = [ views.GenerateView.as_view(), name="generate-document", ), + path( + "export-media/<slug:exporter>/<int:item_pk>/", + views.ExportMediaView.as_view(), + name="export-media" + ), url( r"person_search/(?P<step>.+)?$", check_permissions( diff --git a/ishtar_common/views.py b/ishtar_common/views.py index 4510177f9..95cc37990 100644 --- a/ishtar_common/views.py +++ b/ishtar_common/views.py @@ -25,6 +25,7 @@ import json import logging import os import re +import tempfile import unicodedata import urllib.parse @@ -40,7 +41,7 @@ from django.contrib.auth import logout from django.contrib.auth.decorators import login_required from django.contrib.auth import views as auth_view from django.contrib.contenttypes.models import ContentType -from django.core.exceptions import ObjectDoesNotExist +from django.core.exceptions import ObjectDoesNotExist, PermissionDenied from django.core.cache import cache from django.db.models import Q from django.template import loader @@ -1641,15 +1642,48 @@ class GenerateLabelView(GenerateView): raise Http404() return objects -""" -# TODO v4: suppression -class GlobalVarEdit(IshtarMixin, AdminLoginRequiredMixin, ModelFormSetView): - template_name = "ishtar/formset.html" - model = models.GlobalVar - factory_kwargs = {"extra": 1, "can_delete": True} - page_name = _("Global variables") - fields = ["slug", "value", "description"] -""" + +class ExportMediaView(IshtarMixin, LoginRequiredMixin, View): + def get_exporter(self, slug, request): + try: + exporter = models.MediaExporter.objects.get( + slug=slug, available=True + ) + except models.DocumentTemplate.DoesNotExist: + raise Http404() + if not exporter.is_available(request): + raise PermissionDenied() + return exporter + + def get_item(self, request, model): + item_pk = self.kwargs.get("item_pk") + try: + obj = model.objects.get(pk=item_pk) + if not obj.can_view(request): + raise PermissionDenied() + except model.DoesNotExist: + raise Http404() + return obj + + def get(self, request, *args, **kwargs): + slug = kwargs.get("exporter") + exporter = self.get_exporter(slug, request) + app, __, model_name = exporter.associated_model.klass.split(".") + model = apps.get_model(app, model_name) + obj = self.get_item(request, model) + if not obj: + return HttpResponse(content_type="text/plain") + with tempfile.TemporaryDirectory() as tmpdir: + export = exporter.export(obj, tmpdir=tmpdir) + if not export: + return HttpResponse(content_type="text/plain") + with open(export, "rb") as f: + content_type = "application/zip" + response = HttpResponse(f.read(), content_type=content_type) + response["Content-Disposition"] = "attachment; filename={}".format( + export.split(os.sep)[-1] + ) + return response class BaseImportView(IshtarMixin, LoginRequiredMixin): |