1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
import traceback
from lektor_ng.constants import PRIMARY_ALT
from lektor_ng.environment.expressions import Expression, FormatExpression
from lektor_ng.i18n import get_i18n_block
from lektor_ng.types.base import Type
def _reflow_and_split_labels(labels):
rv = []
for lang, string in labels.items():
for idx, item in enumerate(string.split(",")):
try:
d = rv[idx]
except LookupError:
d = {}
rv.append(d)
d[lang] = item.strip()
return rv
def _parse_choices(options):
s = options.get("choices")
if not s:
return None
choices = []
items = s.split(",")
user_labels = get_i18n_block(options, "choice_labels")
implied_labels = []
for item in items:
if "=" in item:
choice, value = item.split("=", 1)
choice = choice.strip()
if choice.isdigit():
choice = int(choice)
implied_labels.append(value.strip())
choices.append(choice)
else:
choices.append(item.strip())
implied_labels.append(item.strip())
if user_labels:
rv = list(zip(choices, _reflow_and_split_labels(user_labels), strict=False))
else:
rv = [(key, {"en": label}) for key, label in zip(choices, implied_labels, strict=False)]
return rv
class ChoiceSource:
def __init__(self, env, options):
source = options.get("source")
if source is not None:
self.source = Expression(env, source)
self.choices = None
item_key = options.get("item_key") or "{{ this._id }}"
item_label = options.get("item_label")
else:
self.source = None
self.choices = _parse_choices(options)
item_key = options.get("item_key") or "{{ this.0 }}"
item_label = options.get("item_label")
self.item_key = FormatExpression(env, item_key)
if item_label is not None:
item_label = FormatExpression(env, item_label)
self.item_label = item_label
@property
def has_choices(self):
return self.source is not None or self.choices is not None
def iter_choices(self, pad, record=None, alt=PRIMARY_ALT):
values = {}
if record is not None:
values["record"] = record
if self.choices is not None:
iterable = self.choices
else:
try:
iterable = self.source.evaluate(pad, alt=alt, values=values)
except Exception:
traceback.print_exc()
iterable = ()
for item in iterable or ():
key = self.item_key.evaluate(pad, this=item, alt=alt, values=values)
# If there is a label expression, use it. Since in that case
# we only have one language to fill in, we fill it in for the
# default language
if self.item_label is not None:
label = {"en": self.item_label.evaluate(pad, this=item, alt=alt, values=values)}
# Otherwise we create a proper internationalized key out of
# our target label
else:
if isinstance(item, (tuple, list)) and len(item) == 2:
label = item[1]
elif hasattr(item, "get_record_label_i18n"):
label = item.get_record_label_i18n()
else:
label = {"en": item["_id"]}
yield key, label
class MultiType(Type):
def __init__(self, env, options):
Type.__init__(self, env, options)
self.source = ChoiceSource(env, options)
def get_labels(self, pad, record=None, alt=PRIMARY_ALT):
return dict(self.source.iter_choices(pad, record, alt))
def to_json(self, pad, record=None, alt=PRIMARY_ALT):
rv = Type.to_json(self, pad, record, alt)
if self.source.has_choices:
rv["choices"] = list(self.source.iter_choices(pad, record, alt))
return rv
class SelectType(MultiType):
widget = "select"
def value_from_raw(self, raw):
if raw.value is None:
return raw.missing_value("Missing select value")
return raw.value
class CheckboxesType(MultiType):
widget = "checkboxes"
def value_from_raw(self, raw):
rv = [x.strip() for x in (raw.value or "").split(",")]
if rv == [""]:
rv = []
return rv
|