diff options
Diffstat (limited to 'ishtar_common/views_item.py')
| -rw-r--r-- | ishtar_common/views_item.py | 182 | 
1 files changed, 143 insertions, 39 deletions
diff --git a/ishtar_common/views_item.py b/ishtar_common/views_item.py index cde3f8d87..6f4abdee9 100644 --- a/ishtar_common/views_item.py +++ b/ishtar_common/views_item.py @@ -22,7 +22,8 @@ from django.db.models.fields import FieldDoesNotExist  from django.http import HttpResponse  from django.shortcuts import render  from django.template import loader -from django.utils.translation import ugettext, ugettext_lazy as _ +from django.utils.translation import ugettext, ugettext_lazy as _, \ +    activate, deactivate, pgettext_lazy  from tidylib import tidy_document as tidy  from unidecode import unidecode  from weasyprint import HTML, CSS @@ -65,17 +66,29 @@ CURRENT_ITEM_KEYS = (  CURRENT_ITEM_KEYS_DICT = dict(CURRENT_ITEM_KEYS) +def get_autocomplete_query(request, label_attributes, extra=None): +    q = request.GET.get('term') or "" +    if not label_attributes: +        return Q(pk__isnull=True) +    query = Q() +    if extra: +        query = Q(**extra) +    for q in q.split(' '): +        if not q: +            continue +        sub_q = Q(**{label_attributes[0] + "__icontains": q}) +        for other_label in label_attributes[1:]: +            sub_q = sub_q | Q(**{other_label + "__icontains": q}) +        query = query & sub_q +    return query + +  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) +        query = get_autocomplete_query(request, ['cached_label'], extra=extra)          limit = 20          objects = model.objects.filter(query)[:limit]          data = json.dumps([{'id': obj.pk, 'value': obj.cached_label} @@ -133,22 +146,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') +        except (ObjectDoesNotExist, ValueError): +            return HttpResponse('')          doc_type = 'type' in dct and dct.pop('type')          url_name = u"/".join(reverse('show-' + name, args=['0', '']                                       ).split('/')[:-2]) + u"/" @@ -325,6 +341,12 @@ def _push_to_list(obj, current_group, depth):          current_group.append(obj) +def is_true_string(val): +    val = unicode(val).lower().replace(u'"', u"") +    if val in (u"1", u"true", unicode(_(u"True")).lower()): +        return True + +  def _parse_parentheses(s):      """      Parse parentheses into list. @@ -368,14 +390,31 @@ def _parse_query_string(string, request_keys, current_dct, exc_dct):              if excluded:                  term = term[1:]              if term in request_keys: -                term = request_keys[term]                  dct = current_dct -                if excluded: -                    dct = exc_dct -                if term in dct: -                    dct[term] += u";" + query +                term = request_keys[term] +                # callable request key for complex queries +                if callable(term): +                    is_true = is_true_string(query) +                    if excluded: +                        is_true = not is_true +                    cfltr, cexclude, cextra = term(is_true=is_true) +                    if cfltr: +                        if 'and_reqs' not in dct: +                            dct['and_reqs'] = [] +                        dct['and_reqs'].append(cfltr) +                    if cexclude: +                        if 'exc_and_reqs' not in dct: +                            dct['exc_and_reqs'] = [] +                        dct['exc_and_reqs'].append(cexclude) +                    if cextra: +                        dct['extras'].append(cextra)                  else: -                    dct[term] = query +                    if excluded: +                        dct = exc_dct +                    if term in dct: +                        dct[term] += u";" + query +                    else: +                        dct[term] = query                  return u""      for reserved_char in FORBIDDEN_CHAR:          string = string.replace(reserved_char, u"") @@ -487,6 +526,8 @@ def _search_manage_search_vector(model, dct, exc_dct, request_keys):          search_query = \              search_query.replace(u'(', u'').replace(u')', u'').strip()          if search_query: +            if 'extras' not in dct: +                dct['extras'] = []              dct['extras'].append(                  {'where': [model._meta.db_table +                             ".search_vector @@ (to_tsquery(%s, %s)) = true"], @@ -526,13 +567,47 @@ def _manage_bool_fields(model, bool_fields, reversed_bool_fields, dct, or_reqs):                      pass +today_lbl = pgettext_lazy("key for text search", u"today"), +TODAYS = ['today'] + +for language_code, language_lbl in settings.LANGUAGES: +    activate(language_code) +    TODAYS.append(unicode(today_lbl)) +    deactivate() + +  def _manage_dated_fields(dated_fields, dct):      for k in dated_fields:          if k in dct:              if not dct[k]:                  dct.pop(k) +                continue +            value = dct[k].replace('"', '').strip() +            has_today = False +            for today in TODAYS: +                if value.startswith(today): +                    base_date = datetime.date.today() +                    value = value[len(today):].replace(' ', '') +                    if value and value[0] in (u"-", u"+"): +                        sign = value[0] +                        try: +                            days = int(value[1:]) +                        except ValueError: +                            days = 0 +                        if days: +                            if sign == u"-": +                                base_date = base_date - datetime.timedelta( +                                    days=days) +                            else: +                                base_date = base_date + datetime.timedelta( +                                    days=days) +                    dct[k] = base_date.strftime('%Y-%m-%d') +                    has_today = True +                    break +            if has_today: +                continue              try: -                items = dct[k].replace('"', '').split('/') +                items = value.split('/')                  assert len(items) == 3                  dct[k] = virtualtime.datetime(*map(lambda x: int(x),                                                reversed(items))) \ @@ -795,7 +870,12 @@ def _construct_query(relation_types, dct, or_reqs, and_reqs):          query |= Q(**alt_dct)      query = _manage_relation_types(relation_types, dct, query, or_reqs) +    done = []      for and_req in and_reqs: +        str_q = unicode(and_req) +        if str_q in done: +            continue +        done.append(str_q)          query = query & and_req      return query @@ -880,11 +960,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 @@ -904,6 +986,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, @@ -915,10 +999,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') @@ -934,13 +1023,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) @@ -953,32 +1045,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, @@ -1039,6 +1134,7 @@ def get_item(model, func_name, default_name, extra_request_keys=[],          excluded_dct = {}          and_reqs, or_reqs = [], []          exc_and_reqs, exc_or_reqs = [], [] +        dct['extras'], dct['and_reqs'], dct['exc_and_reqs'] = [], [], []          if full == 'shortcut':              if model.SLUG == "warehouse": @@ -1057,7 +1153,10 @@ def get_item(model, func_name, default_name, extra_request_keys=[],              if not val:                  continue              req_keys = request_keys[k] -            if type(req_keys) not in (list, tuple): +            if callable(req_keys): +                # callable request key for complex queries not managed on GET +                continue +            elif type(req_keys) not in (list, tuple):                  dct[req_keys] = val                  continue              # multiple choice target @@ -1080,7 +1179,6 @@ def get_item(model, func_name, default_name, extra_request_keys=[],          else:              request.session[func_name] = dct -        dct['extras'] = []          dct, excluded_dct = _search_manage_search_vector(              model, dct, excluded_dct, request_keys)          search_vector = "" @@ -1116,7 +1214,13 @@ def get_item(model, func_name, default_name, extra_request_keys=[],          _manage_facet_search(model, dct, and_reqs)          _manage_facet_search(model, excluded_dct, exc_and_reqs) -        extras = dct.pop('extras') +        extras = [] +        if 'extras' in dct: +            extras = dct.pop('extras') +        if 'and_reqs' in dct: +            and_reqs += dct.pop('and_reqs') +        if 'exc_and_reqs' in dct: +            exc_and_reqs += dct.pop('exc_and_reqs')          _manage_clean_search_field(dct)          _manage_clean_search_field(excluded_dct)  | 
