diff options
author | Étienne Loks <etienne.loks@iggdrasil.net> | 2023-02-23 18:09:15 +0100 |
---|---|---|
committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2023-02-23 18:11:08 +0100 |
commit | 3f05d499e479b72486e33ff0b48ae4febf91dac3 (patch) | |
tree | ade5f9523698ad0ec5bcf988834f73df8d140dc3 | |
parent | fd62721d39b7976488ae7cdb60d7c8a2527d1508 (diff) | |
download | Ishtar-3f05d499e479b72486e33ff0b48ae4febf91dac3.tar.bz2 Ishtar-3f05d499e479b72486e33ff0b48ae4febf91dac3.zip |
Admin: overload index to add sub-section headers
-rw-r--r-- | CHANGES.md | 3 | ||||
-rw-r--r-- | archaeological_files/models.py | 10 | ||||
-rw-r--r-- | archaeological_finds/models_finds.py | 19 | ||||
-rw-r--r-- | archaeological_finds/models_treatments.py | 4 | ||||
-rw-r--r-- | ishtar_common/apps.py | 105 | ||||
-rw-r--r-- | ishtar_common/models.py | 60 | ||||
-rw-r--r-- | ishtar_common/models_common.py | 8 | ||||
-rw-r--r-- | ishtar_common/models_imports.py | 47 | ||||
-rw-r--r-- | ishtar_common/models_rest.py | 5 | ||||
-rw-r--r-- | ishtar_common/templates/admin/index.html | 91 | ||||
-rw-r--r-- | locale/fr/LC_MESSAGES/django.po | 4 |
11 files changed, 324 insertions, 32 deletions
diff --git a/CHANGES.md b/CHANGES.md index 8b7127a78..0e383eceb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ --- title: Ishtar changelog -date: 2023-02-22 +date: 2023-02-23 --- Ishtar changelog @@ -14,6 +14,7 @@ Ishtar changelog - model: add history for document and containers - remove "Administration" entry - put Account management in "Directory" entry - Admin - Global variable: edit in table, add import/export in CSV/JSON +- Admin: overload index to add sub-section headers - Geo: create/edit form - new openlayers version - add default IGN tiles ### Bug fixes ### diff --git a/archaeological_files/models.py b/archaeological_files/models.py index d81433a25..527138425 100644 --- a/archaeological_files/models.py +++ b/archaeological_files/models.py @@ -84,6 +84,7 @@ class PriceAgreement(GeneralType): "end_date", "label", ) + ADMIN_SECTION = _("Preventive") class Job(GeneralType): @@ -118,6 +119,7 @@ class Job(GeneralType): "-permanent_contract", "label", ) + ADMIN_SECTION = _("Preventive") def __str__(self): lbl = self.label @@ -162,6 +164,7 @@ class GenericEquipmentServiceType(GeneralType): "order", "label", ) + ADMIN_SECTION = _("Preventive") class EquipmentServiceType(GeneralType): @@ -177,6 +180,7 @@ class EquipmentServiceType(GeneralType): "order", "label", ) + ADMIN_SECTION = _("Preventive") ES_UNITS = ( @@ -246,6 +250,7 @@ class EquipmentServiceCost(models.Model): "order", "equipment_service_type__label", ) + ADMIN_SECTION = _("Preventive") def __str__(self): lbl = "" @@ -325,6 +330,7 @@ class AgreementType(GeneralType): verbose_name = _("Agreement type - France") verbose_name_plural = _("Agreement types - France") ordering = ("label",) + ADMIN_SECTION = _("Preventive") post_save.connect(post_save_cache, sender=AgreementType) @@ -344,6 +350,7 @@ class OperationTypeForRoyalties(GeneralType): verbose_name = _("Operation type for royalties - France") verbose_name_plural = _("Operation types for royalties - France") ordering = ("id",) + ADMIN_SECTION = _("Preventive") post_save.connect(post_save_cache, sender=OperationTypeForRoyalties) @@ -1386,6 +1393,7 @@ class PreventiveFileGroundJob(ManDays): class Meta: ordering = ("job",) + ADMIN_SECTION = _("Preventive") @property def cost_planned(self): @@ -1402,6 +1410,7 @@ class PreventiveFileJob(ManDays): class Meta: ordering = ("job",) + ADMIN_SECTION = _("Preventive") @property def cost_planned(self): @@ -1426,6 +1435,7 @@ class PreventiveFileEquipmentServiceCost(models.Model): class Meta: ordering = ("equipment_service_cost",) + ADMIN_SECTION = _("Preventive") @property def quantity_planned(self): diff --git a/archaeological_finds/models_finds.py b/archaeological_finds/models_finds.py index a8565f14e..4538e0d59 100644 --- a/archaeological_finds/models_finds.py +++ b/archaeological_finds/models_finds.py @@ -90,6 +90,7 @@ class MaterialType(HierarchicalType): verbose_name = _("Material type") verbose_name_plural = _("Material types") ordering = ("label",) + ADMIN_SECTION = _("Finds") post_save.connect(post_save_cache, sender=MaterialType) @@ -103,6 +104,7 @@ class MaterialTypeQualityType(GeneralType): verbose_name = _("Material type quality type") verbose_name_plural = _("Material type quality types") ordering = ("order",) + ADMIN_SECTION = _("Finds") post_save.connect(post_save_cache, sender=MaterialTypeQualityType) @@ -119,6 +121,7 @@ class ConservatoryState(HierarchicalType): "order", "label", ) + ADMIN_SECTION = _("Finds") post_save.connect(post_save_cache, sender=ConservatoryState) @@ -177,6 +180,7 @@ class TreatmentType(HierarchicalType): "order", "label", ) + ADMIN_SECTION = _("Treatments") @classmethod def get_types( @@ -228,6 +232,7 @@ class IntegrityType(GeneralType): verbose_name = _("Integrity / interest type") verbose_name_plural = _("Integrity / interest types") ordering = ("label",) + ADMIN_SECTION = _("Finds") post_save.connect(post_save_cache, sender=IntegrityType) @@ -239,6 +244,7 @@ class RemarkabilityType(GeneralType): verbose_name = _("Remarkability type") verbose_name_plural = _("Remarkability types") ordering = ("label",) + ADMIN_SECTION = _("Finds") post_save.connect(post_save_cache, sender=RemarkabilityType) @@ -252,6 +258,7 @@ class BatchType(GeneralType): verbose_name = _("Batch type") verbose_name_plural = _("Batch types") ordering = ("order",) + ADMIN_SECTION = _("Finds") post_save.connect(post_save_cache, sender=BatchType) @@ -266,6 +273,7 @@ class ObjectType(HierarchicalType): "parent__label", "label", ) + ADMIN_SECTION = _("Finds") post_save.connect(post_save_cache, sender=ObjectType) @@ -280,6 +288,7 @@ class FunctionalArea(HierarchicalType): "parent__label", "label", ) + ADMIN_SECTION = _("Finds") post_save.connect(post_save_cache, sender=FunctionalArea) @@ -293,6 +302,7 @@ class ObjectTypeQualityType(GeneralType): verbose_name = _("Object type quality type") verbose_name_plural = _("Object type quality types") ordering = ("order",) + ADMIN_SECTION = _("Finds") post_save.connect(post_save_cache, sender=ObjectTypeQualityType) @@ -307,6 +317,7 @@ class AlterationType(HierarchicalType): "parent__label", "label", ) + ADMIN_SECTION = _("Finds") post_save.connect(post_save_cache, sender=AlterationType) @@ -321,6 +332,7 @@ class AlterationCauseType(HierarchicalType): "parent__label", "label", ) + ADMIN_SECTION = _("Finds") post_save.connect(post_save_cache, sender=AlterationCauseType) @@ -332,6 +344,7 @@ class TreatmentEmergencyType(GeneralType): verbose_name = _("Treatment emergency type") verbose_name_plural = _("Treatment emergency types") ordering = ("label",) + ADMIN_SECTION = _("Finds") post_save.connect(post_save_cache, sender=TreatmentEmergencyType) @@ -346,6 +359,7 @@ class CommunicabilityType(HierarchicalType): "parent__label", "label", ) + ADMIN_SECTION = _("Finds") post_save.connect(post_save_cache, sender=CommunicabilityType) @@ -359,6 +373,7 @@ class CheckedType(GeneralType): verbose_name = _("Checked type") verbose_name_plural = _("Checked types") ordering = ("order",) + ADMIN_SECTION = _("Finds") post_save.connect(post_save_cache, sender=CheckedType) @@ -485,6 +500,7 @@ class BaseFind( indexes = [ GinIndex(fields=["data"]), ] + ADMIN_SECTION = _("Finds") def __str__(self): return self.label @@ -909,6 +925,7 @@ class FindBasket(Basket, MainItem, ValueGetter): ("view_find", "Can view all Finds"), ("view_own_find", "Can view own Find"), ) + ADMIN_SECTION = _("Finds") def get_values(self, prefix="", no_values=False, filtr=None, **kwargs): base_exclude = kwargs["exclude"][:] if "exclude" in kwargs else [] @@ -2057,6 +2074,7 @@ class Find( indexes = [ GinIndex(fields=["data"]), ] + ADMIN_SECTION = _("Finds") def natural_key(self): return (self.uuid,) @@ -3397,6 +3415,7 @@ class Property(LightHistorizedItem): indexes = [ GinIndex(fields=["data"]), ] + ADMIN_SECTION = _("Finds") def __str__(self): return str(self.person) + settings.JOINT + str(self.find) diff --git a/archaeological_finds/models_treatments.py b/archaeological_finds/models_treatments.py index 88f267a9b..2ee23c1c6 100644 --- a/archaeological_finds/models_treatments.py +++ b/archaeological_finds/models_treatments.py @@ -71,6 +71,7 @@ class TreatmentState(GeneralType): "order", "label", ) + ADMIN_SECTION = _("Treatments") @classmethod def get_default(cls): @@ -298,6 +299,7 @@ class Treatment( indexes = [ GinIndex(fields=["data"]), ] + ADMIN_SECTION = _("Treatments") def __str__(self): return self.cached_label or "" @@ -1000,6 +1002,7 @@ class TreatmentFileType(GeneralType): verbose_name = _("Treatment request type") verbose_name_plural = _("Treatment request types") ordering = ("label",) + ADMIN_SECTION = _("Treatments") post_save.connect(post_save_cache, sender=TreatmentFileType) @@ -1187,6 +1190,7 @@ class TreatmentFile( indexes = [ GinIndex(fields=["data"]), ] + ADMIN_SECTION = _("Treatments") def __str__(self): return self.cached_label or "" diff --git a/ishtar_common/apps.py b/ishtar_common/apps.py index eca70b3e1..1f076462d 100644 --- a/ishtar_common/apps.py +++ b/ishtar_common/apps.py @@ -1,5 +1,7 @@ -from django.apps import AppConfig +from django.apps import AppConfig, apps from django.contrib.admin import AdminSite +from django.urls import NoReverseMatch, reverse +from django.utils.text import capfirst from django.utils.translation import ugettext_lazy as _ @@ -7,6 +9,107 @@ class IshtarAdminSite(AdminSite): site_header = _("Ishtar administration") site_title = _("Ishtar administration") + MODEL_OVERLOAD = { + # (app, model) -> new app + ("ishtar_common", "OperationType"): "archaeological_operations" + } + + def _build_app_dict(self, request, label=None): + # copied from contrib/admin/sites.py + # overload to get add "admin_section" in models and use MODEL_OVERLOAD + app_dict = {} + + if label: + models = { + m: m_a for m, m_a in self._registry.items() + if m._meta.app_label == label or self.MODEL_OVERLOAD.get( + (m._meta.app_label, m._meta.object_name), None + ) + } + else: + models = self._registry + + for model, model_admin in models.items(): + app_label = model._meta.app_label + # Ishtar + object_name = model._meta.object_name + if (app_label, object_name) in self.MODEL_OVERLOAD: + app_label_for_dict = self.MODEL_OVERLOAD[(app_label, object_name)] + else: + app_label_for_dict = app_label + # end Ishtar + + has_module_perms = model_admin.has_module_permission(request) + if not has_module_perms: + continue + + perms = model_admin.get_model_perms(request) + + # Check whether user has any perm for this module. + # If so, add the module to the model_list. + if True not in perms.values(): + continue + + + info = (app_label, model._meta.model_name) + model_dict = { + 'name': capfirst(model._meta.verbose_name_plural), + 'object_name': model._meta.object_name, + 'perms': perms, + 'admin_url': None, + 'add_url': None, + # Ishtar change + 'admin_section': getattr(model, "ADMIN_SECTION", ""), + # End Ishtar change + } + if perms.get('change') or perms.get('view'): + model_dict['view_only'] = not perms.get('change') + try: + model_dict['admin_url'] = reverse('admin:%s_%s_changelist' % info, + current_app=self.name) + except NoReverseMatch: + pass + if perms.get('add'): + try: + model_dict['add_url'] = reverse('admin:%s_%s_add' % info, + current_app=self.name) + except NoReverseMatch: + pass + # Ishtar + if app_label_for_dict in app_dict: + app_dict[app_label_for_dict]['models'].append(model_dict) + else: + app_dict[app_label_for_dict] = { + 'name': apps.get_app_config(app_label_for_dict).verbose_name, + 'app_label': app_label_for_dict, + 'app_url': reverse( + 'admin:app_list', + kwargs={'app_label': app_label_for_dict}, + current_app=self.name, + ), + 'has_module_perms': has_module_perms, + 'models': [model_dict], + } + # Ishtar end + + if label: + return app_dict.get(label) + return app_dict + + def get_app_list(self, request): + # copied from contrib/admin/sites.py + # overload to sort models by "admin_section" + app_dict = self._build_app_dict(request) + + # Sort the apps alphabetically. + app_list = sorted(app_dict.values(), key=lambda x: x['name'].lower()) + + # Sort the models alphabetically within each app. + for app in app_list: + app['models'].sort(key=lambda x: (x['admin_section'], x['name'])) # Ishtar change + + return app_list + admin_site = IshtarAdminSite() # AFAC diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 83ca5129b..e4f13ab5f 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -49,7 +49,6 @@ from xml.etree import ElementTree as ET from django.apps import apps from django.conf import settings from django.contrib.auth.models import User, Group -from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.contrib.gis.db import models from django.contrib.gis.db.models.aggregates import Union @@ -117,6 +116,7 @@ from ishtar_common.models_imports import ( Import, TargetKeyGroup, ValueFormater, + ItemKey, ) from ishtar_common.utils import ( @@ -177,6 +177,7 @@ __all__ = [ "ImporterDuplicateField", "Regexp", "ImportTarget", + "ItemKey", "TargetKey", "FormaterType", "Import", @@ -447,29 +448,6 @@ class TinyUrl(models.Model): return i -class ItemKey(models.Model): - key = models.TextField(_("Key")) - content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) - object_id = models.PositiveIntegerField() - content_object = GenericForeignKey("content_type", "object_id") - importer = models.ForeignKey( - Import, - null=True, - blank=True, - help_text=_("Specific key to an import"), - on_delete=models.SET_NULL, - ) - user = models.ForeignKey( - "IshtarUser", blank=True, null=True, on_delete=models.SET_NULL - ) - group = models.ForeignKey( - TargetKeyGroup, blank=True, null=True, on_delete=models.SET_NULL - ) - - def __str__(self): - return self.key - - class ImageModel(models.Model, ImageContainerModel): image = models.ImageField( upload_to=get_image_path, @@ -698,6 +676,7 @@ class JsonDataSection(models.Model): verbose_name_plural = _("Custom data - Menus") ordering = ["order", "name"] unique_together = ("name", "content_type") + ADMIN_SECTION = _("Custom data / custom forms") def natural_key(self): return (self.name, self.content_type.app_label, self.content_type.model) @@ -761,6 +740,7 @@ class JsonDataField(models.Model): verbose_name_plural = _("Custom data - Fields") ordering = ["order", "name"] unique_together = ("content_type", "key") + ADMIN_SECTION = _("Custom data / custom forms") def natural_key(self): return (self.key, self.content_type.app_label, self.content_type.model) @@ -1000,6 +980,7 @@ class Language(GeneralType): class Meta: verbose_name = _("Language") verbose_name_plural = _("Languages") + ADMIN_SECTION = _("Documents") CURRENCY = (("€", _("Euro")), ("$", _("US dollar"))) @@ -1542,6 +1523,7 @@ class IshtarSiteProfile(models.Model, Cached): verbose_name = _("Ishtar site profile") verbose_name_plural = _("Ishtar site profiles") ordering = ["label"] + ADMIN_SECTION = _("General settings") def __str__(self): return str(self.label) @@ -1667,6 +1649,7 @@ class CustomForm(models.Model): verbose_name_plural = _("Custom forms") ordering = ["name", "form"] unique_together = (("name", "form"),) + ADMIN_SECTION = _("Custom data / custom forms") def natural_key(self): return (self.name, self.form) @@ -1815,9 +1798,10 @@ class CustomFormJsonField(models.Model): objects = CustomFormJsonFieldManager() class Meta: - verbose_name = _("Custom form - Json data field") - verbose_name_plural = _("Custom form - Json data fields") + verbose_name = _("Custom forms - Json data field") + verbose_name_plural = _("Custom forms - Json data fields") unique_together = ("custom_form", "json_field") + ADMIN_SECTION = _("Custom data / custom forms") def natural_key(self): return ( @@ -1841,6 +1825,7 @@ class GlobalVar(models.Model, Cached): verbose_name = _("Global variable") verbose_name_plural = _("Global variables") ordering = ["slug"] + ADMIN_SECTION = _("General settings") def natural_key(self): return (self.slug,) @@ -2063,6 +2048,7 @@ class DocumentTemplate(models.Model): verbose_name = _("Document template") verbose_name_plural = _("Document templates") ordering = ["associated_model", "name"] + ADMIN_SECTION = _("General settings") def __str__(self): return self.name @@ -2363,6 +2349,7 @@ class Area(HierarchicalType): verbose_name = _("Town - Area") verbose_name_plural = _("Town - Areas") ordering = ("label",) + ADMIN_SECTION = _("Geography") def __str__(self): if not self.reference: @@ -2492,6 +2479,7 @@ class OrganizationType(GenderedType): verbose_name = _("Organization type") verbose_name_plural = _("Organization types") ordering = ("label",) + ADMIN_SECTION = _("Directory") def get_orga_planning_service_label(): @@ -2591,6 +2579,7 @@ class Organization(Address, Merge, OwnPerms, BaseGenderedType, ValueGetter, Main indexes = [ GinIndex(fields=["data"]), ] + ADMIN_SECTION = _("Directory") def simple_lbl(self): if self.name: @@ -2644,6 +2633,7 @@ class PersonType(GeneralType): verbose_name = _("Person type") verbose_name_plural = _("Person types") ordering = ("label",) + ADMIN_SECTION = _("Directory") post_save.connect(post_save_cache, sender=PersonType) @@ -2717,6 +2707,7 @@ class TitleType(GenderedType): verbose_name = _("Title type") verbose_name_plural = _("Title types") ordering = ("label",) + ADMIN_SECTION = _("Directory") @classmethod def get_documentation_string(cls): @@ -2886,6 +2877,7 @@ class Person(Address, Merge, OwnPerms, ValueGetter, MainItem): ("delete_own_person", "Can delete own Person"), ) ordering = ['name', 'surname'] + ADMIN_SECTION = _("Directory") def natural_key(self): return (self.uuid,) @@ -3165,6 +3157,7 @@ class ProfileType(GeneralType): verbose_name = _("Profile type") verbose_name_plural = _("Profile types") ordering = ("label",) + ADMIN_SECTION = _("Directory") post_save.connect(post_save_cache, sender=ProfileType) @@ -3176,6 +3169,7 @@ class ProfileTypeSummary(ProfileType): proxy = True verbose_name = _("Profile type summary") verbose_name_plural = _("Profile types summary") + ADMIN_SECTION = _("Directory") class UserProfile(models.Model): @@ -3212,6 +3206,7 @@ class UserProfile(models.Model): verbose_name = _("User profile") verbose_name_plural = _("User profiles") unique_together = (("name", "profile_type", "person"),) + ADMIN_SECTION = _("Directory") def __str__(self): lbl = self.name or str(self.profile_type) @@ -3407,6 +3402,7 @@ class IshtarUser(FullSearch): class Meta: verbose_name = _("Ishtar user") verbose_name_plural = _("Ishtar users") + ADMIN_SECTION = _("Directory") def __str__(self): return str(self.person) @@ -3620,6 +3616,7 @@ class AuthorType(GeneralType): verbose_name = _("Author type") verbose_name_plural = _("Author types") ordering = ["order", "label"] + ADMIN_SECTION = _("Directory") post_save.connect(post_save_cache, sender=AuthorType) @@ -3655,6 +3652,7 @@ class Author(FullSearch): ("change_own_author", "Can change own Author"), ("delete_own_author", "Can delete own Author"), ) + ADMIN_SECTION = _("Directory") def __str__(self): return self.cached_label or "" @@ -3717,6 +3715,7 @@ class SourceType(HierarchicalType): verbose_name = _("Document type") verbose_name_plural = _("Document types") ordering = ["label"] + ADMIN_SECTION = _("Documents") post_save.connect(post_save_cache, sender=SourceType) @@ -3734,6 +3733,7 @@ class SupportType(GeneralType): class Meta: verbose_name = _("Support type") verbose_name_plural = _("Support types") + ADMIN_SECTION = _("Documents") post_save.connect(post_save_cache, sender=SupportType) @@ -3762,6 +3762,7 @@ class Format(GeneralType): verbose_name = _("Format type") verbose_name_plural = _("Format types") ordering = ["label"] + ADMIN_SECTION = _("Documents") post_save.connect(post_save_cache, sender=Format) @@ -3775,6 +3776,7 @@ class LicenseType(GeneralType): verbose_name = _("License type") verbose_name_plural = _("License types") ordering = ("label",) + ADMIN_SECTION = _("Documents") class DocumentTag(GeneralType): @@ -3784,6 +3786,7 @@ class DocumentTag(GeneralType): verbose_name = _("Document tag") verbose_name_plural = _("Document tags") ordering = ("label",) + ADMIN_SECTION = _("Documents") post_save.connect(post_save_cache, sender=LicenseType) @@ -4304,6 +4307,7 @@ class Document( indexes = [ GinIndex(fields=["data"]), ] + ADMIN_SECTION = _("Documents") def __str__(self): return self.title @@ -5033,6 +5037,7 @@ class AdministrationScript(models.Model): verbose_name = _("Administration script") verbose_name_plural = _("Administration scripts") ordering = ["name"] + ADMIN_SECTION = _("General settings") def __str__(self): return str(self.name) @@ -5062,6 +5067,7 @@ class AdministrationTask(models.Model): verbose_name = _("Administration task") verbose_name_plural = _("Administration tasks") ordering = ["script"] + ADMIN_SECTION = _("General settings") def __str__(self): state = _("Unknown") @@ -5219,6 +5225,7 @@ class ExportTask(models.Model): verbose_name = _("Archive - Export") verbose_name_plural = _("Archive - Exports") ordering = ["creation_date"] + ADMIN_SECTION = pgettext_lazy("name", "Archive") def __str__(self): state = _("Unknown") @@ -5278,6 +5285,7 @@ class ImportTask(models.Model): verbose_name = _("Archive - Import") verbose_name_plural = _("Archive - Imports") ordering = ["creation_date"] + ADMIN_SECTION = pgettext_lazy("name", "Archive") def __str__(self): state = _("Unknown") diff --git a/ishtar_common/models_common.py b/ishtar_common/models_common.py index bc078df1c..fc73d707c 100644 --- a/ishtar_common/models_common.py +++ b/ishtar_common/models_common.py @@ -2008,6 +2008,7 @@ class Department(models.Model): verbose_name = _("Department") verbose_name_plural = _("Departments") ordering = ["number"] + ADMIN_SECTION = _("Geography") def __str__(self): return self.label @@ -2063,6 +2064,7 @@ class SpatialReferenceSystem(GeneralType): "order", "label", ) + ADMIN_SECTION = _("Geography") @classmethod def get_documentation_string(cls): @@ -2094,6 +2096,7 @@ class GeoOriginType(HierarchicalType): "order", "label", ) + ADMIN_SECTION = _("Geography") class GeoDataType(HierarchicalType): @@ -2110,6 +2113,7 @@ class GeoDataType(HierarchicalType): "order", "label", ) + ADMIN_SECTION = _("Geography") class GeoProviderType(HierarchicalType): @@ -2126,6 +2130,7 @@ class GeoProviderType(HierarchicalType): "order", "label", ) + ADMIN_SECTION = _("Geography") class GeoBufferType(GeneralType): @@ -2138,6 +2143,7 @@ class GeoBufferType(GeneralType): "order", "label", ) + ADMIN_SECTION = _("Geography") GEOJSON_POINT_TPL = { @@ -2266,6 +2272,7 @@ class GeoVectorData(Imported, OwnPerms): ("change_own_geovectordata", "Can change own Geographic - Vector data"), ("delete_own_geovectordata", "Can delete own Geographic - Vector data"), ) + ADMIN_SECTION = _("Geography") def __str__(self): name = self.name @@ -2950,6 +2957,7 @@ class Town(GeographicItem, Imported, models.Model): if settings.COUNTRY == "fr": ordering = ["numero_insee"] unique_together = (("numero_insee", "year"),) + ADMIN_SECTION = _("Geography") def natural_key(self): return (self.numero_insee, self.year) diff --git a/ishtar_common/models_imports.py b/ishtar_common/models_imports.py index 33463e181..6451206a2 100644 --- a/ishtar_common/models_imports.py +++ b/ishtar_common/models_imports.py @@ -33,6 +33,8 @@ import zipfile from django.apps import apps from django.conf import settings +from django.contrib.contenttypes.fields import GenericForeignKey +from django.contrib.contenttypes.models import ContentType from django.contrib.gis.db import models from django.contrib.gis.gdal.error import GDALException from django.contrib.gis.geos import GEOSGeometry @@ -112,9 +114,10 @@ class ImporterModel(models.Model): objects = ImportModelManager() class Meta: - verbose_name = _("Model") - verbose_name_plural = _("Models") + verbose_name = _("Data model") + verbose_name_plural = _("Data models") ordering = ("name",) + ADMIN_SECTION = _("General settings") def __str__(self): return self.name @@ -192,6 +195,7 @@ class ImporterType(models.Model): verbose_name = _("Importer - Type") verbose_name_plural = _("Importer - Types") ordering = ("name",) + ADMIN_SECTION = _("Imports") def natural_key(self): return (self.slug,) @@ -439,6 +443,7 @@ class ImporterDefault(models.Model): verbose_name = _("Importer - Default") verbose_name_plural = _("Importer - Defaults") unique_together = ("importer_type", "target") + ADMIN_SECTION = _("Imports") objects = ImporterDefaultManager() @@ -496,6 +501,7 @@ class ImporterDefaultValues(models.Model): verbose_name = _("Importer - Default value") verbose_name_plural = _("Importer - Default values") unique_together = ("default_target", "target") + ADMIN_SECTION = _("Imports") def natural_key(self): return ( @@ -602,6 +608,7 @@ class ImporterColumn(models.Model): verbose_name_plural = _("Importer - Columns") ordering = ("importer_type", "col_number") unique_together = ("importer_type", "col_number") + ADMIN_SECTION = _("Imports") def __str__(self): return "{} - {}".format(self.importer_type, self.col_number) @@ -655,6 +662,7 @@ class ImporterDuplicateField(models.Model): verbose_name_plural = _("Importer - Duplicate fields") ordering = ("column", "field_name") unique_together = ("column", "field_name") + ADMIN_SECTION = _("Imports") def natural_key(self): return self.column.importer_type.slug, self.column.col_number, self.field_name @@ -674,6 +682,7 @@ class Regexp(models.Model): class Meta: verbose_name = _("Importer - Regular expression") verbose_name_plural = _("Importer - Regular expressions") + ADMIN_SECTION = _("Imports") def __str__(self): return self.name @@ -702,6 +711,7 @@ class ValueFormater(models.Model): class Meta: verbose_name = _("Importer - Value format") verbose_name_plural = _("Importer - Value formats") + ADMIN_SECTION = _("Imports") def __str__(self): return self.name @@ -755,6 +765,7 @@ class ImportTarget(models.Model): verbose_name = _("Importer - Target") verbose_name_plural = _("Importer - Targets") unique_together = ("column", "target") + ADMIN_SECTION = _("Imports") def __str__(self): return self.target[:50] if self.target else self.comment @@ -814,6 +825,7 @@ class TargetKeyGroup(models.Model): class Meta: verbose_name = _("Importer - Target key group") verbose_name_plural = _("Importer - Target key groups") + ADMIN_SECTION = _("Imports") def __str__(self): return self.name @@ -856,6 +868,7 @@ class TargetKey(models.Model): verbose_name = _("Importer - Target key") verbose_name_plural = _("Importer - Targets keys") ordering = ("target", "key") + ADMIN_SECTION = _("Imports") def __str__(self): return " - ".join([str(self.target), self.key[:50]]) @@ -1034,6 +1047,7 @@ class FormaterType(models.Model): verbose_name_plural = _("Importer - Formater types") unique_together = ("formater_type", "options", "many_split") ordering = ("formater_type", "options") + ADMIN_SECTION = _("Imports") def natural_key(self): return self.formater_type, self.options, self.many_split @@ -1284,6 +1298,7 @@ class Import(models.Model): class Meta: verbose_name = _("Import") verbose_name_plural = _("Imports") + ADMIN_SECTION = _("Imports") def __str__(self): return "{} | {}".format(self.name or "-", self.importer_type) @@ -1916,3 +1931,31 @@ def pre_delete_import(sender, **kwargs): pre_delete.connect(pre_delete_import, sender=Import) + + +class ItemKey(models.Model): + key = models.TextField(_("Key")) + content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) + object_id = models.PositiveIntegerField() + content_object = GenericForeignKey("content_type", "object_id") + importer = models.ForeignKey( + Import, + null=True, + blank=True, + help_text=_("Specific key to an import"), + on_delete=models.SET_NULL, + ) + user = models.ForeignKey( + "IshtarUser", blank=True, null=True, on_delete=models.SET_NULL + ) + group = models.ForeignKey( + TargetKeyGroup, blank=True, null=True, on_delete=models.SET_NULL + ) + + class Meta: + verbose_name = _("Importer - Item key") + verbose_name_plural = _("Imports - Item keys") + ADMIN_SECTION = _("Imports") + + def __str__(self): + return self.key diff --git a/ishtar_common/models_rest.py b/ishtar_common/models_rest.py index 5737ec368..411404325 100644 --- a/ishtar_common/models_rest.py +++ b/ishtar_common/models_rest.py @@ -51,6 +51,7 @@ class ApiUser(models.Model): class Meta: verbose_name = _("API - Remote access - User") verbose_name_plural = _("API - Remote access - Users") + ADMIN_SECTION = _("API") def __str__(self): return self.user_ptr.username @@ -77,6 +78,7 @@ class ApiSearchModel(models.Model): class Meta: verbose_name = _("API - Remote access - Search model") verbose_name_plural = _("API - Remote access - Search models") + ADMIN_SECTION = _("API") def __str__(self): return f"{self.user} - {self.content_type}" @@ -89,6 +91,7 @@ class ApiSheetFilter(SheetFilter): class Meta: verbose_name = _("API - Remote access - Sheet filter") verbose_name_plural = _("API - Remote access - Sheet filters") + ADMIN_SECTION = _("API") def get_template(self): ct = self.api_search_model.content_type @@ -125,6 +128,7 @@ class ApiExternalSource(models.Model): verbose_name = _("API - Search - External source") verbose_name_plural = _("API - Search - External sources") ordering = ("name",) + ADMIN_SECTION = _("API") def __str__(self): return self.name @@ -435,3 +439,4 @@ class ApiKeyMatch(models.Model): class Meta: verbose_name = _("API - Search - Key match") verbose_name_plural = _("API - Search - Keys matches") + ADMIN_SECTION = _("API") diff --git a/ishtar_common/templates/admin/index.html b/ishtar_common/templates/admin/index.html new file mode 100644 index 000000000..1019ea7c0 --- /dev/null +++ b/ishtar_common/templates/admin/index.html @@ -0,0 +1,91 @@ +{% extends "admin/base_site.html" %} +{% load i18n static %} + +{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/dashboard.css" %}">{% endblock %} + +{% block coltype %}colMS{% endblock %} + +{% block bodyclass %}{{ block.super }} dashboard{% endblock %} + +{% block breadcrumbs %}{% endblock %} + +{% block content %} +<div id="content-main"> + +{% if app_list %} + {% for app in app_list %} + <div class="app-{{ app.app_label }} module"> + <table> + <caption> + <a href="{{ app.app_url }}" class="section" title="{% blocktrans with name=app.name %}Models in the {{ name }} application{% endblocktrans %}">{{ app.name }}</a> + </caption> + {% for model in app.models %} + {# Ishtar #} + {% ifchanged model.admin_section %} + {% if model.admin_section or forloop.counter0 %}<tr><th colspan="3">{% if model.admin_section %}{{ model.admin_section }}{% else %} {% endif %}</th></tr>{% endif %} + {% endifchanged %} + {# Ishtar end #} + <tr class="model-{{ model.object_name|lower }}"> + {% if model.admin_url %} + <th scope="row"><a href="{{ model.admin_url }}">{{ model.name }}</a></th> + {% else %} + <th scope="row">{{ model.name }}</th> + {% endif %} + + {% if model.add_url %} + <td><a href="{{ model.add_url }}" class="addlink">{% trans 'Add' %}</a></td> + {% else %} + <td> </td> + {% endif %} + + {% if model.admin_url %} + {% if model.view_only %} + <td><a href="{{ model.admin_url }}" class="viewlink">{% trans 'View' %}</a></td> + {% else %} + <td><a href="{{ model.admin_url }}" class="changelink">{% trans 'Change' %}</a></td> + {% endif %} + {% else %} + <td> </td> + {% endif %} + </tr> + {% endfor %} + </table> + </div> + {% endfor %} +{% else %} + <p>{% trans "You don't have permission to view or edit anything." %}</p> +{% endif %} +</div> +{% endblock %} + +{% block sidebar %} +<div id="content-related"> + <div class="module" id="recent-actions-module"> + <h2>{% trans 'Recent actions' %}</h2> + <h3>{% trans 'My actions' %}</h3> + {% load log %} + {% get_admin_log 10 as admin_log for_user user %} + {% if not admin_log %} + <p>{% trans 'None available' %}</p> + {% else %} + <ul class="actionlist"> + {% for entry in admin_log %} + <li class="{% if entry.is_addition %}addlink{% endif %}{% if entry.is_change %}changelink{% endif %}{% if entry.is_deletion %}deletelink{% endif %}"> + {% if entry.is_deletion or not entry.get_admin_url %} + {{ entry.object_repr }} + {% else %} + <a href="{{ entry.get_admin_url }}">{{ entry.object_repr }}</a> + {% endif %} + <br> + {% if entry.content_type %} + <span class="mini quiet">{% filter capfirst %}{{ entry.content_type }}{% endfilter %}</span> + {% else %} + <span class="mini quiet">{% trans 'Unknown content' %}</span> + {% endif %} + </li> + {% endfor %} + </ul> + {% endif %} + </div> +</div> +{% endblock %} diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index d0992ec79..908af761c 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -9353,11 +9353,11 @@ msgstr "Utiliser dans les index de recherche" #: ishtar_common/models.py:760 msgid "Custom data - Field" -msgstr "Donnée personnalisée - Champ" +msgstr "Données personnalisées - Champ" #: ishtar_common/models.py:761 msgid "Custom data - Fields" -msgstr "Donnée personnalisée - Champs" +msgstr "Données personnalisées - Champs" #: ishtar_common/models.py:776 msgid "Content types of the field and of the menu do not match" |