diff options
Diffstat (limited to 'ishtar_common')
-rw-r--r-- | ishtar_common/forms_common.py | 72 | ||||
-rw-r--r-- | ishtar_common/ishtar_menu.py | 15 | ||||
-rw-r--r-- | ishtar_common/migrations/0056_auto_20180601_1555.py | 2 | ||||
-rw-r--r-- | ishtar_common/migrations/0057_document_cache_related_label.py | 20 | ||||
-rw-r--r-- | ishtar_common/models.py | 41 | ||||
-rw-r--r-- | ishtar_common/templates/ishtar/sheet_document.html (renamed from ishtar_common/templates/ishtar/sheet_source.html) | 32 | ||||
-rw-r--r-- | ishtar_common/templates/ishtar/sheet_document_pdf.html | 14 | ||||
-rw-r--r-- | ishtar_common/templates/ishtar/sheet_document_window.html | 3 | ||||
-rw-r--r-- | ishtar_common/urls.py | 23 | ||||
-rw-r--r-- | ishtar_common/utils.py | 63 | ||||
-rw-r--r-- | ishtar_common/views.py | 43 | ||||
-rw-r--r-- | ishtar_common/wizards.py | 49 |
12 files changed, 287 insertions, 90 deletions
diff --git a/ishtar_common/forms_common.py b/ishtar_common/forms_common.py index 854fe2a71..0473d19b6 100644 --- a/ishtar_common/forms_common.py +++ b/ishtar_common/forms_common.py @@ -1052,27 +1052,35 @@ def get_image_help(): 'height': settings.IMAGE_MAX_SIZE[1]} -###################### -# Sources management # -###################### -class SourceForm(CustomForm, ManageOldType): +####################### +# Document management # +####################### + + +class DocumentForm(CustomForm, ManageOldType): form_label = _(u"Documentation informations") - form_admin_name = _("Source - General") + form_admin_name = _("Document - General") file_upload = True associated_models = {'source_type': models.SourceType} - title = forms.CharField(label=_(u"Title"), + title = forms.CharField(label=_(u"Title"), required=False, validators=[validators.MaxLengthValidator(200)]) - source_type = forms.ChoiceField(label=_(u"Source type"), choices=[]) + source_type = forms.ChoiceField(label=_(u"Source type"), choices=[], + required=False) + image = forms.ImageField( + label=_(u"Image"), help_text=mark_safe(get_image_help()), + max_length=255, required=False, widget=widgets.ImageFileInput()) + associated_file = forms.FileField( + label=_(u"File"), max_length=255, required=False) + associated_url = forms.URLField( + max_length=1000, required=False, + label=_(u"Numerical ressource (web address)")) reference = forms.CharField( label=_(u"Reference"), validators=[validators.MaxLengthValidator(100)], required=False) internal_reference = forms.CharField( label=_(u"Internal reference"), validators=[validators.MaxLengthValidator(100)], required=False) - associated_url = forms.URLField( - max_length=1000, required=False, - label=_(u"Numerical ressource (web address)")) receipt_date = forms.DateField(label=_(u"Receipt date"), required=False, widget=DatePicker) creation_date = forms.DateField(label=_(u"Creation date"), required=False, @@ -1089,16 +1097,24 @@ class SourceForm(CustomForm, ManageOldType): required=False) duplicate = forms.BooleanField(label=_(u"Has a duplicate"), required=False) - image = forms.ImageField( - label=_(u"Image"), help_text=mark_safe(get_image_help()), - max_length=255, required=False, widget=widgets.ImageFileInput()) TYPES = [ FieldType('source_type', models.SourceType), ] + def clean(self): + cleaned_data = self.cleaned_data + if not cleaned_data.get('title', None) and \ + not cleaned_data.get('image', None) and \ + not cleaned_data.get('associated_file', None) and \ + not cleaned_data.get('associated_url', None): + raise forms.ValidationError(_(u"You should at least fill one of " + u"this field: title, url, image or " + u"file.")) + return cleaned_data + -class SourceSelect(TableSelect): +class DocumentSelect(TableSelect): search_vector = forms.CharField(label=_(u"Full text search"), widget=widgets.SearchWidget) authors = forms.IntegerField( @@ -1118,10 +1134,30 @@ class SourceSelect(TableSelect): label=_(u"Additional informations")) duplicate = forms.NullBooleanField(label=_(u"Has a duplicate")) - def __init__(self, *args, **kwargs): - super(SourceSelect, self).__init__(*args, **kwargs) - self.fields['source_type'].choices = models.SourceType.get_types() - self.fields['source_type'].help_text = models.SourceType.get_help() + TYPES = [ + FieldType('source_type', models.SourceType), + ] + + +class DocumentFormSelection(forms.Form): + SEARCH_AND_SELECT = True + form_label = _(u"Document search") + associated_models = {'pk': models.Document} + currents = {'pk': models.Document} + + pk = forms.IntegerField( + label="", required=False, + widget=widgets.DataTable( + reverse_lazy('get-document'), DocumentSelect, + models.Document, + ), + validators=[models.valid_id(models.Document)]) + + def clean(self): + cleaned_data = self.cleaned_data + if 'pk' not in cleaned_data or not cleaned_data['pk']: + raise forms.ValidationError(_(u"You should select an item.")) + return cleaned_data class SourceDeletionForm(FinalForm): diff --git a/ishtar_common/ishtar_menu.py b/ishtar_common/ishtar_menu.py index 6e61ede74..fbd65c103 100644 --- a/ishtar_common/ishtar_menu.py +++ b/ishtar_common/ishtar_menu.py @@ -122,6 +122,21 @@ MENU_SECTIONS = [ model=models.Import, access_controls=['change_import']), ])), + (250, SectionItem( + 'document', _(u"Documentation / Images"), + childs=[ + MenuItem('document_search', + _(u"Search"), + model=models.Document, + access_controls=['view_document', + 'view_own_document']), + MenuItem('document_creation', + _(u"Creation"), + model=models.Document, + access_controls=['add_document', + 'add_own_document']), + ]) + ) ] """ SectionItem( diff --git a/ishtar_common/migrations/0056_auto_20180601_1555.py b/ishtar_common/migrations/0056_auto_20180601_1555.py index fee995df9..71def216e 100644 --- a/ishtar_common/migrations/0056_auto_20180601_1555.py +++ b/ishtar_common/migrations/0056_auto_20180601_1555.py @@ -10,7 +10,7 @@ class Migration(migrations.Migration): dependencies = [ ('archaeological_warehouse', '0021_auto_20180601_1555'), - ('archaeological_context_records', '0026_auto_20180601_1555'), + ('archaeological_context_records', '0027_auto_20180601_1555'), ('archaeological_finds', '0029_auto_20180601_1555'), ('archaeological_operations', '0032_auto_20180601_1555'), ('ishtar_common', '0055_auto_20180530_1900'), diff --git a/ishtar_common/migrations/0057_document_cache_related_label.py b/ishtar_common/migrations/0057_document_cache_related_label.py new file mode 100644 index 000000000..61a8ea329 --- /dev/null +++ b/ishtar_common/migrations/0057_document_cache_related_label.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.10 on 2018-06-04 11:52 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ishtar_common', '0056_auto_20180601_1555'), + ] + + operations = [ + migrations.AddField( + model_name='document', + name='cache_related_label', + field=models.TextField(blank=True, db_index=True, help_text='Cached value - do not edit', null=True, verbose_name='Related'), + ), + ] diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 76f16eaae..de34a2b9c 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -3093,9 +3093,21 @@ class Document(OwnPerms, ImageModel, FullSearch): 'treatment_files', 'treatments', 'finds', 'context_records', 'operations', 'sites', 'warehouses', ] - + SLUG = 'document' LINK_SPLIT = u"<||>" + TABLE_COLS = ['title', 'source_type', 'cache_related_label', 'authors', + 'associated_url'] + COL_LINK = ['associated_url'] + BASE_SEARCH_VECTORS = ['title', 'source_type__label', 'external_id', + 'reference', 'description', 'comment', + 'additional_information'] + PARENT_SEARCH_VECTORS = ['authors', ] + + BOOL_FIELDS = ['duplicate'] + + CACHED_LABELS = ['cache_related_label'] + title = models.TextField(_(u"Title"), blank=True, default='') associated_file = models.FileField( upload_to=get_image_path, blank=True, null=True, max_length=255) @@ -3137,13 +3149,9 @@ class Document(OwnPerms, ImageModel, FullSearch): duplicate = models.BooleanField(_(u"Has a duplicate"), default=False) associated_links = models.TextField(_(u"Symbolic links"), blank=True, null=True) - - TABLE_COLS = ['title', 'source_type', 'authors', 'associated_url'] - COL_LINK = ['associated_url'] - BASE_SEARCH_VECTORS = ['title', 'source_type__label', 'external_id', - 'reference', 'description', 'comment', - 'additional_information'] - PARENT_SEARCH_VECTORS = ['authors', ] + cache_related_label = models.TextField( + _(u"Related"), blank=True, null=True, db_index=True, + help_text=_(u"Cached value - do not edit")) class Meta: verbose_name = _(u"Document") @@ -3199,7 +3207,7 @@ class Document(OwnPerms, ImageModel, FullSearch): for related_model in self.RELATED_MODELS: q = getattr(self, related_model).all() if q.count(): - item = q.all()[0].item + item = q.all()[0] yield item._get_base_image_path() def _get_base_image_path(self): @@ -3259,7 +3267,7 @@ class Document(OwnPerms, ImageModel, FullSearch): for related_model in self.RELATED_MODELS: q = getattr(self, related_model).all() if q.count(): - item = q.all()[0].item + item = q.all()[0] base_path = item._get_base_image_path() new_path = base_path + u"/" + filename if not reference_path: @@ -3295,6 +3303,16 @@ class Document(OwnPerms, ImageModel, FullSearch): os.symlink(reference_path, new_path) return links + def related_label(self): + items = [] + for rel_attr in reversed(self.RELATED_MODELS): + for item in getattr(self, rel_attr).all(): + items.append(unicode(item)) + return u" ; ".join(items) + + def _generate_cache_related_label(self): + return self.related_label() + def save(self, *args, **kwargs): no_path_change = 'no_path_change' in kwargs \ and kwargs.pop('no_path_change') @@ -3309,6 +3327,9 @@ class Document(OwnPerms, ImageModel, FullSearch): self.save(no_path_change=True) +post_save.connect(cached_label_changed, sender=Document) + + class Arrondissement(models.Model): name = models.CharField(u"Nom", max_length=30) department = models.ForeignKey(Department, verbose_name=u"Département") diff --git a/ishtar_common/templates/ishtar/sheet_source.html b/ishtar_common/templates/ishtar/sheet_document.html index bedcb587e..3d48963a2 100644 --- a/ishtar_common/templates/ishtar/sheet_source.html +++ b/ishtar_common/templates/ishtar/sheet_document.html @@ -1,11 +1,18 @@ {% extends "ishtar/sheet.html" %} -{% load i18n window_field link_to_window %} +{% load i18n window_field window_header link_to_window %} + +{% block head_title %}<strong>{% trans "Document" %}</strong> - {{item.title}}{% endblock %} {% block content %} -{% block window_nav %}{% endblock %} +{% block window_nav %} +{# {% window_nav item window_id 'show-document' 'document_modify' %} #} +{% window_nav item window_id 'show-document' %} +{% endblock %} + + {% block general %} -{% if item.image %} +{% if item.images.count %} <div class="row"> <div class="offset-lg-4 col-lg-4 offset-md-3 col-md-6 offset-sm-1 col-sm-10 col-12"> <div class="card"> @@ -16,10 +23,6 @@ {% endif %} <div class="row"> - {% block related %} - {% trans "Related item" as related_item_label %} - {% field_flex related_item_label item.owner %} - {% endblock %} {% field_flex "Title" item.title %} {% field_flex "Index" item.index %} {% field_flex "Source type" item.source_type %} @@ -40,6 +43,19 @@ {% field_flex_full "Authors" item.authors|add_links:'person' %} </div> -{% endblock %} +{% block related %} +<h2>{% trans "Related items" %}</h2> +{% if item.operations.count %} +{% field_flex_full "Sites" item.sites|add_links %} +{% field_flex_full "Operations" item.operations|add_links %} +{% field_flex_full "Context records" item.context_records|add_links %} +{% field_flex_full "Finds" item.finds|add_links %} +{% field_flex_full "Treatments" item.treatments|add_links %} +{% field_flex_full "Treatment files" item.treatment_files|add_links %} +{% field_flex_full "Warehouses" item.warehouses|add_links %} +{% endif %} + {% endblock %} +{% endblock %} +{% endblock %} diff --git a/ishtar_common/templates/ishtar/sheet_document_pdf.html b/ishtar_common/templates/ishtar/sheet_document_pdf.html new file mode 100644 index 000000000..3a40fac44 --- /dev/null +++ b/ishtar_common/templates/ishtar/sheet_document_pdf.html @@ -0,0 +1,14 @@ +{% extends "ishtar/sheet_document.html" %} +{% block header %} +{% endblock %} +{% block main_head %} +{{ block.super }} +<div id="pdfheader"> +Ishtar – {{APP_NAME}} – {{item}} +</div> +{% endblock %} +{%block head_sheet%}{%endblock%} +{%block main_foot%} +</body> +</html> +{%endblock%} diff --git a/ishtar_common/templates/ishtar/sheet_document_window.html b/ishtar_common/templates/ishtar/sheet_document_window.html new file mode 100644 index 000000000..c739722e7 --- /dev/null +++ b/ishtar_common/templates/ishtar/sheet_document_window.html @@ -0,0 +1,3 @@ +{% extends "ishtar/sheet_document.html" %} +{% block main_head %}{%endblock%} +{% block main_foot %}{%endblock%} diff --git a/ishtar_common/urls.py b/ishtar_common/urls.py index a64e6ab24..787c72aba 100644 --- a/ishtar_common/urls.py +++ b/ishtar_common/urls.py @@ -24,8 +24,8 @@ from django.views.generic import TemplateView from menus import Menu -from ishtar_common import views -from ishtar_common.wizards import check_rights +from ishtar_common import views, models +from ishtar_common.utils import check_rights, get_urls_for_model # be careful: each check_rights must be relevant with ishtar_menu @@ -223,8 +223,27 @@ urlpatterns += [ name='hide-shortcut-menu'), url(r'show-shortcut-menu/$', views.show_shortcut_menu, name='show-shortcut-menu'), + + url(r'document_search/(?P<step>.+)?$', + check_rights(['view_document', 'view_own_document'])( + views.document_search_wizard), + name='document_search'), + url(r'document_creation/(?P<step>.+)?$', + check_rights(['add_document', 'add_own_document'])( + views.NewDocumentFormView.as_view()), + name='document_creation'), + url(r'document_modification/(?P<step>.+)?$', + check_rights(['change_document', 'change_own_document'])( + views.NewDocumentFormView.as_view()), + name='document_modification'), +] + +urlpatterns += get_urls_for_model(models.Document, views) + +urlpatterns += [ url(r'(?P<action_slug>' + actions + r')/$', views.action, name='action'), ] + """ url(r'operation_source_search/(?P<step>.+)?$', check_rights(['view_operation', 'view_own_operation'])( diff --git a/ishtar_common/utils.py b/ishtar_common/utils.py index 6c77563ef..0b5b1bd57 100644 --- a/ishtar_common/utils.py +++ b/ishtar_common/utils.py @@ -30,6 +30,7 @@ import tempfile from django import forms from django.conf import settings +from django.conf.urls import url from django.contrib.contenttypes.models import ContentType from django.contrib.gis.geos import GEOSGeometry from django.contrib.sessions.backends.db import SessionStore @@ -56,6 +57,52 @@ class BColors: UNDERLINE = '\033[4m' +def check_rights(rights=[], redirect_url='/'): + """ + Decorator that checks the rights to access the view. + """ + + def decorator(view_func): + def _wrapped_view(request, *args, **kwargs): + if not rights: + return view_func(request, *args, **kwargs) + if hasattr(request.user, 'ishtaruser'): + if request.user.ishtaruser.has_right('administrator', + request.session): + kwargs['current_right'] = 'administrator' + return view_func(request, *args, **kwargs) + for right in rights: + # be careful to put the more permissive rights first + # if granted it can allow more + if request.user.ishtaruser.has_right(right, + request.session): + kwargs['current_right'] = right + return view_func(request, *args, **kwargs) + put_session_message( + request.session.session_key, + _(u"You don't have sufficient permissions to do this action."), + 'warning' + ) + return HttpResponseRedirect(redirect_url) + return _wrapped_view + return decorator + + +def check_rights_condition(rights): + """ + To be used to check in wizard condition_dict + """ + def func(self): + request = self.request + if request.user.ishtaruser.has_right('administrator', request.session): + return True + for right in rights: + if request.user.ishtaruser.has_right(right, request.session): + return True + return False + return func + + class MultiValueDict(BaseMultiValueDict): def get(self, *args, **kwargs): v = super(MultiValueDict, self).getlist(*args, **kwargs) @@ -703,3 +750,19 @@ def create_default_json_fields(model): } ) + +def get_urls_for_model(model, views): + """ + Generate get and show url for a model + """ + urls = [ + url(r'show-{}(?:/(?P<pk>.+))?/(?P<type>.+)?$'.format(model.SLUG), + check_rights(['view_' + model.SLUG, 'view_own_' + model.SLUG])( + getattr(views, 'show_' + model.SLUG)), + name="show-" + model.SLUG), + url(r'get-{}/(?P<type>.+)?$'.format(model.SLUG), + check_rights(['view_' + model.SLUG, 'view_own_' + model.SLUG])( + getattr(views, 'get_' + model.SLUG)), + name="get-" + model.SLUG), + ] + return urls diff --git a/ishtar_common/views.py b/ishtar_common/views.py index 5d2b442bf..187712676 100644 --- a/ishtar_common/views.py +++ b/ishtar_common/views.py @@ -2483,12 +2483,49 @@ class OrganizationPersonEdit(LoginRequiredMixin, UpdateView): def get_success_url(self): return reverse('organization_person_edit', args=[self.object.pk]) + +# documents + +show_document = show_item(models.Document, 'document') +get_document = get_item(models.Document, 'get_document', 'document') + + +document_search_wizard = wizards.SearchWizard.as_view( + [('selec-document_search', forms.DocumentFormSelection)], + label=_(u"Document: search"), + url_name='document_search', +) + + +class NewDocumentFormView(IshtarMixin, LoginRequiredMixin, + FormView): + form_class = forms.DocumentForm + template_name = 'ishtar/form.html' + success_url = 'document_search' + """ +class DocumentSelectMixin(IshtarMixin, LoginRequiredMixin, + FormView): + form_class = forms.PersonMergeFormSelection + template_name = 'ishtar/form.html' + redir_url = 'person_manual_merge_items' + + def form_valid(self, form): + self.item = form.get_item() + return super(ManualMergeMixin, self).form_valid(form) + + def get_success_url(self): + return reverse(self.redir_url, args=[self.item.pk]) -show_operationsource = show_item(models.OperationSource, 'operationsource') -get_operationsource = get_item(models.OperationSource, 'get_operationsource', - 'operationsource') +class DocumentSelectView(DocumentSelectMixin, IshtarMixin, LoginRequiredMixin, + FormView): + page_name = _(u"Merge persons") + current_url = 'person-manual-merge' + redir_url = 'person_manual_merge_items' +""" + +""" # operation sources operation_source_search_wizard = SearchWizard.as_view([ diff --git a/ishtar_common/wizards.py b/ishtar_common/wizards.py index b98c698ac..69f7c4c8e 100644 --- a/ishtar_common/wizards.py +++ b/ishtar_common/wizards.py @@ -44,57 +44,10 @@ from django.utils.safestring import mark_safe from ishtar_common import models from ishtar_common.forms import CustomForm, reverse_lazy -from ishtar_common.utils import get_all_field_names, MultiValueDict, \ - put_session_message +from ishtar_common.utils import get_all_field_names, MultiValueDict logger = logging.getLogger(__name__) - -def check_rights(rights=[], redirect_url='/'): - """ - Decorator that checks the rights to access the view. - """ - - def decorator(view_func): - def _wrapped_view(request, *args, **kwargs): - if not rights: - return view_func(request, *args, **kwargs) - if hasattr(request.user, 'ishtaruser'): - if request.user.ishtaruser.has_right('administrator', - request.session): - kwargs['current_right'] = 'administrator' - return view_func(request, *args, **kwargs) - for right in rights: - # be careful to put the more permissive rights first - # if granted it can allow more - if request.user.ishtaruser.has_right(right, - request.session): - kwargs['current_right'] = right - return view_func(request, *args, **kwargs) - put_session_message( - request.session.session_key, - _(u"You don't have sufficient permissions to do this action."), - 'warning' - ) - return HttpResponseRedirect(redirect_url) - return _wrapped_view - return decorator - - -def check_rights_condition(rights): - """ - To be used to check in wizard condition_dict - """ - def func(self): - request = self.request - if request.user.ishtaruser.has_right('administrator', request.session): - return True - for right in rights: - if request.user.ishtaruser.has_right(right, request.session): - return True - return False - return func - # buggy and unecessary at least for the moment... """ def _check_right(step, condition=True): |