Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1"""Widget.""" 

2# Standard Library 

3import csv 

4import json 

5import random 

6 

7# Pyramid 

8from colander import Invalid 

9from colander import Mapping 

10from colander import SchemaNode 

11from colander import SchemaType 

12from colander import Sequence 

13from colander import String 

14from colander import null 

15from iso8601.iso8601 import ISO8601_REGEX 

16from translationstring import TranslationString 

17 

18from .compat import StringIO 

19from .compat import sequence_types 

20from .compat import string 

21from .compat import string_types 

22from .compat import text_ 

23from .compat import text_type 

24from .compat import uppercase 

25from .compat import url_quote 

26from .i18n import _ 

27 

28 

29_BLANK = text_("") 

30 

31 

32def _normalize_choices(values): 

33 result = [] 

34 for item in values: 

35 if isinstance(item, OptGroup): 

36 normalized_options = _normalize_choices(item.options) 

37 result.append(OptGroup(item.label, *normalized_options)) 

38 else: 

39 value, description = item 

40 if not isinstance(value, string_types): 

41 value = str(value) 

42 result.append((value, description)) 

43 return result 

44 

45 

46class _PossiblyEmptyString(String): 

47 def deserialize(self, node, cstruct): 

48 if cstruct == "": 

49 return _BLANK # String.deserialize returns null 

50 return super(_PossiblyEmptyString, self).deserialize(node, cstruct) 

51 

52 

53class _StrippedString(_PossiblyEmptyString): 

54 def deserialize(self, node, cstruct): 

55 appstruct = super(_StrippedString, self).deserialize(node, cstruct) 

56 if isinstance(appstruct, string_types): 

57 appstruct = appstruct.strip() 

58 return appstruct 

59 

60 

61class _FieldStorage(SchemaType): 

62 def deserialize(self, node, cstruct): 

63 if cstruct in (null, None, b""): 

64 return null 

65 # weak attempt at duck-typing 

66 if not hasattr(cstruct, "file"): 

67 raise Invalid(node, "%s is not a FieldStorage instance" % cstruct) 

68 return cstruct 

69 

70 

71_sequence_of_strings = SchemaNode( 

72 Sequence(), SchemaNode(_PossiblyEmptyString()) 

73) 

74 

75 

76class Widget(object): 

77 """ 

78 A widget is the building block for rendering logic. The 

79 :class:`deform.widget.Widget` class is never instantiated 

80 directly: it is the abstract class from which all other widget 

81 types within :mod:`deform.widget` derive. It should likely also 

82 be subclassed by application-developer-defined widgets. 

83 

84 A widget instance is attached to a field during normal operation. 

85 A widget is not meant to carry any state. Instead, widget 

86 implementations should use the ``field`` object passed to them 

87 during :meth:`deform.widget.Widget.serialize` and 

88 :meth:`deform.widget.Widget.deserialize` as a scratchpad for state 

89 information. 

90 

91 All widgets have the following attributes: 

92 

93 hidden 

94 An attribute indicating the hidden state of this widget. The 

95 default is ``False``. If this attribute is not ``False``, the 

96 field associated with this widget will not be rendered in the 

97 form (although, if the widget is a structural widget, its 

98 children will be; ``hidden`` is not a recursive flag). No 

99 label, no error message, nor any furniture such as a close 

100 button when the widget is one of a sequence will exist for the 

101 field in the rendered form. 

102 

103 readonly 

104 If this attribute is true, the readonly rendering of the widget 

105 should be output during HTML serialization. 

106 

107 category 

108 A string value indicating the *category* of this widget. This 

109 attribute exists to inform structural widget rendering 

110 behavior. For example, when a text widget or another simple 

111 'leaf-level' widget is rendered as a child of a mapping widget 

112 using the default template mapping template, the field title 

113 associated with the child widget will be rendered above the 

114 field as a label by default. This is because simple child 

115 widgets are in the ``default`` category and no special action 

116 is taken when a structural widget renders child widgets that 

117 are in the ``default`` category. However, if the default 

118 mapping widget encounters a child widget with the category of 

119 ``structural`` during rendering (the default mapping and 

120 sequence widgets are in this category), it omits the title. 

121 Default: ``default`` 

122 

123 error_class 

124 The name of the CSS class attached to various tags in the form 

125 renderering indicating an error condition for the field 

126 associated with this widget. Default: ``error``. 

127 

128 css_class 

129 The name of the CSS class attached to various tags in 

130 the form renderering specifying a new class for the field 

131 associated with this widget. Default: ``None`` (no class). 

132 

133 item_css_class 

134 The name of the CSS class attached to the li which surrounds the field 

135 when it is rendered inside the mapping_item or sequence_item template. 

136 

137 style 

138 A string that will be placed literally in a ``style`` attribute on 

139 the primary input tag(s) related to the widget. For example, 

140 'width:150px;'. Default: ``None``, meaning no style attribute will 

141 be added to the input tag. 

142 

143 requirements 

144 

145 The requirements are specified as a sequence of either of the 

146 following. 

147 

148 1. Two-tuples in the form ``(requirement_name, version_id)``. 

149 The **logical** requirement name identifiers are resolved to 

150 concrete files using the ``resource_registry``. 

151 2. Dicts in the form ``{requirement_type: 

152 requirement_location(s)}``. The ``resource_registry`` is 

153 bypassed. This is useful for creating custom widgets with 

154 their own resources. 

155 

156 Requirements specified as a sequence of two-tuples should be in the 

157 form ``( (requirement_name, version_id), ...)`` indicating the logical 

158 external requirements needed to make this widget render properly within 

159 a form. The ``requirement_name`` is a string that *logically* 

160 (not concretely, it is not a filename) identifies *one or 

161 more* Javascript or CSS resources that must be included in the 

162 page by the application performing the form rendering. The 

163 requirement name string value should be interpreted as a 

164 logical requirement name (e.g. ``jquery`` for JQuery, 

165 'tinymce' for Tiny MCE). The ``version_id`` is a string 

166 indicating the version number (or ``None`` if no particular 

167 version is required). For example, a rich text widget might 

168 declare ``requirements = (('tinymce', '3.3.8'),)``. 

169 

170 Requirements specified as a sequence of dicts should be in the form 

171 ``({requirement_type: requirement_location(s)}, ...)``. The 

172 ``requirement_type`` key must be either ``js`` or ``css``. The 

173 ``requirement_location(s)`` value must be either a string or a list of 

174 strings. Each string must resolve to a concrete resource. For example, 

175 a widget might declare: 

176 

177 .. code-block:: python 

178 

179 requirements = ( 

180 {"js": "deform:static/tinymce/tinymce.min.js"}, 

181 {"css": "deform:static/tinymce/tinymce.min.css"}, 

182 ) 

183 

184 See also: 

185 :ref:`specifying_widget_requirements` and 

186 :ref:`widget_requirements`. 

187 

188 Default: ``()`` (the empty tuple, meaning no special 

189 requirements). 

190 

191 These attributes are also accepted as keyword arguments to all 

192 widget constructors; if they are passed, they will override the 

193 defaults. 

194 

195 Particular widget types also accept other keyword arguments that 

196 get attached to the widget as attributes. These are documented as 

197 'Attributes/Arguments' within the documentation of each concrete 

198 widget implementation subclass. 

199 """ 

200 

201 hidden = False 

202 readonly = False 

203 category = "default" 

204 error_class = "error" 

205 css_class = None 

206 item_css_class = None 

207 style = None 

208 requirements = () 

209 

210 def __init__(self, **kw): 

211 self.__dict__.update(kw) 

212 

213 def serialize(self, field, cstruct, **kw): 

214 """ 

215 The ``serialize`` method of a widget must serialize a :term:`cstruct` 

216 value to an HTML rendering. A :term:`cstruct` value is the value 

217 which results from a :term:`Colander` schema serialization for the 

218 schema node associated with this widget. ``serialize`` should return 

219 the HTML rendering: the result of this method should always be a 

220 string containing HTML. The ``field`` argument is the :term:`field` 

221 object to which this widget is attached. The ``**kw`` argument 

222 allows a caller to pass named arguments that might be used to 

223 influence individual widget renderings. 

224 """ 

225 raise NotImplementedError 

226 

227 def deserialize(self, field, pstruct): 

228 """ 

229 The ``deserialize`` method of a widget must deserialize a 

230 :term:`pstruct` value to a :term:`cstruct` value and return the 

231 :term:`cstruct` value. The ``pstruct`` argument is a value resulting 

232 from the ``parse`` method of the :term:`Peppercorn` package. The 

233 ``field`` argument is the field object to which this widget is 

234 attached. 

235 """ 

236 raise NotImplementedError 

237 

238 def handle_error(self, field, error): 

239 """ 

240 The ``handle_error`` method of a widget must: 

241 

242 - Set the ``error`` attribute of the ``field`` object it is 

243 passed, if the ``error`` attribute has not already been set. 

244 

245 - Call the ``handle_error`` method of each subfield which also 

246 has an error (as per the ``error`` argument's ``children`` 

247 attribute). 

248 """ 

249 if field.error is None: 

250 field.error = error 

251 for e in error.children: 

252 for num, subfield in enumerate(field.children): 

253 if e.pos == num: 

254 subfield.widget.handle_error(subfield, e) 

255 

256 def get_template_values(self, field, cstruct, kw): 

257 values = {"cstruct": cstruct, "field": field} 

258 values.update(kw) 

259 values.pop("template", None) 

260 return values 

261 

262 

263class TextInputWidget(Widget): 

