diff options
author | Étienne Loks <etienne.loks@iggdrasil.net> | 2020-06-10 18:47:17 +0200 |
---|---|---|
committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2021-02-28 12:15:21 +0100 |
commit | 8b7c617c7ac7f752646bd48f39da97dcfbaaaab2 (patch) | |
tree | c0a931af3e3be561d3779811e70eeaf2c995f332 /ishtar_common | |
parent | 6d030c2e804b5c8f73a6e6aa9510ea23afaeeaa3 (diff) | |
download | Ishtar-8b7c617c7ac7f752646bd48f39da97dcfbaaaab2.tar.bz2 Ishtar-8b7c617c7ac7f752646bd48f39da97dcfbaaaab2.zip |
Documents: better form - improve select2 widget to manage new
Diffstat (limited to 'ishtar_common')
-rw-r--r-- | ishtar_common/backend.py | 8 | ||||
-rw-r--r-- | ishtar_common/forms.py | 10 | ||||
-rw-r--r-- | ishtar_common/forms_common.py | 73 | ||||
-rw-r--r-- | ishtar_common/models.py | 2 | ||||
-rw-r--r-- | ishtar_common/static/js/ishtar.js | 31 | ||||
-rw-r--r-- | ishtar_common/urls.py | 5 | ||||
-rw-r--r-- | ishtar_common/views.py | 13 | ||||
-rw-r--r-- | ishtar_common/views_item.py | 2 | ||||
-rw-r--r-- | ishtar_common/widgets.py | 14 |
9 files changed, 118 insertions, 40 deletions
diff --git a/ishtar_common/backend.py b/ishtar_common/backend.py index cef1f0fa2..916fe6e4a 100644 --- a/ishtar_common/backend.py +++ b/ishtar_common/backend.py @@ -36,11 +36,9 @@ class ObjectPermBackend(ModelBackend): if not user_obj.is_authenticated(): return False if not model: - if user_obj.is_staff: - # let it manage by the default backend - return super(ObjectPermBackend, self).has_perm( - user_obj=user_obj, perm=perm, obj=obj) - return False + # let it manage by the default backend + return super(ObjectPermBackend, self).has_perm( + user_obj=user_obj, perm=perm, obj=obj) try: ishtar_user = models.IshtarUser.objects.get(user_ptr=user_obj) except ObjectDoesNotExist: diff --git a/ishtar_common/forms.py b/ishtar_common/forms.py index 66b5c3796..6193c72c4 100644 --- a/ishtar_common/forms.py +++ b/ishtar_common/forms.py @@ -624,6 +624,8 @@ class IshtarForm(forms.Form, BSForm): CONDITIONAL_FIELDS = [] # dynamic conditions on field display PROFILE_FILTER = {} # profile key associated to field list HEADERS = {} # field key associated to FormHeader instance + # permission check for widget options, ex: forms_common.DocumentForm + OPTIONS_PERMISSIONS = {} SITE_KEYS = {} # archaeological sites fields and associated translation key # to manage translation @@ -650,6 +652,14 @@ class IshtarForm(forms.Form, BSForm): self.fields[site_key].label = profile.get_site_label( self.SITE_KEYS[site_key] ) + for field_name, permissions, options in self.OPTIONS_PERMISSIONS: + if field_name not in self.fields or not getattr(self, "user", None): + continue + if not [True for permission in permissions + if self.user.has_perm(permission)]: + continue + for option, value in options.items(): + setattr(self.fields[field_name].widget, option, value) self._post_init() def _init_type(self, field): diff --git a/ishtar_common/forms_common.py b/ishtar_common/forms_common.py index 20cf0b613..f389ab84d 100644 --- a/ishtar_common/forms_common.py +++ b/ishtar_common/forms_common.py @@ -36,6 +36,7 @@ from django.core.files import File from django.forms.formsets import formset_factory from django.forms.models import BaseModelFormSet, BaseFormSet from django.shortcuts import reverse +from django.utils.text import slugify from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _, pgettext @@ -1229,12 +1230,38 @@ def get_image_help(): ####################### +class AddGenericForm(ManageOldType, NewItemForm): + model = None # to be defined + label = forms.CharField(label=_("Label"), max_length=200) + + def clean_label(self): + value = self.cleaned_data.get('label', None).strip() + if self.model.objects.filter(label=value).count(): + raise forms.ValidationError(_("This value already exist")) + return value + + def save(self, user): + label = self.cleaned_data['label'] + base_slug = slugify(label) + slug = base_slug + idx = 0 + while self.model.objects.filter(txt_idx=slug).count(): + idx += 1 + slug = base_slug + "-" + str(idx) + return self.model.objects.create(label=label, txt_idx=slug) + + +class AddDocumentTagForm(AddGenericForm): + model = models.DocumentTag + form_label = _("Document tag") + + class DocumentForm(forms.ModelForm, CustomForm, ManageOldType): form_label = _("Documentation") form_admin_name = _("Document - General") form_slug = "document-general" file_upload = True - extra_form_modals = ["author", "person", "organization"] + extra_form_modals = ["author", "person", "organization", "documenttag"] associated_models = {'source_type': models.SourceType, 'support_type': models.SupportType, 'publisher': models.Organization, @@ -1259,9 +1286,9 @@ class DocumentForm(forms.ModelForm, CustomForm, ManageOldType): container_ref = widgets.ModelJQueryAutocompleteField( label=_("Reference container"), model=Container, required=False) - authors = widgets.ModelJQueryAutocompleteField( - model=models.Author, multiple=True, label=_("Authors"), new=True, - long_widget=True, required=False) + authors = widgets.Select2MultipleField( + label=_("Authors"), required=False, model=models.Author, + remote="autocomplete-author") publisher = forms.IntegerField( label=_("Publisher"), widget=widgets.JQueryAutoComplete( @@ -1270,15 +1297,16 @@ class DocumentForm(forms.ModelForm, CustomForm, ManageOldType): args=[models.organization_type_pks_lazy( settings.ISHTAR_SLUGS["document-editor"])]), limit={ - 'organization_type': models.organization_type_pks_lazy( - ['responsible_planning_service']) - }, + 'organization_type': [models.organization_type_pks_lazy( + settings.ISHTAR_SLUGS["document-editor"])]}, tips=models.get_publisher_label, - associated_model=models.Organization, new=True), + associated_model=models.Organization), validators=[models.valid_id(models.Organization)], required=False) - license = widgets.Select2MultipleField( - label=_("Licences"), required=False) - tag = widgets.Select2MultipleField(label=_("Tags"), required=False) + licenses = widgets.Select2MultipleField( + label=_("Licences"), required=False, model=models.LicenseType) + tags = widgets.Select2MultipleField( + label=_("Tags"), required=False, model=models.DocumentTag, + remote="autocomplete-documenttag") language = widgets.ModelChoiceField( model=models.Language, label=_("Language"), choices=[], required=False) @@ -1335,8 +1363,8 @@ class DocumentForm(forms.ModelForm, CustomForm, ManageOldType): FieldType('support_type', models.SupportType), FieldType('format_type', models.Format), FieldType('language', models.Language), - FieldType('licence', models.LicenseType, is_multiple=True), - FieldType('tag', models.DocumentTag, is_multiple=True), + FieldType('licences', models.LicenseType, is_multiple=True), + FieldType('tags', models.DocumentTag, is_multiple=True), ] class Meta: @@ -1344,10 +1372,10 @@ class DocumentForm(forms.ModelForm, CustomForm, ManageOldType): fields = [ 'title', 'source_type', 'reference', 'internal_reference', 'format_type', 'support_type', 'scale', - 'image', 'associated_file', 'associated_url', 'tag', + 'image', 'associated_file', 'associated_url', 'tags', 'authors', 'receipt_date', 'receipt_date_in_documentation', 'creation_date', - 'publisher', 'language', 'isbn', 'issn', 'license', + 'publisher', 'language', 'isbn', 'issn', 'licenses', 'container', "container_ref", 'source', 'source_free_input', 'comment', 'description', 'additional_information', 'duplicate' @@ -1364,6 +1392,12 @@ class DocumentForm(forms.ModelForm, CustomForm, ManageOldType): 'comment': FormHeader(_("Advanced"), collapse=True), 'finds': FormHeader(_("Related items")), } + OPTIONS_PERMISSIONS = [ + # field name, permission, options + ("tags", ("ishtar_common.add_documenttag",), {"new": True}), + ("authors", ("ishtar_common.add_author",), {"new": True}), + ("publisher", ("ishtar_common.add_organization",), {"new": True}), + ] def __init__(self, *args, **kwargs): main_items_fields = {} @@ -1403,6 +1437,15 @@ class DocumentForm(forms.ModelForm, CustomForm, ManageOldType): raise forms.ValidationError(_("A document has to be attached at least " "to one item")) + def clean_publisher(self): + if not self.cleaned_data.get("publisher", None): + return + try: + return models.Organization.objects.get( + pk=self.cleaned_data["publisher"]) + except models.Organization.DoesNotExist: + return + def save(self, commit=True): if not self.cleaned_data.get('authors', None): self.cleaned_data['authors'] = [] diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 58385c1dc..6364f69e8 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -5327,6 +5327,7 @@ class LicenseType(GeneralType): class DocumentTag(GeneralType): + SLUG = "documenttag" class Meta: verbose_name = _("Document tag") verbose_name_plural = _("Document tags") @@ -5384,6 +5385,7 @@ class Document(BaseHistorizedItem, QRCodeItem, OwnPerms, ImageModel, SearchVectorConfig("additional_information", "local"), ] PARENT_SEARCH_VECTORS = ['authors', ] + M2M_SEARCH_VECTORS = [SearchVectorConfig("tags__label"), ] BOOL_FIELDS = ['duplicate'] diff --git a/ishtar_common/static/js/ishtar.js b/ishtar_common/static/js/ishtar.js index da1a47de6..655a91eb4 100644 --- a/ishtar_common/static/js/ishtar.js +++ b/ishtar_common/static/js/ishtar.js @@ -707,6 +707,7 @@ function save_and_close_window_data(main_page, name_label, var cur_item; var cur_item_pk; var id_item; + if (main_page){ cur_item = $(main_page).find("#" + name_label); cur_item_pk = $(main_page).find("#" + name_pk); @@ -716,11 +717,27 @@ function save_and_close_window_data(main_page, name_label, cur_item_pk = $("#" + name_pk); id_item = $("#id_" + name_label); } - cur_item.val(item_name); - cur_item.change(); - cur_item_pk.val(item_pk); - cur_item_pk.change(); - id_item.change(); + if (name_label != name_pk){ // custom + cur_item.val(item_name); + cur_item.change(); + cur_item_pk.val(item_pk); + cur_item_pk.change(); + id_item.change(); + } else { // select2 + var data = [{ + "id": item_pk, "value": item_name + }]; + + var new_option = new Option(item_name, item_pk, true, true); + $('#' + name_pk).append(new_option).trigger('change'); + + $('#' + name_pk).trigger({ + type: 'select2:select', + params: { + data: data + } + }); + } } function save_and_close_window_many(name_label, name_pk, item_name, item_pk){ @@ -739,8 +756,8 @@ function save_and_close_window_many_data(main_page, name_label, cur_item = $(main_page).find("#"+name_label); cur_item_pk = $(main_page).find("#"+name_pk); } else { - cur_item = $("#"+name_label); - cur_item_pk = $("#"+name_pk); + cur_item = $("#" + name_label); + cur_item_pk = $("#" + name_pk); } var lbl_ = cur_item; var val_ = cur_item_pk; diff --git a/ishtar_common/urls.py b/ishtar_common/urls.py index 03550302a..79c9676ed 100644 --- a/ishtar_common/urls.py +++ b/ishtar_common/urls.py @@ -333,6 +333,11 @@ urlpatterns += [ check_rights(['change_document', 'change_own_document'])( views.QADocumentDuplicateFormView.as_view()), name='document-qa-duplicate'), + url(r'autocomplete-documenttag/$', + views.autocomplete_documenttag, name='autocomplete-documenttag'), + url(r'new-documenttag/(?:(?P<parent_name>[^/]+)/)?' + r'(?:(?P<limits>[^/]+)/)?$', + views.new_document_tag, name='new-documenttag'), url(r'^qa-not-available(?:/(?P<context>[0-9a-z-]+))?/$', views.QANotAvailable.as_view(), name='qa-not-available'), diff --git a/ishtar_common/views.py b/ishtar_common/views.py index 3e5ef48cf..64219ba66 100644 --- a/ishtar_common/views.py +++ b/ishtar_common/views.py @@ -1928,6 +1928,9 @@ class OrganizationPersonEdit(LoginRequiredMixin, UpdateView): # documents +new_document_tag = new_qa_item(models.DocumentTag, forms.AddDocumentTagForm) +autocomplete_documenttag = get_autocomplete_generic(models.DocumentTag) + show_document = show_item(models.Document, 'document') get_document = get_item(models.Document, 'get_document', 'document', search_form=forms.DocumentSelect) @@ -1956,7 +1959,7 @@ class DocumentFormMixin(IshtarMixin, LoginRequiredMixin): class DocumentCreateView(DocumentFormMixin, CreateView): - page_name = _(u"Document creation") + page_name = _("Document creation") def get_form_kwargs(self): kwargs = super(DocumentCreateView, self).get_form_kwargs() @@ -1998,12 +2001,7 @@ class DocumentSelectView(IshtarMixin, LoginRequiredMixin, class DocumentEditView(DocumentFormMixin, UpdateView): - page_name = _(u"Document modification") - - def get_context_data(self, **kwargs): - data = super(DocumentEditView, self).get_context_data(**kwargs) - data["extra_form_modals"] = ['author', 'person', 'organization'] - return data + page_name = _("Document modification") def get_form_kwargs(self): kwargs = super(DocumentEditView, self).get_form_kwargs() @@ -2034,6 +2032,7 @@ class DocumentEditView(DocumentFormMixin, UpdateView): if related_item.main_image == document: initial[key] = True kwargs['initial'] = initial + kwargs["user"] = self.request.user return kwargs diff --git a/ishtar_common/views_item.py b/ishtar_common/views_item.py index 1ef98e3c2..8b066194f 100644 --- a/ishtar_common/views_item.py +++ b/ishtar_common/views_item.py @@ -145,8 +145,8 @@ def new_qa_item(model, frm, many=False, template="ishtar/forms/qa_new_item.html"): def func(request, parent_name, limits=''): model_name = model._meta.object_name + not_permitted_msg = ugettext("Operation not permitted.") if not check_permission(request, 'add_' + model_name.lower()): - not_permitted_msg = ugettext("Operation not permitted.") return HttpResponse(not_permitted_msg) slug = model.SLUG if model.SLUG == "site": diff --git a/ishtar_common/widgets.py b/ishtar_common/widgets.py index e5f4c67b1..7d954f378 100644 --- a/ishtar_common/widgets.py +++ b/ishtar_common/widgets.py @@ -270,10 +270,13 @@ class Select2Base(Select2Media): if self.new: html = u"<div class='input-group'>" url_new = 'new-' + self.model.SLUG - url_new = reverse(url_new) - new = u'<span class="input-group-append">' \ - u'<a href="#" class="add-button input-group-text" ' \ - u'onclick="open_window(\'%s\');">+</a></span></div>' % url_new + url_new = reverse(url_new, args=["id_" + name]) + # WARNING: the modal for the form must be in the main template + # "extra_form_modals" list is used for that in form or view + new = """<span class="input-group-append">"""\ + """<a href="#" class="add-button input-group-text" """\ + """onclick="dt_qa_open('{}', 'modal-dynamic-form-{}');">"""\ + """+</a></span></div>""".format(url_new, self.model.SLUG) html += super(Select2Base, self).render(name, value, attrs) html += new @@ -641,7 +644,8 @@ class JQueryAutoComplete(forms.TextInput): try: source = "'" + str(self.source) + "'" except: - raise ValueError('source type is not valid') + raise ValueError('{} source type is not valid'.format( + self.source)) dynamic_limit = [] for lim in self.dynamic_limit: field_ids = field_id.split('-') |