summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
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
commit8b7c617c7ac7f752646bd48f39da97dcfbaaaab2 (patch)
treec0a931af3e3be561d3779811e70eeaf2c995f332
parent6d030c2e804b5c8f73a6e6aa9510ea23afaeeaa3 (diff)
downloadIshtar-8b7c617c7ac7f752646bd48f39da97dcfbaaaab2.tar.bz2
Ishtar-8b7c617c7ac7f752646bd48f39da97dcfbaaaab2.zip
Documents: better form - improve select2 widget to manage new
-rw-r--r--ishtar_common/backend.py8
-rw-r--r--ishtar_common/forms.py10
-rw-r--r--ishtar_common/forms_common.py73
-rw-r--r--ishtar_common/models.py2
-rw-r--r--ishtar_common/static/js/ishtar.js31
-rw-r--r--ishtar_common/urls.py5
-rw-r--r--ishtar_common/views.py13
-rw-r--r--ishtar_common/views_item.py2
-rw-r--r--ishtar_common/widgets.py14
-rw-r--r--scss/custom.scss5
10 files changed, 123 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('-')
diff --git a/scss/custom.scss b/scss/custom.scss
index 145cba658..c8a9ff7a1 100644
--- a/scss/custom.scss
+++ b/scss/custom.scss
@@ -59,6 +59,11 @@ dd > pre{
margin: 0;
}
+.form-group .select2-container--default.select2-container--focus .select2-selection--multiple,
+.form-control:focus {
+ border-color: rgba(82,168,236,.8);
+ box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6);
+}
.form-group li .form-control {
width: auto;