| 1 |
# (C) Copyright 2003-2009 Nuxeo SA <http://nuxeo.com> |
|---|
| 2 |
# Authors: |
|---|
| 3 |
# Florent Guillaume <fg@nuxeo.com> |
|---|
| 4 |
# M.-A. Darche <madarche@nuxeo.com> |
|---|
| 5 |
# |
|---|
| 6 |
# This program is free software; you can redistribute it and/or modify |
|---|
| 7 |
# it under the terms of the GNU General Public License version 2 as published |
|---|
| 8 |
# by the Free Software Foundation. |
|---|
| 9 |
# |
|---|
| 10 |
# This program is distributed in the hope that it will be useful, |
|---|
| 11 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 12 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 13 |
# GNU General Public License for more details. |
|---|
| 14 |
# |
|---|
| 15 |
# You should have received a copy of the GNU General Public License |
|---|
| 16 |
# along with this program; if not, write to the Free Software |
|---|
| 17 |
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |
|---|
| 18 |
# 02111-1307, USA. |
|---|
| 19 |
# |
|---|
| 20 |
# $Id$ |
|---|
| 21 |
"""Extended widget types. |
|---|
| 22 |
|
|---|
| 23 |
Definition of extended widget types. |
|---|
| 24 |
""" |
|---|
| 25 |
|
|---|
| 26 |
from logging import getLogger |
|---|
| 27 |
import warnings |
|---|
| 28 |
import zipfile |
|---|
| 29 |
import operator |
|---|
| 30 |
import os |
|---|
| 31 |
from tempfile import mkstemp |
|---|
| 32 |
from cgi import escape |
|---|
| 33 |
from re import match |
|---|
| 34 |
|
|---|
| 35 |
from Globals import InitializeClass |
|---|
| 36 |
from Acquisition import aq_base, aq_parent, aq_inner |
|---|
| 37 |
from DateTime.DateTime import DateTime |
|---|
| 38 |
|
|---|
| 39 |
from Products.PythonScripts.standard import newline_to_br |
|---|
| 40 |
from Products.PythonScripts.standard import structured_text |
|---|
| 41 |
from reStructuredText import HTML |
|---|
| 42 |
|
|---|
| 43 |
from Products.CMFCore.utils import getToolByName |
|---|
| 44 |
|
|---|
| 45 |
from Products.CPSUtil.html import XhtmlSanitizer |
|---|
| 46 |
from Products.CPSSchemas.Widget import CPSWidget |
|---|
| 47 |
from Products.CPSSchemas.Widget import widgetRegistry |
|---|
| 48 |
from Products.CPSSchemas.BasicWidgets import CPSSelectWidget |
|---|
| 49 |
from Products.CPSSchemas.BasicWidgets import CPSMultiSelectWidget |
|---|
| 50 |
from Products.CPSSchemas.BasicWidgets import CPSStringWidget |
|---|
| 51 |
from Products.CPSSchemas.BasicWidgets import CPSImageWidget |
|---|
| 52 |
from Products.CPSSchemas.BasicWidgets import CPSFileWidget |
|---|
| 53 |
from Products.CPSSchemas.BasicWidgets import renderHtmlTag |
|---|
| 54 |
from Products.CPSSchemas.BasicWidgets import CPSProgrammerCompoundWidget |
|---|
| 55 |
from Products.CPSSchemas.swfHeaderData import analyseContent |
|---|
| 56 |
|
|---|
| 57 |
################################################## |
|---|
| 58 |
# previously named CPSTextAreaWidget in BasicWidget r1.78 |
|---|
| 59 |
class CPSTextWidget(CPSStringWidget): |
|---|
| 60 |
"""Text widget.""" |
|---|
| 61 |
meta_type = 'Text Widget' |
|---|
| 62 |
|
|---|
| 63 |
# Warning if configurable the widget require field[1] and field[2] |
|---|
| 64 |
field_types = ('CPS String Field', # text value |
|---|
| 65 |
'CPS String Field', # render_position if configurable |
|---|
| 66 |
'CPS String Field') # render_format if configurable |
|---|
| 67 |
field_inits = ({'is_searchabletext': 1,}, {}, {}) |
|---|
| 68 |
|
|---|
| 69 |
_properties = CPSWidget._properties + ( |
|---|
| 70 |
{'id': 'width', 'type': 'int', 'mode': 'w', |
|---|
| 71 |
'label': 'Width'}, |
|---|
| 72 |
{'id': 'height', 'type': 'int', 'mode': 'w', |
|---|
| 73 |
'label': 'Height'}, |
|---|
| 74 |
{'id': 'size_max', 'type': 'int', 'mode': 'w', |
|---|
| 75 |
'label': 'Max Size'}, |
|---|
| 76 |
|
|---|
| 77 |
{'id': 'xhtml_sanitize', 'type': 'selection', 'mode': 'w', |
|---|
| 78 |
'select_variable': 'all_xhtml_sanitize_options', |
|---|
| 79 |
'label': 'XHTML sanitize the content'}, |
|---|
| 80 |
{'id': 'xhtml_sanitize_system', 'type': 'string', 'mode': 'w', |
|---|
| 81 |
'label': 'XHTML sanitize through system command line'}, |
|---|
| 82 |
{'id': 'file_uploader', 'type': 'boolean', 'mode': 'w', |
|---|
| 83 |
'label': 'Add a file uploader to the widget UI'}, |
|---|
| 84 |
|
|---|
| 85 |
{'id': 'html_editor_type', 'type': 'string', 'mode': 'w', |
|---|
| 86 |
'label': 'The name of the HTML editor to use'}, |
|---|
| 87 |
|
|---|
| 88 |
{'id': 'html_editor_position', 'type': 'selection', 'mode': 'w', |
|---|
| 89 |
'select_variable': 'all_html_editor_positions', |
|---|
| 90 |
'label': 'HTML rich text editor position'}, |
|---|
| 91 |
{'id': 'render_format', 'type': 'selection', 'mode': 'w', |
|---|
| 92 |
'select_variable': 'all_render_formats', |
|---|
| 93 |
'label': 'Render format'}, |
|---|
| 94 |
{'id': 'render_position', 'type': 'selection', 'mode': 'w', |
|---|
| 95 |
'select_variable': 'all_render_positions', |
|---|
| 96 |
'label': 'Render position'}, |
|---|
| 97 |
{'id': 'configurable', 'type': 'selection', 'mode': 'w', |
|---|
| 98 |
'select_variable': 'all_configurable', |
|---|
| 99 |
'label': 'What is user configurable (require extra fields)'}, |
|---|
| 100 |
) |
|---|
| 101 |
all_xhtml_sanitize_options = ['no', 'builtin', 'system'] |
|---|
| 102 |
all_configurable = ['nothing', 'position', 'format', 'position and format'] |
|---|
| 103 |
all_render_positions = ['normal', 'col_left', 'col_right'] |
|---|
| 104 |
all_render_formats = ['text', 'html', 'rst'] |
|---|
| 105 |
all_html_editor_positions = ['popup', 'embedded'] |
|---|
| 106 |
|
|---|
| 107 |
width = 40 |
|---|
| 108 |
height = 5 |
|---|
| 109 |
size_max = 2*1024*1024 |
|---|
| 110 |
xhtml_sanitize = False |
|---|
| 111 |
# Notes about using tidy : |
|---|
| 112 |
# * tidy doesn't know explicitly about the latin9 encoding but specifying |
|---|
| 113 |
# latin1 works well with latin9 encoded content. |
|---|
| 114 |
# * force-output makes tidy produce an output even if errors were found. |
|---|
| 115 |
# * show-body-only outputs only the content of the body tag. |
|---|
| 116 |
# * write-back modifies the file in place. |
|---|
| 117 |
xhtml_sanitize_system = 'tidy -indent -wrap 80 --input-encoding latin1 --output-encoding latin1 --force-output yes --clean yes --drop-font-tags yes --drop-proprietary-attributes yes --show-body-only yes --write-back yes --output-xhtml yes --show-errors 0 --show-warnings no --hide-comments no %s 2>/dev/null' |
|---|
| 118 |
file_uploader = False |
|---|
| 119 |
|
|---|
| 120 |
# Possible values are "tinymce" and "fckeditor" |
|---|
| 121 |
html_editor_type = 'tinymce' |
|---|
| 122 |
|
|---|
| 123 |
render_position = all_render_positions[0] |
|---|
| 124 |
render_format = all_render_formats[0] |
|---|
| 125 |
html_editor_position = all_html_editor_positions[0] |
|---|
| 126 |
configurable = 'nothing' |
|---|
| 127 |
input_encoding = 'iso-8859-15' |
|---|
| 128 |
output_encoding = 'iso-8859-15' |
|---|
| 129 |
|
|---|
| 130 |
# Associating the widget label with an input area to improve the widget |
|---|
| 131 |
# accessibility. |
|---|
| 132 |
has_input_area = True |
|---|
| 133 |
|
|---|
| 134 |
xhtml_sanitizer = XhtmlSanitizer() |
|---|
| 135 |
|
|---|
| 136 |
def prepare(self, datastructure, **kw): |
|---|
| 137 |
"""Prepare datastructure from datamodel.""" |
|---|
| 138 |
datamodel = datastructure.getDataModel() |
|---|
| 139 |
widget_id = self.getWidgetId() |
|---|
| 140 |
datastructure[widget_id] = str(datamodel[self.fields[0]]) |
|---|
| 141 |
rposition = self.render_position |
|---|
| 142 |
rformat = self.render_format |
|---|
| 143 |
if self.configurable != 'nothing': |
|---|
| 144 |
if len(self.fields) > 1: |
|---|
| 145 |
v = datamodel[self.fields[1]] |
|---|
| 146 |
if v in self.all_render_positions: |
|---|
| 147 |
rposition = v |
|---|
| 148 |
if len(self.fields) > 2: |
|---|
| 149 |
v = datamodel[self.fields[2]] |
|---|
| 150 |
if v in self.all_render_formats: |
|---|
| 151 |
rformat = v |
|---|
| 152 |
datastructure[widget_id + '_fileupload'] = None |
|---|
| 153 |
datastructure[widget_id + '_rposition'] = rposition |
|---|
| 154 |
datastructure[widget_id + '_rformat'] = rformat |
|---|
| 155 |
|
|---|
| 156 |
def validate(self, datastructure, **kw): |
|---|
| 157 |
"""Validate datastructure and update datamodel.""" |
|---|
| 158 |
widget_id = self.getWidgetId() |
|---|
| 159 |
file_upload = datastructure.get(widget_id + '_fileupload', None) |
|---|
| 160 |
file_upload_valid = False |
|---|
| 161 |
if file_upload is not None: |
|---|
| 162 |
ms = self.size_max |
|---|
| 163 |
file_upload.seek(0) |
|---|
| 164 |
read_size = len(file_upload.read(ms + 1)) |
|---|
| 165 |
if read_size > ms: |
|---|
| 166 |
# Size is expressed in human readable value |
|---|
| 167 |
max_size_str = self.getHumanReadableSize(ms) |
|---|
| 168 |
err = 'cpsschemas_err_file_too_big ${max_size}' |
|---|
| 169 |
err_mapping = {'max_size': max_size_str} |
|---|
| 170 |
return self.doesNotValidate(err, err_mapping, |
|---|
| 171 |
file, datastructure) |
|---|
| 172 |
file_upload.seek(0) |
|---|
| 173 |
value = file_upload.read() |
|---|
| 174 |
read_size = len(value) |
|---|
| 175 |
if read_size > 0: |
|---|
| 176 |
file_upload_valid = True |
|---|
| 177 |
if not file_upload_valid: |
|---|
| 178 |
value = datastructure[widget_id] |
|---|
| 179 |
err, v = self._extractValue(value) |
|---|
| 180 |
|
|---|
| 181 |
if err: |
|---|
| 182 |
datastructure.setError(widget_id, err) |
|---|
| 183 |
datastructure[widget_id] = v |
|---|
| 184 |
else: |
|---|
| 185 |
datamodel = datastructure.getDataModel() |
|---|
| 186 |
# Validating rposition and rformat entered by the user and |
|---|
| 187 |
# correcting them if necessary. |
|---|
| 188 |
if self.configurable != 'nothing': |
|---|
| 189 |
if len(self.fields) > 1: |
|---|
| 190 |
rposition = datastructure[widget_id + '_rposition'] |
|---|
| 191 |
if rposition and rposition in self.all_render_positions: |
|---|
| 192 |
datamodel[self.fields[1]] = rposition |
|---|
| 193 |
if len(self.fields) > 2: |
|---|
| 194 |
rformat = datastructure[widget_id + '_rformat'] |
|---|
| 195 |
if rformat and rformat in self.all_render_formats: |
|---|
| 196 |
datamodel[self.fields[2]] = rformat |
|---|
| 197 |
else: |
|---|
| 198 |
# Defaulting to the widget property since no fields are used to |
|---|
| 199 |
# store the format or the position. |
|---|
| 200 |
rformat = self.render_format |
|---|
| 201 |
if rformat == 'html': |
|---|
| 202 |
if self.xhtml_sanitize == 'builtin': |
|---|
| 203 |
self.xhtml_sanitizer.reset() |
|---|
| 204 |
self.xhtml_sanitizer.feed(v) |
|---|
| 205 |
v = self.xhtml_sanitizer.getResult() |
|---|
| 206 |
elif self.xhtml_sanitize == 'system': |
|---|
| 207 |
file_to_clean_fd, file_to_clean_path = mkstemp( |
|---|
| 208 |
suffix=".xhtml", |
|---|
| 209 |
prefix="cps-schemas", |
|---|
| 210 |
) |
|---|
| 211 |
file_to_clean = os.fdopen(file_to_clean_fd, 'w') |
|---|
| 212 |
file_to_clean.write(v) |
|---|
| 213 |
file_to_clean.close() |
|---|
| 214 |
os.system(self.xhtml_sanitize_system % file_to_clean_path) |
|---|
| 215 |
file_to_clean = open(file_to_clean_path) |
|---|
| 216 |
v = file_to_clean.read() |
|---|
| 217 |
file_to_clean.close() |
|---|
| 218 |
os.remove(file_to_clean_path) |
|---|
| 219 |
|
|---|
| 220 |
datamodel[self.fields[0]] = v |
|---|
| 221 |
if file_upload_valid or self.xhtml_sanitize: |
|---|
| 222 |
# If the file_upload is valid we update the datastructure so |
|---|
| 223 |
# that the immediate view after the modification has been done |
|---|
| 224 |
# shows the content of the file upload instead of the old value. |
|---|
| 225 |
self.prepare(datastructure) |
|---|
| 226 |
return not err |
|---|
| 227 |
|
|---|
| 228 |
def render(self, mode, datastructure, **kw): |
|---|
| 229 |
"""Render in mode from datastructure.""" |
|---|
| 230 |
render_method = 'widget_text_render' |
|---|
| 231 |
meth = getattr(self, render_method, None) |
|---|
| 232 |
if meth is None: |
|---|
| 233 |
raise RuntimeError("Unknown Render Method %s for widget type %s" |
|---|
| 234 |
% (render_method, self.getId())) |
|---|
| 235 |
widget_id = self.getWidgetId() |
|---|
| 236 |
value = datastructure[widget_id] |
|---|
| 237 |
rposition = datastructure[widget_id + '_rposition'] |
|---|
| 238 |
rformat = datastructure[widget_id + '_rformat'] |
|---|
| 239 |
if mode == 'view': |
|---|
| 240 |
if rformat == 'text': |
|---|
| 241 |
value = newline_to_br(escape(value)) |
|---|
| 242 |
elif rformat == 'html': |
|---|
| 243 |
pass |
|---|
| 244 |
elif rformat == 'rst': |
|---|
| 245 |
value = HTML(value, |
|---|
| 246 |
output_encoding=self.output_encoding, |
|---|
| 247 |
input_encoding=self.input_encoding, |
|---|
| 248 |
initial_header_level=2, report_level=0) |
|---|
| 249 |
# The pre render format is not a proposed choice in the UI anymore. |
|---|
| 250 |
# BBB compatibility code, will be removed in CPS 3.5.0. |
|---|
| 251 |
elif rformat == 'pre': |
|---|
| 252 |
value = '<pre>' + escape(value) + '</pre>' |
|---|
| 253 |
# The stx render format is not a proposed choice in the UI anymore. |
|---|
| 254 |
# BBB compatibility code, will be removed in CPS 3.5.0. |
|---|
| 255 |
elif rformat == 'stx': |
|---|
| 256 |
value = structured_text(value) |
|---|
| 257 |
else: |
|---|
| 258 |
RuntimeError("unknown render_format '%s' for '%s'" % |
|---|
| 259 |
(rformat, self.getId())) |
|---|
| 260 |
return meth(mode=mode, datastructure=datastructure, value=value, |
|---|
| 261 |
file_uploader=self.file_uploader, |
|---|
| 262 |
html_editor_type=self.html_editor_type, |
|---|
| 263 |
render_position=rposition, render_format=rformat, |
|---|
| 264 |
html_editor_position=self.html_editor_position, |
|---|
| 265 |
configurable=str(self.configurable)) |
|---|
| 266 |
|
|---|
| 267 |
InitializeClass(CPSTextWidget) |
|---|
| 268 |
|
|---|
| 269 |
widgetRegistry.register(CPSTextWidget) |
|---|
| 270 |
|
|---|
| 271 |
################################################## |
|---|
| 272 |
# previously named CPSDateWidget in BasicWidget r1.78 |
|---|
| 273 |
class CPSDateTimeWidget(CPSWidget): |
|---|
| 274 |
"""DateTime widget. |
|---|
| 275 |
|
|---|
| 276 |
A widget that displays and makes it possible to edit a DateTime object. |
|---|
| 277 |
View and edit mode can be done in the ISO 8601 date format (YYYY-mm-dd) |
|---|
| 278 |
or in a localized format (mm/dd/YYYY for English and dd/mm/YYYY for the rest |
|---|
| 279 |
of the world) cf. http://www.w3.org/TR/NOTE-datetime |
|---|
| 280 |
""" |
|---|
| 281 |
meta_type = 'DateTime Widget' |
|---|
| 282 |
|
|---|
| 283 |
field_types = ('CPS DateTime Field',) |
|---|
| 284 |
|
|---|
| 285 |
_properties = CPSWidget._properties + ( |
|---|
| 286 |
{'id': 'view_format', 'type': 'string', 'mode': 'w', |
|---|
| 287 |
'label': 'View format (short, medium or long)'}, |
|---|
| 288 |
{'id': 'time_setting', 'type': 'boolean', 'mode': 'w', |
|---|
| 289 |
'label': 'Enabling the setting of time of the day'}, |
|---|
| 290 |
{'id': 'time_hour_default', 'type': 'string', 'mode': 'w', |
|---|
| 291 |
'label': 'default hour for time'}, |
|---|
| 292 |
{'id': 'time_minutes_default', 'type': 'string', 'mode': 'w', |
|---|
| 293 |
'label': 'default minutes for time'}, |
|---|
| 294 |
) |
|---|
| 295 |
# When will CPS default to the more sensible ISO 8601 date format? |
|---|
| 296 |
#view_format = 'iso8601_medium_easy' |
|---|
| 297 |
view_format = 'medium' |
|---|
| 298 |
time_setting = 1 |
|---|
| 299 |
time_hour_default = '12' |
|---|
| 300 |
time_minutes_default = '00' |
|---|
| 301 |
|
|---|
| 302 |
# Associating the widget label with an input area to improve the widget |
|---|
| 303 |
# accessibility. |
|---|
| 304 |
has_input_area = True |
|---|
| 305 |
|
|---|
| 306 |
def getDateTimeInfo(self, value, mode=None): |
|---|
| 307 |
"""Return a tuple that is used to set the datastructure |
|---|
| 308 |
|
|---|
| 309 |
Called in prepare when mode is not known, and called again in render |
|---|
| 310 |
when mode is known, because a default value has to be provided in edit |
|---|
| 311 |
mode (current date time). |
|---|
| 312 |
""" |
|---|
| 313 |
# default values |
|---|
| 314 |
date = '' |
|---|
| 315 |
hour = '' |
|---|
| 316 |
minute = '' |
|---|
| 317 |
|
|---|
| 318 |
# value is set to current time if: |
|---|
| 319 |
# - value is not alrady set and |
|---|
| 320 |
# - widget is required an |
|---|
| 321 |
# - mode is 'edit' or 'create' |
|---|
| 322 |
if not value and self.is_required and mode in ['edit', 'create']: |
|---|
| 323 |
value = DateTime() |
|---|
| 324 |
|
|---|
| 325 |
if value == 'None': |
|---|
| 326 |
value = None |
|---|
| 327 |
if value: |
|---|
| 328 |
# Backward compatibility test, this logic is not used by the |
|---|
| 329 |
# current code. |
|---|
| 330 |
if isinstance(value, str): |
|---|
| 331 |
value = DateTime(value) |
|---|
| 332 |
d = str(value.day()) |
|---|
| 333 |
m = str(value.month()) |
|---|
| 334 |
y = str(value.year()) |
|---|
| 335 |
if self.view_format.startswith('iso8601'): |
|---|
| 336 |
date = '%s-%s-%s' % (y, m, d) |
|---|
| 337 |
else: |
|---|
| 338 |
locale = self.translation_service.getSelectedLanguage() |
|---|
| 339 |
if locale in ('en', 'hu'): |
|---|
| 340 |
date = '%s/%s/%s' % (m, d, y) |
|---|
| 341 |
else: |
|---|
| 342 |
date = '%s/%s/%s' % (d, m, y) |
|---|
| 343 |
hour = str(value.h_24()) |
|---|
| 344 |
minute = str(value.minute()) |
|---|
| 345 |
|
|---|
| 346 |
# if hour and minute are not set, set default values |
|---|
| 347 |
hour = hour or self.time_hour_default |
|---|
| 348 |
minute = minute or self.time_minutes_default |
|---|
| 349 |
|
|---|
| 350 |
return (value, date, hour, minute) |
|---|
| 351 |
|
|---|
| 352 |
def prepare(self, datastructure, **kw): |
|---|
| 353 |
"""Prepare datastructure from datamodel.""" |
|---|
| 354 |
datamodel = datastructure.getDataModel() |
|---|
| 355 |
v = datamodel[self.fields[0]] |
|---|
| 356 |
|
|---|
| 357 |
# get date time info, mode is not known here |
|---|
| 358 |
v, date, hour, minute = self.getDateTimeInfo(v, mode=None) |
|---|
| 359 |
|
|---|
| 360 |
widget_id = self.getWidgetId() |
|---|
| 361 |
datastructure[widget_id] = v |
|---|
| 362 |
datastructure[widget_id + '_date'] = date |
|---|
| 363 |
datastructure[widget_id + '_hour'] = hour or self.time_hour_default |
|---|
| 364 |
datastructure[widget_id + '_minute'] = minute or self.time_minutes_default |
|---|
| 365 |
|
|---|
| 366 |
def validate(self, datastructure, **kw): |
|---|
| 367 |
"""Validate datastructure and update datamodel.""" |
|---|
| 368 |
datamodel = datastructure.getDataModel() |
|---|
| 369 |
field_id = self.fields[0] |
|---|
| 370 |
widget_id = self.getWidgetId() |
|---|
| 371 |
|
|---|
| 372 |
date = datastructure[widget_id + '_date'].strip() |
|---|
| 373 |
hour = datastructure[widget_id + '_hour'].strip() or \ |
|---|
| 374 |
self.time_hour_default |
|---|
| 375 |
minute = datastructure[widget_id + '_minute'].strip() or \ |
|---|
| 376 |
self.time_minutes_default |
|---|
| 377 |
|
|---|
| 378 |
if not (date): |
|---|
| 379 |
if self.is_required: |
|---|
| 380 |
datastructure[widget_id] = '' |
|---|
| 381 |
datastructure.setError(widget_id, 'cpsschemas_err_required') |
|---|
| 382 |
return 0 |
|---|
| 383 |
else: |
|---|
| 384 |
datamodel[field_id] = None |
|---|
| 385 |
return 1 |
|---|
| 386 |
|
|---|
| 387 |
if self.view_format.startswith('iso8601'): |
|---|
| 388 |
if match(r'^[0-9]+-[0-9]{2}-[0-9]{2}', date) is not None: |
|---|
| 389 |
y, m, d = date.split('-') |
|---|
| 390 |
else: |
|---|
| 391 |
datastructure.setError(widget_id, 'cpsschemas_err_date') |
|---|
| 392 |
return 0 |
|---|
| 393 |
else: |
|---|
| 394 |
if match(r'^[0-9]?[0-9]/[0-9]?[0-9]/[0-9]{2,4}$', date) is not None: |
|---|
| 395 |
locale = self.translation_service.getSelectedLanguage() |
|---|
| 396 |
if locale in ('en', 'hu'): |
|---|
| 397 |
m, d, y = date.split('/') |
|---|
| 398 |
else: |
|---|
| 399 |
d, m, y = date.split('/') |
|---|
| 400 |
else: |
|---|
| 401 |
datastructure.setError(widget_id, 'cpsschemas_err_date') |
|---|
| 402 |
return 0 |
|---|
| 403 |
|
|---|
| 404 |
try: |
|---|
| 405 |
v = DateTime(int(y), int(m), int(d), int(hour), int(minute)) |
|---|
| 406 |
except (ValueError, TypeError, DateTime.DateTimeError, |
|---|
| 407 |
DateTime.SyntaxError, DateTime.DateError): |
|---|
| 408 |
datastructure.setError(widget_id, 'cpsschemas_err_date') |
|---|
| 409 |
return 0 |
|---|
| 410 |
else: |
|---|
| 411 |
datastructure[widget_id] = v |
|---|
| 412 |
datamodel[field_id] = v |
|---|
| 413 |
return 1 |
|---|
| 414 |
|
|---|
| 415 |
def render(self, mode, datastructure, **kw): |
|---|
| 416 |
"""Render in mode from datastructure.""" |
|---|
| 417 |
render_method = 'widget_datetime_render' |
|---|
| 418 |
meth = getattr(self, render_method, None) |
|---|
| 419 |
if meth is None: |
|---|
| 420 |
raise RuntimeError("Unknown Render Method %s for widget type %s" |
|---|
| 421 |
% (render_method, self.getId())) |
|---|
| 422 |
|
|---|
| 423 |
# XXX AT: datastructure has to be set again here, in case we're in edit |
|---|
| 424 |
# or create mode, because a default value has to be provided. |
|---|
| 425 |
if mode in ['edit', 'create']: |
|---|
| 426 |
datamodel = datastructure.getDataModel() |
|---|
| 427 |
v = datamodel[self.fields[0]] |
|---|
| 428 |
v, date, hour, minute = self.getDateTimeInfo(v, mode=mode) |
|---|
| 429 |
widget_id = self.getWidgetId() |
|---|
| 430 |
datastructure[widget_id] = v |
|---|
| 431 |
datastructure[widget_id + '_date'] = date |
|---|
| 432 |
datastructure[widget_id + '_hour'] = hour or self.time_hour_default |
|---|
| 433 |
datastructure[widget_id + '_minute'] = minute or self.time_minutes_default |
|---|
| 434 |
|
|---|
| 435 |
return meth(mode=mode, datastructure=datastructure) |
|---|
| 436 |
|
|---|
| 437 |
|
|---|
| 438 |
InitializeClass(CPSDateTimeWidget) |
|---|
| 439 |
|
|---|
| 440 |
widgetRegistry.register(CPSDateTimeWidget) |
|---|
| 441 |
|
|---|
| 442 |
################################################## |
|---|
| 443 |
class CPSAttachedFileWidget(CPSFileWidget): |
|---|
| 444 |
"""AttachedFile widget.""" |
|---|
| 445 |
meta_type = 'AttachedFile Widget' |
|---|
| 446 |
|
|---|
| 447 |
field_types = ('CPS File Field', # File |
|---|
| 448 |
'CPS String Field', # Plain text for indexing (optional) |
|---|
| 449 |
'CPS File Field', # Preview (HTML, optional) |
|---|
| 450 |
'CPS SubObjects Field',) |
|---|
| 451 |
|
|---|
| 452 |
field_inits = ({'is_searchabletext': 0, |
|---|
| 453 |
'suffix_text': '_f1', # _f# are autocomputed field ext |
|---|
| 454 |
'suffix_html': '_f2', |
|---|
| 455 |
'suffix_html_subfiles': '_f3', |
|---|
| 456 |
}, |
|---|
| 457 |
{'is_searchabletext': 1}, {}, {}, |
|---|
| 458 |
) |
|---|
| 459 |
|
|---|
| 460 |
_properties = CPSFileWidget._properties + ( |
|---|
| 461 |
{'id': 'display_html_preview', 'type': 'boolean', 'mode': 'w', |
|---|
| 462 |
'label': 'Display link to HTML preview in view mode'}, |
|---|
| 463 |
{'id': 'display_printable_version', 'type': 'boolean', 'mode': 'w', |
|---|
| 464 |
'label': 'Display link to printable version in view mode'}, |
|---|
| 465 |
{'id': 'allowed_suffixes', 'type': 'tokens', 'mode': 'w', |
|---|
| 466 |
'label': 'Allowed file suffixes (ex: .html .odt)'}, |
|---|
| 467 |
) |
|---|
| 468 |
display_html_preview = True |
|---|
| 469 |
display_printable_version = True |
|---|
| 470 |
allowed_suffixes = [] |
|---|
| 471 |
|
|---|
| 472 |
def prepare(self, datastructure, **kw): |
|---|
| 473 |
"""Prepare datastructure from datamodel.""" |
|---|
| 474 |
CPSFileWidget.prepare(self, datastructure, **kw) |
|---|
| 475 |
datamodel = datastructure.getDataModel() |
|---|
| 476 |
widget_id = self.getWidgetId() |
|---|
| 477 |
|
|---|
| 478 |
# Compute preview info for widget. |
|---|
| 479 |
if len(self.fields) > 2 and datamodel.get(self.fields[2]) is not None: |
|---|
| 480 |
preview_id = self.fields[2] |
|---|
| 481 |
else: |
|---|
| 482 |
preview_id = None |
|---|
| 483 |
datastructure[widget_id + '_preview'] = preview_id |
|---|
| 484 |
|
|---|
| 485 |
def checkFileName(self, fileid, mimetype): |
|---|
| 486 |
if self.allowed_suffixes: |
|---|
| 487 |
base, suffix = os.path.splitext(fileid) |
|---|
| 488 |
if suffix not in self.allowed_suffixes: |
|---|
| 489 |
err = 'cpsschemas_err_file_bad_suffix ${allowed_file_suffixes}' |
|---|
| 490 |
err_mapping = {'allowed_file_suffixes': |
|---|
| 491 |
' '.join(self.allowed_suffixes)} |
|---|
| 492 |
return err, err_mapping |
|---|
| 493 |
return '', {} |
|---|
| 494 |
|
|---|
| 495 |
def render(self, mode, datastructure, **kw): |
|---|
| 496 |
"""Render in mode from datastructure.""" |
|---|
| 497 |
render_method = 'widget_attachedfile_render' |
|---|
| 498 |
meth = getattr(self, render_method, None) |
|---|
| 499 |
if meth is None: |
|---|
| 500 |
raise RuntimeError("Unknown Render Method %s for widget type %s" |
|---|
| 501 |
% (render_method, self.getId())) |
|---|
| 502 |
file_info = self.getFileInfo(datastructure) |
|---|
| 503 |
|
|---|
| 504 |
return meth(mode=mode, datastructure=datastructure, |
|---|
| 505 |
**file_info) |
|---|
| 506 |
|
|---|
| 507 |
InitializeClass(CPSAttachedFileWidget) |
|---|
| 508 |
|
|---|
| 509 |
widgetRegistry.register(CPSAttachedFileWidget) |
|---|
| 510 |
|
|---|
| 511 |
################################################## |
|---|
| 512 |
|
|---|
| 513 |
class CPSZippedHtmlWidget(CPSAttachedFileWidget): |
|---|
| 514 |
"""CPS ZippedHtml widget. |
|---|
| 515 |
|
|---|
| 516 |
A zip file that contains html which can be viewed online. |
|---|
| 517 |
Use index.html or any html file from the zip as preview page. |
|---|
| 518 |
""" |
|---|
| 519 |
|
|---|
| 520 |
meta_type = 'ZippedHtml Widget' |
|---|
| 521 |
|
|---|
| 522 |
size_max = 1024*1024 |
|---|
| 523 |
|
|---|
| 524 |
# FIXME checkFileName() from ancestor is never called after changeset [30791] |
|---|
| 525 |
# We need to fix it over there. |
|---|
| 526 |
#allowed_suffixes = ['.zip'] |
|---|
| 527 |
|
|---|
| 528 |
def _is_zipfile(self, datastructure, **kw): |
|---|
| 529 |
# Check the zip file validity |
|---|
| 530 |
choice = datastructure[self.getWidgetId()+'_choice'] |
|---|
| 531 |
if choice == 'change': |
|---|
| 532 |
validated = False |
|---|
| 533 |
file_upload = datastructure.get(self.getWidgetId()) |
|---|
| 534 |
try: |
|---|
| 535 |
zf = zipfile.ZipFile(file_upload, 'r') |
|---|
| 536 |
except zipfile.BadZipfile: |
|---|
| 537 |
pass |
|---|
| 538 |
else: |
|---|
| 539 |
if zf.testzip() is None: |
|---|
| 540 |
validated = True |
|---|
| 541 |
zf.close() |
|---|
| 542 |
return validated |
|---|
| 543 |
return True |
|---|
| 544 |
|
|---|
| 545 |
def validate(self, datastructure, **kw): |
|---|
| 546 |
# Validate datastructure and update datamodel. |
|---|
| 547 |
return (CPSAttachedFileWidget.validate(self, datastructure, **kw) and |
|---|
| 548 |
self._is_zipfile(datastructure, **kw)) |
|---|
| 549 |
|
|---|
| 550 |
def _getIndexPath(self, datastructure): |
|---|
| 551 |
file_upload = datastructure.get(self.getWidgetId()) |
|---|
| 552 |
# Creation time |
|---|
| 553 |
if not file_upload: |
|---|
| 554 |
return '' |
|---|
| 555 |
# Here the zipfile has been validated already. |
|---|
| 556 |
zf = zipfile.ZipFile(file_upload, 'r') |
|---|
| 557 |
all_files = [info.filename for info in zf.infolist()] |
|---|
| 558 |
html_files = [f for f in all_files |
|---|
| 559 |
if (f.lower().endswith('.html') or |
|---|
| 560 |
f.lower().endswith('.htm'))] |
|---|
| 561 |
index_files = [f for f in html_files |
|---|
| 562 |
if f.lower().find('index.htm') >= 0] |
|---|
| 563 |
index_path = None |
|---|
| 564 |
if index_files: |
|---|
| 565 |
index_path = index_files[0] |
|---|
| 566 |
elif html_files: |
|---|
| 567 |
index_path = html_files[0] |
|---|
| 568 |
zf.close() |
|---|
| 569 |
return index_path |
|---|
| 570 |
|
|---|
| 571 |
def render(self, mode, datastructure, **kw): |
|---|
| 572 |
render_method = 'widget_zippedhtml_render' |
|---|
| 573 |
meth = getattr(self, render_method, None) |
|---|
| 574 |
if meth is None: |
|---|
| 575 |
raise RuntimeError("Unknown Render Method %s for widget type %s" |
|---|
| 576 |
% (render_method, self.getId())) |
|---|
| 577 |
file_info = self.getFileInfo(datastructure) |
|---|
| 578 |
file_info['index_path'] = self._getIndexPath(datastructure) |
|---|
| 579 |
return meth(mode=mode, datastructure=datastructure, **file_info) |
|---|
| 580 |
|
|---|
| 581 |
InitializeClass(CPSZippedHtmlWidget) |
|---|
| 582 |
|
|---|
| 583 |
widgetRegistry.register(CPSZippedHtmlWidget) |
|---|
| 584 |
|
|---|
| 585 |
################################################# |
|---|
| 586 |
|
|---|
| 587 |
class CPSRichTextEditorWidget(CPSWidget): |
|---|
| 588 |
"""Rich Text Editor widget. |
|---|
| 589 |
|
|---|
| 590 |
THIS WIDGET SHOULD NOT BE USED AND IS DEPRECATED. |
|---|
| 591 |
|
|---|
| 592 |
Use the Text Widget which provides both HTML and text formats. |
|---|
| 593 |
""" |
|---|
| 594 |
meta_type = 'Rich Text Editor Widget' |
|---|
| 595 |
|
|---|
| 596 |
field_types = ('CPS String Field',) |
|---|
| 597 |
field_inits = ({'is_searchabletext': 1,},) |
|---|
| 598 |
|
|---|
| 599 |
width = 40 |
|---|
| 600 |
height = 5 |
|---|
| 601 |
_properties = CPSWidget._properties + ( |
|---|
| 602 |
{'id': 'width', 'type': 'int', 'mode': 'w', |
|---|
| 603 |
'label': 'Width'}, |
|---|
| 604 |
{'id': 'height', 'type': 'int', 'mode': 'w', |
|---|
| 605 |
'label': 'Height'}, |
|---|
| 606 |
) |
|---|
| 607 |
|
|---|
| 608 |
def prepare(self, datastructure, **kw): |
|---|
| 609 |
"""Prepare datastructure from datamodel.""" |
|---|
| 610 |
datamodel = datastructure.getDataModel() |
|---|
| 611 |
datastructure[self.getWidgetId()] = datamodel[self.fields[0]] |
|---|
| 612 |
|
|---|
| 613 |
def validate(self, datastructure, **kw): |
|---|
| 614 |
"""Validate datastructure and update datamodel.""" |
|---|
| 615 |
value = datastructure[self.getWidgetId()] |
|---|
| 616 |
try: |
|---|
| 617 |
v = str(value) |
|---|
| 618 |
except ValueError: |
|---|
| 619 |
datastructure.setError(self.getWidgetId(), |
|---|
| 620 |
"cpsschemas_err_textarea") |
|---|
| 621 |
ok = 0 |
|---|
| 622 |
else: |
|---|
| 623 |
datamodel = datastructure.getDataModel() |
|---|
| 624 |
datamodel[self.fields[0]] = v |
|---|
| 625 |
ok = 1 |
|---|
| 626 |
return ok |
|---|
| 627 |
|
|---|
| 628 |
def render(self, mode, datastructure, **kw): |
|---|
| 629 |
"""Render in mode from datastructure.""" |
|---|
| 630 |
warnings.warn("The Rich Text Editor Widget (%s/%s) is deprecated " |
|---|
| 631 |
"and will be removed in CPS 3.5.0. Use a Text Widget " |
|---|
| 632 |
"instead" % (aq_parent(aq_inner(self)).getId(), |
|---|
| 633 |
self.getWidgetId()), DeprecationWarning) |
|---|
| 634 |
value = datastructure[self.getWidgetId()] |
|---|
| 635 |
if mode == 'view': |
|---|
| 636 |
# Return HTML directly |
|---|
| 637 |
return value |
|---|
| 638 |
elif mode == 'edit': |
|---|
| 639 |
render_method = 'widget_rte_render' |
|---|
| 640 |
meth = getattr(self, render_method, None) |
|---|
| 641 |
if meth is None: |
|---|
| 642 |
raise RuntimeError("Unknown Render Method %s for widget type %s" |
|---|
| 643 |
% (render_method, self.getId())) |
|---|
| 644 |
value = datastructure[self.getWidgetId()] |
|---|
| 645 |
if hasattr(aq_base(value), 'getId'): |
|---|
| 646 |
current_name = value.getId() |
|---|
| 647 |
else: |
|---|
| 648 |
current_name = '-' |
|---|
| 649 |
return meth(mode=mode, datastructure=datastructure, |
|---|
| 650 |
current_name=current_name) |
|---|
| 651 |
raise RuntimeError("unknown mode %s" % mode) |
|---|
| 652 |
|
|---|
| 653 |
InitializeClass(CPSRichTextEditorWidget) |
|---|
| 654 |
|
|---|
| 655 |
widgetRegistry.register(CPSRichTextEditorWidget) |
|---|
| 656 |
|
|---|
| 657 |
########################################## |
|---|
| 658 |
|
|---|
| 659 |
class CPSExtendedSelectWidget(CPSSelectWidget): |
|---|
| 660 |
"""Extended Select widget.""" |
|---|
| 661 |
meta_type = 'ExtendedSelect Widget' |
|---|
| 662 |
|
|---|
| 663 |
def render(self, mode, datastructure, **kw): |
|---|
| 664 |
"""Render in mode from datastructure.""" |
|---|
| 665 |
|
|---|
| 666 |
if mode == 'view': |
|---|
| 667 |
return CPSSelectWidget.render(self, mode, datastructure) |
|---|
| 668 |
|
|---|
| 669 |
elif mode == 'edit': |
|---|
| 670 |
render_method = 'widget_extendedselect_render' |
|---|
| 671 |
|
|---|
| 672 |
meth = getattr(self, render_method, None) |
|---|
| 673 |
if meth is None: |
|---|
| 674 |
raise RuntimeError("Unknown Render Method %s for widget type %s" |
|---|
| 675 |
% (render_method, self.getId())) |
|---|
| 676 |
return meth(mode=mode, datastructure=datastructure, |
|---|
| 677 |
vocabulary=self._getVocabulary(datastructure)) |
|---|
| 678 |
|
|---|
| 679 |
else: |
|---|
| 680 |
raise RuntimeError('unknown mode %s' % mode) |
|---|
| 681 |
|
|---|
| 682 |
InitializeClass(CPSExtendedSelectWidget) |
|---|
| 683 |
|
|---|
| 684 |
widgetRegistry.register(CPSExtendedSelectWidget) |
|---|
| 685 |
|
|---|
| 686 |
########################################## |
|---|
| 687 |
|
|---|
| 688 |
class CPSInternalLinksWidget(CPSWidget): |
|---|
| 689 |
"""Internal Links widget.""" |
|---|
| 690 |
meta_type = 'InternalLinks Widget' |
|---|
| 691 |
|
|---|
| 692 |
field_types = ('CPS String List Field',) |
|---|
| 693 |
field_inits = ({'is_searchabletext': 1,},) |
|---|
| 694 |
|
|---|
| 695 |
_properties = CPSWidget._properties + ( |
|---|
| 696 |
{'id': 'new_window', 'type': 'boolean', 'mode': 'w', |
|---|
| 697 |
'label': 'Display in a new window'}, |
|---|
| 698 |
{'id': 'size', 'type': 'int', 'mode': 'w', |
|---|
| 699 |
'label': 'Links displayed'}, |
|---|
| 700 |
{'id': 'absolute', 'type': 'boolean', 'mode': 'w', |
|---|
| 701 |
'label': 'Links displayed with absolute URL'}, |
|---|
| 702 |
) |
|---|
| 703 |
new_window = 0 |
|---|
| 704 |
size = 0 |
|---|
| 705 |
absolute = False |
|---|
| 706 |
|
|---|
| 707 |
def prepare(self, datastructure, **kw): |
|---|
| 708 |
"""Prepare datastructure from datamodel.""" |
|---|
| 709 |
datamodel = datastructure.getDataModel() |
|---|
| 710 |
datastructure[self.getWidgetId()] = datamodel[self.fields[0]] |
|---|
| 711 |
|
|---|
| 712 |
def validate(self, datastructure, **kw): |
|---|
| 713 |
"""Validate datastructure and update datamodel.""" |
|---|
| 714 |
widget_id = self.getWidgetId() |
|---|
| 715 |
value = datastructure[widget_id] |
|---|
| 716 |
err = 0 |
|---|
| 717 |
if self.is_required and (value == [] or value == ['']): |
|---|
| 718 |
err = 'cpsschemas_err_required' |
|---|
| 719 |
v = [] |
|---|
| 720 |
|
|---|
| 721 |
|
|---|
| 722 |
for line in value: |
|---|
| 723 |
if line.strip(): |
|---|
| 724 |
v.append(line) |
|---|
| 725 |
|
|---|
| 726 |
if err: |
|---|
| 727 |
datastructure.setError(widget_id, err) |
|---|
| 728 |
else: |
|---|
| 729 |
datamodel = datastructure.getDataModel() |
|---|
| 730 |
datamodel[self.fields[0]] = v |
|---|
| 731 |
self.prepare(datastructure) |
|---|
| 732 |
|
|---|
| 733 |
return not err |
|---|
| 734 |
|
|---|
| 735 |
def render(self, mode, datastructure, **kw): |
|---|
| 736 |
"""Render in mode from datastructure.""" |
|---|
| 737 |
if mode not in ('view', 'edit'): |
|---|
| 738 |
raise RuntimeError('unknown mode %s' % mode) |
|---|
| 739 |
|
|---|
| 740 |
render_method = 'widget_internallinks_render' |
|---|
| 741 |
meth = getattr(self, render_method, None) |
|---|
| 742 |
|
|---|
| 743 |
return meth(mode=mode, datastructure=datastructure) |
|---|
| 744 |
|
|---|
| 745 |
InitializeClass(CPSInternalLinksWidget) |
|---|
| 746 |
|
|---|
| 747 |
widgetRegistry.register(CPSInternalLinksWidget) |
|---|
| 748 |
|
|---|
| 749 |
################################################## |
|---|
| 750 |
|
|---|
| 751 |
class CPSPhotoWidget(CPSImageWidget): |
|---|
| 752 |
"""Photo widget.""" |
|---|
| 753 |
meta_type = 'Photo Widget' |
|---|
| 754 |
|
|---|
| 755 |
field_types = ('CPS Image Field', # Image |
|---|
| 756 |
'CPS String Field', # Caption |
|---|
| 757 |
'CPS String Field', # render_position if configurable |
|---|
| 758 |
'CPS Image Field', # Original photo |
|---|
| 759 |
'CPS String Field', # Title |
|---|
| 760 |
'CPS String Field', # Alternate text for accessibility |
|---|
| 761 |
) |
|---|
| 762 |
field_inits = ({}, {'is_searchabletext': 1,}, {}, {}, {}, {}) |
|---|
| 763 |
|
|---|
| 764 |
_properties = CPSImageWidget._properties + ( |
|---|
| 765 |
{'id': 'render_position', 'type': 'selection', 'mode': 'w', |
|---|
| 766 |
'select_variable': 'all_render_positions', |
|---|
| 767 |
'label': 'Render position'}, |
|---|
| 768 |
{'id': 'configurable', 'type': 'selection', 'mode': 'w', |
|---|
| 769 |
'select_variable': 'all_configurable', |
|---|
| 770 |
'label': 'What is user configurable, require extra fields'}, |
|---|
| 771 |
{'id': 'keep_original', 'type': 'boolean', 'mode': 'w', |
|---|
| 772 |
'label': 'Keep original image'}, |
|---|
| 773 |
) |
|---|
| 774 |
all_configurable = ['nothing', 'position'] |
|---|
| 775 |
all_render_positions = ['left', 'center', 'right'] |
|---|
| 776 |
|
|---|
| 777 |
allow_resize = True |
|---|
| 778 |
render_position = all_render_positions[0] |
|---|
| 779 |
configurable = all_configurable[0] |
|---|
| 780 |
keep_original = True |
|---|
| 781 |
|
|---|
| 782 |
def prepare(self, datastructure, **kw): |
|---|
| 783 |
"""Prepare datastructure from datamodel.""" |
|---|
| 784 |
CPSImageWidget.prepare(self, datastructure, **kw) |
|---|
| 785 |
datamodel = datastructure.getDataModel() |
|---|
| 786 |
widget_id = self.getWidgetId() |
|---|
| 787 |
|
|---|
| 788 |
if len(self.fields) > 1: |
|---|
| 789 |
datastructure[widget_id + '_subtitle'] = datamodel[self.fields[1]] |
|---|
| 790 |
else: |
|---|
| 791 |
datastructure[widget_id + '_subtitle'] = '' |
|---|
| 792 |
|
|---|
| 793 |
rposition = self.render_position |
|---|
| 794 |
if self.configurable != 'nothing': |
|---|
| 795 |
if len(self.fields) > 2: |
|---|
| 796 |
v = datamodel[self.fields[2]] |
|---|
| 797 |
if v in self.all_render_positions: |
|---|
| 798 |
rposition = v |
|---|
| 799 |
datastructure[widget_id + '_rposition'] = rposition |
|---|
| 800 |
|
|---|
| 801 |
if self.keep_original and len(self.fields) > 3: |
|---|
| 802 |
datastructure[widget_id + '_resize_kept'] = '' |
|---|
| 803 |
|
|---|
| 804 |
title = '' |
|---|
| 805 |
if len(self.fields) > 4: |
|---|
| 806 |
title = datamodel[self.fields[4]] |
|---|
| 807 |
datastructure[widget_id + '_title'] = title |
|---|
| 808 |
|
|---|
| 809 |
alt = '' |
|---|
| 810 |
if len(self.fields) > 5: |
|---|
| 811 |
alt = datamodel[self.fields[5]] |
|---|
| 812 |
# Defaulting to the file name if there is an image file and if no |
|---|
| 813 |
# alt has been given yet. This is the case when the document is |
|---|
| 814 |
# created. |
|---|
| 815 |
if not alt: |
|---|
| 816 |
alt = datastructure[widget_id + '_filename'] |
|---|
| 817 |
datastructure[widget_id + '_alt'] = alt |
|---|
| 818 |
|
|---|
| 819 |
def otherProcessing(self, choice, datastructure): |
|---|
| 820 |
datamodel = datastructure.getDataModel() |
|---|
| 821 |
widget_id = self.getWidgetId() |
|---|
| 822 |
|
|---|
| 823 |
# Caption |
|---|
| 824 |
if len(self.fields) > 1: |
|---|
| 825 |
subtitle = datastructure[widget_id + '_subtitle'] |
|---|
| 826 |
datamodel[self.fields[1]] = subtitle |
|---|
| 827 |
|
|---|
| 828 |
# Title |
|---|
| 829 |
if len(self.fields) > 4: |
|---|
| 830 |
title = datastructure[widget_id + '_title'] |
|---|
| 831 |
datamodel[self.fields[4]] = title |
|---|
| 832 |
|
|---|
| 833 |
# Alt |
|---|
| 834 |
if len(self.fields) > 5: |
|---|
| 835 |
alt = datastructure[widget_id + '_alt'] |
|---|
| 836 |
datamodel[self.fields[5]] = alt |
|---|
| 837 |
|
|---|
| 838 |
# Position |
|---|
| 839 |
if self.configurable != 'nothing' and len(self.fields) > 2: |
|---|
| 840 |
rposition = datastructure[widget_id + '_rposition'] |
|---|
| 841 |
if rposition and rposition in self.all_render_positions: |
|---|
| 842 |
datamodel[self.fields[2]] = rposition |
|---|
| 843 |
|
|---|
| 844 |
# Resize |
|---|
| 845 |
if choice != 'resize': |
|---|
| 846 |
return |
|---|
| 847 |
if not self.canKeepOriginal(): |
|---|
| 848 |
return |
|---|
| 849 |
image = datamodel[self.fields[0]] |
|---|
| 850 |
original_image = datamodel[self.fields[3]] |
|---|
| 851 |
if original_image is None and image is None: |
|---|
| 852 |
return |
|---|
| 853 |
|
|---|
| 854 |
if original_image is None: |
|---|
| 855 |
original_image = image |
|---|
| 856 |
datamodel[self.fields[3]] = original_image |
|---|
| 857 |
|
|---|
| 858 |
filename = original_image.title |
|---|
| 859 |
resize_op = datastructure[widget_id + '_resize_kept'] |
|---|
| 860 |
image = self.getResizedImage(original_image, filename, resize_op) |
|---|
| 861 |
datamodel[self.fields[0]] = image |
|---|
| 862 |
|
|---|
| 863 |
def canKeepOriginal(self): |
|---|
| 864 |
return (self.keep_original and |
|---|
| 865 |
self.allow_resize and |
|---|
| 866 |
len(self.fields) > 3) |
|---|
| 867 |
|
|---|
| 868 |
def maybeKeepOriginal(self, image, datastructure): |
|---|
| 869 |
if self.canKeepOriginal(): |
|---|
| 870 |
datamodel = datastructure.getDataModel() |
|---|
| 871 |
datamodel[self.fields[3]] = image |
|---|
| 872 |
|
|---|
| 873 |
def render(self, mode, datastructure, **kw): |
|---|
| 874 |
"""Render in mode from datastructure.""" |
|---|
| 875 |
render_method = 'widget_photo_render' |
|---|
| 876 |
meth = getattr(self, render_method, None) |
|---|
| 877 |
if meth is None: |
|---|
| 878 |
raise RuntimeError("Unknown Render Method %s for widget type %s" |
|---|
| 879 |
% (render_method, self.getId())) |
|---|
| 880 |
|
|---|
| 881 |
widget_id = self.getWidgetId() |
|---|
| 882 |
rposition = datastructure[widget_id + '_rposition'] |
|---|
| 883 |
subtitle = datastructure[widget_id + '_subtitle'] |
|---|
| 884 |
|
|---|
| 885 |
img_info = self.getImageInfo(datastructure, dump_cid_parts=True, **kw) |
|---|
| 886 |
return meth(mode=mode, datastructure=datastructure, |
|---|
| 887 |
subtitle=subtitle, |
|---|
| 888 |
render_position=rposition, |
|---|
| 889 |
configurable=str(self.configurable), |
|---|
| 890 |
**img_info) |
|---|
| 891 |
|
|---|
| 892 |
|
|---|
| 893 |
InitializeClass(CPSPhotoWidget) |
|---|
| 894 |
|
|---|
| 895 |
widgetRegistry.register(CPSPhotoWidget) |
|---|
| 896 |
|
|---|
| 897 |
################################################## |
|---|
| 898 |
|
|---|
| 899 |
class CPSGenericSelectWidget(CPSSelectWidget): |
|---|
| 900 |
"""Generic Select widget.""" |
|---|
| 901 |
meta_type = 'Generic Select Widget' |
|---|
| 902 |
|
|---|
| 903 |
_properties = CPSSelectWidget._properties + ( |
|---|
| 904 |
{'id': 'render_format', 'type': 'selection', 'mode': 'w', |
|---|
| 905 |
'select_variable': 'render_formats', |
|---|
| 906 |
'label': 'Render format'}, |
|---|
| 907 |
# Provide an 'other' option where free input is accepted |
|---|
| 908 |
# (ignored if render format is 'select') |
|---|
| 909 |
{'id': 'other_option', 'type': 'boolean', 'mode':'w', |
|---|
| 910 |
'label': "Provide an 'other' option"}, |
|---|
| 911 |
{'id': 'other_option_display_width', 'type': 'int', 'mode': 'w', |
|---|
| 912 |
'label': "'other' option display width"}, |
|---|
| 913 |
{'id': 'other_option_size_max', 'type': 'int', 'mode': 'w', |
|---|
| 914 |
'label': "'other' option maximum input width"}, |
|---|
| 915 |
# Enables the possibility to add blank values to vocabulary just to |
|---|
| 916 |
# change the way the list is presented (using items like 'choose a |
|---|
| 917 |
# category' or '------------' to separate items) and not affect the way |
|---|
| 918 |
# the value will be validated if the widget is required. |
|---|
| 919 |
# Before this, if the widget was required, default behavior was to |
|---|
| 920 |
# accept blank values if they were in the vocabulary (e.g |
|---|
| 921 |
# blank_value_ok_if_required = 1) |
|---|
| 922 |
{'id': 'blank_value_ok_if_required', 'type': 'boolean', 'mode':'w', |
|---|
| 923 |
'label': "Accept blank values when validating"}, |
|---|
| 924 |
{'id': 'onchange', 'type': 'string', 'mode':'w', |
|---|
| 925 |
'label': "onchange attribute (edit mode only)"} |
|---|
| 926 |
) |
|---|
| 927 |
render_formats = ['select', 'radio'] |
|---|
| 928 |
|
|---|
| 929 |
render_format = render_formats[0] |
|---|
| 930 |
other_option = 0 |
|---|
| 931 |
other_option_display_width = 20 |
|---|
| 932 |
other_option_size_max = 0 |
|---|
| 933 |
blank_value_ok_if_required = 1 |
|---|
| 934 |
onchange = '' |
|---|
| 935 |
|
|---|
| 936 |
# BBB for [46171]. Remove this once an upgrade step has been written |
|---|
| 937 |
sorted = False |
|---|
| 938 |
|
|---|
| 939 |
def prepare(self, datastructure, **kw): |
|---|
| 940 |
"""Prepare datastructure from datamodel.""" |
|---|
| 941 |
datamodel = datastructure.getDataModel() |
|---|
| 942 |
value = datamodel[self.fields[0]] |
|---|
| 943 |
if isinstance(value, (list, tuple)): |
|---|
| 944 |
if len(value): |
|---|
| 945 |
value = value[0] |
|---|
| 946 |
else: |
|---|
| 947 |
value = '' |
|---|
| 948 |
datastructure[self.getWidgetId()] = value |
|---|
| 949 |
|
|---|
| 950 |
def validate(self, datastructure, **kw): |
|---|
| 951 |
"""Validate datastructure and update datamodel.""" |
|---|
| 952 |
widget_id = self.getWidgetId() |
|---|
| 953 |
value = datastructure[widget_id] |
|---|
| 954 |
try: |
|---|
| 955 |
v = str(value) |
|---|
| 956 |
except ValueError: |
|---|
| 957 |
datastructure.setError(widget_id, "cpsschemas_err_select") |
|---|
| 958 |
return 0 |
|---|
| 959 |
vocabulary = self._getVocabulary(datastructure) |
|---|
| 960 |
if len(value)>0: |
|---|
| 961 |
if not vocabulary.has_key(value): |
|---|
| 962 |
if self.render_format == 'select' or not self.other_option: |
|---|
| 963 |
datastructure.setError(widget_id, "cpsschemas_err_select") |
|---|
| 964 |
return 0 |
|---|
| 965 |
else: |
|---|
| 966 |
if self.is_required: |
|---|
| 967 |
# set error unless vocabulary holds blank values and |
|---|
| 968 |
# blank_value_ok_if_required is set to 1 |
|---|
| 969 |
if vocabulary.has_key(value): |
|---|
| 970 |
if not self.blank_value_ok_if_required: |
|---|
| 971 |
datastructure.setError(widget_id, "cpsschemas_err_required") |
|---|
| 972 |
return 0 |
|---|
| 973 |
else: |
|---|
| 974 |
datastructure.setError(widget_id, "cpsschemas_err_required") |
|---|
| 975 |
return 0 |
|---|
| 976 |
datamodel = datastructure.getDataModel() |
|---|
| 977 |
datamodel[self.fields[0]] = v |
|---|
| 978 |
return 1 |
|---|
| 979 |
|
|---|
| 980 |
def render(self, mode, datastructure, **kw): |
|---|
| 981 |
"""Render in mode from datastructure.""" |
|---|
| 982 |
widget_id = self.getWidgetId() |
|---|
| 983 |
value = datastructure[widget_id] |
|---|
| 984 |
# allow int values to work |
|---|
| 985 |
if value is not None: |
|---|
| 986 |
value = str(value) |
|---|
| 987 |
vocabulary = self._getVocabulary(datastructure) |
|---|
| 988 |
portal = getToolByName(self, 'portal_url').getPortalObject() |
|---|
| 989 |
cpsmcat = portal.translation_service |
|---|
| 990 |
if mode == 'view': |
|---|
| 991 |
if not vocabulary.has_key(value): |
|---|
| 992 |
# for free input |
|---|
| 993 |
if value is not None: |
|---|
| 994 |
return escape(value) |
|---|
| 995 |
else: |
|---|
| 996 |
return '' |
|---|
| 997 |
else: |
|---|
| 998 |
if getattr(self, 'translated', None): |
|---|
| 999 |
return escape(cpsmcat(vocabulary.getMsgid(value, value)).encode('ISO-8859-15', 'ignore')) |
|---|
| 1000 |
else: |
|---|
| 1001 |
return escape(vocabulary.get(value, value)) |
|---|
| 1002 |
elif mode == 'edit': |
|---|
| 1003 |
in_selection = 0 |
|---|
| 1004 |
in_other_selection = 0 |
|---|
| 1005 |
res = '' |
|---|
| 1006 |
html_widget_id = self.getHtmlWidgetId() |
|---|
| 1007 |
render_format = self.render_format |
|---|
| 1008 |
if render_format not in self.render_formats: |
|---|
| 1009 |
raise RuntimeError('unknown render format %s' % render_format) |
|---|
| 1010 |
if render_format == 'select': |
|---|
| 1011 |
res = renderHtmlTag('select', |
|---|
| 1012 |
name=html_widget_id, id=html_widget_id, |
|---|
| 1013 |
onchange=self.onchange or None) |
|---|
| 1014 |
# vocabulary options |
|---|
| 1015 |
vocabulary_items = vocabulary.items() |
|---|
| 1016 |
if self.sorted: |
|---|
| 1017 |
vocabulary_items.sort(key=lambda obj: obj[1].lower()) |
|---|
| 1018 |
for k, v in vocabulary_items: |
|---|
| 1019 |
# this enable to work with vocabulary that have integer keys |
|---|
| 1020 |
k = str(k) |
|---|
| 1021 |
if getattr(self, 'translated', None): |
|---|
| 1022 |
v = cpsmcat(vocabulary.getMsgid(k, k)).encode('ISO-8859-15', 'ignore') |
|---|
| 1023 |
if render_format == 'select': |
|---|
| 1024 |
kw = {'value': k, 'contents': v} |
|---|
| 1025 |
# do not add a selected option if it is already set |
|---|
| 1026 |
if value == k and not in_selection: |
|---|
| 1027 |
kw['selected'] = 'selected' |
|---|
| 1028 |
in_selection = 1 |
|---|
| 1029 |
res += renderHtmlTag('option', **kw) |
|---|
| 1030 |
res += '\n' |
|---|
| 1031 |
else: |
|---|
| 1032 |
kw = {'id': html_widget_id+'_'+k, |
|---|
| 1033 |
'type': render_format, |
|---|
| 1034 |
'name': html_widget_id, |
|---|
| 1035 |
'value': k, |
|---|
| 1036 |
} |
|---|
| 1037 |
# do not add a selected option if it is already set |
|---|
| 1038 |
if value == k and not in_selection: |
|---|
| 1039 |
kw['checked'] = 'checked' |
|---|
| 1040 |
in_selection = 1 |
|---|
| 1041 |
res += renderHtmlTag('input', **kw) |
|---|
| 1042 |
kw = {'for': html_widget_id+'_'+k, |
|---|
| 1043 |
'contents': v, |
|---|
| 1044 |
} |
|---|
| 1045 |
res += renderHtmlTag('label', **kw) |
|---|
| 1046 |
res += '<br/>\n' |
|---|
| 1047 |
# invalid or free selections |
|---|
| 1048 |
if value and not in_selection: |
|---|
| 1049 |
if render_format == 'select': |
|---|
| 1050 |
in_selection = 1 |
|---|
| 1051 |
kw = {'value': value, |
|---|
| 1052 |
'contents': 'invalid: '+value, |
|---|
| 1053 |
'selected': 'selected', |
|---|
| 1054 |
} |
|---|
| 1055 |
res += renderHtmlTag('option', **kw) |
|---|
| 1056 |
res += '\n' |
|---|
| 1057 |
else: |
|---|
| 1058 |
if self.other_option: |
|---|
| 1059 |
in_selection = 1 |
|---|
| 1060 |
in_other_selection = 1 |
|---|
| 1061 |
kw = {'id': html_widget_id+'_other_selection', |
|---|
| 1062 |
'type': render_format, |
|---|
| 1063 |
'name': html_widget_id, |
|---|
| 1064 |
'value': value, |
|---|
| 1065 |
'checked': 'checked', |
|---|
| 1066 |
} |
|---|
| 1067 |
res += renderHtmlTag('input', **kw) |
|---|
| 1068 |
kw = {'for': html_widget_id+'_other_selection', |
|---|
| 1069 |
'contents': cpsmcat('label_other_selection').encode('ISO-8859-15', 'ignore'), |
|---|
| 1070 |
} |
|---|
| 1071 |
res += renderHtmlTag('label', **kw) |
|---|
| 1072 |
kw = {'type': 'text', |
|---|
| 1073 |
'name': html_widget_id+'_other', |
|---|
| 1074 |
'value': value, |
|---|
| 1075 |
'onchange': "document.getElementById('"+html_widget_id+"_other_selection').value = this.value", |
|---|
| 1076 |
'onclick': "document.getElementById('"+html_widget_id+"_other_selection').checked='checked'", |
|---|
| 1077 |
'size': self.other_option_display_width, |
|---|
| 1078 |
} |
|---|
| 1079 |
if self.other_option_size_max: |
|---|
| 1080 |
kw['maxlength'] = self.other_option_size_max |
|---|
| 1081 |
res += renderHtmlTag('input', **kw) |
|---|
| 1082 |
res += '<br/>\n' |
|---|
| 1083 |
else: |
|---|
| 1084 |
kw = {'id': html_widget_id+'_'+value, |
|---|
| 1085 |
'type': render_format, |
|---|
| 1086 |
'name': html_widget_id, |
|---|
| 1087 |
'value': value, |
|---|
| 1088 |
'disabled': 'disabled', |
|---|
| 1089 |
} |
|---|
| 1090 |
res += renderHtmlTag('input', **kw) |
|---|
| 1091 |
kw = {'for': html_widget_id+'_'+value, |
|---|
| 1092 |
'contents': 'invalid: '+value, |
|---|
| 1093 |
} |
|---|
| 1094 |
res += renderHtmlTag('label', **kw) |
|---|
| 1095 |
res += '<br/>\n' |
|---|
| 1096 |
# 'other' option |
|---|
| 1097 |
if self.other_option and not in_other_selection: |
|---|
| 1098 |
if render_format != 'select': |
|---|
| 1099 |
kw = {'id': html_widget_id+'_other_selection', |
|---|
| 1100 |
'type': render_format, |
|---|
| 1101 |
'name': html_widget_id, |
|---|
| 1102 |
'value': '', |
|---|
| 1103 |
} |
|---|
| 1104 |
res += renderHtmlTag('input', **kw) |
|---|
| 1105 |
kw = {'for': html_widget_id+'_other_selection', |
|---|
| 1106 |
'contents': cpsmcat('label_other_selection').encode('ISO-8859-15', 'ignore'), |
|---|
| 1107 |
} |
|---|
| 1108 |
res += renderHtmlTag('label', **kw) |
|---|
| 1109 |
kw = {'type': 'text', |
|---|
| 1110 |
'name': html_widget_id+'_other', |
|---|
| 1111 |
'value': "", |
|---|
| 1112 |
'onchange': "document.getElementById('"+html_widget_id+"_other_selection').value = this.value", |
|---|
| 1113 |
'onclick': "document.getElementById('"+html_widget_id+"_other_selection').checked='checked'", |
|---|
| 1114 |
'size': self.other_option_display_width, |
|---|
| 1115 |
} |
|---|
| 1116 |
if self.other_option_size_max: |
|---|
| 1117 |
kw['maxlength'] = self.other_option_size_max |
|---|
| 1118 |
res += renderHtmlTag('input', **kw) |
|---|
| 1119 |
res += '<br/>\n' |
|---|
| 1120 |
# default option |
|---|
| 1121 |
if not self.is_required and not vocabulary.has_key(''): |
|---|
| 1122 |
if render_format == 'select': |
|---|
| 1123 |
kw = {'value': '', |
|---|
| 1124 |
'contents': cpsmcat('label_none_selection').encode('ISO-8859-15', 'ignore'), |
|---|
| 1125 |
} |
|---|
| 1126 |
if not in_selection: |
|---|
| 1127 |
kw['selected'] = 'selected' |
|---|
| 1128 |
res += renderHtmlTag('option', **kw) |
|---|
| 1129 |
res += '\n' |
|---|
| 1130 |
else: |
|---|
| 1131 |
kw = {'id': html_widget_id+'_empty', |
|---|
| 1132 |
'type': render_format, |
|---|
| 1133 |
'name': html_widget_id, |
|---|
| 1134 |
'value': '', |
|---|
| 1135 |
} |
|---|
| 1136 |
if not in_selection: |
|---|
| 1137 |
kw['checked'] = 'checked' |
|---|
| 1138 |
res += renderHtmlTag('input', **kw) |
|---|
| 1139 |
kw = {'for': html_widget_id+'_empty', |
|---|
| 1140 |
'contents': cpsmcat('label_none_selection').encode('ISO-8859-15', 'ignore'), |
|---|
| 1141 |
} |
|---|
| 1142 |
res += renderHtmlTag('label', **kw) |
|---|
| 1143 |
res += '<br/>\n' |
|---|
| 1144 |
if render_format == 'select': |
|---|
| 1145 |
res += '</select>' |
|---|
| 1146 |
return res |
|---|
| 1147 |
raise RuntimeError('unknown mode %s' % mode) |
|---|
| 1148 |
|
|---|
| 1149 |
InitializeClass(CPSGenericSelectWidget) |
|---|
| 1150 |
|
|---|
| 1151 |
widgetRegistry.register(CPSGenericSelectWidget) |
|---|
| 1152 |
|
|---|
| 1153 |
################################################## |
|---|
| 1154 |
|
|---|
| 1155 |
class CPSGenericMultiSelectWidget(CPSMultiSelectWidget): |
|---|
| 1156 |
"""Generic MultiSelect widget.""" |
|---|
| 1157 |
meta_type = 'Generic MultiSelect Widget' |
|---|
| 1158 |
|
|---|
| 1159 |
_properties = CPSMultiSelectWidget._properties + ( |
|---|
| 1160 |
{'id': 'render_format', 'type': 'selection', 'mode': 'w', |
|---|
| 1161 |
'select_variable': 'render_formats', |
|---|
| 1162 |
'label': 'Render format'}, |
|---|
| 1163 |
# Enables the possibility to add blank values to vocabulary just to |
|---|
| 1164 |
# change the way the list is presented (using items like 'choose a |
|---|
| 1165 |
# category' or '------------' to separate items) and not affect the way |
|---|
| 1166 |
# the value will be validated if the widget is required. |
|---|
| 1167 |
# Before this, if the widget was required, default behavior was to |
|---|
| 1168 |
# accept blank values if they were in the vocabulary (e.g |
|---|
| 1169 |
# blank_value_ok_if_required = 1) |
|---|
| 1170 |
{'id': 'blank_value_ok_if_required', 'type': 'boolean', 'mode':'w', |
|---|
| 1171 |
'label': "Accept blank values when validating"}, |
|---|
| 1172 |
) |
|---|
| 1173 |
render_formats = ['select', 'radio', 'checkbox'] |
|---|
| 1174 |
|
|---|
| 1175 |
render_format = render_formats[0] |
|---|
| 1176 |
blank_value_ok_if_required = 1 |
|---|
| 1177 |
|
|---|
| 1178 |
# BBB for [46171]. Remove this once an upgrade step has been written |
|---|
| 1179 |
sorted = False |
|---|
| 1180 |
|
|---|
| 1181 |
def prepare(self, datastructure, **kw): |
|---|
| 1182 |
"""Prepare datastructure from datamodel.""" |
|---|
| 1183 |
datamodel = datastructure.getDataModel() |
|---|
| 1184 |
value = datamodel[self.fields[0]] |
|---|
| 1185 |
if isinstance(value, str): |
|---|
| 1186 |
if value: |
|---|
| 1187 |
value = [value,] |
|---|
| 1188 |
else: |
|---|
| 1189 |
value = [] |
|---|
| 1190 |
datastructure[self.getWidgetId()] = value |
|---|
| 1191 |
|
|---|
| 1192 |
|
|---|
| 1193 |
def validate(self, datastructure, **kw): |
|---|
| 1194 |
"""Validate datastructure and update datamodel.""" |
|---|
| 1195 |
widget_id = self.getWidgetId() |
|---|
| 1196 |
value = datastructure[widget_id] |
|---|
| 1197 |
if not isinstance(value, (list, tuple)): |
|---|
| 1198 |
datastructure.setError(widget_id, "cpsschemas_err_multiselect") |
|---|
| 1199 |
return 0 |
|---|
| 1200 |
vocabulary = self._getVocabulary(datastructure) |
|---|
| 1201 |
v = [] |
|---|
| 1202 |
for i in value: |
|---|
| 1203 |
if i != '': |
|---|
| 1204 |
try: |
|---|
| 1205 |
i = str(i) |
|---|
| 1206 |
except ValueError: |
|---|
| 1207 |
datastructure.setError(widget_id, "cpsschemas_err_multiselect") |
|---|
| 1208 |
return 0 |
|---|
| 1209 |
if not vocabulary.has_key(i): |
|---|
| 1210 |
datastructure.setError(widget_id, "cpsschemas_err_multiselect") |
|---|
| 1211 |
return 0 |
|---|
| 1212 |
else: |
|---|
| 1213 |
if self.is_required: |
|---|
| 1214 |
# set error unless vocabulary holds blank values and |
|---|
| 1215 |
# blank_value_ok_if_required is set to 1 |
|---|
| 1216 |
if vocabulary.has_key(i): |
|---|
| 1217 |
if not self.blank_value_ok_if_required: |
|---|
| 1218 |
datastructure.setError(widget_id, "cpsschemas_err_required") |
|---|
| 1219 |
return 0 |
|---|
| 1220 |
else: |
|---|
| 1221 |
datastructure.setError(widget_id, "cpsschemas_err_required") |
|---|
| 1222 |
return 0 |
|---|
| 1223 |
v.append(i) |
|---|
| 1224 |
if self.is_required and not len(v): |
|---|
| 1225 |
datastructure.setError(widget_id, "cpsschemas_err_required") |
|---|
| 1226 |
return 0 |
|---|
| 1227 |
datamodel = datastructure.getDataModel() |
|---|
| 1228 |
datamodel[self.fields[0]] = v |
|---|
| 1229 |
return 1 |
|---|
| 1230 |
|
|---|
| 1231 |
def render(self, mode, datastructure, **kw): |
|---|
| 1232 |
"""Render in mode from datastructure.""" |
|---|
| 1233 |
widget_id = self.getWidgetId() |
|---|
| 1234 |
value = datastructure[widget_id] |
|---|
| 1235 |
vocabulary = self._getVocabulary(datastructure) |
|---|
| 1236 |
portal = getToolByName(self, 'portal_url').getPortalObject() |
|---|
| 1237 |
cpsmcat = portal.translation_service |
|---|
| 1238 |
if mode == 'view': |
|---|
| 1239 |
if not value: |
|---|
| 1240 |
# XXX L10N empty format may be subject to i18n. |
|---|
| 1241 |
return self.format_empty |
|---|
| 1242 |
# XXX customize view mode, lots of displays are possible |
|---|
| 1243 |
else: |
|---|
| 1244 |
return self.getEntriesHtml(value, vocabulary, self.translated) |
|---|
| 1245 |
elif mode == 'edit': |
|---|
| 1246 |
in_selection = 0 |
|---|
| 1247 |
res = '' |
|---|
| 1248 |
html_widget_id = self.getHtmlWidgetId() |
|---|
| 1249 |
render_format = self.render_format |
|---|
| 1250 |
if render_format not in self.render_formats: |
|---|
| 1251 |
raise RuntimeError('unknown render format %s' % render_format) |
|---|
| 1252 |
if render_format == 'select': |
|---|
| 1253 |
kw = {'name': html_widget_id + ':list', |
|---|
| 1254 |
'id': html_widget_id, |
|---|
| 1255 |
'multiple': 'multiple', |
|---|
| 1256 |
} |
|---|
| 1257 |
if self.size: |
|---|
| 1258 |
kw['size'] = self.size |
|---|
| 1259 |
res = renderHtmlTag('select', **kw) |
|---|
| 1260 |
# vocabulary options |
|---|
| 1261 |
vocabulary_items = vocabulary.items() |
|---|
| 1262 |
if self.sorted: |
|---|
| 1263 |
vocabulary_items.sort(key=lambda obj: obj[1].lower()) |
|---|
| 1264 |
for k, v in vocabulary_items: |
|---|
| 1265 |
if getattr(self, 'translated', None): |
|---|
| 1266 |
v = cpsmcat(vocabulary.getMsgid(k, k)).encode('ISO-8859-15', 'ignore') |
|---|
| 1267 |
if render_format == 'select': |
|---|
| 1268 |
kw = {'value': k, 'contents': v} |
|---|
| 1269 |
if k in value: |
|---|
| 1270 |
kw['selected'] = 'selected' |
|---|
| 1271 |
in_selection = 1 |
|---|
| 1272 |
res += renderHtmlTag('option', **kw) |
|---|
| 1273 |
res += '\n' |
|---|
| 1274 |
else: |
|---|
| 1275 |
kw = {'id': html_widget_id+'_'+k, |
|---|
| 1276 |
'type': render_format, |
|---|
| 1277 |
'name': html_widget_id+':list', |
|---|
| 1278 |
'value': k, |
|---|
| 1279 |
} |
|---|
| 1280 |
if k in value: |
|---|
| 1281 |
kw['checked'] = 'checked' |
|---|
| 1282 |
in_selection = 1 |
|---|
| 1283 |
res += renderHtmlTag('input', **kw) |
|---|
| 1284 |
kw = {'for': html_widget_id+'_'+k, |
|---|
| 1285 |
'contents': v, |
|---|
| 1286 |
} |
|---|
| 1287 |
res += renderHtmlTag('label', **kw) |
|---|
| 1288 |
res += '<br/>\n' |
|---|
| 1289 |
# invalid selections |
|---|
| 1290 |
for value_item in value: |
|---|
| 1291 |
if value_item and value_item not in vocabulary.keys(): |
|---|
| 1292 |
if render_format == 'select': |
|---|
| 1293 |
kw = {'value': value_item, |
|---|
| 1294 |
# XXX missing i18n for invalid |
|---|
| 1295 |
'contents': 'invalid: ' + value_item, |
|---|
| 1296 |
'selected': 'selected', |
|---|
| 1297 |
} |
|---|
| 1298 |
res += renderHtmlTag('option', **kw) |
|---|
| 1299 |
res += '\n' |
|---|
| 1300 |
else: |
|---|
| 1301 |
kw = {'id': html_widget_id+'_'+value_item, |
|---|
| 1302 |
'type': render_format, |
|---|
| 1303 |
'name': html_widget_id+':list', |
|---|
| 1304 |
'value': value_item, |
|---|
| 1305 |
'disabled': 'disabled', |
|---|
| 1306 |
} |
|---|
| 1307 |
res += renderHtmlTag('input', **kw) |
|---|
| 1308 |
kw = {'for': html_widget_id+'_'+value_item, |
|---|
| 1309 |
'contents': 'invalid: '+value_item, |
|---|
| 1310 |
} |
|---|
| 1311 |
res += renderHtmlTag('label', **kw) |
|---|
| 1312 |
res += '<br/>\n' |
|---|
| 1313 |
# default option |
|---|
| 1314 |
if not self.is_required and not vocabulary.has_key(''): |
|---|
| 1315 |
if render_format == 'select': |
|---|
| 1316 |
kw = {'value': '', |
|---|
| 1317 |
'contents': cpsmcat('label_none_selection').encode('ISO-8859-15', 'ignore'), |
|---|
| 1318 |
} |
|---|
| 1319 |
if not in_selection: |
|---|
| 1320 |
kw['selected'] = 'selected' |
|---|
| 1321 |
res += renderHtmlTag('option', **kw) |
|---|
| 1322 |
res += '\n' |
|---|
| 1323 |
# not interesting to have a default choice for checkboxes |
|---|
| 1324 |
elif render_format == 'radio': |
|---|
| 1325 |
kw = {'id': html_widget_id+'_empty', |
|---|
| 1326 |
'type': render_format, |
|---|
| 1327 |
'name': html_widget_id+':list', |
|---|
| 1328 |
'value': '', |
|---|
| 1329 |
} |
|---|
| 1330 |
if not in_selection: |
|---|
| 1331 |
kw['checked'] = 'checked' |
|---|
| 1332 |
res += renderHtmlTag('input', **kw) |
|---|
| 1333 |
kw = {'for': html_widget_id+'_empty', |
|---|
| 1334 |
'contents': cpsmcat('label_none_selection').encode('ISO-8859-15', 'ignore'), |
|---|
| 1335 |
} |
|---|
| 1336 |
res += renderHtmlTag('label', **kw) |
|---|
| 1337 |
res += '<br/>\n' |
|---|
| 1338 |
if render_format == 'select': |
|---|
| 1339 |
res += '</select>' |
|---|
| 1340 |
default_tag = renderHtmlTag('input', |
|---|
| 1341 |
type='hidden', |
|---|
| 1342 |
name=html_widget_id+':tokens:default', |
|---|
| 1343 |
value='') |
|---|
| 1344 |
return default_tag+res |
|---|
| 1345 |
raise RuntimeError('unknown mode %s' % mode) |
|---|
| 1346 |
|
|---|
| 1347 |
InitializeClass(CPSGenericMultiSelectWidget) |
|---|
| 1348 |
|
|---|
| 1349 |
widgetRegistry.register(CPSGenericMultiSelectWidget) |
|---|
| 1350 |
|
|---|
| 1351 |
################################################## |
|---|
| 1352 |
|
|---|
| 1353 |
class CPSRangeListWidget(CPSWidget): |
|---|
| 1354 |
"""Range List widget.""" |
|---|
| 1355 |
meta_type = 'Range List Widget' |
|---|
| 1356 |
|
|---|
| 1357 |
field_types = ('CPS Range List Field',) |
|---|
| 1358 |
field_inits = ({'is_searchabletext': 1,},) |
|---|
| 1359 |
|
|---|
| 1360 |
_properties = CPSWidget._properties + ( |
|---|
| 1361 |
{'id': 'display_width', 'type': 'int', 'mode': 'w', |
|---|
| 1362 |
'label': 'Display width'}, |
|---|
| 1363 |
{'id': 'format_empty', 'type': 'string', 'mode': 'w', |
|---|
| 1364 |
'label': 'Format for empty list'}, |
|---|
| 1365 |
) |
|---|
| 1366 |
|
|---|
| 1367 |
display_width = 0 |
|---|
| 1368 |
format_empty = '' |
|---|
| 1369 |
|
|---|
| 1370 |
def prepare(self, datastructure, **kw): |
|---|
| 1371 |
"""Prepare datastructure from datamodel.""" |
|---|
| 1372 |
datamodel = datastructure.getDataModel() |
|---|
| 1373 |
value = datamodel[self.fields[0]] |
|---|
| 1374 |
datastructure[self.getWidgetId()] = value |
|---|
| 1375 |
|
|---|
| 1376 |
|
|---|
| 1377 |
def validate(self, datastructure, **kw): |
|---|
| 1378 |
"""Validate datastructure and update datamodel.""" |
|---|
| 1379 |
widget_id = self.getWidgetId() |
|---|
| 1380 |
value = datastructure[widget_id] |
|---|
| 1381 |
if not isinstance(value, list): |
|---|
| 1382 |
datastructure.setError(widget_id, "cpsschemas_err_rangelist") |
|---|
| 1383 |
return 0 |
|---|
| 1384 |
v = [] |
|---|
| 1385 |
for i in value: |
|---|
| 1386 |
i = i.split('-') |
|---|
| 1387 |
if len(i) == 1: |
|---|
| 1388 |
try: |
|---|
| 1389 |
i[0] = int(i[0]) |
|---|
| 1390 |
except ValueError: |
|---|
| 1391 |
datastructure.setError(widget_id, "cpsschemas_err_rangelist") |
|---|
| 1392 |
return 0 |
|---|
| 1393 |
v.append((i[0],)) |
|---|
| 1394 |
elif len(i) == 2: |
|---|
| 1395 |
try: |
|---|
| 1396 |
i[0] = int(i[0]) |
|---|
| 1397 |
i[1] = int(i[1]) |
|---|
| 1398 |
except ValueError: |
|---|
| 1399 |
datastructure.setError(widget_id, "cpsschemas_err_rangelist") |
|---|
| 1400 |
return 0 |
|---|
| 1401 |
v.append((i[0], i[1])) |
|---|
| 1402 |
else: |
|---|
| 1403 |
datastructure.setError(widget_id, "cpsschemas_err_rangelist") |
|---|
| 1404 |
return 0 |
|---|
| 1405 |
if self.is_required and not len(v): |
|---|
| 1406 |
datastructure.setError(widget_id, "cpsschemas_err_required") |
|---|
| 1407 |
return 0 |
|---|
| 1408 |
datamodel = datastructure.getDataModel() |
|---|
| 1409 |
datamodel[self.fields[0]] = v |
|---|
| 1410 |
return 1 |
|---|
| 1411 |
|
|---|
| 1412 |
def render(self, mode, datastructure, **kw): |
|---|
| 1413 |
"""Render in mode from datastructure.""" |
|---|
| 1414 |
value = datastructure[self.getWidgetId()] |
|---|
| 1415 |
res = '' |
|---|
| 1416 |
|
|---|
| 1417 |
for i in value: |
|---|
| 1418 |
if isinstance(i, str): |
|---|
| 1419 |
res += i + ' ' |
|---|
| 1420 |
elif len(i) == 1: |
|---|
| 1421 |
res += str(i[0]) + ' ' |
|---|
| 1422 |
else: |
|---|
| 1423 |
res += str(i[0]) + '-' + str(i[1]) + ' ' |
|---|
| 1424 |
|
|---|
| 1425 |
if mode == 'view': |
|---|
| 1426 |
return escape(res) |
|---|
| 1427 |
elif mode == 'edit': |
|---|
| 1428 |
return renderHtmlTag('input', |
|---|
| 1429 |
type='text', |
|---|
| 1430 |
id=self.getHtmlWidgetId(), |
|---|
| 1431 |
name=self.getHtmlWidgetId()+":tokens", |
|---|
| 1432 |
value=escape(res), |
|---|
| 1433 |
size=self.display_width, |
|---|
| 1434 |
) |
|---|
| 1435 |
raise RuntimeError('unknown mode %s' % mode) |
|---|
| 1436 |
|
|---|
| 1437 |
InitializeClass(CPSRangeListWidget) |
|---|
| 1438 |
|
|---|
| 1439 |
widgetRegistry.register(CPSRangeListWidget) |
|---|
| 1440 |
|
|---|
| 1441 |
################################################## |
|---|
| 1442 |
|
|---|
| 1443 |
class CPSDocumentLanguageSelectWidget(CPSWidget): |
|---|
| 1444 |
"""Document Language Selection widget.""" |
|---|
| 1445 |
meta_type = 'Document Language Select Widget' |
|---|
| 1446 |
|
|---|
| 1447 |
field_types = ('CPS String Field',) |
|---|
| 1448 |
field_inits = ({'is_searchabletext': 0,},) |
|---|
| 1449 |
|
|---|
| 1450 |
def prepare(self, datastructure, **kw): |
|---|
| 1451 |
"""Prepare datastructure from datamodel.""" |
|---|
| 1452 |
datamodel = datastructure.getDataModel() |
|---|
| 1453 |
value = datamodel[self.fields[0]] |
|---|
| 1454 |
datastructure[self.getWidgetId()] = value |
|---|
| 1455 |
|
|---|
| 1456 |
|
|---|
| 1457 |
def validate(self, datastructure, **kw): |
|---|
| 1458 |
"""Validate datastructure and update datamodel.""" |
|---|
| 1459 |
return 1 |
|---|
| 1460 |
|
|---|
| 1461 |
def render(self, mode, datastructure, **kw): |
|---|
| 1462 |
"""Render in mode from datastructure.""" |
|---|
| 1463 |
res = '' |
|---|
| 1464 |
datamodel = datastructure.getDataModel() |
|---|
| 1465 |
proxy = datamodel.getProxy() |
|---|
| 1466 |
if proxy is None: |
|---|
| 1467 |
return res |
|---|
| 1468 |
proxy_url = proxy.absolute_url() |
|---|
| 1469 |
languages = proxy.getProxyLanguages() |
|---|
| 1470 |
if len(languages) <= 1: |
|---|
| 1471 |
return res |
|---|
| 1472 |
current_language = datamodel[self.fields[0]] |
|---|
| 1473 |
translation_service = getToolByName(self, 'translation_service') |
|---|
| 1474 |
if translation_service is not None: |
|---|
| 1475 |
mcat = translation_service |
|---|
| 1476 |
else: |
|---|
| 1477 |
def mcat(msgid): |
|---|
| 1478 |
return msgid |
|---|
| 1479 |
|
|---|
| 1480 |
for language in languages: |
|---|
| 1481 |
language_title = mcat('label_language_%s'% language).encode( |
|---|
| 1482 |
'ISO-8859-15', 'ignore') |
|---|
| 1483 |
contents = language |
|---|
| 1484 |
if current_language != language: |
|---|
| 1485 |
contents = renderHtmlTag('a', href='%s/switchLanguage/%s' % |
|---|
| 1486 |
(proxy_url, language), |
|---|
| 1487 |
title=language_title, |
|---|
| 1488 |
contents=contents, |
|---|
| 1489 |
css_class='availableLang') |
|---|
| 1490 |
else: |
|---|
| 1491 |
contents = renderHtmlTag('span', contents=contents, |
|---|
| 1492 |
title=language_title, |
|---|
| 1493 |
css_class='selectedLang') |
|---|
| 1494 |
contents += ' ' |
|---|
| 1495 |
res += renderHtmlTag('li', contents=contents) |
|---|
| 1496 |
res = renderHtmlTag('ul', contents=res) |
|---|
| 1497 |
res = '<div class="headerActions">%s</div>' % res |
|---|
| 1498 |
return res |
|---|
| 1499 |
|
|---|
| 1500 |
InitializeClass(CPSDocumentLanguageSelectWidget) |
|---|
| 1501 |
|
|---|
| 1502 |
widgetRegistry.register(CPSDocumentLanguageSelectWidget) |
|---|
| 1503 |
|
|---|
| 1504 |
################################################## |
|---|
| 1505 |
|
|---|
| 1506 |
class CPSSubjectWidget(CPSMultiSelectWidget): |
|---|
| 1507 |
"""A widget featuring links to items by subject. |
|---|
| 1508 |
|
|---|
| 1509 |
The CPS Subject Widget is like the CPS MultiSelect Widget from which it |
|---|
| 1510 |
derives, except in "view" mode where the listed entries have link on them |
|---|
| 1511 |
to other documents on the portal which have the same subjects. |
|---|
| 1512 |
""" |
|---|
| 1513 |
meta_type = 'Subject Widget' |
|---|
| 1514 |
|
|---|
| 1515 |
def getEntriesHtml(self, entries, vocabulary, translated=False): |
|---|
| 1516 |
entries_html_list = [] |
|---|
| 1517 |
if translated: |
|---|
| 1518 |
cpsmcat = getToolByName(self, 'translation_service', None) |
|---|
| 1519 |
if cpsmcat is None: |
|---|
| 1520 |
translated = False |
|---|
| 1521 |
for subject_name in entries: |
|---|
| 1522 |
if translated: |
|---|
| 1523 |
subject_label = cpsmcat( |
|---|
| 1524 |
vocabulary.getMsgid(subject_name, subject_name), |
|---|
| 1525 |
subject_name).encode('ISO-8859-15', 'ignore') |
|---|
| 1526 |
entries_html_list.append(self.getSubjectSearchLink( |
|---|
| 1527 |
subject_name, subject_label)) |
|---|
| 1528 |
else: |
|---|
| 1529 |
entries_html_list.append( |
|---|
| 1530 |
self.getSubjectSearchLink(subject_name, |
|---|
| 1531 |
subject_name)) |
|---|
| 1532 |
return ', '.join(entries_html_list) |
|---|
| 1533 |
|
|---|
| 1534 |
def getSubjectSearchLink(self, subject_name, subject_label): |
|---|
| 1535 |
"""Return an HTML link for the subject name with the given label.""" |
|---|
| 1536 |
return ('<a href="%s">%s</a>' |
|---|
| 1537 |
% (self.getSubjectSearchUrl(escape(subject_name)), |
|---|
| 1538 |
escape(subject_label))) |
|---|
| 1539 |
|
|---|
| 1540 |
def getSubjectSearchUrl(self, subject_name): |
|---|
| 1541 |
"""Return the subject search URL. |
|---|
| 1542 |
|
|---|
| 1543 |
The provided links are actually requests to the portal search engine. |
|---|
| 1544 |
""" |
|---|
| 1545 |
return "%s/search_form?Subject=%s" % (self.portal_url(), subject_name) |
|---|
| 1546 |
|
|---|
| 1547 |
InitializeClass(CPSSubjectWidget) |
|---|
| 1548 |
|
|---|
| 1549 |
widgetRegistry.register(CPSSubjectWidget) |
|---|
| 1550 |
|
|---|
| 1551 |
################################################## |
|---|
| 1552 |
|
|---|
| 1553 |
class CPSFlashWidget(CPSAttachedFileWidget): |
|---|
| 1554 |
"""Flash Widget. |
|---|
| 1555 |
""" |
|---|
| 1556 |
meta_type = 'Flash Widget' |
|---|
| 1557 |
|
|---|
| 1558 |
field_types = ('CPS File Field', # File |
|---|
| 1559 |
'CPS String Field', # Caption (optional) |
|---|
| 1560 |
'CPS File Field') # Preview (optional) |
|---|
| 1561 |
field_inits = ({'is_searchabletext': 0, |
|---|
| 1562 |
'suffix_text': '_f1', # _f# are autocomputed field ext |
|---|
| 1563 |
'suffix_html': '_f2',}, |
|---|
| 1564 |
{'is_searchabletext': 1}, {}, |
|---|
| 1565 |
) |
|---|
| 1566 |
|
|---|
| 1567 |
def _flash_validate(self, datastructure, **kw): |
|---|
| 1568 |
"""Check that this is a Flash animation |
|---|
| 1569 |
""" |
|---|
| 1570 |
widget_id = self.getWidgetId() |
|---|
| 1571 |
choice = datastructure[widget_id+'_choice'] |
|---|
| 1572 |
if choice == 'change': |
|---|
| 1573 |
fileinfo = self.getFileInfo(datastructure) |
|---|
| 1574 |
if fileinfo is not None: |
|---|
| 1575 |
cond = fileinfo['mimetype'] == 'application/x-shockwave-flash' |
|---|
| 1576 |
if not cond: |
|---|
| 1577 |
datastructure.setError(self.getWidgetId(), |
|---|
| 1578 |
'cpsschemas_err_file') |
|---|
| 1579 |
return False |
|---|
| 1580 |
return True |
|---|
| 1581 |
|
|---|
| 1582 |
def validate(self, datastructure, **kw): |
|---|
| 1583 |
"""Validate datastructure and update datamodel. |
|---|
| 1584 |
""" |
|---|
| 1585 |
return (CPSAttachedFileWidget.validate(self, datastructure, **kw) and |
|---|
| 1586 |
self._flash_validate(datastructure, **kw)) |
|---|
| 1587 |
|
|---|
| 1588 |
def render(self, mode, datastructure, **kw): |
|---|
| 1589 |
"""Render this widget from the datastructure or datamodel. |
|---|
| 1590 |
""" |
|---|
| 1591 |
render_method = 'widget_flash_render' |
|---|
| 1592 |
meth = getattr(self, render_method, None) |
|---|
| 1593 |
|
|---|
| 1594 |
if meth is None: |
|---|
| 1595 |
raise RuntimeError("Unknown Render Method %s for widget type %s" |
|---|
| 1596 |
% (render_method, self.getId())) |
|---|
| 1597 |
|
|---|
| 1598 |
file = datastructure[self.getWidgetId()] |
|---|
| 1599 |
|
|---|
| 1600 |
# Get common File props |
|---|
| 1601 |
file_info = self.getFileInfo(datastructure) |
|---|
| 1602 |
|
|---|
| 1603 |
# Update with speficic swf header props |
|---|
| 1604 |
if file is not None: |
|---|
| 1605 |
try: |
|---|
| 1606 |
file_info.update(analyseContent( |
|---|
| 1607 |
str(file.read()), file_info['size'])) |
|---|
| 1608 |
except TypeError: |
|---|
| 1609 |
pass |
|---|
| 1610 |
|
|---|
| 1611 |
return meth(mode=mode, datastructure=datastructure, **file_info) |
|---|
| 1612 |
|
|---|
| 1613 |
InitializeClass(CPSFlashWidget) |
|---|
| 1614 |
|
|---|
| 1615 |
widgetRegistry.register(CPSFlashWidget) |
|---|
| 1616 |
|
|---|
| 1617 |
################################################## |
|---|
| 1618 |
|
|---|
| 1619 |
class CPSLinkWidget(CPSProgrammerCompoundWidget): |
|---|
| 1620 |
"""Widget for an HTTP link. |
|---|
| 1621 |
""" |
|---|
| 1622 |
meta_type = 'Link Widget' |
|---|
| 1623 |
render_method = 'widget_link_render' |
|---|
| 1624 |
prepare_validate_method = '' |
|---|
| 1625 |
|
|---|
| 1626 |
InitializeClass(CPSLinkWidget) |
|---|
| 1627 |
|
|---|
| 1628 |
widgetRegistry.register(CPSLinkWidget) |
|---|
| 1629 |
|
|---|
| 1630 |
|
|---|
| 1631 |
class CPSTextImageWidget(CPSProgrammerCompoundWidget): |
|---|
| 1632 |
"""Widget for text+image. |
|---|
| 1633 |
""" |
|---|
| 1634 |
meta_type = 'Text Image Widget' |
|---|
| 1635 |
render_method = 'widget_textimage_render' |
|---|
| 1636 |
prepare_validate_method = 'widget_textimage_prepare_validate' |
|---|
| 1637 |
|
|---|
| 1638 |
InitializeClass(CPSTextImageWidget) |
|---|
| 1639 |
|
|---|
| 1640 |
widgetRegistry.register(CPSTextImageWidget) |
|---|
| 1641 |
|
|---|
| 1642 |
|
|---|
| 1643 |
class CPSImageLinkWidget(CPSProgrammerCompoundWidget): |
|---|
| 1644 |
""" |
|---|
| 1645 |
""" |
|---|
| 1646 |
meta_type = 'Image Link Widget' |
|---|
| 1647 |
render_method = 'widget_imagelink_render' |
|---|
| 1648 |
prepare_validate_method = 'widget_imagelink_prepare_validate' |
|---|
| 1649 |
|
|---|
| 1650 |
InitializeClass(CPSImageLinkWidget) |
|---|
| 1651 |
|
|---|
| 1652 |
widgetRegistry.register(CPSImageLinkWidget) |
|---|
| 1653 |
|
|---|
| 1654 |
class CPSAutocompletionStringWidget(CPSStringWidget): |
|---|
| 1655 |
"""Autocompletion String widget.""" |
|---|
| 1656 |
meta_type = 'Autocompletion String Widget' |
|---|
| 1657 |
server_method = '' |
|---|
| 1658 |
|
|---|
| 1659 |
_properties = CPSStringWidget._properties + ( |
|---|
| 1660 |
{'id': 'server_method', 'type': 'string', 'mode': 'w', |
|---|
| 1661 |
'label': 'Server method',},) |
|---|
| 1662 |
|
|---|
| 1663 |
def render(self, mode, datastructure, **kw): |
|---|
| 1664 |
"""Render in mode from datastructure.""" |
|---|
| 1665 |
render_method = 'widget_autocompletion_string_render' |
|---|
| 1666 |
meth = getattr(self, render_method, None) |
|---|
| 1667 |
if meth is None: |
|---|
| 1668 |
raise RuntimeError("Unknown Render Method %s for widget type %s" |
|---|
| 1669 |
% (render_method, self.getId())) |
|---|
| 1670 |
if mode not in ('view', 'edit'): |
|---|
| 1671 |
raise RuntimeError('unknown mode %s' % mode) |
|---|
| 1672 |
|
|---|
| 1673 |
value = datastructure[self.getWidgetId()] |
|---|
| 1674 |
widget_id = self.getWidgetId() |
|---|
| 1675 |
html_widget_id = self.getHtmlWidgetId() |
|---|
| 1676 |
|
|---|
| 1677 |
return meth(mode=mode, widget_id=widget_id,value=value, |
|---|
| 1678 |
size=self.display_width, server_method=self.server_method, |
|---|
| 1679 |
html_widget_id=html_widget_id) |
|---|
| 1680 |
|
|---|
| 1681 |
InitializeClass(CPSAutocompletionStringWidget) |
|---|
| 1682 |
|
|---|
| 1683 |
widgetRegistry.register(CPSAutocompletionStringWidget) |
|---|
| 1684 |
|
|---|