diff options
Diffstat (limited to 'ishtar_common')
| -rw-r--r-- | ishtar_common/admin.py | 20 | ||||
| -rw-r--r-- | ishtar_common/forms.py | 116 | ||||
| -rw-r--r-- | ishtar_common/forms_common.py | 70 | ||||
| -rw-r--r-- | ishtar_common/lookups.py | 9 | ||||
| -rw-r--r-- | ishtar_common/migrations/0208_auto_20201126_1516.py (renamed from ishtar_common/migrations/0208_auto_20201126_1217.py) | 12 | ||||
| -rw-r--r-- | ishtar_common/models.py | 9 | ||||
| -rw-r--r-- | ishtar_common/static/js/ishtar.js | 19 | ||||
| -rw-r--r-- | ishtar_common/templates/blocks/bs_form_snippet.html | 2 | ||||
| -rw-r--r-- | ishtar_common/templates/ishtar/forms/document.html | 20 | ||||
| -rw-r--r-- | ishtar_common/templatetags/table_form.py | 5 | ||||
| -rw-r--r-- | ishtar_common/views.py | 15 | 
11 files changed, 271 insertions, 26 deletions
| diff --git a/ishtar_common/admin.py b/ishtar_common/admin.py index b5ff67567..26ceb01cd 100644 --- a/ishtar_common/admin.py +++ b/ishtar_common/admin.py @@ -982,8 +982,8 @@ class GeneralTypeAdmin(ImportActionAdmin, ImportJSONActionAdmin):  general_models = [models.OrganizationType, models.SourceType, -                  models.AuthorType, models.TitleType, models.Format, -                  models.SupportType, models.PersonType, models.LicenseType, +                  models.AuthorType, models.TitleType, +                  models.PersonType, models.LicenseType,                    models.Language]  for model in general_models:      admin_site.register(model, GeneralTypeAdmin) @@ -1081,6 +1081,22 @@ class CreateDepartmentActionAdmin(GeneralTypeAdmin):              {'form': form, 'current_action': 'create_area'}          ) +@admin.register(models.SupportType, site=admin_site) +class SupportType(GeneralTypeAdmin): +    model = models.SupportType +    form = make_ajax_form(model, {'document_types': 'source_type'}) + + +@admin.register(models.Format, site=admin_site) +class Format(GeneralTypeAdmin): +    model = models.Format +    form = make_ajax_form(model, {'document_types': 'source_type'}) + + +@admin.register(models.DocumentTag, site=admin_site) +class DocumentTag(MergeActionAdmin, GeneralTypeAdmin): +    pass +  class AreaAdmin(CreateDepartmentActionAdmin):      list_display = ('label', 'reference', 'parent', 'available') diff --git a/ishtar_common/forms.py b/ishtar_common/forms.py index 6193c72c4..f1e5b34ca 100644 --- a/ishtar_common/forms.py +++ b/ishtar_common/forms.py @@ -622,6 +622,7 @@ class FormHeader(object):  class IshtarForm(forms.Form, BSForm):      TYPES = []  # FieldType list      CONDITIONAL_FIELDS = []  # dynamic conditions on field display +                             # can be dynamic with "get_conditional_fields"      PROFILE_FILTER = {}  # profile key associated to field list      HEADERS = {}  # field key associated to FormHeader instance      # permission check for widget options, ex: forms_common.DocumentForm @@ -668,14 +669,19 @@ class IshtarForm(forms.Form, BSForm):          self.fields[field.key].choices = field.get_choices()          self.fields[field.key].help_text = field.get_help() +    def get_headers(self): +        return self.HEADERS +      def headers(self, key): -        if key not in self.HEADERS: +        headers = self.get_headers() +        if key not in headers:              return -        self.current_header = self.HEADERS[key] +        self.current_header = headers[key]          return self.current_header      def extra_render(self): -        return self.get_conditional() +        return (self.get_conditional() or "") + ( +                self.get_conditional_filters() or "")      HIDE_JS_TEMPLATE = """      var %(id)s_item_show_list = ['%(item_list)s']; @@ -688,7 +694,6 @@ class IshtarForm(forms.Form, BSForm):      var %(id)s_item_show_list = ['%(item_list)s'];      var %(id)s_hide_display = function(){          var current_val = $("#id_%(name)s").val(); -        console.log("#id_%(name)s");          if (%(id)s_check_list.indexOf(current_val) != -1){              for (idx in %(id)s_item_show_list){                  $("#main_div-id_" + %(id)s_item_show_list[idx]).removeClass("d-none"); @@ -707,12 +712,15 @@ class IshtarForm(forms.Form, BSForm):      """      def get_conditional(self): -        if not self.CONDITIONAL_FIELDS or not self.TYPES: +        conditional_fields = self.CONDITIONAL_FIELDS +        if hasattr(self, 'get_conditional_fields'): +            conditional_fields = self.get_conditional_fields() +        if not conditional_fields or not self.TYPES:              return          type_dict = dict([(typ.key, typ.model) for typ in self.TYPES])          html = "" -        for condition, target_names in self.CONDITIONAL_FIELDS: +        for condition, target_names in conditional_fields:              condition_field, condition_attr, condition_val = condition              if condition_field not in type_dict:                  continue @@ -740,6 +748,102 @@ class IshtarForm(forms.Form, BSForm):              html = "<script type='text/javascript'>" + html + "</script>"          return html +    CONDITIONAL_FILTER_JS_TEMPLATE = """ +    %(filter_list)s; +    var %(id)s_prefix = "%(prefix)s"; +    var %(id)s_filter_display = function(){ +        var current_val = $("#id_%(name)s").val(); +        if (current_val in %(id)s_filter_list){ +            for (var k in %(id)s_filter_list[current_val]){ +                var cname = k; +                if (%(id)s_prefix) cname = %(id)s_prefix + cname; +                update_select_widget( +                    cname, +                    %(id)s_all_value_list[k], +                    %(id)s_filter_list[current_val][k]); +            } +        } else { +            for (var k in %(id)s_exclude_list){ +                var cname = k; +                if (%(id)s_prefix) cname = %(id)s_prefix + cname; +                update_select_widget( +                    cname, +                    %(id)s_all_value_list[k], +                    null, +                    %(id)s_exclude_list[k]); +            } +        } +    }; + +    $("#id_%(name)s").change(%(id)s_filter_display); +    setTimeout(function(){ +        %(id)s_filter_display(); +    }, 500); +    """ + +    def get_conditional_filters(self): +        if not hasattr(self, 'get_conditional_filter_fields'): +            return +        conditional_fields, excluded_fields, all_values = \ +            self.get_conditional_filter_fields() + +        types = [typ.key for typ in self.TYPES] +        html = "" + +        outputs = set() +        for input_key in conditional_fields: +            if input_key not in types: +                continue +            name = input_key +            if self.prefix: +                name = self.prefix + "-" + input_key +            cidx = name.replace("-", "_") +            filter_list = "var %s_filter_list = {\n" % cidx +            for idx, input_pk in enumerate(conditional_fields[input_key]): +                if idx: +                    filter_list += ",\n" +                filter_list += '  "%s": {\n' % input_pk +                for idx2, output in enumerate( +                        conditional_fields[input_key][input_pk]): +                    if idx2: +                        filter_list += ",\n" +                    if output[0] in excluded_fields: +                        outputs.add(output[0]) +                    filter_list += '    "{}": [{}]'.format(*output) +                filter_list += "  }" +            filter_list += "};\n" + +            html += self.CONDITIONAL_FILTER_JS_TEMPLATE % { +                "id": cidx, +                "name": name, +                "filter_list": filter_list, +                "prefix": self.prefix or "" +            } +            html += "var %s_other_widget_list = [" % cidx +            for idx, k in enumerate(all_values): +                if idx: +                    html += ", " +                html += '"' + k + '"' +            html += "];\n" + +            html += "var %s_exclude_list = {\n" % cidx +            for idx, output in enumerate(outputs): +                if idx: +                    html += ",\n" +                html += '  "%s": [%s]' % (output, excluded_fields[output]) +            html += "\n};\n" + +            html += "var %s_all_value_list = {\n" % cidx +            for idx, k in enumerate(all_values): +                if idx: +                    html += ",\n" +                html += '  "%s": %s' % (k, all_values[k]) +            html += "\n};\n" + +        if html: +            html = "<script type='text/javascript'>" + html + "</script>" +        return html +  class TableSelect(IshtarForm):      def __init__(self, *args, **kwargs): diff --git a/ishtar_common/forms_common.py b/ishtar_common/forms_common.py index c76577e65..c191a2e6e 100644 --- a/ishtar_common/forms_common.py +++ b/ishtar_common/forms_common.py @@ -1398,6 +1398,7 @@ class DocumentForm(forms.ModelForm, CustomForm, ManageOldType):          ]      HEADERS = { +        'finds': FormHeader(_("Related items")),          'title': FormHeader(_("Identification")),          'format_type': FormHeader(_("Format")),          'image': FormHeader(_("Content")), @@ -1407,7 +1408,6 @@ class DocumentForm(forms.ModelForm, CustomForm, ManageOldType):          'source': FormHeader(_("Source"), collapse=True),          'container_id': FormHeader(_("Warehouse"), collapse=True),          'comment': FormHeader(_("Advanced"), collapse=True), -        'finds': FormHeader(_("Related items")),      }      OPTIONS_PERMISSIONS = [          # field name, permission, options @@ -1427,6 +1427,7 @@ class DocumentForm(forms.ModelForm, CustomForm, ManageOldType):          self.user = None          if kwargs.get("user", None):              self.user = kwargs.pop("user") +        self.is_instancied = bool(kwargs.get('instance', False))          super(DocumentForm, self).__init__(*args, **kwargs)          fields = OrderedDict()          for related_key in models.Document.RELATED_MODELS_ALT: @@ -1447,6 +1448,73 @@ class DocumentForm(forms.ModelForm, CustomForm, ManageOldType):              fields[k] = self.fields[k]          self.fields = fields +    def get_headers(self): +        headers = self.HEADERS.copy() +        if self.is_instancied: +            headers["finds"] = FormHeader(headers["finds"].label, collapse=True) +        return headers + +    def get_conditional_filter_fields(self): +        """ +        Helper to get values for filtering select widget on select by another +        widget +        :return: conditional_fields, excluded_fields, all_values +        * conditional_fields: +            {input_key: { +                input_pk: [(output_key1, "pk1,pk2,..."), +                           (output_key2, "pk1,pk2,...")], +                input_pk2: ... +             }, +             input_key2: {...} +            } +        * excluded_fields: {output_key: "pk1,pk2,...", ...}  # list pk for all +        output_key in condition_fields +        * all_values: {output_key: [(pk1, label1), (pk2, label2), ...]} +        """ +        conditional_fields = {} +        excluded_fields = {} +        key = 'source_type' +        for doc_type in models.SourceType.objects.filter( +                available=True, formats__pk__isnull=False).all(): +            if key not in conditional_fields: +                conditional_fields[key] = {} +            sub_key = doc_type.pk +            if sub_key in conditional_fields[key]: +                continue +            lst = [str(f.pk) for f in models.Format.objects.filter( +                available=True, document_types__pk=doc_type.pk)] +            if 'format_type' not in excluded_fields: +                excluded_fields['format_type'] = [] +            for k in lst: +                if k not in excluded_fields['format_type']: +                    excluded_fields['format_type'].append(k) +            conditional_fields[key][sub_key] = [] +            conditional_fields[key][sub_key].append( +                ('format_type', ",".join(lst))) +        for doc_type in models.SourceType.objects.filter( +                available=True, supports__pk__isnull=False).all(): +            if key not in conditional_fields: +                conditional_fields[key] = {} +            lst = [str(f.pk) for f in models.SupportType.objects.filter( +                available=True, document_types__pk=doc_type.pk)] +            if 'support_type' not in excluded_fields: +                excluded_fields['support_type'] = [] +            for k in lst: +                if k not in excluded_fields['support_type']: +                    excluded_fields['support_type'].append(k) +            sub_key = doc_type.pk +            if sub_key not in conditional_fields[key]: +                conditional_fields[key][sub_key] = [] +            conditional_fields[key][sub_key].append( +                ('support_type', ",".join(lst))) +        for k in excluded_fields: +            excluded_fields[k] = ",".join(excluded_fields[k]) +        all_values = { +            "format_type": [list(tp) for tp in models.Format.get_types()], +            "support_type": [list(tp) for tp in models.SupportType.get_types()] +        } +        return conditional_fields, excluded_fields, all_values +      def clean(self):          cleaned_data = self.cleaned_data          if not cleaned_data.get('title', None) and \ diff --git a/ishtar_common/lookups.py b/ishtar_common/lookups.py index 3481ba1a9..66b43f3e4 100644 --- a/ishtar_common/lookups.py +++ b/ishtar_common/lookups.py @@ -156,3 +156,12 @@ class DocumentLookup(LookupChannel):              internal_reference__icontains=q          )          return self.model.objects.filter(query).order_by('title')[:20] + + +@register('source_type') +class SourceTypeLookup(LookupChannel): +    model = models.SourceType + +    def get_query(self, q, request): +        query = Q(label__icontains=q) +        return self.model.objects.filter(query).order_by('label')[:20] diff --git a/ishtar_common/migrations/0208_auto_20201126_1217.py b/ishtar_common/migrations/0208_auto_20201126_1516.py index 53b883075..0bd51e4c0 100644 --- a/ishtar_common/migrations/0208_auto_20201126_1217.py +++ b/ishtar_common/migrations/0208_auto_20201126_1516.py @@ -1,5 +1,5 @@  # -*- coding: utf-8 -*- -# Generated by Django 1.11.27 on 2020-11-26 12:17 +# Generated by Django 1.11.27 on 2020-11-26 15:16  from __future__ import unicode_literals  from django.db import migrations, models @@ -12,6 +12,16 @@ class Migration(migrations.Migration):      ]      operations = [ +        migrations.AddField( +            model_name='format', +            name='document_types', +            field=models.ManyToManyField(blank=True, help_text='Only available for theses document types', related_name='formats', to='ishtar_common.SourceType'), +        ), +        migrations.AddField( +            model_name='supporttype', +            name='document_types', +            field=models.ManyToManyField(blank=True, help_text='Only available for theses document types', related_name='supports', to='ishtar_common.SourceType'), +        ),          migrations.AlterField(              model_name='document',              name='isbn', diff --git a/ishtar_common/models.py b/ishtar_common/models.py index e251fee89..ffe302454 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -2636,6 +2636,11 @@ post_delete.connect(post_save_cache, sender=SourceType)  class SupportType(GeneralType): +    document_types = models.ManyToManyField( +        "SourceType", blank=True, related_name='supports', +        help_text=_("Only available for theses document types") +    ) +      class Meta:          verbose_name = _("Support type")          verbose_name_plural = _("Support types") @@ -2651,6 +2656,10 @@ class Format(GeneralType):          help_text=_("Template to insert an iframe for this format. Use django "                      "template with a {{document}} variable matching the "                      "current document.")) +    document_types = models.ManyToManyField( +        "SourceType", blank=True, related_name='formats', +        help_text=_("Only available for theses document types") +    )      class Meta:          verbose_name = _("Format type") diff --git a/ishtar_common/static/js/ishtar.js b/ishtar_common/static/js/ishtar.js index fcaa73053..6528d3797 100644 --- a/ishtar_common/static/js/ishtar.js +++ b/ishtar_common/static/js/ishtar.js @@ -1856,3 +1856,22 @@ var is_valid_issn = function(str) {  	}  	return (check == str[str.length-1].toUpperCase());  } + +var update_select_widget = function(input_name, values, only_values, excluded_values){ +    cvalue = $("#id_" + input_name).val(); +    var options = ""; +    for (var idx in values){ +        var option = values[idx]; +        if (!(option[0] && option[0] != cvalue +                && ((excluded_values && +                     excluded_values.indexOf(option[0]) != -1) || +                    (only_values && only_values.indexOf(option[0]) == -1) +                    ))){ +            var selected = ""; +            if (option[0] == cvalue) selected = " selected"; +            options += "<option value='" + option[0] + "'" + selected + ">"; +            options += option[1] + "</option>"; +		} +	} +	$("#id_" + input_name).html(options); +};
\ No newline at end of file diff --git a/ishtar_common/templates/blocks/bs_form_snippet.html b/ishtar_common/templates/blocks/bs_form_snippet.html index e24220005..3d84ce3dc 100644 --- a/ishtar_common/templates/blocks/bs_form_snippet.html +++ b/ishtar_common/templates/blocks/bs_form_snippet.html @@ -1,5 +1,5 @@  {% load i18n from_dict %} -{% if form.non_field_errors %} +{% if form.non_field_errors and not no_error %}  <div class="alert alert-danger" role="alert">      {{form.non_field_errors}}  </div> diff --git a/ishtar_common/templates/ishtar/forms/document.html b/ishtar_common/templates/ishtar/forms/document.html index ed5a39daf..8e2683a03 100644 --- a/ishtar_common/templates/ishtar/forms/document.html +++ b/ishtar_common/templates/ishtar/forms/document.html @@ -13,18 +13,18 @@  <div class='form{% if not form.SEARCH_AND_SELECT %} container{% endif %}'>      {% if form.non_field_errors or form.errors %}      <div class="alert alert-danger"> -    <div><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> -        {% trans "Error on validation. Check all your fields. Modification not saved." %} +        <div><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> +            {% trans "Error on validation. Check all your fields. Modification not saved." %} +        </div> +        {% for key, error_details in form.errors.items %} +        {% with field=key|from_dict:form.fields %}<strong>{{ field.label }}</strong>{{error_details}}{% endwith %} +        {% endfor %}      </div> -    {% for error in form.non_field_errors %} -    <p>{{ error }}</p> -    {% endfor %} -    {% for key, error_details in form.errors.items %} -    {% with field=key|from_dict:form.fields %}<strong>{{ field.label }}</strong>{{error_details}}{% endwith %} -    {% endfor %} -</div>      {% endif %} -    {% bs_form form %} +    {% if item_related_label %}<div class="alert alert-info"> +    <strong>{% trans "Related items" %}{% trans ":" %}</strong> {{ item_related_label }} +    </div>{% endif %} +    {% bs_form form 0 True %}  </div>  {% endblock %} diff --git a/ishtar_common/templatetags/table_form.py b/ishtar_common/templatetags/table_form.py index 5da2171d0..d5b20da26 100644 --- a/ishtar_common/templatetags/table_form.py +++ b/ishtar_common/templatetags/table_form.py @@ -8,11 +8,12 @@ register = Library()  @register.inclusion_tag('blocks/bs_form_snippet.html', takes_context=True) -def bs_form(context, form, position=0): +def bs_form(context, form, position=0, no_error=False):      user = context['user']      show_field_number = user.ishtaruser and user.ishtaruser.show_field_number()      return {'form': form, 'odd': position % 2, -            'show_field_number': show_field_number} +            'show_field_number': show_field_number, +            'no_error': no_error}  @register.inclusion_tag('blocks/bs_compact_form_snippet.html', diff --git a/ishtar_common/views.py b/ishtar_common/views.py index 704894d52..ceb8db200 100644 --- a/ishtar_common/views.py +++ b/ishtar_common/views.py @@ -1923,7 +1923,7 @@ display_document = display_item(models.Document)  document_search_wizard = wizards.DocumentSearch.as_view(      [('selec-document_search', forms.DocumentFormSelection)], -    label=_(u"Document: search"), +    label=_("Document: search"),      url_name='search-document',  ) @@ -2016,12 +2016,21 @@ class DocumentEditView(DocumentFormMixin, UpdateView):                  for related_item in getattr(document, k).all():                      key = "{}_{}_main_image".format(k, related_item.pk)                      kwargs["main_items_fields"][k].append( -                        (key, u"{} - {}".format( -                            _(u"Main image for"), related_item))) +                        (key, "{} - {}".format( +                            _("Main image for"), related_item)))                      if related_item.main_image == document:                          initial[key] = True          kwargs['initial'] = initial          kwargs["user"] = self.request.user +        self.document = document +        return kwargs + +    def get_context_data(self, **kwargs): +        kwargs = super(DocumentEditView, self).get_context_data(**kwargs) +        rel = self.document.cache_related_label +        if len(rel) == 1000:  # truncated +            rel += " (...)" +        kwargs["item_related_label"] = rel          return kwargs | 
