Passed
Pull Request — 2.x (#1864)
by Ramon
17:28 queued 11:54
created

UIDReferenceDataConverter.toFieldValue()   A

Complexity

Conditions 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 1
nop 2
1
# -*- coding: utf-8 -*-
2
3
import json
4
import string
5
6
import six
7
8
from bika.lims import api
9
from Products.CMFPlone.utils import base_hasattr
10
from senaite.core.interfaces import ISenaiteFormLayer
11
from senaite.core.schema.interfaces import IUIDReferenceField
12
from senaite.core.z3cform.interfaces import IUIDReferenceWidget
13
from senaite.jsonapi.interfaces import IInfo
14
from z3c.form import interfaces
15
from z3c.form.browser import widget
16
from z3c.form.browser.textlines import TextLinesWidget
17
from z3c.form.converter import TextLinesConverter
18
from z3c.form.interfaces import IDataConverter
19
from z3c.form.interfaces import IFieldWidget
20
from z3c.form.widget import FieldWidget
21
from zope.component import adapter
22
from zope.interface import implementer
23
24
DISPLAY_TEMPLATE = "<a href='${url}' _target='blank'>${title} ${uid}</a>"
25
26
_marker = object
27
28
29
@adapter(IUIDReferenceField, interfaces.IWidget)
30
class UIDReferenceDataConverter(TextLinesConverter):
31
    """Converts the raw field data for widget/field usage
32
    """
33
34
    def toWidgetValue(self, value):
35
        """Converts a list of UIDs for the display/hidden widget
36
37
        returns a list of UIDs when widget is in "display" mode
38
        returns a unicode string when widget is in "hidden" mode
39
        """
40
        if self.widget.mode == "display":
41
            return value
42
        return super(UIDReferenceDataConverter, self).toWidgetValue(value)
43
44
    def toFieldValue(self, value):
45
        """Converts a unicode string to a list of UIDs
46
        """
47
        # remove any blank lines at the end
48
        value = value.rstrip("\r\n")
49
        return super(UIDReferenceDataConverter, self).toFieldValue(value)
50
51
52
@implementer(IUIDReferenceWidget)
53
class UIDReferenceWidget(TextLinesWidget):
54
    """Senaite UID reference widget
55
    """
56
    klass = u"senaite-uidreference-widget"
57
58
    def __init__(self, request, *args, **kw):
59
        super(UIDReferenceWidget, self).__init__(request)
60
        self.request = request
61
62
    def update(self):
63
        super(UIDReferenceWidget, self).update()
64
        widget.addFieldClass(self)
65
66
    def attr(self, name, default=None):
67
        """Get the named attribute of the widget or the field
68
        """
69
        value = getattr(self, name, _marker)
70
        if value is _marker:
71
            return default
72
        if isinstance(value, six.string_types):
73
            if base_hasattr(self.context, value):
74
                attr = getattr(self.context, value)
75
                if callable(attr):
76
                    value = attr()
77
                else:
78
                    value = attr
79
        return value
80
81
    def get_value(self):
82
        """Extract the value from the request or get it from the field
83
        """
84
        # get the processed value from the `update` method
85
        value = self.value
86
        # the value might come from the request, e.g. on object creation
87
        if isinstance(value, six.string_types):
88
            value = IDataConverter(self).toFieldValue(value)
89
        # we handle always lists in the templates
90
        if value is None:
91
            return []
92
        if not isinstance(value, (list, tuple)):
93
            value = [value]
94
        # just to be sure (paranoid)
95
        return [uid for uid in value if api.is_uid(uid)]
96
97
    def get_api_url(self):
98
        portal = api.get_portal()
99
        portal_url = api.get_url(portal)
100
        api_url = "{}/@@API/senaite/v1".format(portal_url)
101
        return api_url
102
103
    def get_display_template(self):
104
        return self.attr("display_template", DISPLAY_TEMPLATE)
105
106
    def get_catalog(self):
107
        return self.attr("catalog", "portal_catalog")
108
109
    def get_query(self):
110
        return self.attr("query", {})
111
112
    def get_columns(self):
113
        return self.attr("columns", [])
114
115
    def get_limit(self):
116
        return self.attr("limit", 25)
117
118
    def is_multi_valued(self):
119
        return getattr(self.field, "multi_valued", False)
120
121
    def get_input_widget_attributes(self):
122
        """Return input widget attributes for the ReactJS component
123
        """
124
        uids = self.get_value()
125
        attributes = {
126
            "data-id": self.id,
127
            "data-name": self.name,
128
            "data-uids": uids,
129
            "data-api_url": self.get_api_url(),
130
            "data-records": dict(zip(uids, map(self.get_obj_info, uids))),
131
            "data-query": self.get_query(),
132
            "data-catalog": self.get_catalog(),
133
            "data-columns": self.get_columns(),
134
            "data-display_template": self.get_display_template(),
135
            "data-limit": self.get_limit(),
136
            "data-multi_valued": self.is_multi_valued(),
137
            "data-disabled": self.disabled or False,
138
            "data-readonly": self.readonly or False,
139
        }
140
141
        # convert all attributes to JSON
142
        for key, value in attributes.items():
143
            attributes[key] = json.dumps(value)
144
145
        return attributes
146
147
    def get_obj_info(self, uid):
148
        """Returns a dictionary with the object info
149
        """
150
        obj = api.get_object(uid)
151
        obj_info = IInfo(obj).to_dict()
152
        obj_info["uid"] = uid
153
        obj_info["url"] = api.get_url(obj)
154
        return obj_info
155
156
    def render_reference(self, uid):
157
        """Returns a rendered HTML element for the reference
158
        """
159
        template = string.Template(self.get_display_template())
160
        obj_info = self.get_obj_info(uid)
161
        return template.safe_substitute(obj_info)
162
163
164
@adapter(IUIDReferenceField, ISenaiteFormLayer)
165
@implementer(IFieldWidget)
166
def UIDReferenceWidgetFactory(field, request):
167
    """Widget factory for UIDReferenceField
168
    """
169
    return FieldWidget(field, UIDReferenceWidget(request))
170