264 """ 

265 Renders an ``<input type="text"/>`` widget. 

266 

267 **Attributes/Arguments** 

268 

269 template 

270 The template name used to render the widget. Default: 

271 ``textinput``. 

272 

273 readonly_template 

274 The template name used to render the widget in read-only mode. 

275 Default: ``readonly/textinput``. 

276 

277 strip 

278 If true, during deserialization, strip the value of leading 

279 and trailing whitespace (default ``True``). 

280 

281 mask 

282 A :term:`jquery.maskedinput` input mask, as a string. 

283 

284 a - Represents an alpha character (A-Z,a-z) 

285 9 - Represents a numeric character (0-9) 

286 * - Represents an alphanumeric character (A-Z,a-z,0-9) 

287 

288 All other characters in the mask will be considered mask 

289 literals. 

290 

291 Example masks: 

292 

293 Date: 99/99/9999 

294 

295 US Phone: (999) 999-9999 

296 

297 US SSN: 999-99-9999 

298 

299 When this option is used, the :term:`jquery.maskedinput` 

300 library must be loaded into the page serving the form for the 

301 mask argument to have any effect. See :ref:`masked_input`. 

302 

303 mask_placeholder 

304 The placeholder for required nonliteral elements when a mask 

305 is used. Default: ``_`` (underscore). 

306 

307 """ 

308 

309 template = "textinput" 

310 readonly_template = "readonly/textinput" 

311 strip = True 

312 mask = None 

313 mask_placeholder = "_" 

314 requirements = () 

315 

316 def __init__(self, **kw): 

317 super(TextInputWidget, self).__init__(**kw) 

318 if getattr(self, "mask", False): 

319 self.requirements = tuple( 

320 list(self.requirements) + [("jquery.maskedinput", None)] 

321 ) 

322 

323 def serialize(self, field, cstruct, **kw): 

324 if cstruct in (null, None): 

325 cstruct = "" 

326 readonly = kw.get("readonly", self.readonly) 

327 template = readonly and self.readonly_template or self.template 

328 values = self.get_template_values(field, cstruct, kw) 

329 return field.renderer(template, **values) 

330 

331 def deserialize(self, field, pstruct): 

332 if pstruct is null: 

333 return null 

334 elif not isinstance(pstruct, string_types): 

335 raise Invalid(field.schema, "Pstruct is not a string") 

336 if self.strip: 

337 pstruct = pstruct.strip() 

338 if not pstruct: 

339 return null 

340 return pstruct 

341 

342 

343class MoneyInputWidget(Widget): 

344 """ 

345 Renders an ``<input type="text"/>`` widget with Javascript which enforces 

346 a valid currency input. It should be used along with the 

347 ``colander.Decimal`` schema type (at least if you care about your money). 

348 This widget depends on the ``jquery-maskMoney`` JQuery plugin. 

349 

350 **Attributes/Arguments** 

351 

352 template 

353 The template name used to render the widget. Default: 

354 ``moneyinput``. 

355 

356 readonly_template 

357 The template name used to render the widget in read-only mode. 

358 Default: ``readonly/textinput``. 

359 

360 options 

361 A dictionary or sequence of two-tuples containing ``jquery-maskMoney`` 

362 options. The valid options are: 

363 

364 symbol 

365 the symbol to be used before of the user values. default: ``$`` 

366 

367 showSymbol 

368 set if the symbol must be displayed or not. default: ``False`` 

369 

370 symbolStay 

371 set if the symbol will stay in the field after the user exists the 

372 field. default: ``False`` 

373 

374 thousands 

375 the thousands separator. default: ``,`` 

376 

377 decimal 

378 the decimal separator. default: ``.`` 

379 

380 precision 

381 how many decimal places are allowed. default: 2 

382 

383 defaultZero 

384 when the user enters the field, it sets a default mask using zero. 

385 default: ``True`` 

386 

387 allowZero 

388 use this setting to prevent users from inputing zero. default: 

389 ``False`` 

390 

391 allowNegative 

392 use this setting to prevent users from inputing negative values. 

393 default: ``False`` 

394 """ 

395 

396 template = "moneyinput" 

397 readonly_template = "readonly/textinput" 

398 requirements = (("jquery.maskMoney", None),) 

399 options = None 

400 

401 def serialize(self, field, cstruct, **kw): 

402 if cstruct in (null, None): 

403 cstruct = "" 

404 readonly = kw.get("readonly", self.readonly) 

405 options = kw.get("options", self.options) 

406 if options is None: 

407 options = {} 

408 options = json.dumps(dict(options)) 

409 kw["mask_options"] = options 

410 values = self.get_template_values(field, cstruct, kw) 

411 template = readonly and self.readonly_template or self.template 

412 return field.renderer(template, **values) 

413 

414 def deserialize(self, field, pstruct): 

415 if pstruct is null: 

416 return null 

417 elif not isinstance(pstruct, string_types): 

418 raise Invalid(field.schema, "Pstruct is not a string") 

419 pstruct = pstruct.strip() 

420 thousands = "," 

421 # Oh jquery-maskMoney, you submit the thousands separator in the 

422 # control value. I'm no genius, but IMO that's not very smart. But 

423 # then again you also don't inject the thousands separator into the 

424 # value attached to the control when editing an existing value. 

425 # Because it's obvious we should have *both* the server and the 

426 # client doing this bullshit on both serialization and 

427 # deserialization. I understand completely, you're just a client 

428 # library, IT'S NO PROBLEM. LET ME HELP YOU. 

429 if self.options: 

430 thousands = dict(self.options).get("thousands", ",") 

431 pstruct = pstruct.replace(thousands, "") 

432 if not pstruct: 

433 return null 

434 return pstruct 

435 

436 

437class AutocompleteInputWidget(Widget): 

438 """ 

439 Renders an ``<input type="text"/>`` widget which provides 

440 autocompletion via a list of values using bootstrap's typeahead plugin 

441 https://github.com/twitter/typeahead.js/ 

442 

443 **Attributes/Arguments** 

444 

445 template 

446 The template name used to render the widget. Default: 

447 ``typeahead_textinput``. 

448 

449 readonly_template 

450 The template name used to render the widget in read-only mode. 

451 Default: ``readonly/typeahead_textinput``. 

452 

453 strip 

454 If true, during deserialization, strip the value of leading 

455 and trailing whitespace (default ``True``). 

456 

457 values 

458 A list of strings or string. 

459 Defaults to ``[]``. 

460 

461 If ``values`` is a string it will be treated as a 

462 URL. If values is an iterable which can be serialized to a 

463 :term:`JSON` array, it will be treated as local data. 

464 

465 If a string is provided to a URL, an :term:`XHR` request will 

466 be sent to the URL. The response should be a JSON 

467 serialization of a list of values. For example: 

468 

469 ['foo', 'bar', 'baz'] 

470 

471 min_length 

472 ``min_length`` is an optional argument to 

473 :term:`jquery.ui.autocomplete`. The number of characters to 

474 wait for before activating the autocomplete call. Defaults to 

475 ``1``. 

476 

477 items 

478 The max number of items to display in the dropdown. Defaults to 

479 ``8``. 

480 

481 """ 

482 

483 min_length = 1 

484 readonly_template = "readonly/textinput" 

485 strip = True 

486 items = 8 

487 template = "autocomplete_input" 

488 values = None 

489 requirements = (("typeahead", None), ("deform", None)) 

490 

491 def serialize(self, field, cstruct, **kw): 

492 if "delay" in kw or getattr(self, "delay", None): 

493 raise ValueError( 

494 "AutocompleteWidget does not support *delay* parameter " 

495 "any longer." 

496 ) 

497 if cstruct in (null, None): 

498 cstruct = "" 

499 self.values = self.values or [] 

500 readonly = kw.get("readonly", self.readonly) 

501 

502 options = {} 

503 if isinstance(self.values, string_types): 

504 options["remote"] = "%s?term=%%QUERY" % self.values 

505 else: 

506 options["local"] = self.values 

507 

508 options["minLength"] = kw.pop("min_length", self.min_length) 

509 options["limit"] = kw.pop("items", self.items) 

510 kw["options"] = json.dumps(options) 

511 tmpl_values = self.get_template_values(field, cstruct, kw) 

512 template = readonly and self.readonly_template or self.template 

513 return field.renderer(template, **tmpl_values) 

514 

515 def deserialize(self, field, pstruct): 

516 if pstruct is null: 

517 return null 

518 elif not isinstance(pstruct, string_types): 

519 raise Invalid(field.schema, "Pstruct is not a string") 

520 if self.strip: 

521 pstruct = pstruct.strip() 

522 if not pstruct: 

523 return null 

524 return pstruct 

525 

526 

527class TimeInputWidget(Widget): 

528 """ 

529 Renders a time picker widget. 

530 

531 The default rendering is as a native HTML5 time input widget, 

532 falling back to pickadate (https://github.com/amsul/pickadate.js.) 

533 

534 Most useful when the schema node is a ``colander.Time`` object. 

535 

536 **Attributes/Arguments** 

537 

538 style 

539 A string that will be placed literally in a ``style`` attribute on 

540 the text input tag. For example, 'width:150px;'. Default: ``None``, 

541 meaning no style attribute will be added to the input tag. 

542 

543 options 

544 Options for configuring the widget (eg: date format) 

545 

546 template 

547 The template name used to render the widget. Default: 

548 ``timeinput``. 

549 

550 readonly_template 

551 The template name used to render the widget in read-only mode. 

552 Default: ``readonly/timeinput``. 

553 """ 

554 

555 template = "timeinput" 

556 readonly_template = "readonly/textinput" 

557 type_name = "time" 

558 size = None 

559 style = None 

560 requirements = (("modernizr", None), ("pickadate", None)) 

561 default_options = (("format", "HH:i"),) 

562 

563 _pstruct_schema = SchemaNode( 

564 Mapping(), 

565 SchemaNode(_StrippedString(), name="time"), 

566 SchemaNode(_StrippedString(), name="time_submit", missing=""), 

567 ) 

568 

569 def __init__(self, *args, **kwargs): 

570 self.options = dict(self.default_options) 

571 self.options["formatSubmit"] = "HH:i" 

572 Widget.__init__(self, *args, **kwargs) 

573 

574 def serialize(self, field, cstruct, **kw): 

575 if cstruct in (null, None): 

576 cstruct = "" 

