summaryrefslogtreecommitdiff
path: root/ishtar_common
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@iggdrasil.net>2025-02-14 17:49:37 +0100
committerÉtienne Loks <etienne.loks@iggdrasil.net>2025-02-19 14:43:48 +0100
commit4f60b4805a7eac04c2a8ec2116a245dbeec3c822 (patch)
tree561f87e11ae60c96320523c80c6317ff8f1d2f99 /ishtar_common
parent94f357939957dc8a5de453224913dbecdc4dc9db (diff)
downloadIshtar-4f60b4805a7eac04c2a8ec2116a245dbeec3c822.tar.bz2
Ishtar-4f60b4805a7eac04c2a8ec2116a245dbeec3c822.zip
✨ generate_permissions
manage: - possession (direct, creation, basket) - heritage - areas association - requests ({USER} special syntax)
Diffstat (limited to 'ishtar_common')
-rw-r--r--ishtar_common/admin.py5
-rw-r--r--ishtar_common/menu_base.py2
-rw-r--r--ishtar_common/menus.py50
-rw-r--r--ishtar_common/models.py95
-rw-r--r--ishtar_common/models_common.py107
-rw-r--r--ishtar_common/models_rest.py30
-rw-r--r--ishtar_common/rest.py5
-rw-r--r--ishtar_common/utils.py77
-rw-r--r--ishtar_common/views.py18
-rw-r--r--ishtar_common/views_item.py135
10 files changed, 331 insertions, 193 deletions
diff --git a/ishtar_common/admin.py b/ishtar_common/admin.py
index 743d643a3..369821b45 100644
--- a/ishtar_common/admin.py
+++ b/ishtar_common/admin.py
@@ -72,7 +72,8 @@ from django import forms
from ishtar_common import models, models_common, models_rest
from ishtar_common.apps import admin_site
from ishtar_common.model_merging import merge_model_objects
-from ishtar_common.utils import get_cache, create_slug, get_person_gdpr_log
+from ishtar_common.utils import API_MAIN_CONTENT_TYPES, get_cache, create_slug,\
+ get_person_gdpr_log
from ishtar_common import forms as common_forms, forms_common as other_common_forms
from ishtar_common.serializers import restore_serialized, IMPORT_MODEL_LIST
@@ -2561,7 +2562,7 @@ admin_site.register(models_rest.ApiUser, ApiUserAdmin)
def get_api_choices():
pks = []
- for app_label, model_name in models_rest.MAIN_CONTENT_TYPES:
+ for app_label, model_name in API_MAIN_CONTENT_TYPES:
try:
ct = ContentType.objects.get(app_label=app_label, model=model_name)
pks.append(ct.pk)
diff --git a/ishtar_common/menu_base.py b/ishtar_common/menu_base.py
index e0f206a57..feb05c6db 100644
--- a/ishtar_common/menu_base.py
+++ b/ishtar_common/menu_base.py
@@ -17,7 +17,7 @@
# See the file COPYING for details.
-from ishtar_common.models import get_current_profile
+from ishtar_common.utils import get_current_profile
class SectionItem:
diff --git a/ishtar_common/menus.py b/ishtar_common/menus.py
index aae127a09..c455fa73a 100644
--- a/ishtar_common/menus.py
+++ b/ishtar_common/menus.py
@@ -30,33 +30,34 @@ from django.urls import reverse
from django.contrib.auth.models import User
-_extra_menus = []
-# collect menu from INSTALLED_APPS
-for app in settings.INSTALLED_APPS:
- mod = __import__(app, fromlist=["ishtar_menu"])
- if hasattr(mod, "ishtar_menu"):
- menu = getattr(mod, "ishtar_menu")
- _extra_menus += menu.MENU_SECTIONS
-
-# sort
-__section_items = [mnu for order, mnu in sorted(_extra_menus, key=lambda x: x[0])]
-# regroup menus
-_section_items, __keys = [], []
-for section_item in __section_items:
- if section_item.idx not in __keys:
- __keys.append(section_item.idx)
- _section_items.append(section_item)
- continue
- section_childs = _section_items[__keys.index(section_item.idx)].childs
- childs_idx = [child.idx for child in section_childs]
- for child in section_item.childs:
- if child.idx not in childs_idx:
- section_childs.append(child)
+def get_section_items():
+ _extra_menus = []
+ # collect menu from INSTALLED_APPS
+ for app in settings.INSTALLED_APPS:
+ mod = __import__(app, fromlist=["ishtar_menu"])
+ if hasattr(mod, "ishtar_menu"):
+ menu = getattr(mod, "ishtar_menu")
+ _extra_menus += menu.MENU_SECTIONS
+
+ # sort
+ __section_items = [mnu for order, mnu in sorted(_extra_menus, key=lambda x: x[0])]
+ # regroup menus
+ _section_items, __keys = [], []
+ for section_item in __section_items:
+ if section_item.idx not in __keys:
+ __keys.append(section_item.idx)
+ _section_items.append(section_item)
+ continue
+ section_childs = _section_items[__keys.index(section_item.idx)].childs
+ childs_idx = [child.idx for child in section_childs]
+ for child in section_item.childs:
+ if child.idx not in childs_idx:
+ section_childs.append(child)
+ return _section_items
-class Menu:
- ref_childs = _section_items
+class Menu:
def __init__(self, user, current_action=None, session=None):
self.user = user
self.initialized = False
@@ -74,6 +75,7 @@ class Menu:
self.selected_idx = None
self.session = session
self.items_by_idx = {}
+ self.ref_childs = get_section_items()
def reinit_menu_for_all_user(self):
"""
diff --git a/ishtar_common/models.py b/ishtar_common/models.py
index 8e5b7f703..3e51f8cb1 100644
--- a/ishtar_common/models.py
+++ b/ishtar_common/models.py
@@ -97,6 +97,7 @@ from ishtar_common.utils import (
InlineClass
)
from ishtar_common.utils_secretary import IshtarSecretaryRenderer
+from ishtar_common.views_item import get_item
from ishtar_common.alternative_configs import (
ALTERNATE_CONFIGS,
@@ -140,7 +141,8 @@ from ishtar_common.utils import (
cached_label_changed,
generate_relation_graph,
max_size_help,
- JSON_SERIALIZATION
+ JSON_SERIALIZATION,
+ SearchAltName,
)
from ishtar_common.models_common import (
@@ -175,7 +177,6 @@ from ishtar_common.models_common import (
PermissionRequest,
post_save_cache,
QuickAction,
- SearchAltName,
SearchVectorConfig,
SpatialReferenceSystem,
TemplateItem,
@@ -198,6 +199,7 @@ __all__ = [
"ImporterColumn",
"ImporterDuplicateField",
"Imported",
+ "PermissionRequest",
"Regexp",
"ImportTarget",
"ItemKey",
@@ -3466,7 +3468,8 @@ class ProfileTypeSummary(ProfileType):
class UserProfile(models.Model):
name = models.CharField(_("Name"), blank=True, default="", max_length=100)
profile_type = models.ForeignKey(
- ProfileType, verbose_name=_("Profile type"), on_delete=models.PROTECT
+ ProfileType, verbose_name=_("Profile type"), on_delete=models.PROTECT,
+ related_name="user_profiles"
)
areas = models.ManyToManyField(
"Area", verbose_name=_("Areas"), blank=True, related_name="profiles"
@@ -3521,7 +3524,9 @@ class UserProfile(models.Model):
def duplicate(self, **kwargs):
areas = [area for area in self.areas.all()]
- external_sources = [external_source for external_source in self.external_sources.all()]
+ external_sources = [
+ external_source for external_source in self.external_sources.all()
+ ]
new_item = self
new_item.pk = None
name = self.name
@@ -3541,50 +3546,95 @@ class UserProfile(models.Model):
new_item.external_sources.add(src)
return new_item
- def _generate_permission(self, ishtar_user, content_type, permission_request):
+ def _generate_permission(self, ishtar_user, content_type, permission_request,
+ permissions, permission_type):
item_ids = []
model_class = content_type.model_class()
- # TODO: gérer les paniers
if permission_request.include_associated_items:
- item_ids += model_class.filter(
+ item_ids += model_class.objects.filter(
ishtar_users__pk=ishtar_user.pk
).values_list("pk", flat=True)
+ item_ids += model_class.objects.filter(
+ history_creator_id=ishtar_user.pk
+ ).values_list("pk", flat=True)
+ if content_type.model == "find" and \
+ permission_type in ("view", "change"):
+ Find = apps.get_model("archaeological_finds", "Find")
+ k = "basket__shared_write_with" if permission_type == "change" \
+ else "basket__shared_with"
+ item_ids += list(
+ Find.objects.filter(**{k: ishtar_user}).values_list("pk", flat=True)
+ )
+ print("ishtar_common/models.py - 3561", item_ids, ishtar_user, content_type, permission_type)
if permission_request.include_upstream_items:
- # TODO....
- item_ids += model_class.get_ids_from_upper_permissions(ishtar_user.user_ptr.pk)
+ item_ids += model_class.get_ids_from_upper_permissions(
+ ishtar_user.user_ptr.pk, permissions
+ )
+ print("ishtar_common/models.py - 3566", item_ids, ishtar_user, content_type, permission_type)
if permission_request.request or permission_request.limit_to_attached_areas:
- # TODO
- pass
- query = model_class.objects
+ _get_item = get_item(
+ content_type.model_class(),
+ "", "", no_permission_check=True,
+ )
+ result = []
+ query = permission_request.request
+ if query:
+ if "{USER}" in query:
+ query = query.replace("{USER}", f"id:{ishtar_user.person_id}")
+ query = {"search_vector": query}
+ q = _get_item(None, return_query=True, ishtaruser=ishtar_user,
+ query=query)
+ result = list(q.values_list("pk", flat=True))
+ if permission_request.limit_to_attached_areas:
+ profile = ishtar_user.current_profile
+ if not profile: # no areas attached
+ return []
+ town_ids = list(profile.query_towns.values_list("pk", flat=True))
+ result_limit = []
+ get_limit_to_area_query = getattr(
+ model_class, "get_limit_to_area_query", None
+ )
+ q = get_limit_to_area_query(town_ids) if get_limit_to_area_query else None
+ if q:
+ result_limit = list(
+ model_class.objects.filter(q).values_list("pk", flat=True)
+ )
+ if result:
+ result = [pk for pk in result if pk in result_limit]
+ else:
+ result = result_limit
+ item_ids += result
+ print("ishtar_common/models.py - 3600", item_ids, ishtar_user, content_type, permission_type)
return item_ids
- def generate_permission(self, content_type):
+ def generate_permission(self, content_type, permission_type):
ishtar_user = self.person.ishtaruser
# add base permissions
for group in self.profile_type.groups.all():
- for perm in group.permissions.all():
+ for perm in group.permissions.filter(
+ codename__startswith=permission_type).all():
ishtar_user.user_ptr.user_permissions.add(perm)
q_has_perm = self.profile_type.groups.filter(
permissions__content_type=content_type,
- permissions__codename__contains="_own_"
+ permissions__codename__startswith=f"{permission_type}_own_",
)
if not q_has_perm.count(): # no permission to generate
return
permissions = []
for group in q_has_perm.all():
- permissions += list(group.permissions.values_list("pk", flat=True))
+ permissions += list(group.permissions.filter(
+ codename__contains=permission_type
+ ).all())
q_req = self.profile_type.permission_requests.filter(
model=content_type, active=True
)
item_ids = []
if not q_req.count():
# TODO v5: delete old behaviour
- """
print(f"WARNING: no permission request for content {content_type.name} and profile {self}")
print("Using old behaviour")
- """
model_class = content_type.model_class()
query = model_class.get_owns(user=ishtar_user, query=True, no_auth_check=True)
if query:
@@ -3594,13 +3644,15 @@ class UserProfile(models.Model):
else:
for perm_request in q_req.all():
item_ids += self._generate_permission(
- ishtar_user, content_type, perm_request
+ ishtar_user, content_type, perm_request, permissions,
+ permission_type
)
user_id = ishtar_user.user_ptr.pk
object_permissions = []
item_ids = list(set(item_ids))
permissions = list(set(permissions))
- for permission_id in permissions:
+ for permission in permissions:
+ permission_id = permission.pk
exclude = list(UserObjectPermission.objects.filter(
content_type_id=content_type.pk, permission_id=permission_id,
user_id=user_id
@@ -3900,7 +3952,8 @@ class IshtarUser(FullSearch):
for ct in content_types:
for profile in self.person.profiles.all():
- profile.generate_permission(ct)
+ for permission_type in ("view", "change", "delete"):
+ profile.generate_permission(ct, permission_type)
def full_label(self):
return self.person.full_label()
diff --git a/ishtar_common/models_common.py b/ishtar_common/models_common.py
index 28f5aba00..920b71584 100644
--- a/ishtar_common/models_common.py
+++ b/ishtar_common/models_common.py
@@ -17,11 +17,12 @@ import re
import shutil
import tempfile
import time
+from unidecode import unidecode
from django import forms
from django.apps import apps
from django.conf import settings
-from django.contrib.auth.models import User
+from django.contrib.auth.models import Permission, User
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.gis.db import models
@@ -49,6 +50,8 @@ from ishtar_common.utils import (
get_image_path,
get_columns_from_class,
human_date,
+ HistoryError,
+ SearchAltName,
SheetItem
)
from simple_history.models import HistoricalRecords as BaseHistoricalRecords
@@ -56,7 +59,8 @@ from simple_history.signals import (
post_create_historical_record,
pre_create_historical_record,
)
-from unidecode import unidecode
+
+from guardian.models import UserObjectPermission
from ishtar_common.data_importer import post_importer_action, ImporterError
from ishtar_common.model_managers import TypeManager
@@ -64,19 +68,20 @@ from ishtar_common.model_merging import merge_model_objects
from ishtar_common.models_imports import Import
from ishtar_common.templatetags.link_to_window import simple_link_to_window
from ishtar_common.utils import (
- get_cache,
+ cached_label_changed,
disable_for_loaddata,
+ duplicate_item,
+ external_id_changed,
+ GENERAL_TYPE_PREFIX,
get_all_field_names,
+ get_cache,
+ get_current_profile,
+ get_generated_id,
merge_tsvectors,
- cached_label_changed,
- external_id_changed,
+ OwnPerms,
post_save_geo,
post_save_geodata,
task,
- duplicate_item,
- get_generated_id,
- get_current_profile,
- OwnPerms
)
@@ -513,12 +518,6 @@ class GeneralType(Cached, models.Model):
res[parent_id].append((item["id"], item["label"]))
return res
- PREFIX = "&#x2502; "
- PREFIX_EMPTY = "&nbsp; "
- PREFIX_MEDIUM = "&#x251C; "
- PREFIX_LAST = "&#x2514; "
- PREFIX_CODES = ["\u2502", "\u251C", "\u2514"]
-
@classmethod
def _get_childs(
cls,
@@ -559,20 +558,20 @@ class GeneralType(Cached, models.Model):
cprefix -= 1
if not cprefix:
if (idx + 1) == total:
- p += cls.PREFIX_LAST
+ p += GENERAL_TYPE_PREFIX["prefix_last"]
else:
- p += cls.PREFIX_MEDIUM
+ p += GENERAL_TYPE_PREFIX["prefix_medium"]
elif is_last:
if mylast_of:
clast = mylast_of.pop(0)
if clast:
- p += cls.PREFIX_EMPTY
+ p += GENERAL_TYPE_PREFIX["prefix_empty"]
else:
- p += cls.PREFIX
+ p += GENERAL_TYPE_PREFIX["prefix"]
else:
- p += cls.PREFIX_EMPTY
+ p += GENERAL_TYPE_PREFIX["prefix_empty"]
else:
- p += cls.PREFIX
+ p += GENERAL_TYPE_PREFIX["prefix"]
lst.append((child[0], SafeText(p + child[1])))
clast_of = last_of[:]
clast_of.append(idx + 1 == total)
@@ -1149,17 +1148,6 @@ class FullSearch(models.Model):
return changed
-class SearchAltName(object):
- def __init__(
- self, search_key, search_query, extra_query=None, distinct_query=False, related_name=None
- ):
- self.search_key = search_key
- self.search_query = search_query
- self.extra_query = extra_query or {}
- self.distinct_query = distinct_query
- self.related_name = related_name
-
-
class Imported(models.Model):
imports = models.ManyToManyField(
Import, blank=True, related_name="imported_%(app_label)s_%(class)s",
@@ -1386,14 +1374,6 @@ class FixAssociated:
setattr(item, subkey, new_value)
-class HistoryError(Exception):
- def __init__(self, value):
- self.value = value
-
- def __str__(self):
- return repr(self.value)
-
-
class HistoricalRecords(BaseHistoricalRecords):
def get_extra_fields(self, model, fields):
def get_history_m2m(attr):
@@ -1561,6 +1541,7 @@ class BaseHistorizedItem(
EXTERNAL_ID_KEY = ""
EXTERNAL_ID_DEPENDENCIES = []
HISTORICAL_M2M = []
+ UPPER_PERMISSIONS = []
history_modifier = models.ForeignKey(
User,
@@ -1634,8 +1615,50 @@ class BaseHistorizedItem(
return cls._meta.verbose_name
@classmethod
- def get_ids_from_upper_permissions(cls, user_id):
- return []
+ def get_ids_from_upper_permissions(cls, user_id, base_permissions):
+ if not cls.UPPER_PERMISSIONS:
+ return []
+ ProfileType = apps.get_model("ishtar_common", "ProfileType")
+ item_ids = []
+ for model, attr in cls.UPPER_PERMISSIONS:
+ if isinstance(model, tuple):
+ app_label, model_name = model
+ model = apps.get_model(app_label, model_name)
+ permissions = list(set([
+ "_".join(permission.codename.split("_")[:-1])
+ + f"_{model._meta.model_name}"
+ for permission in base_permissions
+ ]))
+ q = ProfileType.objects.filter(
+ user_profiles__person__ishtaruser=user_id,
+ groups__permissions__codename__in=permissions
+ )
+ lst = []
+ if not q.count():
+ # no permissions associated for upstream model get direct attachement
+ lst = model.objects.filter(
+ ishtar_users__pk=user_id
+ ).values_list("pk", flat=True)
+ else:
+ perms = []
+ for codename in permissions:
+ perms += [
+ perm
+ for perm in Permission.objects.filter(
+ codename=codename).all()
+ ]
+ lst = []
+ for permission in perms:
+ lst += list(
+ UserObjectPermission.objects.filter(
+ permission=permission,
+ user_id=user_id
+ ).values_list("object_pk", flat=True)
+ )
+ item_ids += cls.objects.filter(
+ **{f"{attr}__in": lst}
+ ).values_list("pk", flat=True)
+ return list(set(item_ids))
def is_locked(self, user=None):
if not user:
diff --git a/ishtar_common/models_rest.py b/ishtar_common/models_rest.py
index 05b1206d1..c47b04168 100644
--- a/ishtar_common/models_rest.py
+++ b/ishtar_common/models_rest.py
@@ -10,6 +10,12 @@ from django.contrib.postgres.fields import ArrayField
from django.core.files import File
from django.utils.text import slugify
+from django.apps import apps
+from django.template import loader
+
+from ishtar_common.models_common import SheetFilter
+from ishtar_common.utils import ugettext_lazy as _
+
UnoCalc = None
ITALIC = None
if settings.USE_LIBREOFFICE:
@@ -19,30 +25,6 @@ if settings.USE_LIBREOFFICE:
except ImportError:
pass
-from django.apps import apps
-from django.template import loader
-
-from ishtar_common.models_common import SheetFilter
-from ishtar_common.utils import ugettext_lazy as _
-
-
-APP_CONTENT_TYPES = [
- ("archaeological_operations", "operation"),
- ("archaeological_context_records", "contextrecord"),
- ("archaeological_finds", "find"),
- ("archaeological_warehouse", "warehouse"),
- ("archaeological_files", "file"),
-]
-
-MAIN_CONTENT_TYPES = APP_CONTENT_TYPES + [
- ("archaeological_operations", "archaeologicalsite"),
- ("archaeological_warehouse", "container"),
-]
-
-MAIN_MODELS = dict(
- [(model_name, app_name) for app_name, model_name in MAIN_CONTENT_TYPES]
-)
-
class ApiUser(models.Model):
user_ptr = models.OneToOneField(
diff --git a/ishtar_common/rest.py b/ishtar_common/rest.py
index 0a831634a..699440f15 100644
--- a/ishtar_common/rest.py
+++ b/ishtar_common/rest.py
@@ -1,10 +1,6 @@
-import datetime
-import requests
-
from django.conf import settings
from django.db.models import Q
from django.http import HttpResponse
-from django.shortcuts import reverse
from django.utils.translation import activate, deactivate
from rest_framework import authentication, permissions, generics
@@ -13,7 +9,6 @@ from rest_framework.views import APIView
from ishtar_common import models_rest
from ishtar_common.models_common import GeneralType
-from ishtar_common.models_imports import Importer
from ishtar_common.views_item import get_item
diff --git a/ishtar_common/utils.py b/ishtar_common/utils.py
index 709f020a4..11ff45fa7 100644
--- a/ishtar_common/utils.py
+++ b/ishtar_common/utils.py
@@ -333,6 +333,83 @@ class SheetItem:
return
+def get_current_item_keys():
+ return (
+ ("file", apps.get_model("archaeological_files", "File")),
+ ("operation", apps.get_model("archaeological_operations", "Operation")),
+ ("site", apps.get_model("archaeological_operations", "ArchaeologicalSite")),
+ ("contextrecord",
+ apps.get_model("archaeological_context_records", "ContextRecord")),
+ ("warehouse", apps.get_model("archaeological_warehouse", "Warehouse")),
+ ("container", apps.get_model("archaeological_warehouse", "Container")),
+ ("find", apps.get_model("archaeological_finds", "Find")),
+ ("findbasket", apps.get_model("archaeological_finds", "FindBasket")),
+ ("treatmentfile", apps.get_model("archaeological_finds", "TreatmentFile")),
+ ("treatment", apps.get_model("archaeological_finds", "Treatment")),
+ ("administrativeact",
+ apps.get_model("archaeological_operations", "AdministrativeAct")),
+ ("administrativeactop",
+ apps.get_model("archaeological_operations", "AdministrativeAct")),
+ ("administrativeactfile",
+ apps.get_model("archaeological_operations", "AdministrativeAct")),
+ ("administrativeacttreatment",
+ apps.get_model("archaeological_operations", "AdministrativeAct")),
+ ("administrativeacttreatmentfile",
+ apps.get_model("archaeological_operations", "AdministrativeAct")),
+ )
+
+
+def get_current_item_keys_dict():
+ return dict(get_current_item_keys())
+
+
+API_APP_CONTENT_TYPES = [
+ ("archaeological_operations", "operation"),
+ ("archaeological_context_records", "contextrecord"),
+ ("archaeological_finds", "find"),
+ ("archaeological_warehouse", "warehouse"),
+ ("archaeological_files", "file"),
+]
+
+API_MAIN_CONTENT_TYPES = API_APP_CONTENT_TYPES + [
+ ("archaeological_operations", "archaeologicalsite"),
+ ("archaeological_warehouse", "container"),
+]
+
+API_MAIN_MODELS = dict(
+ [(model_name, app_name) for app_name, model_name in API_MAIN_CONTENT_TYPES]
+)
+
+
+class HistoryError(Exception):
+ def __init__(self, value):
+ self.value = value
+
+ def __str__(self):
+ return repr(self.value)
+
+
+class SearchAltName(object):
+ def __init__(
+ self, search_key, search_query, extra_query=None, distinct_query=False,
+ related_name=None
+ ):
+ self.search_key = search_key
+ self.search_query = search_query
+ self.extra_query = extra_query or {}
+ self.distinct_query = distinct_query
+ self.related_name = related_name
+
+
+GENERAL_TYPE_PREFIX = {
+ "prefix": "&#x2502; ",
+ "prefix_empty": "&nbsp; ",
+ "prefix_medium": "&#x251C; ",
+ "prefix_last": "&#x2514; ",
+ "prefix_codes": ["\u2502", "\u251C", "\u2514"]
+}
+
+
class OwnPerms:
"""
Manage special permissions for object's owner
diff --git a/ishtar_common/views.py b/ishtar_common/views.py
index 6c209a848..f01e848a0 100644
--- a/ishtar_common/views.py
+++ b/ishtar_common/views.py
@@ -79,6 +79,8 @@ from ishtar_common.utils_migrations import HOMEPAGE_DEFAULT, HOMEPAGE_TITLE
from ishtar_common.utils import (
clean_session_cache,
CSV_OPTIONS,
+ get_current_item_keys,
+ get_current_item_keys_dict,
get_field_labels_from_path,
get_person_gdpr_log,
get_random_item_image_link,
@@ -92,13 +94,7 @@ from ishtar_common.utils import (
from ishtar_common.widgets import JQueryAutoComplete
from ishtar_common import tasks
-convert_document = None
-if settings.USE_LIBREOFFICE:
- from ishtar_common.libreoffice import convert_document
-
from .views_item import (
- CURRENT_ITEM_KEYS,
- CURRENT_ITEM_KEYS_DICT,
check_permission,
display_item,
get_item,
@@ -108,6 +104,10 @@ from .views_item import (
get_short_html_detail,
)
+convert_document = None
+if settings.USE_LIBREOFFICE:
+ from ishtar_common.libreoffice import convert_document
+
logger = logging.getLogger(__name__)
@@ -700,7 +700,8 @@ def shortcut_menu(request):
def get_current_items(request):
currents = {}
- for key, model in CURRENT_ITEM_KEYS:
+ current_item_keys = get_current_item_keys()
+ for key, model in current_item_keys:
currents[key] = None
if key in request.session and request.session[key]:
try:
@@ -711,7 +712,8 @@ def get_current_items(request):
def unpin(request, item_type, cascade=False):
- if item_type not in CURRENT_ITEM_KEYS_DICT.keys():
+ current_item_keys_dict = get_current_item_keys_dict()
+ if item_type not in current_item_keys_dict.keys():
logger.warning("unpin unknow type: {}".format(item_type))
return HttpResponse("nok")
if "administrativeact" in item_type:
diff --git a/ishtar_common/views_item.py b/ishtar_common/views_item.py
index 23752c8a9..b5c63cc65 100644
--- a/ishtar_common/views_item.py
+++ b/ishtar_common/views_item.py
@@ -13,6 +13,7 @@ import requests
import subprocess # nosec
from tempfile import NamedTemporaryFile
+from django.apps import apps
from django.conf import settings
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
@@ -46,6 +47,7 @@ from django.utils.translation import (
deactivate,
pgettext_lazy,
)
+from guardian.models import UserObjectPermission
from tidylib import tidy_document as tidy
from unidecode import unidecode
from weasyprint import HTML, CSS
@@ -54,50 +56,24 @@ from weasyprint.fonts import FontConfiguration
from bootstrap_datepicker.widgets import DateField
from ishtar_common.utils import (
+ API_MAIN_MODELS,
check_model_access_control,
CSV_OPTIONS,
+ GENERAL_TYPE_PREFIX,
get_all_field_names,
- Round,
+ get_current_item_keys_dict,
+ get_current_profile,
+ HistoryError,
PRIVATE_FIELDS,
+ SearchAltName,
+ Round,
)
-from ishtar_common.models import get_current_profile, GeneralType, SearchAltName
-from ishtar_common.models_common import HistoryError
from .menus import Menu
-from . import models, models_rest
-from archaeological_files.models import File
-from archaeological_operations.models import (
- Operation,
- ArchaeologicalSite,
- AdministrativeAct,
-)
-from archaeological_context_records.models import ContextRecord
-from archaeological_finds.models import Find, FindBasket, Treatment, TreatmentFile
-from archaeological_warehouse.models import Warehouse, Container
-
logger = logging.getLogger(__name__)
ENCODING = settings.ENCODING or "utf-8"
-CURRENT_ITEM_KEYS = (
- ("file", File),
- ("operation", Operation),
- ("site", ArchaeologicalSite),
- ("contextrecord", ContextRecord),
- ("warehouse", Warehouse),
- ("container", Container),
- ("find", Find),
- ("findbasket", FindBasket),
- ("treatmentfile", TreatmentFile),
- ("treatment", Treatment),
- ("administrativeact", AdministrativeAct),
- ("administrativeactop", AdministrativeAct),
- ("administrativeactfile", AdministrativeAct),
- ("administrativeacttreatment", AdministrativeAct),
- ("administrativeacttreatmentfile", AdministrativeAct),
-)
-CURRENT_ITEM_KEYS_DICT = dict(CURRENT_ITEM_KEYS)
-
HIERARCHIC_LEVELS = 5
LIST_FIELDS = { # key: hierarchic depth
@@ -354,11 +330,11 @@ def show_source_item(request, source_id, model, name, base_dct, extra_dct):
source_id, external_id = int(source_id), int(external_id)
except ValueError:
raise Http404()
- models_rest.ApiExternalSource.objects.get()
+ ApiExternalSource = apps.get_model("ishtar_common", "ApiExternalSource")
# TODO: check permissions
try:
- src = models_rest.ApiExternalSource.objects.get(pk=source_id)
- except models_rest.ApiExternalSource.DoesNotExist:
+ src = ApiExternalSource.objects.get(pk=source_id)
+ except ApiExternalSource.DoesNotExist:
return HttpResponse("{}", content_type="text/plain")
url = src.url
if not url.endswith("/"):
@@ -618,6 +594,8 @@ def _get_values(request, val):
else:
vals = [val]
new_vals = []
+ Organization = apps.get_model("ishtar_common", "Organization")
+ Person = apps.get_model("ishtar_common", "Person")
for v in vals:
if callable(v):
try:
@@ -626,7 +604,7 @@ def _get_values(request, val):
continue
try:
if (
- not isinstance(v, (models.Person, models.Organization))
+ not isinstance(v, (Person, Organization))
and hasattr(v, "url")
and v.url
):
@@ -1144,7 +1122,7 @@ def _manage_dated_fields(dated_fields, dct):
def _clean_type_val(val):
- for prefix in GeneralType.PREFIX_CODES:
+ for prefix in GENERAL_TYPE_PREFIX["prefix_codes"]:
val = val.replace(prefix, "")
val = val.strip()
if val.startswith('"') and val.endswith('"'):
@@ -1210,6 +1188,7 @@ def _manage_hierarchic_fields(model, dct, and_reqs):
hierarchic_fields = HIERARCHIC_FIELDS[:]
if hasattr(model, "hierarchic_fields"):
hierarchic_fields += model.hierarchic_fields()
+ Town = apps.get_model("ishtar_common", "Town")
for reqs in dct.copy():
if type(reqs) not in (list, tuple):
reqs = [reqs]
@@ -1257,6 +1236,7 @@ def _manage_hierarchic_fields(model, dct, and_reqs):
and_reqs.append(main_req)
continue
+ Container = apps.get_model("archaeological_warehouse", "Container")
for val in vals:
attr = "cached_label__iexact"
if val.endswith("*"):
@@ -1277,7 +1257,7 @@ def _manage_hierarchic_fields(model, dct, and_reqs):
vals = [v.replace('"', "") for v in val.split(";")]
town_ids = []
for val in vals:
- q = models.Town.objects.filter(cached_label__iexact=val).values_list(
+ q = Town.objects.filter(cached_label__iexact=val).values_list(
"id", flat=True)
if not q.count():
continue
@@ -1287,7 +1267,7 @@ def _manage_hierarchic_fields(model, dct, and_reqs):
for rel_query in ("parents__", "children__"):
for idx in range(HIERARCHIC_LEVELS):
k = rel_query * (idx + 1) + "pk"
- q = models.Town.objects.filter(
+ q = Town.objects.filter(
**{k: town_id}).values_list("id", flat=True)
if not q.count():
break
@@ -1595,6 +1575,7 @@ def _manage_default_search(
dct, request, model, default_name, my_base_request, my_relative_session_names
):
pinned_search = ""
+ current_item_keys_dict = get_current_item_keys_dict()
pin_key = "pin-search-" + default_name
base_request = my_base_request if isinstance(my_base_request, dict) else {}
dct = {k: v for k, v in dct.items() if v}
@@ -1606,6 +1587,7 @@ def _manage_default_search(
): # an item is pinned
value = request.session[default_name]
if "basket-" in value:
+ FindBasket = apps.get_model("archaeological_finds", "FindBasket")
try:
dct = {"basket__pk": request.session[default_name].split("-")[-1]}
pinned_search = str(FindBasket.objects.get(pk=dct["basket__pk"]))
@@ -1630,9 +1612,9 @@ def _manage_default_search(
name in request.session
and request.session[name]
and "basket-" not in request.session[name]
- and name in CURRENT_ITEM_KEYS_DICT
+ and name in current_item_keys_dict
):
- up_model = CURRENT_ITEM_KEYS_DICT[name]
+ up_model = current_item_keys_dict[name]
try:
dct.update({key: request.session[name]})
up_item = up_model.objects.get(pk=dct[key])
@@ -2031,6 +2013,7 @@ def get_item(
no_link=False,
no_limit=False,
return_query=False,
+ ishtaruser=None, # could be provided when request is None
**dct,
):
available_perms = []
@@ -2044,7 +2027,8 @@ def get_item(
if "json" in data_type:
EMPTY = "[]"
- if data_type not in ("json", "csv", "json-image", "json-map", "json-stats"):
+ if not return_query and data_type not in (
+ "json", "csv", "json-image", "json-map", "json-stats"):
return HttpResponse(EMPTY, content_type="text/plain")
if data_type == "json-stats" and len(model.STATISTIC_MODALITIES) < 2:
@@ -2070,6 +2054,7 @@ def get_item(
own = True
if (
full == "shortcut"
+ and request
and "SHORTCUT_SEARCH" in request.session
and request.session["SHORTCUT_SEARCH"] == "own"
):
@@ -2077,13 +2062,23 @@ def get_item(
query_own = None
if own:
- q = models.IshtarUser.objects.filter(user_ptr=request.user)
- if not q.count():
- return HttpResponse(EMPTY, content_type="text/plain")
+ # TODO: verify alt_query_own
+ """
if alt_query_own:
query_own = getattr(model, alt_query_own)(q.all()[0])
else:
query_own = model.get_query_owns(q.all()[0])
+ print(query_own) # TODO - get old request to transform them
+ """
+ user_pk = request.user.pk if request else ishtaruser.pk
+ q = UserObjectPermission.objects.filter(
+ user_id=user_pk,
+ permission__codename=f"view_own_{model._meta.model_name}",
+ content_type=ContentType.objects.get_for_model(model)
+ )
+ query_own = Q(
+ pk__in=[int(pk) for pk in q.values_list("object_pk", flat=True)]
+ )
query_parameters = {}
@@ -2191,14 +2186,10 @@ def get_item(
request_keys.update(my_extra_request_keys)
# manage search on json fields and excluded fields
- if (
- search_form
- and request
- and request.user
- and getattr(request.user, "ishtaruser", None)
- ):
+ if search_form:
+ ishtaruser = request.user.ishtaruser if request else ishtaruser
available, __, excluded_fields, json_fields = search_form.check_custom_form(
- request.user.ishtaruser
+ ishtaruser
)
# for now no manage on excluded_fields: should we prevent search on
# some fields regarding the user concerned?
@@ -2218,10 +2209,12 @@ def get_item(
if "query" in dct:
request_items = dct["query"]
request_items["submited"] = True
- elif request.method == "POST":
+ elif request and request.method == "POST":
request_items = request.POST
- else:
+ elif request:
request_items = request.GET
+ else:
+ return HttpResponse(EMPTY, content_type="text/plain")
count = dct.get("count", False)
@@ -2271,7 +2264,7 @@ def get_item(
key = "name__icontains"
else:
key = "cached_label__icontains"
- dct[key] = request.GET.get("term", None)
+ dct[key] = (request and request.GET.get("term", None)) or None
try:
old = "old" in request_items and int(request_items["old"])
@@ -2332,11 +2325,12 @@ def get_item(
# manage default and pinned search and not bookmark
if (
not has_a_search
+ and request
and not request_items.get("search_vector", "")
and not request_items.get("submited", "")
and full != "shortcut"
):
- if data_type == "csv" and func_name in request.session:
+ if data_type == "csv" and func_name and func_name in request.session:
dct = request.session[func_name]
else:
# default search
@@ -2451,6 +2445,10 @@ def get_item(
# manage hierarchic in shortcut menu
if full == "shortcut":
+ File = apps.get_model("archaeological_files", "File")
+ Operation = apps.get_model("archaeological_operations", "Operation")
+ ContextRecord = apps.get_model("archaeological_context_records", "ContextRecord")
+ Find = apps.get_model("archaeological_finds", "Find")
ASSOCIATED_ITEMS = {
Operation: (File, "associated_file__pk"),
ContextRecord: (Operation, "operation__pk"),
@@ -2463,6 +2461,7 @@ def get_item(
if current:
dct = {upper_key: current}
query &= Q(**dct)
+ # print("ishtar_common/views_item.py - 2455")
# print(query, distinct_queries, base_query, exc_query, extras)
items = model.objects.filter(query)
for d_q in distinct_queries:
@@ -2540,6 +2539,7 @@ def get_item(
for col in cols:
query_table_cols += col.split("|")
+ Document = apps.get_model("ishtar_common", "Document")
# contextual (full, simple, etc.) col
contxt = full and "full" or "simple"
if (
@@ -2559,7 +2559,7 @@ def get_item(
table_cols.append("cached_label")
if data_type == "json-image":
prefix = ""
- if model != models.Document:
+ if model != Document:
prefix = "main_image__"
query_table_cols.append(prefix + "thumbnail")
table_cols.append(prefix + "thumbnail")
@@ -2584,7 +2584,7 @@ def get_item(
for k in request_items:
if not k.startswith("order["):
continue
- num = int(k.split("]")[0][len("order[") :])
+ num = int(k.split("]")[0][len("order["):])
if num not in sorts:
sorts[num] = ["", ""] # sign, col_num
if k.endswith("[dir]"):
@@ -2907,9 +2907,10 @@ def adapt_distant_search(params, src, model):
search_vector = params["search_vector"][0]
match = RE_FACET.search(search_vector)
final_search_vector = ""
+ ApiKeyMatch = apps.get_model("ishtar_common", "ApiKeyMatch")
while match:
key, value, __ = match.groups()
- q = models_rest.ApiKeyMatch.objects.filter(
+ q = ApiKeyMatch.objects.filter(
source=src,
search_model__model__iexact=model,
search_keys__contains=[key],
@@ -2929,10 +2930,11 @@ def adapt_distant_search(params, src, model):
def get_distant_item(request, model, external_source_id, data_type=None):
- # TODO: check permissions
+ # TODO: verify/test check permissions
+ ApiExternalSource = apps.get_model("ishtar_common", "ApiExternalSource")
try:
- src = models_rest.ApiExternalSource.objects.get(pk=external_source_id)
- except (models_rest.ApiExternalSource.DoesNotExist, ValueError):
+ src = ApiExternalSource.objects.get(pk=external_source_id)
+ except (ApiExternalSource.DoesNotExist, ValueError):
return HttpResponse("{}", content_type="text/plain")
url = src.url
url += reverse(f"api-search-{model}")
@@ -2954,7 +2956,7 @@ def get_distant_item(request, model, external_source_id, data_type=None):
"submited",
"data_type",
]
- app = models_rest.MAIN_MODELS[model]
+ app = API_MAIN_MODELS[model]
model_class = ContentType.objects.get(app_label=app, model=model).model_class()
bool_fields = model_class.REVERSED_BOOL_FIELDS + model_class.BOOL_FIELDS + model_class.CALLABLE_BOOL_FIELDS
is_empty_params = not any(
@@ -3010,9 +3012,10 @@ def external_export(request, source_id, model_name, slug):
if not url:
return HttpResponse('Unauthorized', status=401)
+ ApiExternalSource = apps.get_model("ishtar_common", "ApiExternalSource")
try:
- src = models_rest.ApiExternalSource.objects.get(pk=source_id)
- except (models_rest.ApiExternalSource.DoesNotExist, ValueError):
+ src = ApiExternalSource.objects.get(pk=source_id)
+ except (ApiExternalSource.DoesNotExist, ValueError):
return HttpResponse('Unauthorized', status=401)
url = src.url + url