Passed
Push — 2.x ( 814943...eeb59a )
by Ramon
09:33
created

RemarksDataConverter.to_dict()   A

Complexity

Conditions 2

Size

Total Lines 4
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 2
nop 2
1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of SENAITE.CORE.
4
#
5
# SENAITE.CORE is free software: you can redistribute it and/or modify it under
6
# the terms of the GNU General Public License as published by the Free Software
7
# Foundation, version 2.
8
#
9
# This program is distributed in the hope that it will be useful, but WITHOUT
10
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
# details.
13
#
14
# You should have received a copy of the GNU General Public License along with
15
# this program; if not, write to the Free Software Foundation, Inc., 51
16
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
#
18
# Copyright 2018-2025 by it's authors.
19
# Some rights reserved, see README and LICENSE.
20
21
import copy
22
import six
23
24
from Acquisition import aq_inner
25
from Acquisition import aq_parent
26
from Products.CMFPlone.utils import safe_unicode
27
from Products.Five.browser import BrowserView
28
from bika.lims import api
29
from bika.lims.decorators import returns_json
30
from senaite.core.api.dtime import to_localized_time
31
from senaite.core.schema.interfaces import IRemarksField
32
from senaite.core.schema.remarksfield import fill_remark_object
33
from senaite.core.interfaces import ISenaiteFormLayer
34
from senaite.core.p3compat import cmp
35
from senaite.core.permissions import FieldEditRemarks
36
from senaite.core.z3cform.interfaces import IRemarksWidget
37
from z3c.form.browser import widget
38
from z3c.form.converter import BaseDataConverter
39
from z3c.form.interfaces import INPUT_MODE
40
from z3c.form.interfaces import DISPLAY_MODE
41
from z3c.form.interfaces import IAddForm
42
from z3c.form.interfaces import IFieldWidget
43
from z3c.form.interfaces import IWidget
44
from z3c.form.widget import FieldWidget
45
from z3c.form.widget import Widget
46
from zope.browserpage.viewpagetemplatefile import ViewPageTemplateFile
47
from zope.component import adapter
48
from zope.interface import implementer
49
50
51
def check_permission_edit_remark(context):
52
    """Check is can add remark
53
    """
54
    tool = api.get_tool("portal_membership")
55
    return tool.checkPermission(FieldEditRemarks, context)
56
57
58
@adapter(IRemarksField, IWidget)
59
class RemarksDataConverter(BaseDataConverter):
60
    """Value conversion between field and widget
61
    """
62
    def to_list_of_dicts(self, value):
63
        if not isinstance(value, list):
64
            value = [value]
65
        value = filter(None, value)
66
        return map(self.to_dict, value)
67
68
    def to_dict(self, value):
69
        if not isinstance(value, dict):
70
            return {}
71
        return value
72
73
    def toWidgetValue(self, value):
74
        """Returns the field value with encoded string
75
        """
76
        values = self.to_list_of_dicts(value)
77
        values = self.to_utf8(values)
78
        values.sort(lambda x, y: cmp(y["created"], x["created"]))
79
        return values
80
81
    def toFieldValue(self, value):
82
        """Converts from widget value to safe_unicode
83
        """
84
        if api.is_string(value):
85
            value = fill_remark_object(value)
86
        values = self.to_list_of_dicts(value)
87
        return self.to_safe_unicode(values)
88
89 View Code Duplication
    def to_safe_unicode(self, data):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
90
        """Converts the data to unicode
91
        """
92
        if isinstance(data, unicode):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable unicode does not seem to be defined.
Loading history...
93
            return data
94
        if isinstance(data, list):
95
            return [self.to_safe_unicode(item) for item in data]
96
        if isinstance(data, dict):
97
            return {
98
                self.to_safe_unicode(key): self.to_safe_unicode(value)
99
                for key, value in six.iteritems(data)
100
            }
101
        return safe_unicode(data)
102
103 View Code Duplication
    def to_utf8(self, data):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
104
        """Encodes the data to utf-8
105
        """
106
        if isinstance(data, unicode):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable unicode does not seem to be defined.
Loading history...
107
            return data.encode("utf-8")
108
        if isinstance(data, list):
109
            return [self.to_utf8(item) for item in data]
110
        if isinstance(data, dict):
111
            return {
112
                self.to_utf8(key): self.to_utf8(value)
113
                for key, value in six.iteritems(data)
114
            }
115
        return data
116
117
118
@implementer(IRemarksWidget)
119
class RemarksWidget(widget.HTMLFormElement, Widget):
120
    """SENAITE Remarks Widget
121
    """