577 readonly = kw.get("readonly", self.readonly) 

578 template = readonly and self.readonly_template or self.template 

579 options = dict( 

580 kw.get("options") or self.options or self.default_options 

581 ) 

582 options["formatSubmit"] = "HH:i" 

583 kw.setdefault("options_json", json.dumps(options)) 

584 values = self.get_template_values(field, cstruct, kw) 

585 return field.renderer(template, **values) 

586 

587 def deserialize(self, field, pstruct): 

588 if pstruct in ("", null): 

589 return null 

590 try: 

591 validated = self._pstruct_schema.deserialize(pstruct) 

592 except Invalid as exc: 

593 raise Invalid(field.schema, text_("Invalid pstruct: %s" % exc)) 

594 return validated["time_submit"] or validated["time"] 

595 

596 

597class DateInputWidget(Widget): 

598 """ 

599 Renders a date picker widget. 

600 

601 The default rendering is as a native HTML5 date input widget, 

602 falling back to pickadate (https://github.com/amsul/pickadate.js.) 

603 

604 Most useful when the schema node is a ``colander.Date`` object. 

605 

606 **Attributes/Arguments** 

607 

608 options 

609 Dictionary of options for configuring the widget (eg: date format) 

610 

611 template 

612 The template name used to render the widget. Default: 

613 ``dateinput``. 

614 

615 readonly_template 

616 The template name used to render the widget in read-only mode. 

617 Default: ``readonly/textinput``. 

618 """ 

619 

620 template = "dateinput" 

621 readonly_template = "readonly/textinput" 

622 type_name = "date" 

623 requirements = (("modernizr", None), ("pickadate", None)) 

624 default_options = ( 

625 ("format", "yyyy-mm-dd"), 

626 ("selectMonths", True), 

627 ("selectYears", True), 

628 ) 

629 options = None 

630 

631 _pstruct_schema = SchemaNode( 

632 Mapping(), 

633 SchemaNode(_StrippedString(), name="date"), 

634 SchemaNode(_StrippedString(), name="date_submit", missing=""), 

635 ) 

636 

637 def serialize(self, field, cstruct, **kw): 

638 if cstruct in (null, None): 

639 cstruct = "" 

640 readonly = kw.get("readonly", self.readonly) 

641 template = readonly and self.readonly_template or self.template 

642 options = dict( 

643 kw.get("options") or self.options or self.default_options 

644 ) 

645 options["formatSubmit"] = "yyyy-mm-dd" 

646 kw.setdefault("options_json", json.dumps(options)) 

647 values = self.get_template_values(field, cstruct, kw) 

648 return field.renderer(template, **values) 

649 

650 def deserialize(self, field, pstruct): 

651 if pstruct in ("", null): 

652 return null 

653 try: 

654 validated = self._pstruct_schema.deserialize(pstruct) 

655 except Invalid as exc: 

656 raise Invalid(field.schema, "Invalid pstruct: %s" % exc) 

657 return validated["date_submit"] or validated["date"] 

658 

659 

660class DateTimeInputWidget(Widget): 

661 """ 

662 Renders a datetime picker widget. 

663 

664 The default rendering is as a pair of inputs (a date and a time) using 

665 pickadate.js (https://github.com/amsul/pickadate.js). 

666 

667 Used for ``colander.DateTime`` schema nodes. 

668 

669 **Attributes/Arguments** 

670 

671 date_options 

672 A dictionary of date options passed to pickadate. 

673 

674 time_options 

675 A dictionary of time options passed to pickadate. 

676 

677 template 

678 The template name used to render the widget. Default: 

679 ``dateinput``. 

680 

681 readonly_template 

682 The template name used to render the widget in read-only mode. 

683 Default: ``readonly/textinput``. 

684 """ 

685 

686 template = "datetimeinput" 

687 readonly_template = "readonly/datetimeinput" 

688 type_name = "datetime" 

689 requirements = (("modernizr", None), ("pickadate", None)) 

690 default_date_options = ( 

691 ("format", "yyyy-mm-dd"), 

692 ("selectMonths", True), 

693 ("selectYears", True), 

694 ) 

695 date_options = None 

696 default_time_options = (("format", "h:i A"), ("interval", 30)) 

697 time_options = None 

698 

699 _pstruct_schema = SchemaNode( 

700 Mapping(), 

701 SchemaNode(_StrippedString(), name="date"), 

702 SchemaNode(_StrippedString(), name="time"), 

703 SchemaNode(_StrippedString(), name="date_submit", missing=""), 

704 SchemaNode(_StrippedString(), name="time_submit", missing=""), 

705 ) 

706 

707 def serialize(self, field, cstruct, **kw): 

708 if cstruct in (null, None): 

709 cstruct = "" 

710 readonly = kw.get("readonly", self.readonly) 

711 if cstruct: 

712 parsed = ISO8601_REGEX.match(cstruct) 

713 if parsed: # strip timezone if it's there 

714 timezone = parsed.groupdict()["timezone"] 

715 if timezone and cstruct.endswith(timezone): 

716 cstruct = cstruct[: -len(timezone)] 

717 

718 try: 

719 date, time = cstruct.split("T", 1) 

720 try: 

721 # get rid of milliseconds 

722 time, _ = time.split(".", 1) 

723 except ValueError: 

724 pass 

725 kw["date"], kw["time"] = date, time 

726 except ValueError: # need more than one item to unpack 

727 kw["date"] = kw["time"] = "" 

728 

729 date_options = dict( 

730 kw.get("date_options") 

731 or self.date_options 

732 or self.default_date_options 

733 ) 

734 date_options["formatSubmit"] = "yyyy-mm-dd" 

735 kw["date_options_json"] = json.dumps(date_options) 

736 

737 time_options = dict( 

738 kw.get("time_options") 

739 or self.time_options 

740 or self.default_time_options 

741 ) 

742 time_options["formatSubmit"] = "HH:i" 

743 kw["time_options_json"] = json.dumps(time_options) 

744 

745 values = self.get_template_values(field, cstruct, kw) 

746 template = readonly and self.readonly_template or self.template 

747 return field.renderer(template, **values) 

748 

749 def deserialize(self, field, pstruct): 

750 if pstruct is null: 

751 return null 

752 else: 

753 try: 

754 validated = self._pstruct_schema.deserialize(pstruct) 

755 except Invalid as exc: 

756 raise Invalid(field.schema, "Invalid pstruct: %s" % exc) 

757 # seriously pickadate? oh. right. i forgot. you're javascript. 

758 date = validated["date_submit"] or validated["date"] 

759 time = validated["time_submit"] or validated["time"] 

760 

761 if not time and not date: 

762 return null 

763 

764 result = "T".join([date, time]) 

765 

766 if not date: 

767 raise Invalid(field.schema, _("Incomplete date"), result) 

768 

769 if not time: 

770 raise Invalid(field.schema, _("Incomplete time"), result) 

771 

772 return result 

773 

774 

775class TextAreaWidget(TextInputWidget): 

776 """ 

777 Renders a ``<textarea>`` widget. 

778 

779 **Attributes/Arguments** 

780 

781 cols 

782 The size, in columns, of the text input field. Defaults to 

783 ``None``, meaning that the ``cols`` is not included in the 

784 widget output (uses browser default cols). 

785 

786 rows 

787 The size, in rows, of the text input field. Defaults to 

788 ``None``, meaning that the ``rows`` is not included in the 

789 widget output (uses browser default cols). 

790 

791 template 

792 The template name used to render the widget. Default: 

793 ``textarea``. 

794 

795 readonly_template 

796 The template name used to render the widget in read-only mode. 

797 Default: ``readonly/textinput``. 

798 

799 

800 strip 

801 If true, during deserialization, strip the value of leading 

802 and trailing whitespace (default ``True``). 

803 """ 

804 

805 template = "textarea" 

806 readonly_template = "readonly/textinput" 

807 cols = None 

808 rows = None 

809 strip = True 

810 

811 

812class RichTextWidget(TextInputWidget): 

813 """ 

814 Renders a ``<textarea>`` widget with the 

815 :term:`TinyMCE Editor`. 

816 

817 To use this widget the :term:`TinyMCE Editor` library must be 

818 provided in the page where the widget is rendered. A version of 

819 :term:`TinyMCE Editor` is included in Deform's ``static`` directory. 

820 

821 

822 **Attributes/Arguments** 

823 

824 readonly_template 

825 The template name used to render the widget in read-only mode. 

826 Default: ``readonly/richtext``. 

827 

828 delayed_load 

829 If you have many richtext fields, you can set this option to 

830 ``True``, and the richtext editor will only be loaded upon 

831 the user clicking the field. Default: ``False``. 

832 

833 **Security Note**: Enabling ``delayed_load`` can create an 

834 HTML injection vulnerability. When enabled, any existing value 

835 for the field will be rendered without HTML escaping. Also, 

836 on form re-display, any user-submitted value which passes 

837 validation will be rendered unescaped. (If the field has a 

838 validation error, ``delayed_load`` will be disabled during 

839 re-display.) You should not enable ``delayed_load`` unless you 

840 trust both existing and valid user-submitted values for the field 

841 to be 'safe HTML'. 

842 

843 strip 

844 If true, during deserialization, strip the value of leading 

845 and trailing whitespace. Default: ``True``. 

846 

847 template 

848 The template name used to render the widget. Default: 

849 ``richtext``. 

850 

851 options 

852 A dictionary or sequence of two-tuples containing additional 

853 options to pass to the TinyMCE ``init`` function call. All types 

854 within such structure should be Python native as the structure 

855 will be converted to JSON on serialization. This widget provides 

856 some sensible defaults, as described below in 

857 :attr:`default_options`. 

858 

859 You should refer to the `TinyMCE Configuration 

860 <https://www.tiny.cloud/docs/configure/>`_ documentation 

861 for details regarding all available configuration options. 

862 

863 The ``language`` option is passed to TinyMCE within the default 

864 template, using i18n machinery to determine the language to use. 

865 This option can be overridden if it is specified here in ``options``. 

866 

867 *Note*: the ``elements`` option for TinyMCE is set automatically 

868 according to the given field's ``oid``. 

869 

870 Default: ``None`` (no additional options) 

871 

872 Note that the RichTextWidget template does not honor the ``css_class`` 

873 or ``style`` attributes of the widget. 

874 

875 """ 

