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 datetime import datetime |
9
|
|
|
|
10
|
|
|
from BTrees.OOBTree import OOBTree |
11
|
|
|
from DateTime import DateTime |
12
|
|
|
from Products.Five.browser import BrowserView |
13
|
|
|
from plone import protect |
14
|
|
|
from plone.memoize.volatile import cache |
15
|
|
|
from zope.annotation.interfaces import IAnnotations |
16
|
|
|
from zope.i18n.locales import locales |
17
|
|
|
from zope.interface import implements |
18
|
|
|
from zope.publisher.interfaces import IPublishTraverse |
19
|
|
|
|
20
|
|
|
from bika.lims import api |
21
|
|
|
from bika.lims import logger |
22
|
|
|
from bika.lims.utils import cache_key |
23
|
|
|
from bika.lims.utils import returns_json |
24
|
|
|
|
25
|
|
|
|
26
|
|
|
class BaseManageAddView(BrowserView): |
27
|
|
|
""" |
28
|
|
|
Base view to create a visibility and order manager for an Add View. |
29
|
|
|
""" |
30
|
|
|
template = None |
31
|
|
|
|
32
|
|
|
def __init__(self, context, request): |
33
|
|
|
BrowserView.__init__(self, context, request) |
34
|
|
|
self.context = context |
35
|
|
|
self.request = request |
36
|
|
|
self.tmp_obj = None |
37
|
|
|
self.CONFIGURATION_STORAGE = None |
38
|
|
|
self.SKIP_FIELD_ON_COPY = [] |
39
|
|
|
|
40
|
|
|
def __call__(self): |
41
|
|
|
protect.CheckAuthenticator(self.request.form) |
42
|
|
|
form = self.request.form |
43
|
|
|
if form.get("submitted", False) and form.get("save", False): |
44
|
|
|
order = form.get("order") |
45
|
|
|
self.set_field_order(order) |
46
|
|
|
visibility = form.get("visibility") |
47
|
|
|
self.set_field_visibility(visibility) |
48
|
|
|
if form.get("submitted", False) and form.get("reset", False): |
49
|
|
|
self.flush() |
50
|
|
|
return self.template() |
51
|
|
|
|
52
|
|
|
def get_obj(self): |
53
|
|
|
""" |
54
|
|
|
This method should return a temporary object. This object should be |
55
|
|
|
of the same type as the one we are creating. |
56
|
|
|
:return: ATObjectType |
57
|
|
|
""" |
58
|
|
|
raise NotImplementedError("get_obj is not implemented.") |
59
|
|
|
|
60
|
|
|
def get_annotation(self): |
61
|
|
|
bika_setup = api.get_bika_setup() |
62
|
|
|
return IAnnotations(bika_setup) |
63
|
|
|
|
64
|
|
|
@property |
65
|
|
|
def storage(self): |
66
|
|
|
annotation = self.get_annotation() |
67
|
|
|
if annotation.get(self.CONFIGURATION_STORAGE) is None: |
68
|
|
|
annotation[self.CONFIGURATION_STORAGE] = OOBTree() |
69
|
|
|
return annotation[self.CONFIGURATION_STORAGE] |
70
|
|
|
|
71
|
|
|
def flush(self): |
72
|
|
|
annotation = self.get_annotation() |
73
|
|
|
if annotation.get(self.CONFIGURATION_STORAGE) is not None: |
74
|
|
|
del annotation[self.CONFIGURATION_STORAGE] |
75
|
|
|
|
76
|
|
|
def set_field_order(self, order): |
77
|
|
|
self.storage.update({"order": order}) |
78
|
|
|
|
79
|
|
|
def get_field_order(self): |
80
|
|
|
order = self.storage.get("order") |
81
|
|
|
if order is None: |
82
|
|
|
return map(lambda f: f.getName(), self.get_fields()) |
83
|
|
|
return order |
84
|
|
|
|
85
|
|
|
def set_field_visibility(self, visibility): |
86
|
|
|
self.storage.update({"visibility": visibility}) |
87
|
|
|
|
88
|
|
|
def get_field_visibility(self): |
89
|
|
|
return self.storage.get("visibility") |
90
|
|
|
|
91
|
|
|
def is_field_visible(self, field): |
92
|
|
|
if field.required: |
93
|
|
|
return True |
94
|
|
|
visibility = self.get_field_visibility() |
95
|
|
|
if visibility is None: |
96
|
|
|
return True |
97
|
|
|
return visibility.get(field.getName(), True) |
98
|
|
|
|
99
|
|
|
def get_field(self, name): |
100
|
|
|
"""Get a field of the object by name |
101
|
|
|
""" |
102
|
|
|
obj = self.get_obj() |
103
|
|
|
return obj.getField(name) |
104
|
|
|
|
105
|
|
|
def get_fields(self): |
106
|
|
|
"""Return all Object fields |
107
|
|
|
""" |
108
|
|
|
obj = self.get_obj() |
109
|
|
|
return obj.Schema().fields() |
110
|
|
|
|
111
|
|
|
def get_sorted_fields(self): |
112
|
|
|
"""Return the sorted fields |
113
|
|
|
""" |
114
|
|
|
inf = float("inf") |
115
|
|
|
order = self.get_field_order() |
116
|
|
|
|
117
|
|
|
def field_cmp(field1, field2): |
118
|
|
|
_n1 = field1.getName() |
119
|
|
|
_n2 = field2.getName() |
120
|
|
|
_i1 = _n1 in order and order.index(_n1) + 1 or inf |
121
|
|
|
_i2 = _n2 in order and order.index(_n2) + 1 or inf |
122
|
|
|
return cmp(_i1, _i2) |
123
|
|
|
|
124
|
|
|
return sorted(self.get_fields(), cmp=field_cmp) |
125
|
|
|
|
126
|
|
|
def get_fields_with_visibility(self, visibility="edit", mode="add"): |
127
|
|
|
"""Return the fields with visibility |
128
|
|
|
""" |
129
|
|
|
fields = self.get_sorted_fields() |
130
|
|
|
|
131
|
|
|
out = [] |
132
|
|
|
|
133
|
|
|
for field in fields: |
134
|
|
|
v = field.widget.isVisible( |
135
|
|
|
self.context, mode, default='invisible', field=field) |
136
|
|
|
|
137
|
|
|
if self.is_field_visible(field) is False: |
138
|
|
|
v = "hidden" |
139
|
|
|
|
140
|
|
|
visibility_guard = True |
141
|
|
|
# visibility_guard is a widget field defined in the schema in order |
142
|
|
|
# to know the visibility of the widget when the field is related to |
143
|
|
|
# a dynamically changing content such as workflows. For instance |
144
|
|
|
# those fields related to the workflow will be displayed only if |
145
|
|
|
# the workflow is enabled, otherwise they should not be shown. |
146
|
|
|
if 'visibility_guard' in dir(field.widget): |
147
|
|
|
visibility_guard = eval(field.widget.visibility_guard) |
148
|
|
|
if v == visibility and visibility_guard: |
149
|
|
|
out.append(field) |
150
|
|
|
|
151
|
|
|
return out |
152
|
|
|
|
153
|
|
|
|
154
|
|
|
class BaseAddView(BrowserView): |
155
|
|
|
""" |
156
|
|
|
Base Object Add view |
157
|
|
|
|
158
|
|
|
This class offers the necessary methods to create a Add view. |
159
|
|
|
""" |
160
|
|
|
template = None |
161
|
|
|
|
162
|
|
|
def __init__(self, context, request): |
163
|
|
|
BrowserView.__init__(self, context, request) |
164
|
|
|
self.request = request |
165
|
|
|
self.context = context |
166
|
|
|
self.fieldvalues = {} |
167
|
|
|
self.tmp_obj = None |
168
|
|
|
self.icon = None |
169
|
|
|
self.portal = None |
170
|
|
|
self.portal_url = None |
171
|
|
|
self.bika_setup = None |
172
|
|
|
self.came_from = None |
173
|
|
|
self.obj_count = None |
174
|
|
|
self.specifications = None |
175
|
|
|
self.ShowPrices = None |
176
|
|
|
self.SKIP_FIELD_ON_COPY = [] |
177
|
|
|
|
178
|
|
|
def __call__(self): |
179
|
|
|
self.portal = api.get_portal() |
180
|
|
|
self.portal_url = self.portal.absolute_url() |
181
|
|
|
self.bika_setup = api.get_bika_setup() |
182
|
|
|
self.request.set('disable_plone.rightcolumn', 1) |
183
|
|
|
self.came_from = "add" |
184
|
|
|
self.tmp_obj = self.get_obj() |
185
|
|
|
self.obj_count = self.get_obj_count() |
186
|
|
|
self.ShowPrices = self.bika_setup.getShowPrices() |
187
|
|
|
|
188
|
|
|
def get_view_url(self): |
189
|
|
|
"""Return the current view url including request parameters |
190
|
|
|
""" |
191
|
|
|
request = self.request |
192
|
|
|
url = request.getURL() |
193
|
|
|
qs = request.getHeader("query_string") |
194
|
|
|
if not qs: |
195
|
|
|
return url |
196
|
|
|
return "{}?{}".format(url, qs) |
197
|
|
|
|
198
|
|
|
def get_object_by_uid(self, uid): |
199
|
|
|
"""Get the object by UID |
200
|
|
|
""" |
201
|
|
|
logger.debug("get_object_by_uid::UID={}".format(uid)) |
202
|
|
|
obj = api.get_object_by_uid(uid, None) |
203
|
|
|
if obj is None: |
204
|
|
|
logger.warn("!! No object found for UID #{} !!") |
205
|
|
|
return obj |
206
|
|
|
|
207
|
|
|
def get_currency(self): |
208
|
|
|
"""Returns the configured currency |
209
|
|
|
""" |
210
|
|
|
bika_setup = api.get_bika_setup() |
211
|
|
|
currency = bika_setup.getCurrency() |
212
|
|
|
currencies = locales.getLocale('en').numbers.currencies |
213
|
|
|
return currencies[currency] |
214
|
|
|
|
215
|
|
|
def get_obj_count(self): |
216
|
|
|
"""Return the obj_count request parameter |
217
|
|
|
""" |
218
|
|
|
try: |
219
|
|
|
obj_count = int(self.request.form.get("obj_count", 1)) |
220
|
|
|
except (TypeError, ValueError): |
221
|
|
|
obj_count = 1 |
222
|
|
|
return obj_count |
223
|
|
|
|
224
|
|
|
def get_obj(self): |
225
|
|
|
"""Create a temporary Object to fetch the fields from |
226
|
|
|
""" |
227
|
|
|
raise NotImplementedError("get_obj is not implemented.") |
228
|
|
|
|
229
|
|
|
def get_obj_schema(self): |
230
|
|
|
"""Return the Object schema |
231
|
|
|
""" |
232
|
|
|
obj = self.get_obj() |
233
|
|
|
return obj.Schema() |
234
|
|
|
|
235
|
|
|
def get_obj_fields(self): |
236
|
|
|
"""Return the Object schema fields (including extendend fields) |
237
|
|
|
""" |
238
|
|
|
schema = self.get_obj_schema() |
239
|
|
|
return schema.fields() |
240
|
|
|
|
241
|
|
|
def get_fieldname(self, field, objnum): |
242
|
|
|
"""Generate a new fieldname with a '-<objnum>' suffix |
243
|
|
|
""" |
244
|
|
|
name = field.getName() |
245
|
|
|
# ensure we have only *one* suffix |
246
|
|
|
base_name = name.split("-")[0] |
247
|
|
|
suffix = "-{}".format(objnum) |
248
|
|
|
return "{}{}".format(base_name, suffix) |
249
|
|
|
|
250
|
|
|
def get_input_widget(self, fieldname, objnum=0, **kw): |
251
|
|
|
"""Get the field widget of the Object in column <objnum> |
252
|
|
|
|
253
|
|
|
:param fieldname: The base fieldname |
254
|
|
|
:type fieldname: string |
255
|
|
|
""" |
256
|
|
|
|
257
|
|
|
# temporary Object Context |
258
|
|
|
context = self.get_obj() |
259
|
|
|
# request = self.request |
260
|
|
|
schema = context.Schema() |
261
|
|
|
|
262
|
|
|
# get original field in the schema from the base_fieldname |
263
|
|
|
base_fieldname = fieldname.split("-")[0] |
264
|
|
|
field = context.getField(base_fieldname) |
265
|
|
|
|
266
|
|
|
# fieldname with -<objnum> suffix |
267
|
|
|
new_fieldname = self.get_fieldname(field, objnum) |
268
|
|
|
new_field = field.copy(name=new_fieldname) |
269
|
|
|
|
270
|
|
|
# get the default value for this field |
271
|
|
|
fieldvalues = self.fieldvalues |
272
|
|
|
field_value = fieldvalues.get(new_fieldname) |
273
|
|
|
# request_value = request.form.get(new_fieldname) |
274
|
|
|
# value = request_value or field_value |
275
|
|
|
value = field_value |
276
|
|
|
|
277
|
|
|
def getAccessor(instance): |
278
|
|
|
def accessor(**kw): |
279
|
|
|
return value |
280
|
|
|
return accessor |
281
|
|
|
|
282
|
|
|
# inject the new context for the widget renderer |
283
|
|
|
# see: Products.Archetypes.Renderer.render |
284
|
|
|
kw["here"] = context |
285
|
|
|
kw["context"] = context |
286
|
|
|
kw["fieldName"] = new_fieldname |
287
|
|
|
|
288
|
|
|
# make the field available with this name |
289
|
|
|
# XXX: This is actually a hack to make the widget |
290
|
|
|
# available in the template |
291
|
|
|
schema._fields[new_fieldname] = new_field |
292
|
|
|
new_field.getAccessor = getAccessor |
293
|
|
|
|
294
|
|
|
# set the default value |
295
|
|
|
form = dict() |
296
|
|
|
form[new_fieldname] = value |
297
|
|
|
self.request.form.update(form) |
298
|
|
|
|
299
|
|
|
logger.info( |
300
|
|
|
"get_input_widget: fieldname={} objnum={} -> " |
301
|
|
|
"new_fieldname={} value={}" |
302
|
|
|
.format(fieldname, objnum, new_fieldname, value)) |
303
|
|
|
widget = context.widget(new_fieldname, **kw) |
304
|
|
|
return widget |
305
|
|
|
|
306
|
|
|
def get_copy_from(self): |
307
|
|
|
"""Returns a mapping of UID index -> Object |
308
|
|
|
""" |
309
|
|
|
# Create a mapping of source Objects for copy |
310
|
|
|
copy_from = self.request.form.get("copy_from", "").split(",") |
311
|
|
|
# clean out empty strings |
312
|
|
|
copy_from_uids = filter(lambda x: x, copy_from) |
313
|
|
|
out = dict().fromkeys(range(len(copy_from_uids))) |
314
|
|
|
for n, uid in enumerate(copy_from_uids): |
315
|
|
|
obj = self.get_object_by_uid(uid) |
316
|
|
|
if obj is None: |
317
|
|
|
continue |
318
|
|
|
out[n] = obj |
319
|
|
|
logger.info("get_copy_from: uids={}".format(copy_from_uids)) |
320
|
|
|
return out |
321
|
|
|
|
322
|
|
|
def get_default_value(self, field, context): |
323
|
|
|
"""Get the default value of the field |
324
|
|
|
""" |
325
|
|
|
name = field.getName() |
326
|
|
|
default = field.getDefault(context) |
327
|
|
|
if name == "Batch": |
328
|
|
|
batch = self.get_batch() |
329
|
|
|
if batch is not None: |
330
|
|
|
default = batch |
331
|
|
|
if name == "Client": |
332
|
|
|
client = self.get_client() |
333
|
|
|
if client is not None: |
334
|
|
|
default = client |
335
|
|
|
if name == "Contact": |
336
|
|
|
contact = self.get_default_contact() |
337
|
|
|
if contact is not None: |
338
|
|
|
default = contact |
339
|
|
|
logger.info( |
340
|
|
|
"get_default_value: context={} field={} value={}" |
341
|
|
|
.format(context, name, default)) |
342
|
|
|
return default |
343
|
|
|
|
344
|
|
|
def get_field_value(self, field, context): |
345
|
|
|
"""Get the stored value of the field |
346
|
|
|
""" |
347
|
|
|
name = field.getName() |
348
|
|
|
value = context.getField(name).get(context) |
349
|
|
|
logger.info( |
350
|
|
|
"get_field_value: context={} field={} value={}" |
351
|
|
|
.format(context, name, value)) |
352
|
|
|
return value |
353
|
|
|
|
354
|
|
|
def get_client(self): |
355
|
|
|
"""Returns the Client |
356
|
|
|
""" |
357
|
|
|
context = self.context |
358
|
|
|
parent = api.get_parent(context) |
359
|
|
|
if context.portal_type == "Client": |
360
|
|
|
return context |
361
|
|
|
elif parent.portal_type == "Client": |
362
|
|
|
return parent |
363
|
|
|
elif context.portal_type == "Batch": |
364
|
|
|
return context.getClient() |
365
|
|
|
elif parent.portal_type == "Batch": |
366
|
|
|
return context.getClient() |
367
|
|
|
return None |
368
|
|
|
|
369
|
|
|
def get_batch(self): |
370
|
|
|
"""Returns the Batch |
371
|
|
|
""" |
372
|
|
|
context = self.context |
373
|
|
|
parent = api.get_parent(context) |
374
|
|
|
if context.portal_type == "Batch": |
375
|
|
|
return context |
376
|
|
|
elif parent.portal_type == "Batch": |
377
|
|
|
return parent |
378
|
|
|
return None |
379
|
|
|
|
380
|
|
|
def generate_fieldvalues(self, count=1): |
381
|
|
|
"""Returns a mapping of '<fieldname>-<count>' to the default value |
382
|
|
|
of the field or the field value of the source Object (copy from) |
383
|
|
|
""" |
384
|
|
|
raise NotImplementedError("generate_fieldvalues is not implemented.") |
385
|
|
|
|
386
|
|
|
def is_field_visible(self, field): |
387
|
|
|
"""Check if the field is visible |
388
|
|
|
""" |
389
|
|
|
context = self.context |
390
|
|
|
fieldname = field.getName() |
391
|
|
|
|
392
|
|
|
# hide the Client field on client and batch contexts |
393
|
|
|
if fieldname == "Client" and context.portal_type in ("Client", ): |
394
|
|
|
return False |
395
|
|
|
|
396
|
|
|
# hide the Batch field on batch contexts |
397
|
|
|
if fieldname == "Batch" and context.portal_type in ("Batch", ): |
398
|
|
|
return False |
399
|
|
|
|
400
|
|
|
return True |
401
|
|
|
|
402
|
|
|
def get_fields_with_visibility(self, visibility, mode="add"): |
403
|
|
|
"""Return the Object fields with the current visibility. |
404
|
|
|
|
405
|
|
|
It is recommended to get those values from an AddManagerView |
406
|
|
|
""" |
407
|
|
|
raise NotImplementedError( |
408
|
|
|
"get_fields_with_visibility is not implemented.") |
409
|
|
|
|
410
|
|
|
|
411
|
|
|
class BaseAjaxAddView(BaseAddView): |
412
|
|
|
"""Ajax helpers for an Object Add form |
413
|
|
|
""" |
414
|
|
|
implements(IPublishTraverse) |
415
|
|
|
|
416
|
|
|
def __init__(self, context, request): |
417
|
|
|
BaseAddView.__init__(self, context, request) |
418
|
|
|
self.traverse_subpath = [] |
419
|
|
|
# Errors are aggregated here, and returned together to the browser |
420
|
|
|
self.errors = {} |
421
|
|
|
|
422
|
|
|
def publishTraverse(self, request, name): |
423
|
|
|
""" get's called before __call__ for each path name |
424
|
|
|
""" |
425
|
|
|
self.traverse_subpath.append(name) |
426
|
|
|
return self |
427
|
|
|
|
428
|
|
|
@returns_json |
429
|
|
|
def __call__(self): |
430
|
|
|
"""Dispatch the path to a method and return JSON. |
431
|
|
|
""" |
432
|
|
|
protect.CheckAuthenticator(self.request.form) |
433
|
|
|
protect.PostOnly(self.request.form) |
434
|
|
|
|
435
|
|
|
if len(self.traverse_subpath) != 1: |
436
|
|
|
return self.error("Not found", status=404) |
437
|
|
|
func_name = "ajax_{}".format(self.traverse_subpath[0]) |
438
|
|
|
func = getattr(self, func_name, None) |
439
|
|
|
if func is None: |
440
|
|
|
return self.error("Invalid function", status=400) |
441
|
|
|
return func() |
442
|
|
|
|
443
|
|
|
def error(self, message, status=500, **kw): |
444
|
|
|
"""Set a JSON error object and a status to the response |
445
|
|
|
""" |
446
|
|
|
self.request.response.setStatus(status) |
447
|
|
|
result = {"success": False, "errors": message} |
448
|
|
|
result.update(kw) |
449
|
|
|
return result |
450
|
|
|
|
451
|
|
|
def to_iso_date(self, dt): |
452
|
|
|
"""Return the ISO representation of a date object |
453
|
|
|
""" |
454
|
|
|
if dt is None: |
455
|
|
|
return "" |
456
|
|
|
if isinstance(dt, DateTime): |
457
|
|
|
return dt.ISO8601() |
458
|
|
|
if isinstance(dt, datetime): |
459
|
|
|
return dt.isoformat() |
460
|
|
|
raise TypeError("{} is neither an instance of DateTime nor datetime" |
461
|
|
|
.format(repr(dt))) |
462
|
|
|
|
463
|
|
|
def get_records(self): |
464
|
|
|
"""Returns a list of Sample records |
465
|
|
|
|
466
|
|
|
All fields coming from `request.form` have a number prefix, |
467
|
|
|
e.g. `Contact-0`. |
468
|
|
|
All fields with the same suffix number are grouped together in a |
469
|
|
|
record. |
470
|
|
|
Each record represents the data for one column in the Sample Add |
471
|
|
|
form and contains a mapping of the fieldName (w/o prefix) -> value. |
472
|
|
|
|
473
|
|
|
Example: |
474
|
|
|
[{"Contact": "Rita Mohale", ...}, {Contact: "Neil Standard"} ...] |
475
|
|
|
""" |
476
|
|
|
form = self.request.form |
477
|
|
|
obj_count = self.get_obj_count() |
478
|
|
|
|
479
|
|
|
records = [] |
480
|
|
|
# Group belonging Object fields together |
481
|
|
|
for objenum in range(obj_count): |
482
|
|
|
record = {} |
483
|
|
|
s1 = "-{}".format(objenum) |
484
|
|
|
keys = filter(lambda key: s1 in key, form.keys()) |
485
|
|
|
for key in keys: |
486
|
|
|
new_key = key.replace(s1, "") |
487
|
|
|
value = form.get(key) |
488
|
|
|
record[new_key] = value |
489
|
|
|
records.append(record) |
490
|
|
|
return records |
491
|
|
|
|
492
|
|
|
def get_objs_from_record(self, record, key): |
493
|
|
|
"""Returns a mapping of UID -> object |
494
|
|
|
""" |
495
|
|
|
uids = self.get_uids_from_record(record, key) |
496
|
|
|
objs = map(self.get_object_by_uid, uids) |
497
|
|
|
return dict(zip(uids, objs)) |
498
|
|
|
|
499
|
|
|
def get_uids_from_record(self, record, key): |
500
|
|
|
"""Returns a list of parsed UIDs from a single form field identified |
501
|
|
|
by the given key. |
502
|
|
|
|
503
|
|
|
A form field ending with `_uid` can contain an empty value, a |
504
|
|
|
single UID or multiple UIDs separated by a comma. |
505
|
|
|
|
506
|
|
|
This method parses the UID value and returns a list of non-empty UIDs. |
507
|
|
|
""" |
508
|
|
|
value = record.get(key, None) |
509
|
|
|
if value is None: |
510
|
|
|
return [] |
511
|
|
|
if isinstance(value, basestring): |
512
|
|
|
value = value.split(",") |
513
|
|
|
return filter(lambda uid: uid, value) |
514
|
|
|
|
515
|
|
|
@cache(cache_key) |
516
|
|
|
def get_base_info(self, obj): |
517
|
|
|
"""Returns the base info of an object |
518
|
|
|
""" |
519
|
|
|
if obj is None: |
520
|
|
|
return {} |
521
|
|
|
|
522
|
|
|
info = { |
523
|
|
|
"id": obj.getId(), |
524
|
|
|
"uid": obj.UID(), |
525
|
|
|
"title": obj.Title(), |
526
|
|
|
"description": obj.Description(), |
527
|
|
|
"url": obj.absolute_url(), |
528
|
|
|
} |
529
|
|
|
|
530
|
|
|
return info |
531
|
|
|
|
532
|
|
|
@cache(cache_key) |
533
|
|
|
def get_container_info(self, obj): |
534
|
|
|
"""Returns the info for a Container |
535
|
|
|
""" |
536
|
|
|
info = self.get_base_info(obj) |
537
|
|
|
info.update({}) |
538
|
|
|
return info |
539
|
|
|
|
540
|
|
|
@cache(cache_key) |
541
|
|
|
def get_preservation_info(self, obj): |
542
|
|
|
"""Returns the info for a Preservation |
543
|
|
|
""" |
544
|
|
|
info = self.get_base_info(obj) |
545
|
|
|
info.update({}) |
546
|
|
|
return info |
547
|
|
|
|
548
|
|
|
def ajax_get_global_settings(self): |
549
|
|
|
"""Returns the global Bika settings |
550
|
|
|
""" |
551
|
|
|
bika_setup = api.get_bika_setup() |
552
|
|
|
settings = { |
553
|
|
|
"show_prices": bika_setup.getShowPrices(), |
554
|
|
|
} |
555
|
|
|
return settings |
556
|
|
|
|
557
|
|
|
def ajax_recalculate_records(self): |
558
|
|
|
"""Recalculate all Object records and dependencies |
559
|
|
|
""" |
560
|
|
|
raise NotImplementedError( |
561
|
|
|
"ajax_recalculate_records is not implemented.") |
562
|
|
|
|
563
|
|
|
def ajax_submit(self): |
564
|
|
|
"""Submit & create the Object |
565
|
|
|
""" |
566
|
|
|
raise NotImplementedError( |
567
|
|
|
"ajax_recalculate_records is not implemented.") |
568
|
|
|
|