Completed
Branch master (9edffc)
by Jordi
04:36
created

HeaderTableView.__call__()   B

Complexity

Conditions 6

Size

Total Lines 23
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 19
dl 0
loc 23
rs 8.5166
c 0
b 0
f 0
cc 6
nop 1
1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of SENAITE.CORE
4
#
5
# Copyright 2018 by it's authors.
6
# Some rights reserved. See LICENSE.rst, CONTRIBUTORS.rst.
7
8
from AccessControl import getSecurityManager
9
from AccessControl.Permissions import view
10
from Products.CMFCore.permissions import ModifyPortalContent
11
from Products.CMFPlone import PloneMessageFactory as _p
12
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
13
from bika.lims import bikaMessageFactory as _
14
from bika.lims import logger
15
from bika.lims.browser import BrowserView
16
from bika.lims.interfaces import IHeaderTableFieldRenderer
17
from bika.lims.utils import t
18
from zope.component import getAdapter
19
from zope.component.interfaces import ComponentLookupError
20
21
22
class HeaderTableView(BrowserView):
23
    """Table rendered at the top in AR View
24
    """
25
    template = ViewPageTemplateFile("templates/header_table.pt")
26
27
    def __call__(self):
28
        self.errors = {}
29
        if "header_table_submitted" in self.request:
30
            schema = self.context.Schema()
31
            fields = schema.fields()
32
            form = self.request.form
33
            for field in fields:
34
                fieldname = field.getName()
35
                if fieldname in form:
36
                    # Handle (multiValued) reference fields
37
                    # https://github.com/bikalims/bika.lims/issues/2270
38
                    uid_fieldname = "{}_uid".format(fieldname)
39
                    if uid_fieldname in form:
40
                        value = form[uid_fieldname]
41
                        if field.multiValued:
42
                            value = value.split(",")
43
                        field.getMutator(self.context)(value)
44
                    else:
45
                        # other fields
46
                        field.getMutator(self.context)(form[fieldname])
47
            message = _p("Changes saved.")
48
            self.context.plone_utils.addPortalMessage(message, "info")
49
        return self.template()
50
51
    def three_column_list(self, input_list):
52
        list_len = len(input_list)
53
54
        # Calculate the length of the sublists
55
        sublist_len = (list_len % 3 == 0 and list_len / 3 or list_len / 3 + 1)
56
57
        def _list_end(num):
58
            # Calculate the list end point given the list number
59
            return num == 2 and list_len or (num + 1) * sublist_len
60
61
        # Generate only filled columns
62
        final = []
63
        for i in range(3):
64
            column = input_list[i * sublist_len:_list_end(i)]
65
            if len(column) > 0:
66
                final.append(column)
67
        return final
68
69
    # TODO Revisit this
70
    def render_field_view(self, field):
71
        fieldname = field.getName()
72
        field = self.context.Schema()[fieldname]
73
        ret = {"fieldName": fieldname, "mode": "view"}
74
        try:
75
            adapter = getAdapter(self.context,
76
                                 interface=IHeaderTableFieldRenderer,
77
                                 name=fieldname)
78
79
        except ComponentLookupError:
80
            adapter = None
81
        if adapter:
82
            ret = {'fieldName': fieldname,
83
                   'mode': 'structure',
84
                   'html': adapter(field)}
85
        else:
86
            if field.getWidgetName() == "BooleanWidget":
87
                value = field.get(self.context)
88
                ret = {
89
                    "fieldName": fieldname,
90
                    "mode": "structure",
91
                    "html": t(_("Yes")) if value else t(_("No"))
92
                }
93
            elif field.getType().find("Reference") > -1:
94
                # Prioritize method retrieval over schema"s field
95
                targets = None
96
                if hasattr(self.context, "get%s" % fieldname):
97
                    fieldaccessor = getattr(self.context, "get%s" % fieldname)
98
                    if callable(fieldaccessor):
99
                        targets = fieldaccessor()