876 

877 readonly_template = "readonly/richtext" 

878 delayed_load = False 

879 strip = True 

880 template = "richtext" 

881 requirements = ({"js": "deform:static/tinymce/tinymce.min.js"},) 

882 

883 #: Default options passed to TinyMCE. Customise by using :attr:`options`. 

884 default_options = ( 

885 ("height", 240), 

886 ("width", 0), 

887 ("skin", "lightgray"), 

888 ("theme", "modern"), 

889 ("mode", "exact"), 

890 ("strict_loading_mode", True), 

891 ("theme_advanced_resizing", True), 

892 ("theme_advanced_toolbar_align", "left"), 

893 ("theme_advanced_toolbar_location", "top"), 

894 ) 

895 #: Options to pass to TinyMCE that will override :attr:`default_options`. 

896 options = None 

897 

898 def serialize(self, field, cstruct, **kw): 

899 if cstruct in (null, None): 

900 cstruct = "" 

901 readonly = kw.get("readonly", self.readonly) 

902 

903 options = dict(self.default_options) 

904 # Accept overrides from keywords or as an attribute 

905 options_overrides = dict(kw.get("options", self.options or {})) 

906 options.update(options_overrides) 

907 # Dump to JSON and strip curly braces at start and end 

908 kw["tinymce_options"] = json.dumps(options)[1:-1] 

909 

910 values = self.get_template_values(field, cstruct, kw) 

911 template = readonly and self.readonly_template or self.template 

912 return field.renderer(template, **values) 

913 

914 

915class PasswordWidget(TextInputWidget): 

916 """ 

917 Renders a single <input type="password"/> input field. 

918 

919 **Attributes/Arguments** 

920 

921 template 

922 The template name used to render the widget. Default: 

923 ``password``. 

924 

925 readonly_template 

926 The template name used to render the widget in read-only mode. 

927 Default: ``readonly/password``. 

928 

929 strip 

930 If true, during deserialization, strip the value of leading 

931 and trailing whitespace. Default: ``True``. 

932 

933 redisplay 

934 If true, on validation failure, retain and redisplay the password 

935 input. If false, on validation failure, this field will be 

936 rendered empty. Default: ``False``. 

937 

938 """ 

939 

940 template = "password" 

941 readonly_template = "readonly/password" 

942 redisplay = False 

943 

944 

945class HiddenWidget(Widget): 

946 """ 

947 Renders an ``<input type="hidden"/>`` widget. 

948 

949 **Attributes/Arguments** 

950 

951 template 

952 The template name used to render the widget. Default: 

953 ``hidden``. 

954 """ 

955 

956 template = "hidden" 

957 hidden = True 

958 

959 def serialize(self, field, cstruct, **kw): 

960 if cstruct in (null, None): 

961 cstruct = "" 

962 values = self.get_template_values(field, cstruct, kw) 

963 return field.renderer(self.template, **values) 

964 

965 def deserialize(self, field, pstruct): 

966 if not pstruct: 

967 return null 

968 elif not isinstance(pstruct, string_types): 

969 raise Invalid(field.schema, "Pstruct is not a string") 

970 return pstruct 

971 

972 

973class CheckboxWidget(Widget): 

974 """ 

975 Renders an ``<input type="checkbox"/>`` widget. 

976 

977 **Attributes/Arguments** 

978 

979 true_val 

980 The value which should be returned during deserialization if 

981 the box is checked. Default: ``true``. 

982 

983 false_val 

984 The value which should be returned during deserialization if 

985 the box was left unchecked. Default: ``false``. 

986 

987 template 

988 The template name used to render the widget. Default: 

989 ``checkbox``. 

990 

991 readonly_template 

992 The template name used to render the widget in read-only mode. 

993 Default: ``readonly/checkbox``. 

994 

995 """ 

996 

997 true_val = "true" 

998 false_val = "false" 

999 

1000 template = "checkbox" 

1001 readonly_template = "readonly/checkbox" 

1002 

1003 def serialize(self, field, cstruct, **kw): 

1004 readonly = kw.get("readonly", self.readonly) 

1005 template = readonly and self.readonly_template or self.template 

1006 values = self.get_template_values(field, cstruct, kw) 

1007 return field.renderer(template, **values) 

1008 

1009 def deserialize(self, field, pstruct): 

1010 if pstruct is null: 

1011 return self.false_val 

1012 elif not isinstance(pstruct, string_types): 

1013 raise Invalid(field.schema, "Pstruct is not a string") 

1014 return (pstruct == self.true_val) and self.true_val or self.false_val 

1015 

1016 

1017class OptGroup(object): 

1018 """ 

1019 Used in the ``values`` argument passed to an instance of 

1020 ``SelectWidget`` to render an ``<optgroup>`` HTML tag. 

1021 

1022 **Attributes/Arguments** 

1023 

1024 label 

1025 The label of the ``<optgroup>`` HTML tag. 

1026 

1027 options 

1028 A sequence that describes the ``<options>`` HTML tag(s). It 

1029 must have the same structure as the ``values`` 

1030 argument/parameter in the ``SelectWidget`` class, but should 

1031 not contain ``OptGroup`` instances since ``<optgroup>`` HTML 

1032 tags cannot be nested. 

1033 """ 

1034 

1035 def __init__(self, label, *options): 

1036 self.label = label 

1037 self.options = options 

1038 

1039 

1040class SelectWidget(Widget): 

1041 """ 

1042 Renders ``<select>`` field based on a predefined set of values. 

1043 

1044 **Attributes/Arguments** 

1045 

1046 values 

1047 A sequence type (list, tuple, or range) of items where each item must 

1048 be either: 

1049 

1050 - a two-tuple (the first value must be of type string, unicode 

1051 or integer, the second value must be string or unicode) 

1052 indicating allowable, displayed values, e.g. ``('jsmith', 

1053 'John Smith')``. The first element in the tuple is the value 

1054 that should be returned when the form is posted. The second 

1055 is the display value; 

1056 

1057 - or an instance of ``optgroup_class`` (which is 

1058 ``deform.widget.OptGroup`` by default). 

1059 

1060 null_value 

1061 The value which represents the null value. When the null 

1062 value is encountered during serialization, the 

1063 :attr:`colander.null` sentinel is returned to the caller. 

1064 Default: ``''`` (the empty string). 

1065 

1066 template 

1067 The template name used to render the widget. Default: 

1068 ``select``. 

1069 

1070 readonly_template 

1071 The template name used to render the widget in read-only mode. 

1072 Default: ``readonly/select``. 

1073 

1074 multiple 

1075 Enable multiple on the select widget ( default: ``False`` ) 

1076 

1077 optgroup_class 

1078 The class used to represent ``<optgroup>`` HTML tags. Default: 

1079 ``deform.widget.OptGroup``. 

1080 

1081 long_label_generator 

1082 A function that returns the "long label" used as the 

1083 description for very old browsers that do not support the 

1084 ``<optgroup>`` HTML tag. If a function is provided, the 

1085 ``label`` attribute will receive the (short) description, 

1086 while the content of the ``<option>`` tag will receive the 

1087 "long label". The function is called with two parameters: the 

1088 group label and the option (short) description. 

1089 

1090 For example, with the following widget: 

1091 

1092 .. code-block:: python 

1093 

1094 long_label_gener = lambda group, label: ' - '.join((group, label)) 

1095 SelectWidget( 

1096 values=( 

1097 ('', 'Select your favorite musician'), 

1098 OptGroup('Guitarists', 

1099 ('page', 'Jimmy Page'), 

1100 ('hendrix', 'Jimi Hendrix')), 

1101 OptGroup('Drummers', 

1102 ('cobham', 'Billy Cobham'), 

1103 ('bonham', 'John Bonham'))), 

1104 long_label_generator=long_label_gener) 

1105 

1106 ... the rendered options would look like: 

1107 

1108 .. code-block:: html 

1109 

1110 <option value="">Select your favorite musician</option> 

1111 <optgroup label="Guitarists"> 

1112 <option value="page" 

1113 label="Jimmy Page">Guitarists - Jimmy Page</option> 

1114 <option value="hendrix" 

1115 label="Jimi Hendrix">Guitarists - Jimi Hendrix</option> 

1116 </optgroup> 

1117 <optgroup label="Drummers"> 

1118 <option value="cobham" 

1119 label="Billy Cobham">Drummers - Billy Cobham</option> 

1120 <option value="bonham" 

1121 label="John Bonham">Drummers - John Bonham</option> 

1122 </optgroup> 

1123 

1124 Default: ``None`` (which means that the ``label`` attribute is 

1125 not rendered). 

1126 

1127 size 

1128 The size, in rows, of the select list. Defaults to 

1129 ``None``, meaning that the ``size`` is not included in the 

1130 widget output (uses browser default size). 

1131 """ 

1132 

1133 template = "select" 

1134 readonly_template = "readonly/select" 

1135 null_value = "" 

1136 values = () 

1137 size = None 

1138 multiple = False 

1139 optgroup_class = OptGroup 

1140 long_label_generator = None 

1141 selectize_options = None 

1142 default_selectize_options = (("allowEmptyOption", True),) 

1143 

1144 def get_select_value(self, cstruct, value): 

1145 """Choose whether <opt> is selected or not. 

1146 

1147 Incoming value is always string, as it has been passed through HTML. 

1148 However, our values might be given as integer, UUID. 

1149 """ 

1150 

1151 if self.multiple: 

1152 if value in map(text_type, cstruct): 

1153 return "selected" 

1154 else: 

1155 if value == text_type(cstruct): 

1156 return "selected" 