122
    klass = u"senaite-remarks-widget"
123
124
    def update(self):
125
        widget.HTMLFormElement.update(self)
126
        Widget.update(self)
127
        widget.addFieldClass(self)
128
129
    def render(self):
130
        if self.is_add_form():
131
            return ViewPageTemplateFile("add.pt")(self)
132
        return Widget.render(self)
133
134
    def is_add_form(self):
135
        """Check if we are in an add form
136
        :returns: True if in add form, False if in edit form
137
        """
138
        # Check if the parent form implements IAddForm
139
        form = getattr(self, "form", None)
140
        if form is None:
141
            return False
142
        is_add = IAddForm.providedBy(form)
143
144
        # form maybe includes into group(fieldset)
145
        # check that recursively
146
        parent = aq_parent(aq_inner(form))
147
        while not is_add and parent is not None:
148
            is_add = IAddForm.providedBy(parent)
149
            parent = aq_parent(aq_inner(parent))
150
        return is_add
151
152
    def localized_time(self, value):
153
        return to_localized_time(value,
154
                                 long_format=True,
155
                                 context=self.context,
156
                                 request=self.request)
157
158
    def html_content(self, value):
159
        return api.text_to_html(value)
160
161
    @property
162
    def portal(self):
163
        """Return the portal object
164
        """
165
        return api.get_portal()
166
167
    @property
168
    def portal_url(self):
169
        """Return the portal object URL
170
        """
171
        return api.get_url(self.portal)
172
173
    @property
174
    def can_add_remark(self):
175
        """Check is can add remark
176
        """
177
        modes = [INPUT_MODE, DISPLAY_MODE]
178
        can_edit_field = check_permission_edit_remark(self.context)
179
        return self.mode in modes and can_edit_field
180
181
182
@adapter(IRemarksField, ISenaiteFormLayer)
183
@implementer(IFieldWidget)
184
def RemarksWidgetFactory(field, request):
185
    """Widget factory for Address Widget
186
    """
187
    return FieldWidget(field, RemarksWidget(request))
188
189
190
class AjaxAddRemark(BrowserView):
191
    """Endpoint for the add remark for object
192
    """
193
194
    @returns_json
195
    def __call__(self):
196
        """Returns a json with the result about of added remark for object
197
        """
198
        field_name = self.request.form.get("fieldName", None)
199
        uid = self.request.form.get("uid", None)
200
        value = self.request.form.get("value", None)
201
        if not field_name or not uid or not value:
202
            return {"success": False}
203
        obj = api.get_object_by_uid(uid, None)
204
        if not obj:
205
            return {"success": False}
206
        new_remark = self.add_remark_to_obj(obj, field_name, value)
207
        if not new_remark:
208
            return {"success": False}
209
        return {"success": True}
210
211
    def add_remark_to_obj(self, obj, field_name, value):
212
        """Add new remark to `obj` by `field_name` with `value`
213
        """
214
        fields = api.get_fields(obj)
215
        if field_name not in fields:
216
            return False
217
        field = fields.get(field_name)
218
        if not IRemarksField.providedBy(field):
219
            return False
220
        if not check_permission_edit_remark(obj):
221
            self.request.response.setStatus(403)
222
            return False
223
        field.add(obj, value)
224
        return True
225
226
227
class AjaxFetchRemarks(BrowserView):
228
    """Endpoint for fetching remarks for object
229
    """
230
    @returns_json
231
    def __call__(self):
232
        uid = self.request.form.get("uid", None)
233
        field_name = self.request.form.get("fieldName", None)
234
        if not uid or not field_name:
235
            return {"success": False}
236
        obj = api.get_object_by_uid(uid, None)
237
        if not obj:
238
            return {"success": False}
239
240
        value = self.get_remarks_by_field(obj, field_name)
241
        if not value:
242
            return {"success": False}
243
        remarks = copy.deepcopy(value)
244
        remarks.sort(lambda x, y: cmp(y["created"], x["created"]))
245
        for r in remarks:
246
            r["created"] = to_localized_time(r["created"],
247
                                             long_format=True,
248
                                             context=self.context,
249
                                             request=self.request)
250
        return {
251
            "success": True,
252
            "remarks": remarks,
253
        }
254
255
    def get_remarks_by_field(self, obj, field_name):
256
        fields = api.get_fields(obj)
257
        if field_name not in fields:
258
            return False
259
        field = fields.get(field_name)
260
        if not IRemarksField.providedBy(field):
261
            return False
262
        return field.get(obj)
263