diff options
author | Étienne Loks <etienne.loks@iggdrasil.net> | 2018-06-05 20:42:14 +0200 |
---|---|---|
committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2018-06-12 09:57:24 +0200 |
commit | 52f6b37f1a1deac66f0b84c466be6c8dab277514 (patch) | |
tree | 2e9d8c696298f89e33e713d4eaf2a4c1c48af3b5 /ishtar_common/views.py | |
parent | e7418c19b122c5ac0505ad2be5350068d3bf6f6b (diff) | |
download | Ishtar-52f6b37f1a1deac66f0b84c466be6c8dab277514.tar.bz2 Ishtar-52f6b37f1a1deac66f0b84c466be6c8dab277514.zip |
Document form - refactoring (refs #4107)
Diffstat (limited to 'ishtar_common/views.py')
-rw-r--r-- | ishtar_common/views.py | 1003 |
1 files changed, 47 insertions, 956 deletions
diff --git a/ishtar_common/views.py b/ishtar_common/views.py index 187712676..81397dd70 100644 --- a/ishtar_common/views.py +++ b/ishtar_common/views.py @@ -17,74 +17,50 @@ # See the file COPYING for details. -from copy import copy, deepcopy import csv import datetime import json import logging -from markdown import markdown -import optparse -import re -from tempfile import NamedTemporaryFile -from tidylib import tidy_document as tidy import unicodedata -from unidecode import unidecode -import unicodecsv -from weasyprint import HTML, CSS -from weasyprint.fonts import FontConfiguration - -from extra_views import ModelFormSetView +import unicodecsv from django.conf import settings from django.contrib.auth import logout from django.contrib.auth.decorators import login_required -from django.contrib.postgres.search import SearchQuery -from django.contrib.staticfiles.templatetags.staticfiles import static from django.core.exceptions import ObjectDoesNotExist from django.core.urlresolvers import reverse, NoReverseMatch -from django.db.models import Q, ImageField -from django.db.models.fields import FieldDoesNotExist +from django.db.models import Q from django.forms.models import modelformset_factory from django.http import HttpResponse, Http404, HttpResponseRedirect, \ HttpResponseBadRequest from django.shortcuts import redirect, render -from django.template import loader from django.utils.decorators import method_decorator from django.utils.translation import ugettext, ugettext_lazy as _ -from django.views.generic import ListView, UpdateView, TemplateView, FormView +from django.views.generic import ListView, UpdateView, TemplateView from django.views.generic.edit import CreateView, DeleteView, FormView +from extra_views import ModelFormSetView +from markdown import markdown -from xhtml2odt import xhtml2odt - -from menus import Menu - -from archaeological_files.models import File -from archaeological_operations.models import Operation +import models from archaeological_context_records.models import ContextRecord -from archaeological_finds.models import Find, Treatment, TreatmentFile, \ - FindBasket - -from archaeological_operations.forms import DashboardForm as DashboardFormOpe from archaeological_files.forms import DashboardForm as DashboardFormFile +from archaeological_files.models import File from archaeological_finds.forms import DashboardTreatmentForm, \ DashboardTreatmentFileForm - -from ishtar_common.forms import FinalForm, FinalDeleteForm -from ishtar_common.widgets import JQueryAutoComplete -from ishtar_common.utils import clean_session_cache, \ - get_all_field_names, get_field_labels_from_path, \ - get_random_item_image_link, shortify +from archaeological_finds.models import Find, Treatment, TreatmentFile +from archaeological_operations.forms import DashboardForm as DashboardFormOpe +from archaeological_operations.models import Operation from ishtar_common import forms_common as forms from ishtar_common import wizards -from ishtar_common.models import HistoryError, PRIVATE_FIELDS, \ - get_current_profile - +from ishtar_common.forms import FinalForm, FinalDeleteForm +from ishtar_common.models import get_current_profile from ishtar_common.templatetags.link_to_window import link_to_window +from ishtar_common.utils import clean_session_cache, CSV_OPTIONS, \ + get_field_labels_from_path, get_random_item_image_link, shortify +from ishtar_common.widgets import JQueryAutoComplete -import models - -CSV_OPTIONS = {'delimiter': ',', 'quotechar': '"', 'quoting': csv.QUOTE_ALL} -ENCODING = settings.ENCODING or 'utf-8' +from views_item import CURRENT_ITEM_KEYS, display_item, get_item, new_item, \ + show_item logger = logging.getLogger(__name__) @@ -367,15 +343,6 @@ def shortcut_menu(request): return render(request, 'ishtar/blocks/shortcut_menu.html', dct) -CURRENT_ITEM_KEYS = (('file', File), - ('operation', Operation), - ('contextrecord', ContextRecord), - ('find', Find), - ('treatmentfile', TreatmentFile), - ('treatment', Treatment)) -CURRENT_ITEM_KEYS_DICT = dict(CURRENT_ITEM_KEYS) - - def get_current_items(request): currents = {} for key, model in CURRENT_ITEM_KEYS: @@ -466,17 +433,27 @@ def update_current_item(request, item_type=None, pk=None): return HttpResponse('ok') -def check_permission(request, action_slug, obj_id=None): - MAIN_MENU = Menu(None) - MAIN_MENU.init() - if action_slug not in MAIN_MENU.items: - # TODO - return True - if obj_id: - return MAIN_MENU.items[action_slug].is_available( - request.user, obj_id, session=request.session) - return MAIN_MENU.items[action_slug].can_be_available( - request.user, session=request.session) +def get_by_importer(request, slug, data_type='json', full=False, + force_own=False, **dct): + q = models.ImporterType.objects.filter(slug=slug) + if not q.count(): + res = '' + if data_type == "json": + res = '{}' + return HttpResponse(res, content_type='text/plain') + imp = q.all()[0].get_importer_class() + cols, col_names = [], [] + for formater in imp.LINE_EXPORT_FORMAT: + if not formater: + cols.append('') + col_names.append("") + continue + cols.append(formater.export_field_name) + col_names.append(formater.label) + obj_name = imp.OBJECT_CLS.__name__.lower() + return get_item( + imp.OBJECT_CLS, 'get_' + obj_name, obj_name, own_table_cols=cols + )(request, data_type, full, force_own, col_names=col_names, **dct) def autocomplete_person_permissive(request, person_types=None, @@ -603,873 +580,6 @@ def department_by_state(request, state_id=''): return HttpResponse(data, content_type='text/plain') -def format_val(val): - if val is None: - return u"" - if type(val) == bool: - if val: - return unicode(_(u"True")) - else: - return unicode(_(u"False")) - if type(val) == str: - val = val.decode('utf-8') - return unicode(val) - - -HIERARCHIC_LEVELS = 5 -HIERARCHIC_FIELDS = ['periods', 'period', 'unit', 'material_types', - 'material_type', 'conservatory_state', 'object_types'] - - -def _get_values(request, val): - if hasattr(val, 'all'): # manage related objects - vals = list(val.all()) - else: - vals = [val] - new_vals = [] - for v in vals: - if callable(v): - v = v() - if hasattr(v, 'url'): - v = request.is_secure() and \ - 'https' or 'http' + '://' + \ - request.get_host() + v.url - new_vals.append(v) - return new_vals - - -def _search_manage_search_vector(dct): - if 'search_vector' in dct: - dct['search_vector'] = SearchQuery( - unidecode(dct['search_vector']), - config=settings.ISHTAR_SEARCH_LANGUAGE - ) - return dct - - -DEFAULT_ROW_NUMBER = 10 -# length is used by ajax DataTables requests -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): - """ - Generic treatment of tables - - :param model: model used for query - :param func_name: name of the function (used for session storage) - :param default_name: key used for default search in session - :param extra_request_keys: default query limitation - :param base_request: - :param bool_fields: - :param reversed_bool_fields: - :param dated_fields: - :param associated_models: - :param relative_session_names: - :param specific_perms: - :param own_table_cols: - :param relation_types_prefix: - :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). - :return: - """ - def func(request, data_type='json', full=False, force_own=False, - col_names=None, **dct): - available_perms = [] - if specific_perms: - available_perms = specific_perms[:] - EMPTY = '' - if 'type' in dct: - data_type = dct.pop('type') - if not data_type: - EMPTY = '[]' - data_type = 'json' - - allowed, own = models.check_model_access_control(request, model, - available_perms) - if not allowed: - return HttpResponse(EMPTY, content_type='text/plain') - - if force_own: - own = True - if full == 'shortcut' and 'SHORTCUT_SEARCH' in request.session and \ - request.session['SHORTCUT_SEARCH'] == 'own': - own = True - - # 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) - if base_request is None and hasattr(model, 'BASE_REQUEST'): - my_base_request = copy(model.BASE_REQUEST) - elif base_request is not None: - my_base_request = copy(base_request) - else: - my_base_request = {} - if not bool_fields and hasattr(model, 'BOOL_FIELDS'): - my_bool_fields = model.BOOL_FIELDS[:] - else: - my_bool_fields = bool_fields[:] - 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[:] - if not dated_fields and hasattr(model, 'DATED_FIELDS'): - my_dated_fields = model.DATED_FIELDS[:] - else: - my_dated_fields = dated_fields[:] - if not associated_models and hasattr(model, 'ASSOCIATED_MODELS'): - my_associated_models = model.ASSOCIATED_MODELS[:] - else: - my_associated_models = associated_models[:] - 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[:] - 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) - - fields = [model._meta.get_field(k) - for k in get_all_field_names(model)] - - request_keys = dict([ - (field.name, - field.name + (hasattr(field, 'rel') and field.rel and '__pk' - or '')) - for field in fields]) - for associated_model, key in my_associated_models: - if type(associated_model) in (str, unicode): - if associated_model not in globals(): - continue - associated_model = globals()[associated_model] - associated_fields = [ - associated_model._meta.get_field(k) - for k in get_all_field_names(associated_model)] - request_keys.update( - dict([(key + "__" + field.name, - key + "__" + field.name + - (hasattr(field, 'rel') and field.rel and '__pk' or '')) - for field in associated_fields])) - request_keys.update(my_extra_request_keys) - request_items = request.method == 'POST' and request.POST \ - or request.GET - - # pager - try: - row_nb = int(request_items.get('length')) - except (ValueError, TypeError): - row_nb = DEFAULT_ROW_NUMBER - dct_request_items = {} - - # filter requested fields - for k in request_items: - if k in EXCLUDED_FIELDS: - continue - key = k[:] - if key.startswith('searchprefix_'): - key = key[len('searchprefix_'):] - dct_request_items[key] = request_items[k] - request_items = dct_request_items - - dct = my_base_request - if full == 'shortcut': - dct['cached_label__icontains'] = request.GET.get('term', None) - and_reqs, or_reqs = [], [] - try: - old = 'old' in request_items and int(request_items['old']) - except ValueError: - return HttpResponse('[]', content_type='text/plain') - - # manage relations types - if 'relation_types' not in my_relation_types_prefix: - my_relation_types_prefix['relation_types'] = '' - relation_types = {} - for rtype_key in my_relation_types_prefix: - relation_types[my_relation_types_prefix[rtype_key]] = set() - for k in request_items: - if k.startswith(rtype_key): - relation_types[my_relation_types_prefix[rtype_key]].add( - request_items[k]) - continue - - for k in request_keys: - val = request_items.get(k) - if not val: - continue - req_keys = request_keys[k] - if type(req_keys) not in (list, tuple): - dct[req_keys] = val - continue - # multiple choice target - reqs = Q(**{req_keys[0]: val}) - for req_key in req_keys[1:]: - q = Q(**{req_key: val}) - reqs |= q - and_reqs.append(reqs) - - pinned_search = "" - if 'submited' not in request_items and full != 'shortcut': - # default search - # an item is selected in the default menu - if default_name in request.session and \ - request.session[default_name]: - value = request.session[default_name] - if 'basket-' in value: - try: - dct = {"basket__pk": - request.session[default_name].split('-')[-1]} - pinned_search = unicode(FindBasket.objects.get( - pk=dct["basket__pk"])) - except FindBasket.DoesNotExist: - pass - else: - try: - dct = {"pk": request.session[default_name]} - pinned_search = unicode(model._meta.verbose_name)\ - + u" - " + unicode( - model.objects.get(pk=dct["pk"])) - except model.DoesNotExist: - pass - elif dct == (my_base_request or {}): - # a parent item may be selected in the default menu - for name, key in my_relative_session_names: - if name in request.session and request.session[name] \ - and 'basket-' not in request.session[name] \ - and name in CURRENT_ITEM_KEYS_DICT: - up_model = CURRENT_ITEM_KEYS_DICT[name] - try: - dct.update({key: request.session[name]}) - pinned_search = unicode(up_model._meta.verbose_name)\ - + u" - " + unicode( - up_model.objects.get(pk=dct[key])) - break - except up_model.DoesNotExist: - pass - if (not dct or data_type == 'csv') \ - and func_name in request.session: - dct = request.session[func_name] - else: - request.session[func_name] = dct - for k in (list(my_bool_fields) + list(my_reversed_bool_fields)): - if k in dct: - if dct[k] == u"1": - dct.pop(k) - else: - dct[k] = dct[k] == u"2" and True or False - if k in my_reversed_bool_fields: - dct[k] = not dct[k] - # check also for empty value with image field - field_name = k.split('__')[0] - # TODO: can be improved in later version of Django - try: - c_field = model._meta.get_field(field_name) - if k.endswith('__isnull') and \ - isinstance(c_field, ImageField): - if dct[k]: - or_reqs.append( - (k, {k.split('__')[0] + '__exact': ''})) - else: - dct[k.split('__')[0] + '__regex'] = '.{1}.*' - except FieldDoesNotExist: - pass - for k in my_dated_fields: - if k in dct: - if not dct[k]: - dct.pop(k) - try: - items = dct[k].split('/') - assert len(items) == 3 - dct[k] = datetime.date(*map(lambda x: int(x), - reversed(items)))\ - .strftime('%Y-%m-%d') - except AssertionError: - dct.pop(k) - # manage hierarchic conditions - for req in dct.copy(): - if req.endswith('town__pk') or req.endswith('towns__pk'): - val = dct.pop(req) - reqs = Q(**{req: val}) - base_req = req[:-2] + '__' - req = base_req[:] - for idx in range(HIERARCHIC_LEVELS): - req = req[:-2] + 'parents__pk' - q = Q(**{req: val}) - reqs |= q - req = base_req[:] - for idx in range(HIERARCHIC_LEVELS): - req = req[:-2] + 'children__pk' - q = Q(**{req: val}) - reqs |= q - and_reqs.append(reqs) - continue - - for k_hr in HIERARCHIC_FIELDS: - if type(req) in (list, tuple): - val = dct.pop(req) - q = None - for idx, r in enumerate(req): - if not idx: - q = Q(**{r: val}) - else: - q |= Q(**{r: val}) - and_reqs.append(q) - break - elif req.endswith(k_hr + '__pk'): - val = dct.pop(req) - reqs = Q(**{req: val}) - req = req[:-2] + '__' - for idx in range(HIERARCHIC_LEVELS): - req = req[:-2] + 'parent__pk' - q = Q(**{req: val}) - reqs |= q - and_reqs.append(reqs) - break - dct = _search_manage_search_vector(dct) - query = Q(**dct) - for k, or_req in or_reqs: - alt_dct = dct.copy() - alt_dct.pop(k) - alt_dct.update(or_req) - query |= Q(**alt_dct) - - for rtype_prefix in relation_types: - vals = list(relation_types[rtype_prefix]) - if not vals: - continue - alt_dct = { - rtype_prefix + 'right_relations__relation_type__pk__in': vals} - for k in dct: - val = dct[k] - if rtype_prefix: - # only get conditions related to the object - if rtype_prefix not in k: - continue - # tricky: reconstruct the key to make sense - remove the - # prefix from the key - k = k[0:k.index(rtype_prefix)] + k[ - k.index(rtype_prefix) + len(rtype_prefix):] - if k.endswith('year'): - k += '__exact' - alt_dct[rtype_prefix + 'right_relations__right_record__' + k] =\ - val - if not dct: - # fake condition to trick Django (1.4): without it only the - # alt_dct is managed - query &= Q(pk__isnull=False) - query |= Q(**alt_dct) - for k, or_req in or_reqs: - altor_dct = alt_dct.copy() - altor_dct.pop(k) - for j in or_req: - val = or_req[j] - if j == 'year': - j = 'year__exact' - altor_dct[ - rtype_prefix + 'right_relations__right_record__' + j] =\ - val - query |= Q(**altor_dct) - - if own: - q = models.IshtarUser.objects.filter(user_ptr=request.user) - if q.count(): - query = query & model.get_query_owns(q.all()[0]) - else: - return HttpResponse(EMPTY, content_type='text/plain') - - for and_req in and_reqs: - query = query & and_req - - # manage hierarchic in shortcut menu - if full == 'shortcut': - ASSOCIATED_ITEMS = { - Operation: (File, 'associated_file__pk'), - ContextRecord: (Operation, 'operation__pk'), - Find: (ContextRecord, 'base_finds__context_record__pk'), - } - if model in ASSOCIATED_ITEMS: - upper_model, upper_key = ASSOCIATED_ITEMS[model] - model_name = upper_model.SLUG - current = model_name in request.session \ - and request.session[model_name] - if current: - dct = {upper_key: current} - query &= Q(**dct) - - items = model.objects.filter(query).distinct() - # print(items.query) - - if 'search_vector' in dct: # for serialization - dct['search_vector'] = dct['search_vector'].value - - # table cols - if own_table_cols: - table_cols = own_table_cols - else: - if full: - table_cols = [field.name for field in model._meta.fields - if field.name not in PRIVATE_FIELDS] - table_cols += [field.name for field in model._meta.many_to_many - if field.name not in PRIVATE_FIELDS] - if hasattr(model, 'EXTRA_FULL_FIELDS'): - table_cols += model.EXTRA_FULL_FIELDS - else: - table_cols = model.TABLE_COLS - query_table_cols = [] - for cols in table_cols: - if type(cols) not in (list, tuple): - cols = [cols] - for col in cols: - query_table_cols += col.split('|') - - # contextual (full, simple, etc.) col - contxt = full and 'full' or 'simple' - if hasattr(model, 'CONTEXTUAL_TABLE_COLS') and \ - contxt in model.CONTEXTUAL_TABLE_COLS: - for idx, col in enumerate(table_cols): - if col in model.CONTEXTUAL_TABLE_COLS[contxt]: - query_table_cols[idx] = \ - model.CONTEXTUAL_TABLE_COLS[contxt][col] - if full == 'shortcut': - query_table_cols = ['cached_label'] - table_cols = ['cached_label'] - - # manage sort tables - manual_sort_key = None - - sorts = {} - for k in request_items: - if not k.startswith('order['): - continue - num = int(k.split(']')[0][len("order["):]) - if num not in sorts: - sorts[num] = ['', ''] # sign, col_num - if k.endswith('[dir]'): - order = request_items[k] - sign = order and order == u'desc' and "-" or '' - sorts[num][0] = sign - if k.endswith('[column]'): - sorts[num][1] = request_items[k] - sign = "" - if not sorts and model._meta.ordering: - orders = [k for k in model._meta.ordering] - items = items.order_by(*orders) - else: - orders = [] - for idx in sorted(sorts.keys()): - signe, col_num = sorts[idx] - k = query_table_cols[int(col_num) - 2] # remove id and link col - if k in request_keys: - ks = request_keys[k] - if type(ks) not in (tuple, list): - ks = [ks] - for k in ks: - if k.endswith("__pk"): - k = k[:-len("__pk")] + "__label" - if '__' in k: - k = k.split('__')[0] - orders.append(signe + k) - else: - # not a standard request key - if idx: # not the first - we ignore this sort - continue - sign = signe - manual_sort_key = k - logger.warning( - "**WARN get_item - {}**: manual sort key '{}'".format( - func_name, k)) - break - if not manual_sort_key: - items = items.order_by(*orders) - - # pager management - start, end = 0, None - page_nb = 1 - if row_nb and data_type == "json": - try: - start = int(request_items.get('start')) - page_nb = start / row_nb + 1 - assert page_nb >= 1 - except (TypeError, ValueError, AssertionError): - start = 0 - page_nb = 1 - end = page_nb * row_nb - if full == 'shortcut': - start = 0 - end = 20 - - items_nb = items.count() - if manual_sort_key: - items = items.all() - else: - items = items[start:end] - - datas = [] - if old: - items = [item.get_previous(old) for item in items] - c_ids = [] - for item in items: - # manual deduplicate when distinct is not enough - if not do_not_deduplicate and item.pk in c_ids: - continue - c_ids.append(item.pk) - data = [item.pk] - for keys in query_table_cols: - if type(keys) not in (list, tuple): - keys = [keys] - my_vals = [] - for k in keys: - if hasattr(model, 'EXTRA_REQUEST_KEYS') \ - and k in model.EXTRA_REQUEST_KEYS: - k = model.EXTRA_REQUEST_KEYS[k] - if type(k) in (list, tuple): - k = k[0] - for filtr in ('__icontains', '__contains'): - if k.endswith(filtr): - k = k[:len(k) - len(filtr)] - vals = [item] - # foreign key may be divided by "." or "__" - splitted_k = [] - for ky in k.split('.'): - if '__' in ky: - splitted_k += ky.split('__') - else: - splitted_k.append(ky) - for ky in splitted_k: - new_vals = [] - for val in vals: - if hasattr(val, 'all'): # manage related objects - val = list(val.all()) - for v in val: - v = getattr(v, ky) - new_vals += _get_values(request, v) - elif val: - try: - val = getattr(val, ky) - new_vals += _get_values(request, val) - except AttributeError: - # must be a query key such as "contains" - pass - vals = new_vals - # manage last related objects - if vals and hasattr(vals[0], 'all'): - new_vals = [] - for val in vals: - new_vals += list(val.all()) - vals = new_vals - if not my_vals: - my_vals = [format_val(va) for va in vals] - else: - new_vals = [] - if not vals: - for idx, my_v in enumerate(my_vals): - new_vals.append(u"{}{}{}".format( - my_v, u' - ', '')) - else: - for idx, v in enumerate(vals): - new_vals.append(u"{}{}{}".format( - vals[idx], u' - ', format_val(v))) - my_vals = new_vals[:] - data.append(u" & ".join(my_vals) or u"") - datas.append(data) - if manual_sort_key: - # +1 because the id is added as a first col - idx_col = None - if manual_sort_key in query_table_cols: - idx_col = query_table_cols.index(manual_sort_key) + 1 - else: - for idx, col in enumerate(query_table_cols): - if type(col) in (list, tuple) and \ - manual_sort_key in col: - idx_col = idx + 1 - if idx_col is not None: - datas = sorted(datas, key=lambda x: x[idx_col]) - if sign == '-': - datas = reversed(datas) - datas = list(datas)[start:end] - link_template = "<a class='display_details' href='#' "\ - "onclick='load_window(\"%s\")'>"\ - "<i class=\"fa fa-info-circle\" aria-hidden=\"true\"></i></a>" - link_ext_template = '<a href="{}" target="_blank">{}</a>' - if data_type == "json": - rows = [] - for data in datas: - try: - lnk = link_template % reverse('show-' + default_name, - args=[data[0], '']) - except NoReverseMatch: - logger.warning( - '**WARN "show-' + default_name + '" args (' - + unicode(data[0]) + ") url not available") - lnk = '' - res = {'id': data[0], 'link': lnk} - for idx, value in enumerate(data[1:]): - if value: - table_col = table_cols[idx] - if type(table_col) not in (list, tuple): - table_col = [table_col] - tab_cols = [] - # foreign key may be divided by "." or "__" - for tc in table_col: - if '.' in tc: - tab_cols += tc.split('.') - elif '__' in tc: - tab_cols += tc.split('__') - else: - tab_cols.append(tc) - k = "__".join(tab_cols) - if hasattr(model, 'COL_LINK') and k in model.COL_LINK: - value = link_ext_template.format(value, value) - res[k] = value - if full == 'shortcut' and 'cached_label' in res: - res['value'] = res.pop('cached_label') - rows.append(res) - if full == 'shortcut': - data = json.dumps(rows) - else: - data = json.dumps({ - "recordsTotal": items_nb, - "recordsFiltered": items_nb, - "rows": rows, - "pinned-search": pinned_search, - "page": page_nb, - "total": (items_nb / row_nb + 1) if row_nb else items_nb, - }) - return HttpResponse(data, content_type='text/plain') - elif data_type == "csv": - response = HttpResponse(content_type='text/csv') - n = datetime.datetime.now() - filename = u'%s_%s.csv' % (default_name, - n.strftime('%Y%m%d-%H%M%S')) - response['Content-Disposition'] = 'attachment; filename=%s'\ - % filename - writer = csv.writer(response, **CSV_OPTIONS) - if col_names: - col_names = [name.encode(ENCODING, errors='replace') - for name in col_names] - else: - col_names = [] - for field_name in table_cols: - if type(field_name) in (list, tuple): - field_name = u" & ".join(field_name) - if hasattr(model, 'COL_LABELS') and\ - field_name in model.COL_LABELS: - field = model.COL_LABELS[field_name] - col_names.append(unicode(field).encode(ENCODING)) - continue - else: - try: - field = model._meta.get_field(field_name) - except: - col_names.append(u"".encode(ENCODING)) - logger.warning( - "**WARN get_item - csv export**: no col name " - "for {}\nadd explicit label to " - "COL_LABELS attribute of " - "{}".format(field_name, model)) - continue - col_names.append( - unicode(field.verbose_name).encode(ENCODING)) - writer.writerow(col_names) - for data in datas: - row, delta = [], 0 - # regroup cols with join "|" - for idx, col_name in enumerate(table_cols): - if len(data[1:]) <= idx + delta: - break - val = data[1:][idx + delta].encode( - ENCODING, errors='replace') - if col_name and "|" in col_name[0]: - for delta_idx in range( - len(col_name[0].split('|')) - 1): - delta += 1 - val += data[1:][idx + delta].encode( - ENCODING, errors='replace') - row.append(val) - writer.writerow(row) - return response - return HttpResponse('{}', content_type='text/plain') - - return func - - -def get_by_importer(request, slug, data_type='json', full=False, - force_own=False, **dct): - q = models.ImporterType.objects.filter(slug=slug) - if not q.count(): - res = '' - if data_type == "json": - res = '{}' - return HttpResponse(res, content_type='text/plain') - imp = q.all()[0].get_importer_class() - cols, col_names = [], [] - for formater in imp.LINE_EXPORT_FORMAT: - if not formater: - cols.append('') - col_names.append("") - continue - cols.append(formater.export_field_name) - col_names.append(formater.label) - obj_name = imp.OBJECT_CLS.__name__.lower() - return get_item( - imp.OBJECT_CLS, 'get_' + obj_name, obj_name, own_table_cols=cols - )(request, data_type, full, force_own, col_names=col_names, **dct) - - -def display_item(model, extra_dct=None, show_url=None): - def func(request, pk, **dct): - if show_url: - dct['show_url'] = "/{}{}/".format(show_url, pk) - else: - dct['show_url'] = "/show-{}/{}/".format(model.SLUG, pk) - return render(request, 'ishtar/display_item.html', dct) - return func - - -def show_item(model, name, extra_dct=None): - def func(request, pk, **dct): - allowed, own = models.check_model_access_control(request, model) - if not allowed: - return HttpResponse('', content_type="application/xhtml") - q = model.objects - if own: - query_own = model.get_query_owns(request.user) - if query_own: - q = q.filter(query_own) - try: - item = q.get(pk=pk) - except ObjectDoesNotExist: - return HttpResponse('NOK') - doc_type = 'type' in dct and dct.pop('type') - url_name = u"/".join(reverse('show-' + name, args=['0', ''] - ).split('/')[:-2]) + u"/" - dct['CURRENCY'] = get_current_profile().currency - dct['ENCODING'] = settings.ENCODING - dct['DOT_GENERATION'] = settings.DOT_BINARY and True - dct['current_window_url'] = url_name - date = None - if 'date' in dct: - date = dct.pop('date') - dct['sheet_id'] = "%s-%d" % (name, item.pk) - dct['window_id'] = "%s-%d-%s" % ( - name, item.pk, datetime.datetime.now().strftime('%M%s')) - if hasattr(item, 'history'): - if date: - try: - date = datetime.datetime.strptime(date, - '%Y-%m-%dT%H:%M:%S.%f') - item = item.get_previous(date=date) - assert item is not None - except (ValueError, AssertionError): - return HttpResponse(None, content_type='text/plain') - dct['previous'] = item._previous - dct['next'] = item._next - else: - historized = item.history.all() - if historized: - item.history_date = historized[0].history_date - if len(historized) > 1: - dct['previous'] = historized[1].history_date - dct['item'], dct['item_name'] = item, name - # add context - if extra_dct: - dct.update(extra_dct(request, item)) - context_instance = deepcopy(dct) - context_instance['output'] = 'html' - if hasattr(item, 'history_object'): - filename = item.history_object.associated_filename - else: - filename = item.associated_filename - if doc_type == "odt" and settings.ODT_TEMPLATE: - tpl = loader.get_template('ishtar/sheet_%s.html' % name) - context_instance['output'] = 'ODT' - content = tpl.render(context_instance, request) - try: - tidy_options = {'output-xhtml': 1, 'indent': 1, - 'tidy-mark': 0, 'doctype': 'auto', - 'add-xml-decl': 1, 'wrap': 1} - html, errors = tidy(content, options=tidy_options) - html = html.encode('utf-8').replace(" ", " ") - html = re.sub('<pre([^>]*)>\n', '<pre\\1>', html) - - odt = NamedTemporaryFile() - options = optparse.Values() - options.with_network = True - for k, v in (('input', ''), - ('output', odt.name), - ('template', settings.ODT_TEMPLATE), - ('with_network', True), - ('top_header_level', 1), - ('img_width', '8cm'), - ('img_height', '6cm'), - ('verbose', False), - ('replace_keyword', 'ODT-INSERT'), - ('cut_start', 'ODT-CUT-START'), - ('htmlid', None), - ('url', "#")): - setattr(options, k, v) - odtfile = xhtml2odt.ODTFile(options) - odtfile.open() - odtfile.import_xhtml(html) - odtfile = odtfile.save() - except xhtml2odt.ODTExportError: - return HttpResponse(content, content_type="application/xhtml") - response = HttpResponse( - content_type='application/vnd.oasis.opendocument.text') - response['Content-Disposition'] = 'attachment; filename=%s.odt' % \ - filename - response.write(odtfile) - return response - elif doc_type == 'pdf': - tpl = loader.get_template('ishtar/sheet_%s_pdf.html' % name) - context_instance['output'] = 'PDF' - html = tpl.render(context_instance, request) - font_config = FontConfiguration() - css = CSS(string=''' - @font-face { - font-family: Gentium; - src: url(%s); - } - body{ - font-family: Gentium - } - ''' % (static("gentium/GentiumPlus-R.ttf"))) - css2 = CSS(filename=settings.STATIC_ROOT + '/media/style_basic.css') - pdf = HTML(string=html, base_url=request.build_absolute_uri() - ).write_pdf(stylesheets=[css, css2], - font_config=font_config) - response = HttpResponse(pdf, content_type='application/pdf') - response['Content-Disposition'] = 'attachment; filename=%s.pdf' % \ - filename - return response - else: - tpl = loader.get_template('ishtar/sheet_%s_window.html' % name) - content = tpl.render(context_instance, request) - return HttpResponse(content, content_type="application/xhtml") - return func - - -def revert_item(model): - def func(request, pk, date, **dct): - try: - item = model.objects.get(pk=pk) - date = datetime.datetime.strptime(date, '%Y-%m-%dT%H:%M:%S.%f') - item.rollback(date) - except (ObjectDoesNotExist, ValueError, HistoryError): - return HttpResponse(None, content_type='text/plain') - return HttpResponse("True", content_type='text/plain') - return func - - def autocomplete_organization(request, orga_type=None): if (not request.user.has_perm('ishtar_common.view_organization', models.Organization) and @@ -1521,31 +631,6 @@ def autocomplete_author(request): return HttpResponse(data, content_type='text/plain') -def new_item(model, frm, many=False): - def func(request, parent_name, limits=''): - model_name = model._meta.object_name - if not check_permission(request, 'add_' + model_name.lower()): - not_permitted_msg = ugettext(u"Operation not permitted.") - return HttpResponse(not_permitted_msg) - dct = {'title': unicode(_(u'New %s' % model_name.lower())), - 'many': many} - if request.method == 'POST': - dct['form'] = frm(request.POST, limits=limits) - if dct['form'].is_valid(): - new_item = dct['form'].save(request.user) - dct['new_item_label'] = unicode(new_item) - dct['new_item_pk'] = new_item.pk - dct['parent_name'] = parent_name - dct['parent_pk'] = parent_name - if dct['parent_pk'] and '_select_' in dct['parent_pk']: - parents = dct['parent_pk'].split('_') - dct['parent_pk'] = "_".join([parents[0]] + parents[2:]) - return render(request, 'window.html', dct) - else: - dct['form'] = frm(limits=limits) - return render(request, 'window.html', dct) - return func - new_person = new_item(models.Person, forms.PersonForm) new_person_noorga = new_item(models.Person, forms.NoOrgaPersonForm) new_organization = new_item(models.Organization, forms.OrganizationForm) @@ -2488,6 +1573,7 @@ class OrganizationPersonEdit(LoginRequiredMixin, UpdateView): show_document = show_item(models.Document, 'document') get_document = get_item(models.Document, 'get_document', 'document') +display_document = display_item(models.Document) document_search_wizard = wizards.SearchWizard.as_view( @@ -2497,11 +1583,16 @@ document_search_wizard = wizards.SearchWizard.as_view( ) -class NewDocumentFormView(IshtarMixin, LoginRequiredMixin, - FormView): +class DocumentFormView(IshtarMixin, LoginRequiredMixin, + CreateView): + page_name = _(u"New Document") form_class = forms.DocumentForm template_name = 'ishtar/form.html' - success_url = 'document_search' + model = models.Document + + def get_success_url(self): + return reverse('display-document', args=[self.object.pk]) + """ class DocumentSelectMixin(IshtarMixin, LoginRequiredMixin, |