1157 

1158 return None 

1159 

1160 def serialize(self, field, cstruct, **kw): 

1161 if cstruct in (null, None): 

1162 cstruct = self.null_value 

1163 readonly = kw.get("readonly", self.readonly) 

1164 values = kw.get("values", self.values) 

1165 if not isinstance(values, sequence_types): 

1166 e = "Values must be a sequence type (list, tuple, or range)." 

1167 raise TypeError(e) 

1168 template = readonly and self.readonly_template or self.template 

1169 kw["values"] = _normalize_choices(values) 

1170 

1171 selectize_options = dict( 

1172 kw.get("selectize_options") 

1173 or self.selectize_options 

1174 or self.default_selectize_options 

1175 ) 

1176 kw["selectize_options_json"] = json.dumps(selectize_options) 

1177 tmpl_values = self.get_template_values(field, cstruct, kw) 

1178 return field.renderer(template, **tmpl_values) 

1179 

1180 def deserialize(self, field, pstruct): 

1181 if pstruct in (null, self.null_value): 

1182 return null 

1183 if self.multiple: 

1184 try: 

1185 return _sequence_of_strings.deserialize(pstruct) 

1186 except Invalid as exc: 

1187 raise Invalid(field.schema, "Invalid pstruct: %s" % exc) 

1188 else: 

1189 if not isinstance(pstruct, string_types): 

1190 raise Invalid(field.schema, "Pstruct is not a string") 

1191 return pstruct 

1192 

1193 

1194class Select2Widget(SelectWidget): 

1195 """ 

1196 Renders ``<select>`` field based on a predefined set of values using 

1197 `select2 <https://select2.org/>`_ library. 

1198 

1199 **Attributes/Arguments** 

1200 

1201 Same as :func:`~deform.widget.SelectWidget`, with some extra options 

1202 listed here. 

1203 

1204 tags: *bool* 

1205 Allow dynamic option creation ( default: ``False`` ). 

1206 See `select2 docs on tagging <https://select2.org/tagging>`_ for 

1207 more details. 

1208 """ 

1209 

1210 template = "select2" 

1211 requirements = ( 

1212 ("deform", None), 

1213 { 

1214 "js": "deform:static/select2/select2.js", 

1215 "css": "deform:static/select2/select2.css", 

1216 }, 

1217 ) 

1218 

1219 

1220class SelectizeWidget(SelectWidget): 

1221 """ 

1222 Renders ``<select>`` field based on a predefined set of values using 

1223 `selectize.js <https://github.com/selectize/selectize.js>`_ library. 

1224 

1225 **Attributes/Arguments** 

1226 

1227 Same as :func:`~deform.widget.SelectWidget`, with some extra options 

1228 listed here. 

1229 

1230 selectize_options: *dict* or *two-tuples* 

1231 Allows configuration of arbitrary options for Selectize. See 

1232 `Selectize docs under Usage for configuration options 

1233 <https://github.com/selectize/selectize.js/blob/master/docs/usage.md#configuration>` 

1234 for details. 

1235 """ 

1236 

1237 template = "selectize" 

1238 requirements = ( 

1239 ("deform", None), 

1240 { 

1241 "js": "deform:static/selectize/selectize.js", 

1242 "css": "deform:static/selectize/selectize.bootstrap3.css", 

1243 }, 

1244 ) 

1245 

1246 

1247class RadioChoiceWidget(SelectWidget): 

1248 """ 

1249 Renders a sequence of ``<input type="radio"/>`` buttons based on a 

1250 predefined set of values. 

1251 

1252 **Attributes/Arguments** 

1253 

1254 values 

1255 A sequence type (list, tuple, or range) of two-tuples (the first value 

1256 must be of type string, unicode, or integer, the second value must be 

1257 string or unicode) indicating allowable, displayed values, e.g. ``( 

1258 ('true', 'True'), ('false', 'False') )``. The first element 

1259 in the tuple is the value that should be returned when the 

1260 form is posted. The second is the display value. 

1261 

1262 template 

1263 The template name used to render the widget. Default: 

1264 ``radio_choice``. 

1265 

1266 readonly_template 

1267 The template name used to render the widget in read-only mode. 

1268 Default: ``readonly/radio_choice``. 

1269 

1270 null_value 

1271 The value used to replace the ``colander.null`` value when it 

1272 is passed to the ``serialize`` or ``deserialize`` method. 

1273 Default: the empty string. 

1274 

1275 inline 

1276 If true, choices will be rendered on a single line. 

1277 Otherwise choices will be rendered one per line. 

1278 Default: false. 

1279 """ 

1280 

1281 template = "radio_choice" 

1282 readonly_template = "readonly/radio_choice" 

1283 

1284 

1285class CheckboxChoiceWidget(Widget): 

1286 """ 

1287 Renders a sequence of ``<input type="check"/>`` buttons based on a 

1288 predefined set of values. 

1289 

1290 **Attributes/Arguments** 

1291 

1292 values 

1293 A sequence type (list, tuple, or range) of two-tuples (the first value 

1294 must be of type string, unicode or integer, the second value must be 

1295 string or unicode) indicating allowable, displayed values, e.g. ``( 

1296 ('true', 'True'), ('false', 'False') )``. The first element 

1297 in the tuple is the value that should be returned when the 

1298 form is posted. The second is the display value. 

1299 

1300 template 

1301 The template name used to render the widget. Default: 

1302 ``checkbox_choice``. 

1303 

1304 readonly_template 

1305 The template name used to render the widget in read-only mode. 

1306 Default: ``readonly/checkbox_choice``. 

1307 

1308 null_value 

1309 The value used to replace the ``colander.null`` value when it 

1310 is passed to the ``serialize`` or ``deserialize`` method. 

1311 Default: the empty string. 

1312 

1313 inline 

1314 If true, choices will be rendered on a single line. 

1315 Otherwise choices will be rendered one per line. 

1316 Default: false. 

1317 """ 

1318 

1319 template = "checkbox_choice" 

1320 readonly_template = "readonly/checkbox_choice" 

1321 values = () 

1322 

1323 def serialize(self, field, cstruct, **kw): 

1324 if cstruct in (null, None): 

1325 cstruct = () 

1326 readonly = kw.get("readonly", self.readonly) 

1327 values = kw.get("values", self.values) 

1328 if not isinstance(values, sequence_types): 

1329 e = "Values must be a sequence type (list, tuple, or range)." 

1330 raise TypeError(e) 

1331 kw["values"] = _normalize_choices(values) 

1332 template = readonly and self.readonly_template or self.template 

1333 tmpl_values = self.get_template_values(field, cstruct, kw) 

1334 return field.renderer(template, **tmpl_values) 

1335 

1336 def deserialize(self, field, pstruct): 

1337 if pstruct is null: 

1338 return null 

1339 if isinstance(pstruct, string_types): 

1340 return (pstruct,) 

1341 try: 

1342 validated = _sequence_of_strings.deserialize(pstruct) 

1343 except Invalid as exc: 

1344 raise Invalid(field.schema, "Invalid pstruct: %s" % exc) 

1345 return tuple(validated) 

1346 

1347 

1348class CheckedInputWidget(Widget): 

1349 """ 

1350 Renders two text input fields: 'value' and 'confirm'. 

1351 Validates that the 'value' value matches the 'confirm' value. 

1352 

1353 **Attributes/Arguments** 

1354 

1355 template 

1356 The template name used to render the widget. Default: 

1357 ``checked_input``. 

1358 

1359 readonly_template 

1360 The template name used to render the widget in read-only mode. 

1361 Default: ``readonly/textinput``. 

1362 

1363 mismatch_message 

1364 The message to be displayed when the value in the primary 

1365 field does not match the value in the confirm field. 

1366 

1367 mask 

1368 A :term:`jquery.maskedinput` input mask, as a string. Both 

1369 input fields will use this mask. 

1370 

1371 a - Represents an alpha character (A-Z,a-z) 

1372 9 - Represents a numeric character (0-9) 

1373 * - Represents an alphanumeric character (A-Z,a-z,0-9) 

1374 

1375 All other characters in the mask will be considered mask 

1376 literals. 

1377 

1378 Example masks: 

1379 

1380 Date: 99/99/9999 

1381 

1382 US Phone: (999) 999-9999 

1383 

1384 US SSN: 999-99-9999 

1385 

1386 When this option is used, the :term:`jquery.maskedinput` 

1387 library must be loaded into the page serving the form for the 

1388 mask argument to have any effect. See :ref:`masked_input`. 

1389 

1390 mask_placeholder 

1391 The placeholder for required nonliteral elements when a mask 

1392 is used. Default: ``_`` (underscore). 

1393 """ 

1394 

1395 template = "checked_input" 

1396 readonly_template = "readonly/textinput" 

1397 mismatch_message = _("Fields did not match") 

1398 subject = _("Value") 

1399 confirm_subject = _("Confirm Value") 

1400 mask = None 

1401 mask_placeholder = "_" 

1402 requirements = () 

1403 

1404 def __init__(self, **kw): 

1405 super(CheckedInputWidget, self).__init__(**kw) 

1406 if getattr(self, "mask", False): 

1407 self.requirements = tuple( 

1408 list(self.requirements) + [("jquery.maskedinput", None)] 

1409 ) 

1410 

1411 def serialize(self, field, cstruct, **kw): 

1412 if cstruct in (null, None): 

1413 cstruct = "" 

1414 readonly = kw.get("readonly", self.readonly) 

1415 kw.setdefault("subject", self.subject) 

1416 kw.setdefault("confirm_subject", self.confirm_subject) 

1417 confirm = getattr(field, "%s-confirm" % (field.name,), cstruct) 

1418 kw["confirm"] = confirm 

1419 template = readonly and self.readonly_template or self.template 

1420 values = self.get_template_values(field, cstruct, kw) 

1421 return field.renderer(template, **values) 

1422 

1423 def deserialize(self, field, pstruct): 

1424 if pstruct is null: 

