summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ishtar_common/migrations/0263_media_exporter.py36
-rw-r--r--ishtar_common/models_common.py4
-rw-r--r--ishtar_common/models_imports.py107
3 files changed, 145 insertions, 2 deletions
diff --git a/ishtar_common/migrations/0263_media_exporter.py b/ishtar_common/migrations/0263_media_exporter.py
new file mode 100644
index 000000000..2776b1271
--- /dev/null
+++ b/ishtar_common/migrations/0263_media_exporter.py
@@ -0,0 +1,36 @@
+# Generated by Django 2.2.24 on 2025-06-16 15:18
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('ishtar_common', '0262_migrate_custom_form_slug'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='MediaExporter',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=200, verbose_name='Name')),
+ ('slug', models.SlugField(max_length=100, unique=True, verbose_name='Slug')),
+ ('available', models.BooleanField(default=True, verbose_name='Available')),
+ ('files_to_export', models.CharField(choices=[('I', 'Images'), ('F', 'Associated files'), ('A', 'All')], default='I', max_length=1, verbose_name='Files to export')),
+ ('thumbnail_for_images', models.BooleanField(default=False, verbose_name='Use thumbnails for images')),
+ ('cascade', models.BooleanField(default=False, help_text='Export media of all attached items. For instance, for a context record, get all media attached to the attached finds.', verbose_name='Cascade export')),
+ ('query', models.TextField(blank=True, default='', help_text="Use 'text' query used in Ishtar search input. Can be left empty to export all.", verbose_name='Filter query')),
+ ('naming', models.TextField(blank=True, default='', help_text='Can use jinja template logic to have conditionnal naming. If left empty, each media will be named with incremented numbers.', verbose_name='Naming')),
+ ('associated_model', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='media_exporters', to='ishtar_common.ImporterModel', verbose_name='Associated model')),
+ ('profile_types', models.ManyToManyField(blank=True, help_text='Limit to user with theses profiles', to='ishtar_common.ProfileType', verbose_name='Profile types')),
+ ('users', models.ManyToManyField(blank=True, help_text='Limit to theses users', to='ishtar_common.IshtarUser', verbose_name='Users')),
+ ],
+ options={
+ 'verbose_name': 'Media exporter',
+ 'verbose_name_plural': 'Media exporters',
+ 'ordering': ('name',),
+ },
+ ),
+ ]
diff --git a/ishtar_common/models_common.py b/ishtar_common/models_common.py
index bb786d48d..0c22b8e7f 100644
--- a/ishtar_common/models_common.py
+++ b/ishtar_common/models_common.py
@@ -3168,8 +3168,8 @@ class PermissionQuery(models.Model):
name = models.CharField(_("Name"), max_length=200)
slug = models.SlugField(_("Slug"), unique=True)
request = models.TextField(
- _("Request"), blank=True, null=False, default="",
- help_text=_("Use 'text' request used in Ishtar search input")
+ _("Query"), blank=True, null=False, default="",
+ help_text=_("Use 'text' query used in Ishtar search input")
)
active = models.BooleanField(_("Active"), default=True)
include_associated_items = models.BooleanField(
diff --git a/ishtar_common/models_imports.py b/ishtar_common/models_imports.py
index 55bbd1906..3d4bf632b 100644
--- a/ishtar_common/models_imports.py
+++ b/ishtar_common/models_imports.py
@@ -140,6 +140,9 @@ class ImporterModel(models.Model):
def natural_key(self):
return (self.klass,)
+ def get_class(self):
+ return import_class(self.klass)
+
IMPORT_TYPES = (
("tab", _("Table")),
@@ -3005,3 +3008,107 @@ class ItemKey(models.Model):
linked_to_group.boolean = True
linked_to_group.short_description = _("Linked to group")
+
+
+MEDIA_EXPORT_TYPES = (
+ ("I", _("Images")),
+ ("F", _("Associated files")),
+ ("A", _("All")),
+)
+
+
+class MediaExporter(models.Model):
+ """
+ Settings to export all media attached to an item
+ """
+
+ name = models.CharField(_("Name"), max_length=200)
+ slug = models.SlugField(_("Slug"), unique=True, max_length=100)
+ available = models.BooleanField(_("Available"), default=True)
+ profile_types = models.ManyToManyField(
+ "ProfileType",
+ verbose_name=_("Profile types"), blank=True,
+ help_text=_("Limit to user with theses profiles")
+ )
+ users = models.ManyToManyField(
+ "IshtarUser", verbose_name=_("Users"), blank=True,
+ help_text=_("Limit to theses users")
+ )
+ associated_model = models.ForeignKey(
+ ImporterModel,
+ verbose_name=_("Associated model"),
+ on_delete=models.CASCADE,
+ related_name="media_exporters",
+ )
+ files_to_export = models.CharField(_("Files to export"), max_length=1,
+ choices=MEDIA_EXPORT_TYPES, default="I")
+ cascade = models.BooleanField(
+ _("Cascade export"),
+ default=False,
+ help_text=_("Export media of all attached items. For instance, for a context "
+ "record, get all media attached to the attached finds."))
+ query = models.TextField(
+ _("Filter query"), blank=True, null=False, default="",
+ help_text=_(
+ "Use 'text' query used in Ishtar search input. Can be left empty "
+ "to export all."
+ )
+ )
+ naming = models.TextField(
+ _("Naming"), blank=True, null=False, default="",
+ help_text=_(
+ "Can use jinja template logic to have conditionnal naming. If left empty, "
+ "each media will be named with incremented numbers."
+ )
+ )
+
+ class Meta:
+ verbose_name = _("Media exporter")
+ verbose_name_plural = _("Media exporters")
+ ordering = ("name",)
+ ADMIN_SECTION = _("Documents")
+
+ def __str__(self):
+ return f"{self.associated_model} - {self.name}"
+
+ @classmethod
+ def get_available_query(cls, ishtar_user_id):
+ q = Q(users=None, profile_types=None)
+ q |= Q(users__pk=ishtar_user_id)
+ q |= Q(
+ profile_types__user_profiles__person__ishtaruser__pk=ishtar_user_id
+ )
+ return cls.objects.filter(q)
+
+ @classmethod
+ def get_available(cls, model, request):
+ if not request or not request.user or not hasattr(request.user, "ishtaruser")\
+ or not request.user.ishtaruser:
+ return []
+ model_klass = f"{model.__module__}.{model.__name__}"
+ model_klasses = [model_klass]
+ # manage compatibility with alternate notation for finds and treatments
+ if "models_finds" in model_klass:
+ model_klasses.append(model_klass.replace("models_finds", "models"))
+ elif "models_treatments" in model_klass:
+ model_klasses.append(model_klass.replace("models_treatments", "models"))
+ return list(
+ cls.get_available_query(request.user.ishtaruser.pk).filter(
+ associated_model__klass__in=model_klasses
+ ).order_by("name").all()
+ )
+
+ def is_available(self, request):
+ if not request or not request.user or not hasattr(request.user, "ishtaruser")\
+ or not request.user.ishtaruser or not self.available:
+ return False
+ # no limitation
+ if not self.profile_types.count() and not self.users.count():
+ return True
+ user_id = request.user.ishtaruser.pk
+ if self.users.filter(pk=user_id).exists():
+ return True
+ if self.profile_types.filter(
+ user_profiles__person__ishtaruser__pk=user_id).exists():
+ return True
+ return False