100
                if not targets:
101
                    targets = field.get(self.context)
102
103
                if targets:
104
                    if not type(targets) == list:
105
                        targets = [targets, ]
106
                    sm = getSecurityManager()
107
                    if all([sm.checkPermission(view, ta) for ta in targets]):
108
                        elements = [
109
                            "<div id='{id}' class='field reference'>"
110
                            "  <a class='link' uid='{uid}' href='{url}'>"
111
                            "    {title}"
112
                            "  </a>"
113
                            "</div>"
114
                            .format(id=target.getId(),
115
                                    uid=target.UID(),
116
                                    url=target.absolute_url(),
117
                                    title=target.Title())
118
                            for target in targets]
119
120
                        ret = {
121
                            "fieldName": fieldname,
122
                            "mode": "structure",
123
                            "html": "".join(elements),
124
                        }
125
                    else:
126
                        ret = {
127
                            "fieldName": fieldname,
128
                            "mode": "structure",
129
                            "html": ", ".join([ta.Title() for ta in targets]),
130
                        }
131
                else:
132
                    ret = {
133
                        "fieldName": fieldname,
134
                        "mode": "structure",
135
                        "html": "",
136
                    }
137
            elif field.getType().lower().find("datetime") > -1:
138
                value = field.get(self.context)
139
                ret = {
140
                    "fieldName": fieldname,
141
                    "mode": "structure",
142
                    "html": self.ulocalized_time(value, long_format=True)
143
                }
144
        return ret
145
146
    def get_field_visibility_mode(self, field):
147
        """Returns "view" or "edit" modes, together with the place within where
148
        this field has to be rendered, based on the permissions the current
149
        user has for the context and the field passed in
150
        """
151
        fallback_mode = ("hidden", "hidden")
152
        widget = field.widget
153
154
        # TODO This needs to be done differently
155
        # Check where the field has to be located
156
        layout = widget.isVisible(self.context, "header_table")
157
        if layout in ["invisible", "hidden"]:
158
            return fallback_mode
159
160
        # Check permissions. We want to display field (either in view or edit
161
        # modes) only if the current user has enough privileges.
162
        if field.checkPermission("edit", self.context):
163
            mode = "edit"
164
            sm = getSecurityManager()
165
            if not sm.checkPermission(ModifyPortalContent, self.context):
166
                logger.warn("Permission '{}' granted for the edition of '{}', "
167
                            "but 'Modify portal content' not granted"
168
                            .format(field.write_permission, field.getName()))
169
        elif field.checkPermission("view", self.context):
170
            mode = "view"
171
        else:
172
            return fallback_mode
173
174
        # Check if the field needs to be displayed or not, even if the user has
175
        # the permissions for edit or view. This may depend on criteria other
176
        # than permissions (e.g. visibility depending on a setup setting, etc.)
177
        if widget.isVisible(self.context, mode, field=field) != "visible":
178
            if mode == "view":
179
                return fallback_mode
180
            # The field cannot be rendered in edit mode, but maybe can be
181
            # rendered in view mode.
182
            mode = "view"
183
            if widget.isVisible(self.context, mode, field=field) != "visible":
184
                return fallback_mode
185
        return (mode, layout)
186
187
    def get_fields_grouped_by_location(self):
188
        standard = []
189
        prominent = []
190
        for field in self.context.Schema().fields():
191
            mode, layout = self.get_field_visibility_mode(field)
192
            if mode == "hidden":
193
                # Do not render this field
194
                continue
195
            field_mapping = {"fieldName": field.getName(), "mode": mode}
196
            if mode == "view":
197
                # Special formatting (e.g. links, etc.)
198
                field_mapping = self.render_field_view(field)
199
            if layout == "prominent":
200
                # Add the field at the top of the fields table
201
                prominent.append(field_mapping)
202
            else:
203
                # Add the field at standard location
204
                standard.append(field_mapping)
205
        return prominent, self.three_column_list(standard)
206