1425 return null 

1426 confirm_name = "%s-confirm" % field.name 

1427 schema = SchemaNode( 

1428 Mapping(), 

1429 SchemaNode(_PossiblyEmptyString(), name=field.name), 

1430 SchemaNode(_PossiblyEmptyString(), name=confirm_name), 

1431 ) 

1432 try: 

1433 validated = schema.deserialize(pstruct) 

1434 except Invalid as exc: 

1435 raise Invalid(field.schema, "Invalid pstruct: %s" % exc) 

1436 value = validated[field.name] 

1437 confirm = validated[confirm_name] 

1438 setattr(field, confirm_name, confirm) 

1439 if (value or confirm) and (value != confirm): 

1440 raise Invalid(field.schema, self.mismatch_message, value) 

1441 if not value: 

1442 return null 

1443 return value 

1444 

1445 

1446class CheckedPasswordWidget(CheckedInputWidget): 

1447 """ 

1448 Renders two password input fields: 'password' and 'confirm'. 

1449 Validates that the 'password' value matches the 'confirm' value. 

1450 

1451 **Attributes/Arguments** 

1452 

1453 template 

1454 The template name used to render the widget. Default: 

1455 ``checked_password``. 

1456 

1457 readonly_template 

1458 The template name used to render the widget in read-only mode. 

1459 Default: ``readonly/checked_password``. 

1460 

1461 mismatch_message 

1462 The string shown in the error message when a validation failure is 

1463 caused by the confirm field value does not match the password 

1464 field value. Default: ``Password did not match confirm``. 

1465 

1466 redisplay 

1467 If true, on validation failure involving a field with this widget, 

1468 retain and redisplay the provided values in the password inputs. If 

1469 false, on validation failure, the fields will be rendered empty. 

1470 Default:: ``False``. 

1471 """ 

1472 

1473 template = "checked_password" 

1474 readonly_template = "readonly/checked_password" 

1475 mismatch_message = _("Password did not match confirm") 

1476 redisplay = False 

1477 

1478 

1479class MappingWidget(Widget): 

1480 """ 

1481 Renders a mapping into a set of fields. 

1482 

1483 **Attributes/Arguments** 

1484 

1485 template 

1486 The template name used to render the widget. Default: 

1487 ``mapping``. See also ``mapping_accordion`` template for hideable 

1488 user experience. 

1489 

1490 readonly_template 

1491 The template name used to render the widget in read-only mode. 

1492 Default: ``readonly/mapping``. 

1493 

1494 item_template 

1495 The template name used to render each item in the mapping. 

1496 Default: ``mapping_item``. 

1497 

1498 readonly_item_template 

1499 The template name used to render each item in the form. 

1500 Default: ``readonly/mapping_item``. 

1501 

1502 open: bool 

1503 Used with ``mapping_accordion`` template to define if the 

1504 mapping subform accordion is open or closed by default. 

1505 

1506 Note that the MappingWidget template does not honor the ``css_class`` 

1507 or ``style`` attributes of the widget. 

1508 """ 

1509 

1510 template = "mapping" 

1511 readonly_template = "readonly/mapping" 

1512 item_template = "mapping_item" 

1513 readonly_item_template = "readonly/mapping_item" 

1514 error_class = None 

1515 category = "structural" 

1516 requirements = (("deform", None),) 

1517 

1518 def serialize(self, field, cstruct, **kw): 

1519 if cstruct in (null, None): 

1520 cstruct = {} 

1521 readonly = kw.get("readonly", self.readonly) 

1522 kw.setdefault("null", null) 

1523 template = readonly and self.readonly_template or self.template 

1524 values = self.get_template_values(field, cstruct, kw) 

1525 return field.renderer(template, **values) 

1526 

1527 def deserialize(self, field, pstruct): 

1528 error = None 

1529 

1530 result = {} 

1531 

1532 if pstruct is null: 

1533 pstruct = {} 

1534 elif not isinstance(pstruct, dict): 

1535 raise Invalid(field.schema, "Pstruct is not a dict") 

1536 

1537 for num, subfield in enumerate(field.children): 

1538 name = subfield.name 

1539 subval = pstruct.get(name, null) 

1540 

1541 try: 

1542 result[name] = subfield.deserialize(subval) 

1543 except Invalid as e: 

1544 result[name] = e.value 

1545 if error is None: 

1546 error = Invalid(field.schema, value=result) 

1547 error.add(e, num) 

1548 

1549 if error is not None: 

1550 raise error 

1551 

1552 return result 

1553 

1554 

1555class FormWidget(MappingWidget): 

1556 """ 

1557 The top-level widget; represents an entire form. 

1558 

1559 **Attributes/Arguments** 

1560 

1561 template 

1562 The template name used to render the widget. Default: 

1563 ``form``. 

1564 

1565 readonly_template 

1566 The template name used to render the widget in read-only mode. 

1567 Default: ``readonly/form``. 

1568 

1569 item_template 

1570 The template name used to render each item in the form. 

1571 Default: ``mapping_item``. 

1572 

1573 readonly_item_template 

1574 The template name used to render each item in the form. 

1575 Default: ``readonly/mapping_item``. 

1576 

1577 """ 

1578 

1579 template = "form" 

1580 readonly_template = "readonly/form" 

1581 

1582 

1583class SequenceWidget(Widget): 

1584 """Renders a sequence (0 .. N widgets, each the same as the other) 

1585 into a set of fields. 

1586 

1587 **Attributes/Arguments** 

1588 

1589 template 

1590 The template name used to render the widget. Default: 

1591 ``sequence``. 

1592 

1593 readonly_template 

1594 The template name used to render the widget in read-only mode. 

1595 Default: ``readonly/sequence``. 

1596 

1597 item_template 

1598 The template name used to render each value in the sequence. 

1599 Default: ``sequence_item``. 

1600 

1601 add_subitem_text_template 

1602 The string used as the add link text for the widget. 

1603 Interpolation markers in the template will be replaced in this 

1604 string during serialization with a value as follows: 

1605 

1606 ``${subitem_title}`` 

1607 The title of the subitem field 

1608 

1609 ``${subitem_description}`` 

1610 The description of the subitem field 

1611 

1612 ``${subitem_name}`` 

1613 The name of the subitem field 

1614 

1615 Default: ``Add ${subitem_title}``. 

1616 

1617 min_len 

1618 Integer indicating minimum number of acceptable subitems. Default: 

1619 ``None`` (meaning no minimum). On the first rendering of a form 

1620 including this sequence widget, at least this many subwidgets will be 

1621 rendered. The JavaScript sequence management will not allow fewer 

1622 than this many subwidgets to be present in the sequence. 

1623 

1624 max_len 

1625 Integer indicating maximum number of acceptable subwidgets. Default: 

1626 ``None`` (meaning no maximum). The JavaScript sequence management 

1627 will not allow more than this many subwidgets to be added to the 

1628 sequence. 

1629 

1630 orderable 

1631 Boolean indicating whether the Javascript sequence management will 

1632 allow the user to explicitly re-order the subwidgets. 

1633 Default: ``False``. 

1634 

1635 Note that the SequenceWidget template does not honor the ``css_class`` 

1636 or ``style`` attributes of the widget. 

1637 

1638 """ 

1639 

1640 template = "sequence" 

1641 readonly_template = "readonly/sequence" 

1642 item_template = "sequence_item" 

1643 readonly_item_template = "readonly/sequence_item" 

1644 error_class = None 

1645 add_subitem_text_template = _("Add ${subitem_title}") 

1646 min_len = None 

1647 max_len = None 

1648 orderable = False 

1649 requirements = ( 

1650 ("deform", None), 

1651 {"js": "deform:static/scripts/jquery-sortable.js"}, 

1652 ) 

1653 

1654 def prototype(self, field): 

1655 # we clone the item field to bump the oid (for easier 

1656 # automated testing; finding last node) 

1657 item_field = field.children[0].clone() 

1658 if not item_field.name: 

1659 info = "Prototype for %r has no name" % field 

1660 raise ValueError(info) 

1661 # NB: item_field default should already be set up 

1662 proto = item_field.render_template(self.item_template, parent=field) 

1663 if isinstance(proto, string_types): 

1664 proto = proto.encode("utf-8") 

1665 proto = url_quote(proto) 

1666 return proto 

1667 

1668 def serialize(self, field, cstruct, **kw): 

1669 # XXX make it possible to override min_len in kw 

1670 

1671 if cstruct in (null, None): 

1672 if self.min_len is not None: 

1673 cstruct = [null] * self.min_len 

1674 else: 

1675 cstruct = [] 

1676 

1677 cstructlen = len(cstruct) 

1678 

1679 if self.min_len is not None and (cstructlen < self.min_len): 

1680 cstruct = list(cstruct) + ([null] * (self.min_len - cstructlen)) 

1681 

1682 item_field = field.children[0] 

1683 

1684 if getattr(field, "sequence_fields", None): 

1685 # this serialization is being performed as a result of a 

1686 # validation failure (``deserialize`` was previously run) 

1687 assert len(cstruct) == len(field.sequence_fields) 

1688 subfields = list(zip(cstruct, field.sequence_fields)) 

1689 else: 

1690 # this serialization is being performed as a result of a 

1691 # first-time rendering 

1692 subfields = [] 

1693 for val in cstruct: 

1694 cloned = item_field.clone() 

1695 if val is not null: 

1696 # item field has already been set up with a default by 

1697 # virtue of its constructor and setting cstruct to null 

1698 # here wil overwrite the real default 

1699 cloned.cstruct = val 

1700 subfields.append((cloned.cstruct, cloned)) 

1701 

1702 readonly = kw.get("readonly", self.readonly) 

1703 template = readonly and self.readonly_template or self.template 

1704 translate = field.translate 

1705 subitem_title = kw.get("subitem_title", item_field.title) 

1706 subitem_description = kw.get( 

1707 "subitem_description", item_field.description 

1708 ) 

