diff options
| -rw-r--r-- | archaeological_finds/forms.py | 40 | ||||
| -rw-r--r-- | archaeological_finds/ishtar_menu.py | 19 | ||||
| -rw-r--r-- | archaeological_finds/migrations/0037_auto_20181018_1756.py | 41 | ||||
| -rw-r--r-- | archaeological_finds/models_finds.py | 14 | ||||
| -rw-r--r-- | archaeological_finds/templates/ishtar/sheet_findbasket.html | 23 | ||||
| -rw-r--r-- | archaeological_finds/urls.py | 19 | ||||
| -rw-r--r-- | archaeological_finds/views.py | 54 | ||||
| -rw-r--r-- | archaeological_finds/wizards.py | 4 | ||||
| -rw-r--r-- | ishtar_common/models.py | 22 | ||||
| -rw-r--r-- | ishtar_common/views_item.py | 14 | 
10 files changed, 202 insertions, 48 deletions
| diff --git a/archaeological_finds/forms.py b/archaeological_finds/forms.py index 610c309d0..370f29cad 100644 --- a/archaeological_finds/forms.py +++ b/archaeological_finds/forms.py @@ -26,6 +26,7 @@ import logging  from django import forms  from django.core import validators  from django.core.exceptions import PermissionDenied +from django.db.models import Q  from django.forms.formsets import formset_factory  from django.utils.translation import ugettext_lazy as _ @@ -71,7 +72,7 @@ __all__ = [      'AdministrativeActTreatmentFileModifForm',      'DashboardTreatmentForm', 'DashboardTreatmentFileForm',      'RecordFormSelection', 'FindForm', 'DateForm', 'DatingFormSet', -    'PreservationForm', +    'PreservationForm', 'FindBasketFormSelection',      'FindSelect', 'FindFormSelection', 'FindFormSelectionWarehouseModule',      'MultipleFindFormSelection', 'MultipleFindFormSelectionWarehouseModule',      'FindMultipleFormSelection', 'check_form', 'check_exist', 'check_not_exist', @@ -918,6 +919,34 @@ class UpstreamFindFormSelection(FindFormSelection):          self.fields['resulting_pk'] = self.fields.pop('pk') +class FindBasketSelect(CustomForm, TableSelect): +    _model = models.FindBasket + +    form_admin_name = _(u"Find basket - 001 - Search") +    form_slug = "findbasket-001-search" +    search_vector = forms.CharField( +        label=_(u"Full text search"), widget=widgets.SearchWidget( +            'archaeological-finds', 'findbasket' +        )) + +    label = forms.CharField(label=_(u"Denomination")) + + +class FindBasketFormSelection(CustomFormSearch): +    SEARCH_AND_SELECT = True +    form_label = _("Basket search") +    associated_models = {'pk': models.FindBasket} +    currents = {'pk': models.FindBasket} + +    pk = forms.IntegerField( +        label="", required=False, +        widget=widgets.DataTable( +            reverse_lazy('get-findbasket'), +            FindBasketSelect, models.FindBasket, +        ), +        validators=[valid_id(models.FindBasket)]) + +  class NewFindBasketForm(forms.ModelForm):      class Meta:          model = models.FindBasket @@ -959,7 +988,9 @@ class SelectFindBasketForm(IshtarForm):              return          self.fields['basket'].choices = [('', '--')] + [              (b.pk, unicode(b)) -            for b in models.FindBasket.objects.filter(user=self.user)] +            for b in models.FindBasket.objects.filter( +                Q(user=self.user) | Q(shared_with=self.user) +            )]  class DeleteFindBasketForm(SelectFindBasketForm): @@ -979,8 +1010,9 @@ class FindBasketAddItemForm(forms.Form):      def save(self, user):          try: -            basket = models.FindBasket.objects.get( -                pk=self.cleaned_data['basket_id'], user=user) +            basket = models.FindBasket.objects.filter( +                Q(user=user) | Q(shared_with=user) +            ).get(pk=self.cleaned_data['basket_id'])              item = models.Find.objects.get(                  pk=self.cleaned_data['item_id'])          except models.FindBasket.DoesNotExist or\ diff --git a/archaeological_finds/ishtar_menu.py b/archaeological_finds/ishtar_menu.py index c72e9c089..3dd98bdbd 100644 --- a/archaeological_finds/ishtar_menu.py +++ b/archaeological_finds/ishtar_menu.py @@ -24,7 +24,7 @@ from ishtar_common.menu_base import SectionItem, MenuItem  from archaeological_operations.models import AdministrativeAct  import models -# be carreful: each access_controls must be relevant with check_rights in urls +# be careful: each access_controls must be relevant with check_rights in urls  MENU_SECTIONS = [      (50, @@ -56,22 +56,27 @@ MENU_SECTIONS = [               SectionItem(                   'find_basket', _(u"Basket"),                   childs=[ +                     MenuItem('find_basket_search', +                              _(u"Search"), +                              model=models.FindBasket, +                              access_controls=['view_find', +                                               'view_own_find']),                       MenuItem('find_basket_creation',                                _(u"Creation"),                                model=models.FindBasket, -                              access_controls=['change_find', -                                               'change_own_find']), +                              access_controls=['view_find', +                                               'view_own_find']),                       MenuItem('find_basket_modification_add',                                _(u"Manage items"),                                model=models.FindBasket,                                access_controls=[ -                                  'change_find', -                                  'change_own_find']), +                                  'view_find', +                                  'view_own_find']),                       MenuItem('find_basket_deletion',                                _(u"Deletion"),                                model=models.FindBasket, -                              access_controls=['change_find', -                                               'change_own_find']), +                              access_controls=['view_find', +                                               'view_own_find']),                   ]),               # MenuItem(               #     'treatment_creation', _(u"Add a treatment"), diff --git a/archaeological_finds/migrations/0037_auto_20181018_1756.py b/archaeological_finds/migrations/0037_auto_20181018_1756.py new file mode 100644 index 000000000..0a91b860c --- /dev/null +++ b/archaeological_finds/migrations/0037_auto_20181018_1756.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.10 on 2018-10-18 17:56 +from __future__ import unicode_literals + +import django.contrib.postgres.search +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + +    dependencies = [ +        ('ishtar_common', '0074_auto_20181017_1854'), +        ('archaeological_finds', '0036_auto_20181017_1854'), +    ] + +    operations = [ +        migrations.AlterModelOptions( +            name='findbasket', +            options={'permissions': (('view_find', 'Can view all Finds'), ('view_own_find', 'Can view own Find'))}, +        ), +        migrations.AddField( +            model_name='findbasket', +            name='search_vector', +            field=django.contrib.postgres.search.SearchVectorField(blank=True, help_text='Auto filled at save', null=True, verbose_name='Search vector'), +        ), +        migrations.AddField( +            model_name='findbasket', +            name='shared_with', +            field=models.ManyToManyField(blank=True, related_name='shared_findbaskets', to='ishtar_common.IshtarUser', verbose_name='Shared with'), +        ), +        migrations.AlterField( +            model_name='findbasket', +            name='user', +            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='findbaskets', to='ishtar_common.IshtarUser', verbose_name='Owner'), +        ), +        migrations.AlterUniqueTogether( +            name='findbasket', +            unique_together=set([]), +        ), +    ] diff --git a/archaeological_finds/models_finds.py b/archaeological_finds/models_finds.py index e0ba9206a..f6c8c6bf6 100644 --- a/archaeological_finds/models_finds.py +++ b/archaeological_finds/models_finds.py @@ -566,9 +566,19 @@ WEIGHT_UNIT = (('g', _(u"g")),                 ('kg', _(u"kg")),) -class FindBasket(Basket): +class FindBasket(Basket, OwnPerms):      items = models.ManyToManyField('Find', blank=True, related_name='basket') +    class Meta: +        permissions = ( +            ("view_find", u"Can view all Finds"), +            ("view_own_find", u"Can view own Find"), +        ) + +    @classmethod +    def get_query_owns(cls, ishtaruser): +        return Q(user=ishtaruser) +  class FirstBaseFindView(object):      CREATE_SQL = """ @@ -708,7 +718,7 @@ class Find(BulkUpdatedItem, ValueGetter, BaseHistorizedItem, OwnPerms,          'base_finds__find__description':              'base_finds__find__description__icontains',          'base_finds__batch': 'base_finds__batch', -        'basket': 'basket', +        'basket_id': 'basket__pk',          'denomination': 'denomination',          'cached_label': 'cached_label__icontains',          'documents__image__isnull': 'documents__image__isnull', diff --git a/archaeological_finds/templates/ishtar/sheet_findbasket.html b/archaeological_finds/templates/ishtar/sheet_findbasket.html index b6d4ffd42..c9c442ccd 100644 --- a/archaeological_finds/templates/ishtar/sheet_findbasket.html +++ b/archaeological_finds/templates/ishtar/sheet_findbasket.html @@ -1,13 +1,24 @@  {% extends "ishtar/sheet.html" %}  {% load i18n window_tables window_header from_dict window_field %} -{% block head_title %}{% trans "Find basket" %}{% endblock %} +{% block head_title %}{% trans "Basket" %} - {{item.label}}{% endblock %} + +{% block toolbar %} +{% window_nav item window_id 'show-findbasket' 'select_itemsinbasket' %} +{% endblock %}  {% block content %} -{% window_nav item window_id 'show-findbasket' 'select_itemsinbasket' %} -<p class="window-refs">{{ item.label|default:"" }}</p> -{% field "Owned by" item.user %} -{% field "Comment" item.comment %} -{% dynamic_table_document finds 'finds_for_ope' 'basket' item.pk 'TABLE_COLS_FOR_OPE' output %} +<div class='row'> +    {% field_flex "Label" item.label %} +    {% field_flex_detail "Owned by" item.user.person %} +    {% field_flex_multiple "Shared_with" item.shared_with %} +    {% field_flex "Comment" item.comment %} +</div> + +<h3>{% trans "Content" %}</h3> + +{% dynamic_table_document finds 'finds' 'basket_id' item.pk 'TABLE_COLS' output %} + +  {% endblock %} diff --git a/archaeological_finds/urls.py b/archaeological_finds/urls.py index 9629a3f38..afc9bcba7 100644 --- a/archaeological_finds/urls.py +++ b/archaeological_finds/urls.py @@ -43,32 +43,37 @@ urlpatterns = [              views.find_deletion_wizard), name='find_deletion'),      url(r'find_modify/(?P<pk>.+)/$',          views.find_modify, name='find_modify'), +    url(r'get-findbasket/$', views.get_find_basket, +        name='get-findbasket'), +    url(r'find_basket_search/(?P<step>.+)?$', +        check_rights(['view_find', 'view_own_find'])( +            views.basket_search_wizard), name='find_basket_search'),      url(r'^find_basket_creation/$', -        check_rights(['change_find', 'change_own_find'])( +        check_rights(['view_find', 'view_own_find'])(              views.NewFindBasketView.as_view()), name='new_findbasket'),      url(r'^find_basket_modification_add/$', -        check_rights(['change_find', 'change_own_find'])( +        check_rights(['view_find', 'view_own_find'])(              views.SelectBasketForManagement.as_view()),          name='select_findbasketforadd'),      url(r'^find_basket_modification_add/(?P<pk>[0-9]+)?/$', -        check_rights(['change_find', 'change_own_find'])( +        check_rights(['view_find', 'view_own_find'])(              views.SelectItemsInBasket.as_view()),          name='select_itemsinbasket'),      url(r'^find_basket_modification_add_item/$', -        check_rights(['change_find', 'change_own_find'])( +        check_rights(['view_find', 'view_own_find'])(              views.FindBasketAddItemView.as_view()),          name='add_iteminbasket'),      url(r'^find_basket_modification_delete_item/(?P<basket>[0-9]+)?'          r'/(?P<find_pk>[0-9]+)?/$', -        check_rights(['change_find', 'change_own_find'])( +        check_rights(['view_find', 'view_own_find'])(              views.FindBasketDeleteItemView.as_view()),          name='delete_iteminbasket'),      url(r'^find_basket_list/(?P<pk>[0-9]+)?/$', -        check_rights(['change_find', 'change_own_find'])( +        check_rights(['view_find', 'view_own_find'])(              views.FindBasketListView.as_view()),          name='list_iteminbasket'),      url(r'^find_basket_deletion/$', -        check_rights(['change_find', 'change_own_find'])( +        check_rights(['view_find', 'view_own_find'])(              views.DeleteFindBasketView.as_view()), name='delete_findbasket'),      url(r'^find-qa-bulk-update/(?P<pks>[0-9-]+)?/$', diff --git a/archaeological_finds/views.py b/archaeological_finds/views.py index 7855b5099..7523df145 100644 --- a/archaeological_finds/views.py +++ b/archaeological_finds/views.py @@ -113,6 +113,16 @@ show_findbasket = show_item(models.FindBasket, 'findbasket')  display_findbasket = display_item(models.FindBasket,                                    show_url='show-find/basket-') +get_find_basket = get_item( +    models.FindBasket, 'get_findbasket', 'findbasket', +) + +basket_search_wizard = FindBasketSearch.as_view( +    [('general-basket_search', FindBasketFormSelection)], +    label=_(u"Basket search"), +    url_name='find_basket_search', +) +  def check_preservation_module(self):      return get_current_profile().preservation @@ -234,6 +244,16 @@ class NewFindBasketView(IshtarMixin, LoginRequiredMixin, CreateView):          return HttpResponseRedirect(self.get_success_url()) +class OwnBasket(object): +    def get_basket(self, user, pk): +        try: +            return models.FindBasket.objects.filter( +                Q(user=user) | Q(shared_with=user) +            ).get(pk=pk) +        except models.FindBasket.DoesNotExist: +            raise PermissionDenied + +  class SelectBasketForManagement(IshtarMixin, LoginRequiredMixin, FormView):      template_name = 'ishtar/form.html'      form_class = SelectFindBasketForm @@ -255,7 +275,8 @@ class SelectBasketForManagement(IshtarMixin, LoginRequiredMixin, FormView):              form.cleaned_data['basket'])) -class SelectItemsInBasket(IshtarMixin, LoginRequiredMixin, TemplateView): +class SelectItemsInBasket(OwnBasket, IshtarMixin, LoginRequiredMixin, +                          TemplateView):      template_name = 'ishtar/manage_basket.html'      page_name = _(u"Manage basket") @@ -263,11 +284,9 @@ class SelectItemsInBasket(IshtarMixin, LoginRequiredMixin, TemplateView):          context = super(SelectItemsInBasket, self).get_context_data(              *args, **kwargs)          self.user = IshtarUser.objects.get(pk=self.request.user.pk) -        try: -            self.basket = models.FindBasket.objects.get( -                pk=self.kwargs['pk'], user=self.user) -        except models.FindBasket.DoesNotExist: -            raise PermissionDenied +        self.basket = self.get_basket( +            user=self.user, pk=self.kwargs['pk'] +        )          context['basket'] = self.basket          if get_current_profile().warehouse:              context['form'] = MultipleFindFormSelectionWarehouseModule() @@ -296,18 +315,17 @@ class FindBasketAddItemView(IshtarMixin, LoginRequiredMixin, FormView):          return HttpResponseRedirect(self.get_success_url(basket)) -class FindBasketListView(IshtarMixin, LoginRequiredMixin, TemplateView): +class FindBasketListView(OwnBasket, IshtarMixin, LoginRequiredMixin, +                         TemplateView):      template_name = 'ishtar/basket_list.html'      def get_context_data(self, *args, **kwargs):          context = super(FindBasketListView, self).get_context_data(              *args, **kwargs)          self.user = IshtarUser.objects.get(pk=self.request.user.pk) -        try: -            self.basket = models.FindBasket.objects.get( -                pk=self.kwargs['pk'], user=self.user) -        except models.FindBasket.DoesNotExist: -            raise PermissionDenied +        self.basket = self.get_basket( +            user=self.user, pk=self.kwargs['pk'] +        )          context['basket'] = self.basket          context['item_url'] = '/'.join(              reverse(models.Find.SHOW_URL, args=[1]).split('/')[:-1]) @@ -316,7 +334,8 @@ class FindBasketListView(IshtarMixin, LoginRequiredMixin, TemplateView):          return context -class FindBasketDeleteItemView(IshtarMixin, LoginRequiredMixin, TemplateView): +class FindBasketDeleteItemView(OwnBasket, IshtarMixin, LoginRequiredMixin, +                               TemplateView):      template_name = 'ishtar/simple_form.html'      def get_success_url(self, basket): @@ -330,11 +349,10 @@ class FindBasketDeleteItemView(IshtarMixin, LoginRequiredMixin, TemplateView):                  pk=self.kwargs['find_pk'])          except models.Find.DoesNotExist:              raise PermissionDenied -        try: -            basket = models.FindBasket.objects.get( -                pk=self.kwargs['basket'], user=ishtaruser) -        except models.FindBasket.DoesNotExist: -            raise PermissionDenied + +        basket = self.get_basket( +            user=ishtaruser, pk=self.kwargs['basket'] +        )          if not user.is_superuser and \                  not ishtaruser.has_right('view_find') and \                  not (ishtaruser.has_right('view_own_find') diff --git a/archaeological_finds/wizards.py b/archaeological_finds/wizards.py index 6486e5da1..ef51d85d1 100644 --- a/archaeological_finds/wizards.py +++ b/archaeological_finds/wizards.py @@ -230,3 +230,7 @@ class TreatmentFileEditAdministrativeActWizard(      def get_associated_item(self, dct):          return self.get_current_object().treatment_file + + +class FindBasketSearch(SearchWizard): +    model = models.FindBasket diff --git a/ishtar_common/models.py b/ishtar_common/models.py index a1bae96c2..82c754fa0 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -3408,7 +3408,7 @@ class IshtarUser(FullSearch):          return self.person.full_label() -class Basket(models.Model): +class Basket(FullSearch):      """      Abstract class for a basket      Subclass must be defined with an "items" ManyToManyField @@ -3416,8 +3416,19 @@ class Basket(models.Model):      IS_BASKET = True      label = models.CharField(_(u"Label"), max_length=1000)      comment = models.TextField(_(u"Comment"), blank=True, null=True) -    user = models.ForeignKey(IshtarUser, blank=True, null=True) +    user = models.ForeignKey( +        IshtarUser, blank=True, null=True, related_name='%(class)ss', +        verbose_name=_(u"Owner"))      available = models.BooleanField(_(u"Available"), default=True) +    shared_with = models.ManyToManyField( +        IshtarUser, verbose_name=_(u"Shared with"), blank=True, +        related_name='shared_%(class)ss' +    ) + +    TABLE_COLS = ['label', 'user'] + +    BASE_SEARCH_VECTORS = ['label', 'comment'] +    M2M_SEARCH_VECTORS = ['items']      class Meta:          abstract = True @@ -3426,6 +3437,13 @@ class Basket(models.Model):      def __unicode__(self):          return self.label +    @classmethod +    def BASE_REQUEST(cls, request): +        if not request.user or not getattr(request.user, 'ishtaruser', None): +            return Q(pk=None) +        ishtaruser = request.user.ishtaruser +        return Q(user=ishtaruser) | Q(shared_with=ishtaruser) +      @property      def cached_label(self):          return unicode(self) diff --git a/ishtar_common/views_item.py b/ishtar_common/views_item.py index 5cd3eb826..7c6cbc24a 100644 --- a/ishtar_common/views_item.py +++ b/ishtar_common/views_item.py @@ -902,7 +902,10 @@ def get_item(model, func_name, default_name, extra_request_keys=[],          else:              my_extra_request_keys = copy(extra_request_keys)          if base_request is None and hasattr(model, 'BASE_REQUEST'): -            my_base_request = copy(model.BASE_REQUEST) +            if callable(model.BASE_REQUEST): +                my_base_request = model.BASE_REQUEST(request) +            else: +                my_base_request = copy(model.BASE_REQUEST)          elif base_request is not None:              my_base_request = copy(base_request)          else: @@ -987,7 +990,12 @@ def get_item(model, func_name, default_name, extra_request_keys=[],          request_items = dct_request_items -        dct = my_base_request +        base_query = None +        if isinstance(my_base_request, Q): +            base_query = my_base_request +            dct = {} +        else: +            dct = my_base_request          excluded_dct = {}          and_reqs, or_reqs = [], []          exc_and_reqs, exc_or_reqs = [], [] @@ -1099,6 +1107,8 @@ def get_item(model, func_name, default_name, extra_request_keys=[],          # print(query)          items = model.objects.filter(query) +        if base_query: +            items = items.filter(base_query)          if exc_query:              items = items.exclude(exc_query) | 
