Passed
Pull Request — 2.x (#1864)
by Ramon
07:04
created

UIDReferenceWidget.get_value()   A

Complexity

Conditions 4

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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