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 /ishtar_common | |
parent | c0c093276dec2a34c5702711e3c3147b98ca4df8 (diff) | |
download | Ishtar-afd7fb9c2be01a44a45f582eebdf02632a10be99.tar.bz2 Ishtar-afd7fb9c2be01a44a45f582eebdf02632a10be99.zip |
Basket: manage basket share (ro and edit) - manage alt query own
Diffstat (limited to 'ishtar_common')
-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 |
9 files changed, 129 insertions, 30 deletions
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: |