diff options
| author | Étienne Loks <etienne.loks@iggdrasil.net> | 2018-11-29 19:34:31 +0100 | 
|---|---|---|
| committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2018-11-29 19:34:31 +0100 | 
| commit | afd7fb9c2be01a44a45f582eebdf02632a10be99 (patch) | |
| tree | 05cdd1da4246ddb4e3e8b69c9a968f3e0d14d7b9 | |
| parent | c0c093276dec2a34c5702711e3c3147b98ca4df8 (diff) | |
| download | Ishtar-afd7fb9c2be01a44a45f582eebdf02632a10be99.tar.bz2 Ishtar-afd7fb9c2be01a44a45f582eebdf02632a10be99.zip | |
Basket: manage basket share (ro and edit) - manage alt query own
| -rw-r--r-- | archaeological_finds/forms.py | 72 | ||||
| -rw-r--r-- | archaeological_finds/migrations/0042_auto_20181129_1755.py | 30 | ||||
| -rw-r--r-- | archaeological_finds/models_finds.py | 24 | ||||
| -rw-r--r-- | archaeological_finds/templates/ishtar/sheet_findbasket.html | 3 | ||||
| -rw-r--r-- | archaeological_finds/urls.py | 2 | ||||
| -rw-r--r-- | archaeological_finds/views.py | 17 | ||||
| -rw-r--r-- | archaeological_finds/wizards.py | 1 | ||||
| -rw-r--r-- | ishtar_common/migrations/0077_auto_20181129_1755.py | 20 | ||||
| -rw-r--r-- | ishtar_common/models.py | 17 | ||||
| -rw-r--r-- | ishtar_common/templates/ishtar/blocks/window_field_flex_multiple_full.html | 8 | ||||
| -rw-r--r-- | ishtar_common/templates/ishtar/form.html | 3 | ||||
| -rw-r--r-- | ishtar_common/templatetags/window_field.py | 8 | ||||
| -rw-r--r-- | ishtar_common/urls.py | 2 | ||||
| -rw-r--r-- | ishtar_common/views.py | 23 | ||||
| -rw-r--r-- | ishtar_common/views_item.py | 60 | ||||
| -rw-r--r-- | ishtar_common/wizards.py | 18 | ||||
| -rw-r--r-- | scss/custom.scss | 1 | 
17 files changed, 256 insertions, 53 deletions
| diff --git a/archaeological_finds/forms.py b/archaeological_finds/forms.py index 90e1390bf..811e71a60 100644 --- a/archaeological_finds/forms.py +++ b/archaeological_finds/forms.py @@ -57,7 +57,7 @@ from ishtar_common.forms import CustomForm, CustomFormSearch, FormSet, \      PkWizardSearch  from ishtar_common.forms_common import get_town_field  from ishtar_common.models import valid_id, valid_ids, get_current_profile, \ -    SpatialReferenceSystem, Area, OperationType +    SpatialReferenceSystem, Area, OperationType, IshtarUser  from ishtar_common.utils import convert_coordinates_to_point  __all__ = [ @@ -74,6 +74,7 @@ __all__ = [      'DashboardTreatmentForm', 'DashboardTreatmentFileForm',      'RecordFormSelection', 'FindForm', 'SimpleFindForm', 'DateForm',      'DatingFormSet', 'PreservationForm', 'FindBasketFormSelection', +    'FindBasketForWriteFormSelection',      'FindBasketForm', 'FindSelect', 'FindFormSelection',      'FindFormSelectionWarehouseModule', 'MultipleFindFormSelection',      'MultipleFindFormSelectionWarehouseModule', 'FindMultipleFormSelection', @@ -883,7 +884,8 @@ class FindSelect(HistorySelect):              if self.current_user:                  self.fields['basket'].choices += [                      (b.pk, b.label) for b in models.FindBasket.objects.filter( -                        user=self.current_user).all()] +                        models.FindBasket.get_query_owns(self.current_user) +                    ).all()]      def get_input_ids(self):          ids = super(FindSelect, self).get_input_ids() @@ -1169,19 +1171,57 @@ class FindBasketFormSelection(CustomFormSearch):          validators=[valid_id(models.FindBasket)]) +class FindBasketForWriteFormSelection(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-write'), +            FindBasketSelect, models.FindBasket, +        ), +        validators=[valid_id(models.FindBasket)]) + +  class FindBasketForm(IshtarForm):      form_label = _(u"Find basket") +    associated_models = {"shared_with": IshtarUser, +                         "shared_write_with": IshtarUser}      label = forms.CharField(          label=_(u"Label"),          validators=[validators.MaxLengthValidator(1000)])      comment = forms.CharField(label=_(u"Comment"),                                widget=forms.Textarea, required=False) +    shared_with = widgets.Select2MultipleField( +        model=IshtarUser, remote=True, +        label=_(u"Shared (read) with"), +        required=False, long_widget=True +    ) +    shared_write_with = widgets.Select2MultipleField( +        model=IshtarUser, remote=True, +        label=_(u"Shared (read/edit) with"), +        required=False, long_widget=True +    ) -class NewFindBasketForm(forms.ModelForm): +class NewFindBasketForm(forms.ModelForm, IshtarForm): +    shared_with = widgets.Select2MultipleField( +        model=IshtarUser, remote=True, +        label=_(u"Shared (read) with"), +        required=False, long_widget=True +    ) +    shared_write_with = widgets.Select2MultipleField( +        model=IshtarUser, remote=True, +        label=_(u"Shared (read/edit) with"), +        required=False, long_widget=True +    ) +      class Meta:          model = models.FindBasket -        fields = ('label', 'comment') +        fields = ('label', 'comment', 'shared_with', 'shared_write_with')      def __init__(self, *args, **kwargs):          self.user = kwargs.pop('user') @@ -1217,14 +1257,26 @@ class SelectFindBasketForm(IshtarForm):          super(SelectFindBasketForm, self).__init__(*args, **kwargs)          if not self.user:              return -        self.fields['basket'].choices = [('', '--')] + [ -            (b.pk, unicode(b)) +        self.fields['basket'].choices = self.get_basket_choices() + +    def get_basket_choices(self): +        return [('', u'--')] + [ +            (str(b.pk), unicode(b))              for b in models.FindBasket.objects.filter( -                Q(user=self.user) | Q(shared_with=self.user) -            )] +                Q(user=self.user) | Q(shared_write_with=self.user) +            ) +        ]  class DeleteFindBasketForm(SelectFindBasketForm): +    def get_basket_choices(self): +        return [('', u'--')] + [ +            (str(b.pk), unicode(b)) +            for b in models.FindBasket.objects.filter( +                Q(user=self.user) +            ) +        ] +      def save(self):          try:              models.FindBasket.objects.get(pk=self.cleaned_data['basket'], @@ -1242,8 +1294,8 @@ class FindBasketAddItemForm(forms.Form):      def save(self, user):          try:              basket = models.FindBasket.objects.filter( -                Q(user=user) | Q(shared_with=user) -            ).get(pk=self.cleaned_data['basket_id']) +                Q(user=user) | Q(shared_with=user) | Q(shared_write_with=user) +            ).distinct().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/migrations/0042_auto_20181129_1755.py b/archaeological_finds/migrations/0042_auto_20181129_1755.py new file mode 100644 index 000000000..42d732cf2 --- /dev/null +++ b/archaeological_finds/migrations/0042_auto_20181129_1755.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.10 on 2018-11-29 17:55 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + +    dependencies = [ +        ('ishtar_common', '0077_auto_20181129_1755'), +        ('archaeological_finds', '0041_auto_20181121_1225'), +    ] + +    operations = [ +        migrations.AlterModelOptions( +            name='treatmentfile', +            options={'ordering': ('cached_label',), 'permissions': (('view_treatmentfile', 'Can view all Treatment requests'), ('view_own_treatmentfile', 'Can view own Treatment request'), ('add_own_treatmentfile', 'Can add own Treatment request'), ('change_own_treatmentfile', 'Can change own Treatment request'), ('delete_own_treatmentfile', 'Can delete own Treatment request')), 'verbose_name': 'Treatment request', 'verbose_name_plural': 'Treatment requests'}, +        ), +        migrations.AddField( +            model_name='findbasket', +            name='shared_write_with', +            field=models.ManyToManyField(blank=True, related_name='shared_write_findbaskets', to='ishtar_common.IshtarUser', verbose_name='Shared (read/edit) with'), +        ), +        migrations.AlterField( +            model_name='findbasket', +            name='shared_with', +            field=models.ManyToManyField(blank=True, related_name='shared_findbaskets', to='ishtar_common.IshtarUser', verbose_name='Shared (read) with'), +        ), +    ] diff --git a/archaeological_finds/models_finds.py b/archaeological_finds/models_finds.py index 9baced4e0..0c9770186 100644 --- a/archaeological_finds/models_finds.py +++ b/archaeological_finds/models_finds.py @@ -591,6 +591,11 @@ class FindBasket(Basket, OwnPerms):      @classmethod      def get_query_owns(cls, ishtaruser): +        return Q(user=ishtaruser) | Q(shared_with=ishtaruser) | Q( +            shared_write_with=ishtaruser) + +    @classmethod +    def get_write_query_owns(cls, ishtaruser):          return Q(user=ishtaruser)      def get_extra_actions(self, request): @@ -599,13 +604,18 @@ class FindBasket(Basket, OwnPerms):          """          # url, base_text, icon, extra_text, extra css class, is a quick action -        # no particular rights: if you can view an itm you can add it to your -        # own basket -        actions = [ -            (reverse("select_itemsinbasket", args=[self.pk]), -             _(u"Manage basket"), -             "fa fa-shopping-basket", "", "", False), -        ] +        if not request.user or not request.user.ishtaruser: +            return [] + +        ishtaruser = request.user.ishtaruser +        actions = [] +        if self.user == ishtaruser or ishtaruser.pk in [ +                user.pk for user in self.shared_write_with.all()]: +            actions = [ +                (reverse("select_itemsinbasket", args=[self.pk]), +                 _(u"Manage basket"), +                 "fa fa-shopping-basket", "", "", False), +            ]          return actions diff --git a/archaeological_finds/templates/ishtar/sheet_findbasket.html b/archaeological_finds/templates/ishtar/sheet_findbasket.html index 3c3ca1d3f..4a101d8f2 100644 --- a/archaeological_finds/templates/ishtar/sheet_findbasket.html +++ b/archaeological_finds/templates/ishtar/sheet_findbasket.html @@ -12,8 +12,9 @@  <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 %} +    {% field_flex_multiple_full "Shared (read) with" item.shared_with %} +    {% field_flex_multiple_full "Shared (read/edit) with" item.shared_write_with %}  </div>  <h3>{% trans "Content" %}</h3> diff --git a/archaeological_finds/urls.py b/archaeological_finds/urls.py index 6e9f59b8c..0ef3bac89 100644 --- a/archaeological_finds/urls.py +++ b/archaeological_finds/urls.py @@ -45,6 +45,8 @@ urlpatterns = [          views.find_modify, name='find_modify'),      url(r'get-findbasket/$', views.get_find_basket,          name='get-findbasket'), +    url(r'get-findbasket-write/$', views.get_find_basket_for_write, +        name='get-findbasket-write'),      url(r'find_basket_search/(?P<step>.+)?$',          check_rights(['view_find', 'view_own_find'])(              views.basket_search_wizard), name='find_basket_search'), diff --git a/archaeological_finds/views.py b/archaeological_finds/views.py index 29f0f75af..125567044 100644 --- a/archaeological_finds/views.py +++ b/archaeological_finds/views.py @@ -109,12 +109,19 @@ show_find = show_item(models.Find, 'find')  display_find = display_item(models.Find)  revert_find = revert_item(models.Find) -show_findbasket = show_item(models.FindBasket, 'findbasket') +show_findbasket = show_item(models.FindBasket, 'findbasket', +                            model_for_perms=models.Find)  display_findbasket = display_item(models.FindBasket,                                    show_url='show-find/basket-')  get_find_basket = get_item(      models.FindBasket, 'get_findbasket', 'findbasket', +    model_for_perms=models.Find +) + +get_find_basket_for_write = get_item( +    models.FindBasket, 'get_findbasket', 'findbasket', +    model_for_perms=models.Find, alt_query_own='get_write_query_owns'  )  basket_search_wizard = FindBasketSearch.as_view( @@ -125,10 +132,10 @@ basket_search_wizard = FindBasketSearch.as_view(  basket_modify_wizard = FindBasketEditWizard.as_view(      [ -        ('selec-find_basket_modification', FindBasketFormSelection), +        ('selec-find_basket_modification', FindBasketForWriteFormSelection),          ('basket-find_basket_modification', FindBasketForm),          ('final-find_basket_modification', FinalForm) -     ], +    ],      label=_(u"Basket modify"),      url_name='find_basket_modification',  ) @@ -290,8 +297,8 @@ 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) +                Q(user=user) | Q(shared_with=user) | Q(shared_write_with=user) +            ).distinct().get(pk=pk)          except models.FindBasket.DoesNotExist:              raise PermissionDenied diff --git a/archaeological_finds/wizards.py b/archaeological_finds/wizards.py index 43f48ab59..6d4fd2d95 100644 --- a/archaeological_finds/wizards.py +++ b/archaeological_finds/wizards.py @@ -500,3 +500,4 @@ class FindBasketWizard(Wizard):  class FindBasketEditWizard(FindBasketWizard):      edit = True +    alt_is_own_method = 'get_write_query_owns' diff --git a/ishtar_common/migrations/0077_auto_20181129_1755.py b/ishtar_common/migrations/0077_auto_20181129_1755.py new file mode 100644 index 000000000..bd9003946 --- /dev/null +++ b/ishtar_common/migrations/0077_auto_20181129_1755.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.10 on 2018-11-29 17:55 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + +    dependencies = [ +        ('ishtar_common', '0076_migrate_treatmentfile_permissions'), +    ] + +    operations = [ +        migrations.AlterField( +            model_name='importertype', +            name='is_template', +            field=models.BooleanField(default=False, verbose_name='Can be exported'), +        ), +    ] diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 1aa94836f..1a0d80ac3 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -254,7 +254,7 @@ class OwnPerms(object):                     action_own_name, request.session)                  and self.is_own(request.user.ishtaruser)) -    def is_own(self, user): +    def is_own(self, user, alt_query_own=None):          """          Check if the current object is owned by the user          """ @@ -264,7 +264,10 @@ class OwnPerms(object):              ishtaruser = user.ishtaruser          else:              return False -        query = self.get_query_owns(ishtaruser) +        if not alt_query_own: +            query = self.get_query_owns(ishtaruser) +        else: +            query = getattr(self, alt_query_own)(ishtaruser)          if not query:              return False          query &= Q(pk=self.pk) @@ -3293,6 +3296,7 @@ post_save.connect(post_save_userprofile, sender=UserProfile)  class IshtarUser(FullSearch): +    SLUG = "ishtaruser"      TABLE_COLS = ('username', 'person__name', 'person__surname',                    'person__email', 'person__person_types_list',                    'person__attached_to__name') @@ -3441,9 +3445,13 @@ class Basket(FullSearch):          verbose_name=_(u"Owner"))      available = models.BooleanField(_(u"Available"), default=True)      shared_with = models.ManyToManyField( -        IshtarUser, verbose_name=_(u"Shared with"), blank=True, +        IshtarUser, verbose_name=_(u"Shared (read) with"), blank=True,          related_name='shared_%(class)ss'      ) +    shared_write_with = models.ManyToManyField( +        IshtarUser, verbose_name=_(u"Shared (read/edit) with"), blank=True, +        related_name='shared_write_%(class)ss' +    )      TABLE_COLS = ['label', 'user'] @@ -3462,7 +3470,8 @@ class Basket(FullSearch):          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) +        return Q(user=ishtaruser) | Q(shared_with=ishtaruser) | Q( +            shared_write_with=ishtaruser)      @property      def cached_label(self): diff --git a/ishtar_common/templates/ishtar/blocks/window_field_flex_multiple_full.html b/ishtar_common/templates/ishtar/blocks/window_field_flex_multiple_full.html new file mode 100644 index 000000000..b70c1d2fc --- /dev/null +++ b/ishtar_common/templates/ishtar/blocks/window_field_flex_multiple_full.html @@ -0,0 +1,8 @@ +{% load i18n %}{% if data.count %} +    <dl class="col-12 row"> +        <dt class="col-12">{% trans caption %}</dt> +        <dd class="col-12">{% for d in data.distinct.all %} +            {% if forloop.counter0 %} ; {% endif %}{{ d }} +        {% endfor %}</dd> +    </dl> +{% endif %} diff --git a/ishtar_common/templates/ishtar/form.html b/ishtar_common/templates/ishtar/form.html index b99d504a0..bcd69959e 100644 --- a/ishtar_common/templates/ishtar/form.html +++ b/ishtar_common/templates/ishtar/form.html @@ -1,5 +1,8 @@  {% 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 %} diff --git a/ishtar_common/templatetags/window_field.py b/ishtar_common/templatetags/window_field.py index 46329a3fa..a5bae3b72 100644 --- a/ishtar_common/templatetags/window_field.py +++ b/ishtar_common/templatetags/window_field.py @@ -108,6 +108,14 @@ def field_flex_multiple(caption, data, small=False):      return field_multiple(caption, data, size=size) +@register.inclusion_tag('ishtar/blocks/window_field_flex_multiple_full.html') +def field_flex_multiple_full(caption, data, small=False): +    size = None +    if small: +        size = 2 +    return field_multiple(caption, data, size=size) + +  @register.inclusion_tag('ishtar/blocks/window_field_detail.html')  def field_detail(caption, item, li=False, size=None):      return {'caption': caption, 'item': item, 'link': link_to_window(item), diff --git a/ishtar_common/urls.py b/ishtar_common/urls.py index 8d06b6862..aea419d08 100644 --- a/ishtar_common/urls.py +++ b/ishtar_common/urls.py @@ -174,6 +174,8 @@ urlpatterns += [          views.new_person_noorga, name='new-person-noorga'),      url(r'autocomplete-user/$',          views.autocomplete_user, name='autocomplete-user'), +    url(r'autocomplete-ishtaruser/$', +        views.autocomplete_ishtaruser, name='autocomplete-ishtaruser'),      url(r'autocomplete-person(?:/([0-9_]+))?(?:/([0-9_]*))?/(user)?$',          views.autocomplete_person, name='autocomplete-person'),      url(r'autocomplete-person-permissive(?:/([0-9_]+))?(?:/([0-9_]*))?' diff --git a/ishtar_common/views.py b/ishtar_common/views.py index 3d64535d4..bc9c9432a 100644 --- a/ishtar_common/views.py +++ b/ishtar_common/views.py @@ -527,6 +527,29 @@ def autocomplete_user(request):      return HttpResponse(data, content_type='text/plain') +def autocomplete_ishtaruser(request): +    if not request.user.has_perm('ishtar_common.view_person', models.Person): +        return HttpResponse('[]', content_type='text/plain') +    q = request.GET.get('term') +    limit = request.GET.get('limit', 20) +    try: +        limit = int(limit) +    except ValueError: +        return HttpResponseBadRequest() +    query = Q() +    for q in q.split(' '): +        qu = (Q(person__name__icontains=q) | +              Q(person__surname__icontains=q) | +              Q(person__raw_name__icontains=q)) +        query = query & qu +    users = models.IshtarUser.objects.filter(query)[:limit] +    data = json.dumps([ +        {'id': user.pk, +         'value': unicode(user)} +        for user in users]) +    return HttpResponse(data, content_type='text/plain') + +  def autocomplete_person(request, person_types=None, attached_to=None,                          is_ishtar_user=None, permissive=False):      all_items = request.user.has_perm('ishtar_common.view_person', diff --git a/ishtar_common/views_item.py b/ishtar_common/views_item.py index 71890fa6e..f34b2357f 100644 --- a/ishtar_common/views_item.py +++ b/ishtar_common/views_item.py @@ -133,22 +133,25 @@ def display_item(model, extra_dct=None, show_url=None):      return func -def show_item(model, name, extra_dct=None): +def show_item(model, name, extra_dct=None, model_for_perms=None):      def func(request, pk, **dct): -        allowed, own = check_model_access_control(request, model) +        check_model = model +        if model_for_perms: +            check_model = model_for_perms +        allowed, own = check_model_access_control(request, check_model)          if not allowed:              return HttpResponse('', content_type="application/xhtml")          q = model.objects          if own:              if not hasattr(request.user, 'ishtaruser'): -                return HttpResponse('NOK') +                return HttpResponse('')              query_own = model.get_query_owns(request.user.ishtaruser)              if query_own:                  q = q.filter(query_own).distinct()          try:              item = q.get(pk=pk)          except ObjectDoesNotExist: -            return HttpResponse('NOK') +            return HttpResponse('')          doc_type = 'type' in dct and dct.pop('type')          url_name = u"/".join(reverse('show-' + name, args=['0', '']                                       ).split('/')[:-2]) + u"/" @@ -879,11 +882,13 @@ DEFAULT_ROW_NUMBER = 10  EXCLUDED_FIELDS = ['length'] -def get_item(model, func_name, default_name, extra_request_keys=[], -             base_request=None, bool_fields=[], reversed_bool_fields=[], -             dated_fields=[], associated_models=[], relative_session_names=[], -             specific_perms=[], own_table_cols=None, relation_types_prefix={}, -             do_not_deduplicate=False): +def get_item(model, func_name, default_name, extra_request_keys=None, +             base_request=None, bool_fields=None, reversed_bool_fields=None, +             dated_fields=None, associated_models=None, +             relative_session_names=None, specific_perms=None, +             own_table_cols=None, relation_types_prefix=None, +             do_not_deduplicate=False, model_for_perms=None, +             alt_query_own=None):      """      Generic treatment of tables @@ -903,6 +908,8 @@ def get_item(model, func_name, default_name, extra_request_keys=[],      :param do_not_deduplicate: duplication of id can occurs on large queryset a      mecanism of deduplication is used. But duplicate ids can be normal (for      instance for record_relations view). +    :param model_for_perms: use another model to check permission +    :param alt_query_own: name of alternate method to get query_own      :return:      """      def func(request, data_type='json', full=False, force_own=False, @@ -914,10 +921,15 @@ def get_item(model, func_name, default_name, extra_request_keys=[],          if 'type' in dct:              data_type = dct.pop('type')          if not data_type: -            EMPTY = '[]'              data_type = 'json' +        if data_type == "json": +            EMPTY = '[]' -        allowed, own = check_model_access_control(request, model, +        model_to_check = model +        if model_for_perms: +            model_to_check = model_for_perms + +        allowed, own = check_model_access_control(request, model_to_check,                                                    available_perms)          if not allowed:              return HttpResponse(EMPTY, content_type='text/plain') @@ -933,13 +945,16 @@ def get_item(model, func_name, default_name, extra_request_keys=[],              q = models.IshtarUser.objects.filter(user_ptr=request.user)              if not q.count():                  return HttpResponse(EMPTY, content_type='text/plain') -            query_own = model.get_query_owns(q.all()[0]) +            if alt_query_own: +                query_own = getattr(model, alt_query_own)(q.all()[0]) +            else: +                query_own = model.get_query_owns(q.all()[0])          # get defaults from model          if not extra_request_keys and hasattr(model, 'EXTRA_REQUEST_KEYS'):              my_extra_request_keys = copy(model.EXTRA_REQUEST_KEYS)          else: -            my_extra_request_keys = copy(extra_request_keys) +            my_extra_request_keys = copy(extra_request_keys or [])          if base_request is None and hasattr(model, 'BASE_REQUEST'):              if callable(model.BASE_REQUEST):                  my_base_request = model.BASE_REQUEST(request) @@ -952,32 +967,35 @@ def get_item(model, func_name, default_name, extra_request_keys=[],          if not bool_fields and hasattr(model, 'BOOL_FIELDS'):              my_bool_fields = model.BOOL_FIELDS[:]          else: -            my_bool_fields = bool_fields[:] +            my_bool_fields = bool_fields[:] if bool_fields else []          if not reversed_bool_fields and hasattr(model, 'REVERSED_BOOL_FIELDS'):              my_reversed_bool_fields = model.REVERSED_BOOL_FIELDS[:]          else: -            my_reversed_bool_fields = reversed_bool_fields[:] +            my_reversed_bool_fields = reversed_bool_fields[:] \ +                if reversed_bool_fields else []          if not dated_fields and hasattr(model, 'DATED_FIELDS'):              my_dated_fields = model.DATED_FIELDS[:]          else: -            my_dated_fields = dated_fields[:] +            my_dated_fields = dated_fields[:] if dated_fields else []          if not associated_models and hasattr(model, 'ASSOCIATED_MODELS'):              my_associated_models = model.ASSOCIATED_MODELS[:]          else: -            my_associated_models = associated_models[:] +            my_associated_models = associated_models[:] \ +                if associated_models else []          if not relative_session_names and hasattr(model,                                                    'RELATIVE_SESSION_NAMES'):              my_relative_session_names = model.RELATIVE_SESSION_NAMES[:]          else: -            my_relative_session_names = relative_session_names[:] +            my_relative_session_names = relative_session_names[:] \ +                if relative_session_names else []          if not relation_types_prefix and hasattr(model,                                                   'RELATION_TYPES_PREFIX'):              my_relation_types_prefix = copy(model.RELATION_TYPES_PREFIX)          else: -            my_relation_types_prefix = copy(relation_types_prefix) +            my_relation_types_prefix = copy(relation_types_prefix) \ +                if relation_types_prefix else {} -        fields = [model._meta.get_field(k) -                  for k in get_all_field_names(model)] +        fields = [model._meta.get_field(k) for k in get_all_field_names(model)]          request_keys = dict([              (field.name, diff --git a/ishtar_common/wizards.py b/ishtar_common/wizards.py index b3fc4b55a..19eb312e1 100644 --- a/ishtar_common/wizards.py +++ b/ishtar_common/wizards.py @@ -23,6 +23,7 @@ import os  # from functools import wraps  from django.conf import settings +from django.contrib import messages  from formtools.wizard.views import NamedUrlWizardView, normalize_name, \      get_storage, StepsHelper @@ -115,6 +116,7 @@ class Wizard(IshtarWizard):      )      main_item_select_keys = ('selec-',)      formset_pop_deleted = True +    alt_is_own_method = None  # alternate method name for "is_own" check      saved_args = {}  # argument to pass on object save @@ -164,11 +166,17 @@ class Wizard(IshtarWizard):              ishtaruser = request.user.ishtaruser \                  if hasattr(request.user, 'ishtaruser') else None -            # not the fisrt step and current object is not owned -            if self.steps and self.steps.first != step and\ -                    current_object and not current_object.is_own(ishtaruser): -                self.session_reset(request, self.url_name) -                return HttpResponseRedirect('/') +            # not the first step and current object is not owned +            if self.steps and self.steps.first != step and current_object: +                is_own = current_object.is_own( +                    ishtaruser, alt_query_own=self.alt_is_own_method) +                if not is_own: +                    messages.add_message( +                        request, messages.WARNING, +                        _(u"Permission error: you cannot do this action.") +                    ) +                    self.session_reset(request, self.url_name) +                    return HttpResponseRedirect('/')              # extra filter on forms              self.filter_owns_items = True          else: diff --git a/scss/custom.scss b/scss/custom.scss index 09074de0b..56dbffab6 100644 --- a/scss/custom.scss +++ b/scss/custom.scss @@ -610,6 +610,7 @@ ul.compact{      margin: 0;      display: block;      outline: none; +    font-size: 1.1em;  }  .ui-helper-hidden { | 
