diff options
| -rw-r--r-- | archaeological_finds/urls.py | 22 | ||||
| -rw-r--r-- | archaeological_finds/views.py | 8 | ||||
| -rw-r--r-- | ishtar_common/forms.py | 47 | ||||
| -rw-r--r-- | ishtar_common/forms_common.py | 53 | ||||
| -rw-r--r-- | ishtar_common/models.py | 5 | ||||
| -rw-r--r-- | ishtar_common/templates/blocks/bs_form_snippet.html | 15 | ||||
| -rw-r--r-- | ishtar_common/templates/ishtar/forms/document.html | 38 | ||||
| -rw-r--r-- | ishtar_common/templatetags/from_dict.py | 5 | ||||
| -rw-r--r-- | ishtar_common/utils.py | 21 | ||||
| -rw-r--r-- | ishtar_common/views.py | 2 | ||||
| -rw-r--r-- | ishtar_common/views_item.py | 19 | 
11 files changed, 204 insertions, 31 deletions
| diff --git a/archaeological_finds/urls.py b/archaeological_finds/urls.py index ca7f10296..9a71c66d8 100644 --- a/archaeological_finds/urls.py +++ b/archaeological_finds/urls.py @@ -19,7 +19,7 @@  from django.conf.urls import url -from ishtar_common.utils import check_rights +from ishtar_common.utils import check_rights, get_urls_for_model  from archaeological_finds import views  from archaeological_operations.views import administrativeactfile_document @@ -166,10 +166,6 @@ urlpatterns = [          name='autocomplete-integritytype'),      url(r'autocomplete-treatmentfile/$', views.autocomplete_treatmentfile,          name='autocomplete-treatmentfile'), -    url(r'get-find/own/(?P<type>.+)?$', views.get_find, -        name='get-own-find', kwargs={'force_own': True}), -    url(r'get-find/(?P<type>.+)?$', views.get_find, -        name='get-find'),      url(r'get-find-for-ope/own/(?P<type>.+)?$', views.get_find_for_ope,          name='get-own-find-for-ope', kwargs={'force_own': True}),      url(r'get-find-for-ope/(?P<type>.+)?$', views.get_find_for_ope, @@ -190,21 +186,13 @@ urlpatterns = [          name='show-findbasket'),      url(r'^display-find/basket-(?P<pk>.+)/$', views.display_findbasket,          name='display-findbasket'), -    url(r'^show-find(?:/(?P<pk>.+))?/(?P<type>.+)?$', views.show_find, -        name=models.Find.SHOW_URL), -    url(r'^display-find/(?P<pk>.+)/$', views.display_find, -        name='display-' + models.Find.SLUG),      url(r'^show-historized-find/(?P<pk>.+)?/(?P<date>.+)?$',          views.show_find, name='show-historized-find'),      url(r'^revert-find/(?P<pk>.+)/(?P<date>.+)$',          views.revert_find, name='revert-find'), -    url(r'^get-treatment/(?P<type>.+)?$', -        views.get_treatment, name='get-treatment'),      url(r'get-treatment-shortcut/(?P<type>.+)?$',          views.get_treatment, name='get-treatment-shortcut',          kwargs={'full': 'shortcut'}), -    url(r'^show-treatment(?:/(?P<pk>.+))?/(?P<type>.+)?$', views.show_treatment, -        name=models.Treatment.SHOW_URL),      url(r'show-historized-treatment/(?P<pk>.+)?/(?P<date>.+)?$',          views.show_treatment, name='show-historized-treatment'),      url(r'^revert-treatment/(?P<pk>.+)/(?P<date>.+)$', @@ -231,6 +219,8 @@ urlpatterns = [          kwargs={'treatment_file': True}),  ] -# url(r'show-treatmentfile(?:/(?P<pk>.+))?/(?P<type>.+)?$', -# 'show_treatmentfile', -#     name=models.TreatmentFile.SHOW_URL), +urlpatterns += get_urls_for_model(models.Find, views, own=True, +                                  autocomplete=True) +urlpatterns += get_urls_for_model(models.Treatment, views, +                                  autocomplete=True) + diff --git a/archaeological_finds/views.py b/archaeological_finds/views.py index 850151578..76f1977e6 100644 --- a/archaeological_finds/views.py +++ b/archaeological_finds/views.py @@ -37,7 +37,7 @@ from ishtar_common.models import IshtarUser, get_current_profile  from ishtar_common.views import get_autocomplete_generic, IshtarMixin, \      LoginRequiredMixin  from ishtar_common.views_item import display_item, get_item, show_item, \ -    revert_item +    revert_item, get_autocomplete_item  from ishtar_common.wizards import SearchWizard  from wizards import * @@ -50,9 +50,15 @@ get_find_for_treatment = get_item(      models.Find, 'get_find', 'find',      own_table_cols=models.Find.TABLE_COLS_FOR_OPE, base_request={}) +autocomplete_find = get_autocomplete_item(model=models.Find) + +  show_treatment = show_item(models.Treatment, 'treatment')  revert_treatment = revert_item(models.Treatment)  get_treatment = get_item(models.Treatment, 'get_treatment', 'treatment') +display_treatment = display_item(models.Treatment) + +autocomplete_treatment = get_autocomplete_item(model=models.Treatment)  get_administrativeacttreatment = get_item(      AdministrativeAct, 'get_administrativeacttreatment', diff --git a/ishtar_common/forms.py b/ishtar_common/forms.py index 59f3e141a..b0f1920df 100644 --- a/ishtar_common/forms.py +++ b/ishtar_common/forms.py @@ -33,6 +33,7 @@ from django.forms.formsets import BaseFormSet, DELETION_FIELD_NAME  from django.utils import formats, translation  from django.utils.functional import lazy  from django.utils.safestring import mark_safe +from django.utils.text import slugify  from django.utils.translation import ugettext_lazy as _  from bootstrap_datepicker.widgets import DatePicker, DATE_FORMAT, DateField @@ -356,14 +357,46 @@ class FieldType(object):  class FormHeader(object): -    def __init__(self, label, level=4): +    def __init__(self, label, level=4, collapse=False):          self.label = label +        self.collapse = collapse +        if collapse: +            level = 5          self.level = level      def render(self): -        return mark_safe(u"<h{level}>{label}</h{level}>".format( -            label=self.label, level=self.level -        )) +        if not self.collapse: +            return mark_safe(u"<h{level}>{label}</h{level}>".format( +                label=self.label, level=self.level +            )) +        html = u"""<div id="collapse-parent-{slug}"> +  <div class="card"> +    <div class="card-header" id="collapse-head-{slug}"> +      <h{level} href="#"> +        <button class="btn btn-link" type="button" data-toggle="collapse"  +        data-target="#collapse-{slug}" aria-expanded="true"  +        aria-controls="collapse-{slug}"> +          <i class="fa fa-compress" aria-hidden="true"></i> +          {label} +        </button> +      </h{level}> +    </div> + +    <div id="collapse-{slug}" class="collapse" +        aria-labelledby="collapse-head-{slug}" +        data-parent="#colapse-parent-{slug}"> +      <div class="card-body"> +""".format(label=self.label, slug=slugify(self.label), level=self.level) +        return mark_safe(html) + +    def render_end(self): +        if not self.collapse: +            return "" +        return mark_safe(u""" +      </div> +    </div> +  </div> +  </div>""")  class IshtarForm(forms.Form): @@ -404,6 +437,12 @@ class IshtarForm(forms.Form):          self.fields[field.key].choices = field.get_choices()          self.fields[field.key].help_text = field.get_help() +    def headers(self, key): +        if key not in self.HEADERS: +            return +        self.current_header = self.HEADERS[key] +        return self.current_header +  class TableSelect(IshtarForm):      def __init__(self, *args, **kwargs): diff --git a/ishtar_common/forms_common.py b/ishtar_common/forms_common.py index 9abc6551a..0cfc2a609 100644 --- a/ishtar_common/forms_common.py +++ b/ishtar_common/forms_common.py @@ -41,7 +41,7 @@ import widgets  from bootstrap_datepicker.widgets import DatePicker  from ishtar_common.templatetags.link_to_window import link_to_window  from forms import FinalForm, FormSet, reverse_lazy, name_validator, \ -    TableSelect, ManageOldType, CustomForm, FieldType, \ +    TableSelect, ManageOldType, CustomForm, FieldType, FormHeader, \      FormSetWithDeleteSwitches, IshtarForm, get_data_from_formset  from ishtar_common.utils import is_downloadable, clean_session_cache @@ -1079,7 +1079,7 @@ class DocumentForm(forms.ModelForm, CustomForm, ManageOldType):          label=_(u"Image"), help_text=mark_safe(get_image_help()),          max_length=255, required=False, widget=widgets.ImageFileInput())      associated_file = forms.FileField( -        label=pgettext(u"File", u"Not directory"), max_length=255, +        label=pgettext(u"Not directory", u"File"), max_length=255,          required=False)      reference = forms.CharField(          label=_(u"Reference"), @@ -1111,12 +1111,31 @@ class DocumentForm(forms.ModelForm, CustomForm, ManageOldType):      class Meta:          model = models.Document          fields = [ -            'title', 'source_type', 'authors', 'associated_url', 'image', -            'associated_file', 'reference', 'internal_reference', -            'receipt_date', 'creation_date', 'receipt_date_in_documentation', +            'title', 'source_type', 'reference', 'internal_reference', +            'image', 'associated_file', 'associated_url', +            'authors', 'receipt_date', +            'receipt_date_in_documentation', 'creation_date',              'comment', 'description', 'additional_information', 'duplicate'          ] +    HEADERS = { +        'title': FormHeader(_(u"Identification")), +        'image': FormHeader(_(u"Content")), +        'authors': FormHeader(_(u"Authors")), +        'receipt_date': FormHeader(_(u"Dates")), +        'comment': FormHeader(_(u"Advanced"), collapse=True), +        'finds': FormHeader(_(u"Related items")), +    } + +    def __init__(self, *args, **kwargs): +        super(DocumentForm, self).__init__(*args, **kwargs) +        for related_key in models.Document.RELATED_MODELS_ALT: +            model = models.Document._meta.get_field(related_key).related_model +            self.fields[related_key] = widgets.Select2MultipleField( +                model=model, remote=True, label=model._meta.verbose_name_plural, +                required=False, long_widget=True +            ) +      def clean(self):          cleaned_data = self.cleaned_data          if not cleaned_data.get('title', None) and \ @@ -1126,7 +1145,29 @@ class DocumentForm(forms.ModelForm, CustomForm, ManageOldType):              raise forms.ValidationError(_(u"You should at least fill one of "                                            u"this field: title, url, image or "                                            u"file.")) -        return cleaned_data +        for rel in models.Document.RELATED_MODELS: +            if cleaned_data.get(rel, None): +                return cleaned_data +        raise forms.ValidationError(_(u"A document have to attached at least " +                                      u"to one item")) + +    def save(self, commit=True): +        item = super(DocumentForm, self).save(commit=commit) +        for related_key in models.Document.RELATED_MODELS: +            related = getattr(item, related_key) +            initial = dict([(item.pk, item) +                            for item in related.all()]) +            new = [int(pk) +                   for pk in sorted(self.cleaned_data.get(related_key, []))] +            for pk in initial.keys(): +                if pk in new: +                    continue +                related.remove(initial[pk]) +            for new_pk in new: +                if new_pk in initial.keys(): +                    continue +                related.add(related.model.objects.get(pk=new_pk)) +        return item  class DocumentSelect(TableSelect): diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 758596279..3715c0326 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -3060,6 +3060,11 @@ class Document(OwnPerms, ImageModel, FullSearch):          'treatment_files', 'treatments', 'finds', 'context_records',          'operations', 'sites', 'warehouses',      ] +    # same fields but in order for forms +    RELATED_MODELS_ALT = [ +        'finds', 'context_records', 'operations', 'sites', 'warehouses', +        'treatments', 'treatment_files', +    ]      SLUG = 'document'      LINK_SPLIT = u"<||>" diff --git a/ishtar_common/templates/blocks/bs_form_snippet.html b/ishtar_common/templates/blocks/bs_form_snippet.html index 47779cdc1..74dbadf5c 100644 --- a/ishtar_common/templates/blocks/bs_form_snippet.html +++ b/ishtar_common/templates/blocks/bs_form_snippet.html @@ -63,13 +63,26 @@  {% if field.name in form.HEADERS %}  {% if forloop.counter0 %}  </div>{% endif %} -<h3>{{field.name|from_dict:form.HEADERS|call:'render'}}</h3> + +{% if form.current_header %} +    {{form.current_header|call:'render_end'}} +{% endif %} + +{% with "headers,"|add:field.name as call_arg %} +{{form|call:call_arg|call:"render"}} +{% endwith %} +  <div class="form-row{% if odd %} odd{% endif %}">  {% elif not search and not forloop.counter0 or search and forloop.counter0 == 1 %}    <div class="form-row{% if odd %} odd{% endif %}">  {% endif %}        {% include "blocks/bs_field_snippet.html" %}  {% if forloop.last %} + +{% if form.current_header %} +    {{form.current_header|call:'render_end'}} +{% endif %} +  {% if search and forloop.counter0 >= 1 %}              </div>              <div class="modal-footer"> diff --git a/ishtar_common/templates/ishtar/forms/document.html b/ishtar_common/templates/ishtar/forms/document.html new file mode 100644 index 000000000..38bb3cacf --- /dev/null +++ b/ishtar_common/templates/ishtar/forms/document.html @@ -0,0 +1,38 @@ +{% extends "base.html" %} +{% load i18n inline_formset table_form %} +{% block extra_head %} +{{form.media}} +{% endblock %} + +{% block pre_container %} +<form enctype="multipart/form-data" action="." method="post"{% if confirm %} +      onsubmit='return confirm("{{confirm}}");'{% endif %}>{% csrf_token %} +{% endblock %} +{% block content %} +<h2>{{page_name}}</h2> +<div class='form'> +    {% for error in form.non_field_errors %} +    <p>{{ error }}</p> +    {% endfor %} +    {% bs_form form %} +</div> +{% endblock %} + +{% block footer %} +<div id="footer"> +    {% if form.SEARCH_AND_SELECT %} +    <p class="confirm-message">{% trans "Search and select an item in the table" %}</p> +    {% endif %} +    <div id='validation-bar' class="row text-center"> +        <div class="col-sm"> +            <button type="submit" id="submit_form" name='validate' +                    value="validate" class="btn btn-success"> +               {% if submit_label %}{{submit_label}}{% else%}{% trans "Validate" %}{% endif %} +            </button> +        </div> +    </div> +    {% include 'ishtar/blocks/footer.html' %} +</div> +</form> +{% endblock %} + diff --git a/ishtar_common/templatetags/from_dict.py b/ishtar_common/templatetags/from_dict.py index c64190ab1..f9b5bdb99 100644 --- a/ishtar_common/templatetags/from_dict.py +++ b/ishtar_common/templatetags/from_dict.py @@ -15,4 +15,7 @@ def from_dict(value, dct):  @register.filter  def call(value, call): -    return getattr(value, call)() +    args = call.split(",") +    return getattr(value, args[0])(*args[1:]) + + diff --git a/ishtar_common/utils.py b/ishtar_common/utils.py index 443a22111..b089ddcee 100644 --- a/ishtar_common/utils.py +++ b/ishtar_common/utils.py @@ -791,7 +791,7 @@ def create_default_json_fields(model):          ) -def get_urls_for_model(model, views): +def get_urls_for_model(model, views, own=False, autocomplete=False):      """      Generate get and show url for a model      """ @@ -804,9 +804,28 @@ def get_urls_for_model(model, views):              check_rights(['view_' + model.SLUG, 'view_own_' + model.SLUG])(                  getattr(views, 'display_' + model.SLUG)),              name='display-' + model.SLUG), +    ] +    if own: +        urls += [ +            url(r'get-{}/own/(?P<type>.+)?$'.format(model.SLUG), +                check_rights(['view_' + model.SLUG, 'view_own_' + model.SLUG])( +                    getattr(views, 'get_' + model.SLUG)), +                name="get-own-" + model.SLUG, kwargs={'force_own': True}), +        ] + +    urls += [          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),      ] + +    if autocomplete: +        urls += [ +            url(r'autocomplete-{}/$'.format(model.SLUG), +                check_rights(['view_' + model.SLUG, 'view_own_' + model.SLUG])( +                    getattr(views, 'autocomplete_' + model.SLUG)), +                name='autocomplete-' + model.SLUG), +        ] +      return urls diff --git a/ishtar_common/views.py b/ishtar_common/views.py index 81397dd70..cfe98cff1 100644 --- a/ishtar_common/views.py +++ b/ishtar_common/views.py @@ -1587,7 +1587,7 @@ class DocumentFormView(IshtarMixin, LoginRequiredMixin,                         CreateView):      page_name = _(u"New Document")      form_class = forms.DocumentForm -    template_name = 'ishtar/form.html' +    template_name = 'ishtar/forms/document.html'      model = models.Document      def get_success_url(self): diff --git a/ishtar_common/views_item.py b/ishtar_common/views_item.py index eef3440bc..4be1ec144 100644 --- a/ishtar_common/views_item.py +++ b/ishtar_common/views_item.py @@ -53,6 +53,25 @@ CURRENT_ITEM_KEYS = (('file', File),  CURRENT_ITEM_KEYS_DICT = dict(CURRENT_ITEM_KEYS) +def get_autocomplete_item(model, extra=None): +    if not extra: +        extra = {} + +    def func(request, current_right=None): +        q = request.GET.get('term') or "" +        query = Q(**extra) +        for q in q.split(' '): +            if not q: +                continue +            query = query & Q(cached_label__icontains=q) +        limit = 20 +        objects = model.objects.filter(query)[:limit] +        data = json.dumps([{'id': obj.pk, 'value': obj.cached_label} +                           for obj in objects]) +        return HttpResponse(data, content_type='text/plain') +    return func + +  def check_permission(request, action_slug, obj_id=None):      MAIN_MENU = Menu(None)      MAIN_MENU.init() | 
