summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
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
commit3f05d499e479b72486e33ff0b48ae4febf91dac3 (patch)
treeade5f9523698ad0ec5bcf988834f73df8d140dc3
parentfd62721d39b7976488ae7cdb60d7c8a2527d1508 (diff)
downloadIshtar-3f05d499e479b72486e33ff0b48ae4febf91dac3.tar.bz2
Ishtar-3f05d499e479b72486e33ff0b48ae4febf91dac3.zip
Admin: overload index to add sub-section headers
-rw-r--r--CHANGES.md3
-rw-r--r--archaeological_files/models.py10
-rw-r--r--archaeological_finds/models_finds.py19
-rw-r--r--archaeological_finds/models_treatments.py4
-rw-r--r--ishtar_common/apps.py105
-rw-r--r--ishtar_common/models.py60
-rw-r--r--ishtar_common/models_common.py8
-rw-r--r--ishtar_common/models_imports.py47
-rw-r--r--ishtar_common/models_rest.py5
-rw-r--r--ishtar_common/templates/admin/index.html91
-rw-r--r--locale/fr/LC_MESSAGES/django.po4
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 %}&nbsp;{% 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>&nbsp;</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>&nbsp;</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"