1709 add_subitem_text_template = kw.get( 

1710 "add_subitem_text_template", self.add_subitem_text_template 

1711 ) 

1712 add_template_mapping = dict( 

1713 subitem_title=translate(subitem_title), 

1714 subitem_description=translate(subitem_description), 

1715 subitem_name=item_field.name, 

1716 ) 

1717 if isinstance(add_subitem_text_template, TranslationString): 

1718 add_subitem_text = add_subitem_text_template % add_template_mapping 

1719 else: 

1720 add_subitem_text = _( 

1721 add_subitem_text_template, mapping=add_template_mapping 

1722 ) 

1723 

1724 kw.setdefault("subfields", subfields) 

1725 kw.setdefault("add_subitem_text", add_subitem_text) 

1726 kw.setdefault("item_field", item_field) 

1727 

1728 values = self.get_template_values(field, cstruct, kw) 

1729 

1730 return field.renderer(template, **values) 

1731 

1732 def deserialize(self, field, pstruct): 

1733 result = [] 

1734 error = None 

1735 

1736 if pstruct is null: 

1737 pstruct = [] 

1738 elif not isinstance(pstruct, list): 

1739 raise Invalid(field.schema, "Pstruct is not a list") 

1740 

1741 field.sequence_fields = [] 

1742 item_field = field.children[0] 

1743 

1744 for num, substruct in enumerate(pstruct): 

1745 subfield = item_field.clone() 

1746 try: 

1747 subval = subfield.deserialize(substruct) 

1748 except Invalid as e: 

1749 subval = e.value 

1750 if error is None: 

1751 error = Invalid(field.schema, value=result) 

1752 error.add(e, num) 

1753 

1754 subfield.cstruct = subval 

1755 result.append(subval) 

1756 field.sequence_fields.append(subfield) 

1757 

1758 if error is not None: 

1759 raise error 

1760 

1761 return result 

1762 

1763 def handle_error(self, field, error): 

1764 if field.error is None: 

1765 field.error = error 

1766 # XXX exponential time 

1767 sequence_fields = getattr(field, "sequence_fields", []) 

1768 for e in error.children: 

1769 for num, subfield in enumerate(sequence_fields): 

1770 if e.pos == num: 

1771 subfield.widget.handle_error(subfield, e) 

1772 

1773 

1774class filedict(dict): 

1775 """Use a dict subclass to make it easy to detect file upload 

1776 dictionaries in application code before trying to write them to 

1777 persistent objects.""" 

1778 

1779 

1780class FileUploadWidget(Widget): 

1781 """ 

1782 Represent a file upload. Meant to work with a 

1783 :class:`deform.FileData` schema node. 

1784 

1785 This widget accepts a single required positional argument in its 

1786 constructor: ``tmpstore``. This argument should be passed an 

1787 instance of an object that implements the 

1788 :class:`deform.interfaces.FileUploadTempStore` interface. Such an 

1789 instance will hold on to file upload data during the validation 

1790 process, so the user doesn't need to reupload files if other parts 

1791 of the form rendering fail validation. See also 

1792 :class:`deform.interfaces.FileUploadTempStore`. 

1793 

1794 **Attributes/Arguments** 

1795 

1796 template 

1797 The template name used to render the widget. Default: 

1798 ``file_upload``. 

1799 

1800 readonly_template 

1801 The template name used to render the widget in read-only mode. 

1802 Default: ``readonly/file_upload``. 

1803 

1804 accept 

1805 The ``accept`` attribute of the input field (default ``None``). 

1806 """ 

1807 

1808 template = "file_upload" 

1809 readonly_template = "readonly/file_upload" 

1810 accept = None 

1811 

1812 requirements = ({"js": "deform:static/scripts/file_upload.js"},) 

1813 

1814 _pstruct_schema = SchemaNode( 

1815 Mapping(), 

1816 SchemaNode(_FieldStorage(), name="upload", missing=None), 

1817 SchemaNode(_PossiblyEmptyString(), name="uid", missing=None), 

1818 ) 

1819 

1820 def __init__(self, tmpstore, **kw): 

1821 Widget.__init__(self, **kw) 

1822 self.tmpstore = tmpstore 

1823 

1824 def random_id(self): 

1825 return "".join( 

1826 [random.choice(uppercase + string.digits) for i in range(10)] 

1827 ) 

1828 

1829 def serialize(self, field, cstruct, **kw): 

1830 if cstruct in (null, None): 

1831 cstruct = {} 

1832 if cstruct: 

1833 uid = cstruct["uid"] 

1834 if uid not in self.tmpstore: 

1835 self.tmpstore[uid] = cstruct 

1836 

1837 readonly = kw.get("readonly", self.readonly) 

1838 template = readonly and self.readonly_template or self.template 

1839 values = self.get_template_values(field, cstruct, kw) 

1840 return field.renderer(template, **values) 

1841 

1842 def deserialize(self, field, pstruct): 

1843 if pstruct is null: 

1844 return null 

1845 try: 

1846 validated = self._pstruct_schema.deserialize(pstruct) 

1847 except Invalid as exc: 

1848 raise Invalid(field.schema, "Invalid pstruct: %s" % exc) 

1849 

1850 upload = validated["upload"] 

1851 uid = validated["uid"] 

1852 

1853 if hasattr(upload, "file"): 

1854 # the upload control had a file selected 

1855 data = filedict() 

1856 data["fp"] = upload.file 

1857 filename = upload.filename 

1858 # sanitize IE whole-path filenames 

1859 filename = filename[filename.rfind("\\") + 1 :].strip() 

1860 data["filename"] = filename 

1861 data["mimetype"] = upload.type 

1862 data["size"] = upload.length 

1863 if uid is None: 

1864 # no previous file exists 

1865 while 1: 

1866 uid = self.random_id() 

1867 if self.tmpstore.get(uid) is None: 

1868 data["uid"] = uid 

1869 self.tmpstore[uid] = data 

1870 preview_url = self.tmpstore.preview_url(uid) 

1871 self.tmpstore[uid]["preview_url"] = preview_url 

1872 break 

1873 else: 

1874 # a previous file exists 

1875 data["uid"] = uid 

1876 self.tmpstore[uid] = data 

1877 preview_url = self.tmpstore.preview_url(uid) 

1878 self.tmpstore[uid]["preview_url"] = preview_url 

1879 else: 

1880 # the upload control had no file selected 

1881 if uid is None: 

1882 # no previous file exists 

1883 return null 

1884 else: 

1885 # a previous file should exist 

1886 data = self.tmpstore.get(uid) 

1887 # but if it doesn't, don't blow up 

1888 if data is None: 

1889 return null 

1890 

1891 return data 

1892 

1893 

1894class DatePartsWidget(Widget): 

1895 """ 

1896 Renders a set of ``<input type='text'/>`` controls based on the 

1897 year, month, and day parts of the serialization of a 

1898 :class:`colander.Date` object or a string in the format 

1899 ``YYYY-MM-DD``. This widget is usually meant to be used as widget 

1900 which renders a :class:`colander.Date` type; validation 

1901 likely won't work as you expect if you use it against a 

1902 :class:`colander.String` object, but it is possible to use it 

1903 with one if you use a proper validator. 

1904 

1905 **Attributes/Arguments** 

1906 

1907 template 

1908 The template name used to render the input widget. Default: 

1909 ``dateparts``. 

1910 

1911 readonly_template 

1912 The template name used to render the widget in read-only mode. 

1913 Default: ``readonly/dateparts``. 

1914 

1915 assume_y2k 

1916 If a year is provided in 2-digit form, assume it means 

1917 2000+year. Default: ``True``. 

1918 

1919 """ 

1920 

1921 template = "dateparts" 

1922 readonly_template = "readonly/dateparts" 

1923 assume_y2k = True 

1924 

1925 _pstruct_schema = SchemaNode( 

1926 Mapping(), 

1927 SchemaNode(_StrippedString(), name="year"), 

1928 SchemaNode(_StrippedString(), name="month"), 

1929 SchemaNode(_StrippedString(), name="day"), 

1930 ) 

1931 

1932 def serialize(self, field, cstruct, **kw): 

1933 if cstruct is null: 

1934 year = "" 

1935 month = "" 

1936 day = "" 

1937 else: 

1938 year, month, day = cstruct.split("-", 2) 

1939 

1940 kw.setdefault("year", year) 

1941 kw.setdefault("day", day) 

1942 kw.setdefault("month", month) 

1943 

1944 readonly = kw.get("readonly", self.readonly) 

1945 template = readonly and self.readonly_template or self.template 

1946 values = self.get_template_values(field, cstruct, kw) 

1947 return field.renderer(template, **values) 

1948 

1949 def deserialize(self, field, pstruct): 

1950 if pstruct is null: 

1951 return null 

1952 else: 

1953 try: 

1954 validated = self._pstruct_schema.deserialize(pstruct) 

1955 except Invalid as exc: 

1956 raise Invalid(field.schema, text_("Invalid pstruct: %s" % exc)) 

1957 year = validated["year"] 

1958 month = validated["month"] 

1959 day = validated["day"] 

1960 

1961 if not year and not month and not day: 

1962 return null 

1963 

1964 if self.assume_y2k and len(year) == 2: 

1965 year = "20" + year 

1966 result = "-".join([year, month, day]) 

1967 

1968 if not year or not month or not day: 

1969 raise Invalid(field.schema, _("Incomplete date"), result) 

1970 

1971 return result 

1972 

1973 

1974class TextAreaCSVWidget(Widget): 

1975 """ 

1976 Widget used for a sequence of tuples of scalars; allows for 

1977 editing CSV within a text area. Used with a schema node which is 

1978 a sequence of tuples. 

1979 

1980 **Attributes/Arguments** 

1981 

1982 cols 

1983 The size, in columns, of the text input field. Defaults to 

1984 ``None``, meaning that the ``cols`` is not included in the 

1985 widget output (uses browser default cols). 

1986 

1987 rows 

1988 The size, in rows, of the text input field. Defaults to 

1989 ``None``, meaning that the ``rows`` is not included in the 

1990 widget output (uses browser default cols). 

1991 

1992 template 

1993 The template name used to render the widget. Default: 

1994 ``textarea``. 

1995 

1996 readonly_template 

1997 The template name used to render the widget in read-only mode. 

1998 Default: ``readonly/textarea``. 

1999 

2000 delimiter 

2001 The csv module delimiter character. 

2002 Default: ``,``. 

2003 

2004 quotechar 

2005 The csv module quoting character. 

2006 Default: ``"``. 

2007 

2008 quoting 

2009 The csv module quoting dialect. 

2010 Default: ``csv.QUOTE_MINIMAL``. 

2011 """ 

2012 

2013 template = "textarea" 

2014 readonly_template = "readonly/textarea" 

2015 cols = None 

2016 rows = None 

2017 delimiter = "," 

2018 quotechar = '"' 

2019 quoting = csv.QUOTE_MINIMAL 

2020 

2021 def serialize(self, field, cstruct, **kw): 

2022 # XXX make cols and rows overrideable 

2023 if cstruct is null: 

2024 cstruct = [] 

2025 textrows = getattr(field, "unparseable", None) 

2026 if textrows is None: 

2027 outfile = StringIO() 

2028 writer = csv.writer( 

2029 outfile, 

2030 delimiter=self.delimiter, 

2031 quotechar=self.quotechar, 

2032 quoting=self.quoting, 

2033 ) 

2034 writer.writerows(cstruct) 

2035 textrows = outfile.getvalue() 

2036 readonly = kw.get("readonly", self.readonly) 

2037 if readonly: 

2038 template = self.readonly_template 

2039 else: 

2040 template = self.template 

2041 values = self.get_template_values(field, textrows, kw) 

2042 return field.renderer(template, **values) 

2043 

2044 def deserialize(self, field, pstruct): 

2045 if pstruct is null: 

2046 return null 

2047 elif not isinstance(pstruct, string_types): 

2048 raise Invalid(field.schema, "Pstruct is not a string") 

2049 if not pstruct.strip(): 

2050 return null 

2051 try: 

2052 infile = StringIO(pstruct) 

2053 reader = csv.reader( 

2054 infile, 

2055 delimiter=self.delimiter, 

2056 quotechar=self.quotechar, 

2057 quoting=self.quoting, 

2058 ) 

2059 rows = list(reader) 

2060 except Exception as e: 

2061 field.unparseable = pstruct 

2062 raise Invalid(field.schema, str(e)) 

2063 return rows 

2064 

2065 def handle_error(self, field, error): 

2066 msgs = [] 

2067 if error.msg: 

2068 field.error = error 

2069 else: 

2070 for e in error.children: 

2071 msgs.append("line %s: %s" % (e.pos + 1, e)) 

2072 field.error = Invalid(field.schema, "\n".join(msgs)) 

2073 

2074 

2075class TextInputCSVWidget(Widget): 

2076 """ 

2077 Widget used for a tuple of scalars; allows for editing a single 

2078 CSV line within a text input. Used with a schema node which is a 

2079 tuple composed entirely of scalar values (integers, strings, etc). 

2080 

2081 **Attributes/Arguments** 

2082 

2083 template 

2084 The template name used to render the widget. Default: 

2085 ``textinput``. 

2086 

2087 readonly_template 

2088 The template name used to render the widget in read-only mode. 

2089 Default: ``readonly/textinput``. 

2090 

2091 """ 

2092 

2093 template = "textinput" 

2094 readonly_template = "readonly/textinput" 

2095 mask = None 

2096 mask_placeholder = "_" 

2097 

2098 def serialize(self, field, cstruct, **kw): 

2099 # XXX make size and mask overrideable 

2100 if cstruct is null: 

2101 cstruct = "" 

2102 textrow = getattr(field, "unparseable", None) 

2103 if textrow is None: 

2104 outfile = StringIO() 

2105 writer = csv.writer(outfile) 

2106 writer.writerow(cstruct) 

2107 textrow = outfile.getvalue().strip() 

2108 readonly = kw.get("readonly", self.readonly) 

2109 if readonly: 

2110 template = self.readonly_template 

2111 else: 

2112 template = self.template 

2113 values = self.get_template_values(field, textrow, kw) 

2114 return field.renderer(template, **values) 

2115 

2116 def deserialize(self, field, pstruct): 

2117 if pstruct is null: 

2118 return null 

2119 elif not isinstance(pstruct, string_types): 

2120 raise Invalid(field.schema, "Pstruct is not a string") 

2121 if not pstruct.strip(): 

2122 return null 

2123 try: 

2124 infile = StringIO(pstruct) 

2125 reader = csv.reader(infile) 

2126 # row = reader.next() 

2127 row = next(reader) 

2128 except Exception as e: 

2129 field.unparseable = pstruct 

2130 raise Invalid(field.schema, str(e)) 

2131 return row 

2132 

2133 def handle_error(self, field, error): 

2134 msgs = [] 

2135 if error.msg: 

2136 field.error = error 

2137 else: 

2138 for e in error.children: 

2139 msgs.append("%s" % e) 

2140 field.error = Invalid(field.schema, "\n".join(msgs)) 

2141 

2142 

2143class ResourceRegistry(object): 

2144 """A resource registry maps :term:`widget requirement` name/version 

2145 pairs to one or more relative resources. A resource registry can 

2146 be passed to a :class:`deform.Form` constructor; if a resource 

2147 registry is *not* passed to the form constructor, a default 

2148 resource registry is used by that form. The default resource 

2149 registry contains only mappings from requirement names to 

2150 resources required by the built-in Deform widgets (not by any 

2151 add-on widgets). 

2152 

2153 If the ``use_defaults`` flag is True, the default set of Deform 

2154 requirement-to-resource mappings is loaded into the registry. 

2155 Otherwise, the registry is initialized without any mappings. 

2156 """ 

2157 

2158 def __init__(self, use_defaults=True): 

2159 if use_defaults is True: 

2160 self.registry = default_resources.copy() 

2161 else: 

2162 self.registry = {} 

2163 

2164 def set_js_resources(self, requirement, version, *resources): 

2165 """Set the Javascript resources for the requirement/version 

2166 pair, using ``resources`` as the set of relative resource paths.""" 

2167 reqt = self.registry.setdefault(requirement, {}) 

2168 ver = reqt.setdefault(version, {}) 

2169 ver["js"] = resources 

2170 

2171 def set_css_resources(self, requirement, version, *resources): 

2172 """Set the CSS resources for the requirement/version 

2173 pair, using ``resources`` as the set of relative resource paths.""" 

2174 reqt = self.registry.setdefault(requirement, {}) 

2175 ver = reqt.setdefault(version, {}) 

2176 ver["css"] = resources 

2177 

2178 def __call__(self, requirements): 

2179 """Return a dictionary representing the resources required for a 

2180 particular set of requirements (as returned by 

2181 :meth:`deform.Field.get_widget_requirements`). The dictionary will be 

2182 a mapping from resource type (``js`` and ``css`` are both keys in the 

2183 dictionary) to a list of asset specifications paths. Each asset 

2184 specification is a full path to a static resource in the form 

2185 ``package:path``. You can use the paths for each resource type to 

2186 inject CSS and Javascript on-demand into the head of dynamic pages that 

2187 render Deform forms.""" 

2188 result = {"js": [], "css": []} 

2189 for requirement, version in requirements: 

2190 tmp = self.registry.get(requirement) 

2191 if tmp is None: 

2192 raise ValueError( 

2193 "Cannot resolve widget requirement %r" % requirement 

2194 ) 

2195 versioned = tmp.get(version) 

2196 if versioned is None: 

2197 raise ValueError( 

2198 "Cannot resolve widget requirement %r (version %r)" 

2199 % ((requirement, version)) 

2200 ) 

2201 for thing in ("js", "css"): 

2202 sources = versioned.get(thing) 

2203 if sources is None: 

2204 continue 

2205 if isinstance(sources, string_types): 

2206 sources = (sources,) 

2207 for source in sources: 

2208 if source not in result[thing]: 

2209 result[thing].append(source) 

2210 

2211 return result 

2212 

2213 

2214default_resources = { 

2215 "jquery.form": {None: {"js": "deform:static/scripts/jquery.form-3.09.js"}}, 

2216 "jquery.maskedinput": { 

2217 None: {"js": "deform:static/scripts/jquery.maskedinput-1.3.1.min.js"} 

2218 }, 

2219 "jquery.maskMoney": { 

2220 None: {"js": "deform:static/scripts/jquery.maskMoney-3.1.1.min.js"} 

2221 }, 

2222 "deform": { 

2223 None: { 

2224 "js": ( 

2225 "deform:static/scripts/jquery.form-3.09.js", 

2226 "deform:static/scripts/deform.js", 

2227 ) 

2228 } 

2229 }, 

2230 "typeahead": { 

2231 None: { 

2232 "js": "deform:static/scripts/typeahead.min.js", 

2233 "css": "deform:static/css/typeahead.css", 

2234 } 

2235 }, 

2236 "modernizr": { 

2237 None: { 

2238 "js": "deform:static/scripts/modernizr.custom.input-types-and-atts.js" # noQA 

2239 } 

2240 }, 

2241 "pickadate": { 

2242 None: { 

2243 "js": ( 

2244 "deform:static/pickadate/picker.js", 

2245 "deform:static/pickadate/picker.date.js", 

2246 "deform:static/pickadate/picker.time.js", 

2247 "deform:static/pickadate/legacy.js", 

2248 ), 

2249 "css": ( 

2250 "deform:static/pickadate/themes/default.css", 

2251 "deform:static/pickadate/themes/default.date.css", 

2252 "deform:static/pickadate/themes/default.time.css", 

2253 ), 

2254 } 

2255 }, 

2256} 

2257 

2258default_resource_registry = ResourceRegistry()