]>
Commit | Line | Data |
---|---|---|
f33e1d2f MT |
1 | #!/usr/bin/python3 |
2 | ############################################################################### | |
3 | # # | |
4 | # Westferry - The IPFire web user interface # | |
5 | # Copyright (C) 2015 IPFire development team # | |
6 | # # | |
7 | # This program is free software: you can redistribute it and/or modify # | |
8 | # it under the terms of the GNU General Public License as published by # | |
9 | # the Free Software Foundation, either version 3 of the License, or # | |
10 | # (at your option) any later version. # | |
11 | # # | |
12 | # This program is distributed in the hope that it will be useful, # | |
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of # | |
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # | |
15 | # GNU General Public License for more details. # | |
16 | # # | |
17 | # You should have received a copy of the GNU General Public License # | |
18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. # | |
19 | # # | |
20 | ############################################################################### | |
21 | ||
f33e1d2f MT |
22 | from . import base |
23 | ||
208738dd | 24 | import logging |
edf0b75a MT |
25 | log = logging.getLogger(__name__) |
26 | ||
b57004d7 | 27 | class Form(object): |
edf0b75a | 28 | def __init__(self, handler, id, method="POST", action="", |
b57004d7 MT |
29 | form=None, label=None, help=None, disabled=False): |
30 | self.handler = handler | |
edf0b75a | 31 | self.id = id |
b57004d7 MT |
32 | self.method = method |
33 | self.action = action | |
ac38be45 | 34 | self.form = form |
b57004d7 MT |
35 | self.label = label |
36 | self.help = help | |
37 | ||
38 | self.elements = [] | |
ac38be45 MT |
39 | |
40 | # Is this form disabled? | |
b57004d7 | 41 | self.disabled = disabled |
ac38be45 | 42 | |
acef41fe MT |
43 | def __repr__(self): |
44 | return "<%s id=%s>" % (self.__class__.__name__, self.id) | |
45 | ||
ac38be45 MT |
46 | def __iter__(self): |
47 | """ | |
48 | Iterating over a form will iterate over its elements. | |
49 | """ | |
b57004d7 MT |
50 | for element in self.elements: |
51 | yield element | |
ac38be45 | 52 | |
b57004d7 MT |
53 | if isinstance(element, Form): |
54 | for e in element: | |
55 | yield e | |
56 | ||
57 | def export(self): | |
58 | """ | |
59 | Returns a dictionary with all keys and values | |
60 | of the form. | |
61 | """ | |
62 | ret = {} | |
ac38be45 | 63 | |
b57004d7 MT |
64 | for element in self: |
65 | # Skip any sub-form elements | |
66 | if isinstance(element, Form): | |
67 | continue | |
ac38be45 | 68 | |
b57004d7 MT |
69 | ret[element.name] = element.value |
70 | ||
71 | return ret | |
72 | ||
edf0b75a MT |
73 | def execute(self): |
74 | """ | |
75 | Executes the form action | |
76 | """ | |
77 | log.debug("Executing form %s" % self) | |
78 | ||
4d13129f MT |
79 | for element in self: |
80 | log.debug(" Processing element %s" % element) | |
81 | ||
82 | # Call the store callback if available | |
83 | if element.store and callable(element.store): | |
84 | element.store(element.value) | |
edf0b75a | 85 | |
4d13129f MT |
86 | # Otherwise try setting attribute to object |
87 | elif element.object and element.attribute: | |
88 | setattr(element.object, element.attribute, element.value) | |
edf0b75a | 89 | |
4d13129f MT |
90 | else: |
91 | log.warning("Value of %s could not be stored" % element) | |
edf0b75a | 92 | |
b57004d7 | 93 | # Sub-Forms |
ac38be45 | 94 | |
b57004d7 | 95 | def _add_subform(self, cls, *args, **kwargs): |
edf0b75a | 96 | form = cls(self.handler, self.id, *args, form=self, **kwargs) |
b57004d7 | 97 | self.elements.append(form) |
ac38be45 | 98 | |
b57004d7 MT |
99 | return form |
100 | ||
101 | def add_group(self, *args, **kwargs): | |
102 | return self._add_subform(Group, *args, **kwargs) | |
103 | ||
104 | def add_fieldset(self, *args, **kwargs): | |
105 | return self._add_subform(Fieldset, *args, **kwargs) | |
106 | ||
107 | # Elements | |
108 | ||
109 | def _add_element(self, cls, *args, **kwargs): | |
ac38be45 MT |
110 | """ |
111 | Adds a new form element to this form. | |
112 | """ | |
b57004d7 MT |
113 | element = cls(self, *args, **kwargs) |
114 | self.elements.append(element) | |
ac38be45 | 115 | |
b57004d7 | 116 | return element |
ac38be45 MT |
117 | |
118 | def get_element_by_name(self, name): | |
119 | """ | |
120 | Returns the element with the given name (if any). | |
121 | """ | |
b57004d7 MT |
122 | for element in self: |
123 | if element.name == name: | |
124 | return element | |
ac38be45 MT |
125 | |
126 | # Have a shortcut to find elements by their name quickly. | |
b57004d7 | 127 | __getattr__ = get_element_by_name |
ac38be45 MT |
128 | |
129 | def add_text_input(self, *args, **kwargs): | |
130 | """ | |
b57004d7 | 131 | Creates a new text input |
ac38be45 | 132 | """ |
b57004d7 | 133 | return self._add_element(TextInput, *args, **kwargs) |
ac38be45 MT |
134 | |
135 | def add_multiline_text_input(self, *args, **kwargs): | |
136 | """ | |
b57004d7 | 137 | Creates a new multiline text input |
ac38be45 | 138 | """ |
b57004d7 | 139 | return self._add_element(MultilineTextInput, *args, **kwargs) |
ac38be45 MT |
140 | |
141 | def add_password_input(self, *args, **kwargs): | |
142 | """ | |
b57004d7 | 143 | Creates a new password input |
ac38be45 | 144 | """ |
b57004d7 | 145 | return self._add_element(PasswordInput, *args, **kwargs) |
ac38be45 MT |
146 | |
147 | def add_yesno_input(self, *args, **kwargs): | |
148 | """ | |
b57004d7 | 149 | Creates a new yes/no input |
ac38be45 | 150 | """ |
b57004d7 | 151 | return self._add_element(YesNoInput, *args, **kwargs) |
ac38be45 MT |
152 | |
153 | def add_single_choice_input(self, *args, **kwargs): | |
154 | """ | |
b57004d7 | 155 | Creates a new single choice input |
ac38be45 | 156 | """ |
b57004d7 | 157 | return self._add_element(SingleChoiceInput, *args, **kwargs) |
ac38be45 MT |
158 | |
159 | def add_multiple_choice_input(self, *args, **kwargs): | |
160 | """ | |
b57004d7 | 161 | Creates a new multiple choice input |
ac38be45 | 162 | """ |
b57004d7 | 163 | return self._add_element(MultipleChoiceInput, *args, **kwargs) |
ac38be45 MT |
164 | |
165 | ||
b57004d7 MT |
166 | class Fieldset(Form): |
167 | pass | |
ac38be45 MT |
168 | |
169 | ||
170 | class FormFieldsetModule(base.BaseUIModule): | |
171 | def render(self, fieldset): | |
172 | return self.render_string("modules/forms/fieldset.html", fs=fieldset) | |
173 | ||
174 | ||
b57004d7 MT |
175 | class Group(Form): |
176 | pass | |
ac38be45 MT |
177 | |
178 | ||
179 | class Element(object): | |
180 | # The element type | |
181 | type = None | |
182 | ||
b57004d7 | 183 | def __init__(self, form, name=None, label=None, default=None, |
4d13129f | 184 | disabled=False, readonly=False, object=None, attribute=None, store=None, **kwargs): |
ac38be45 MT |
185 | assert self.type |
186 | ||
187 | self.form = form | |
188 | self.name = name | |
189 | self.label = label | |
190 | self.default = default | |
191 | ||
192 | # Is this element editable? | |
b57004d7 MT |
193 | self.disabled = disabled |
194 | self.readonly = readonly | |
ac38be45 | 195 | |
4d13129f MT |
196 | # Object & Attribute |
197 | self.object = object | |
198 | self.attribute = attribute or name | |
199 | ||
200 | # Import default value from object and attribute if none set | |
201 | if self.default is None and self.object and self.attribute: | |
202 | self.default = getattr(self.object, self.attribute) | |
203 | ||
204 | # Callbacks | |
205 | self.store = store | |
206 | ||
ac38be45 MT |
207 | # Help text |
208 | self.help = None | |
209 | ||
b57004d7 MT |
210 | # Call initializer for inheriting classes |
211 | self.initialize(**kwargs) | |
ac38be45 | 212 | |
acef41fe MT |
213 | def __repr__(self): |
214 | return "<%s name=\"%s\">" % (self.__class__.__name__, self.name) | |
215 | ||
b57004d7 | 216 | def initialize(self): |
ac38be45 MT |
217 | pass |
218 | ||
219 | @property | |
220 | def value(self): | |
221 | """ | |
222 | Returns the value that the user entered for this element. | |
223 | """ | |
b57004d7 | 224 | return self.form.handler.get_argument(self.name, self.default) |
ac38be45 MT |
225 | |
226 | ||
227 | class SingleChoiceInput(Element): | |
228 | type = "single-choice" | |
229 | ||
230 | ||
231 | class MultipleChoiceInput(Element): | |
232 | type = "multiple-choice" | |
233 | ||
cdcc050f | 234 | |
f33e1d2f MT |
235 | class FormModule(base.BaseUIModule): |
236 | def render(self, form): | |
b57004d7 | 237 | return self.render_string("modules/forms/index.html", form=form) |
f33e1d2f MT |
238 | |
239 | ||
1b19941d MT |
240 | class FormElementsModule(base.BaseUIModule): |
241 | def render(self, elements): | |
242 | return self.render_string("modules/forms/elements.html", elements=elements) | |
f33e1d2f MT |
243 | |
244 | ||
ac38be45 MT |
245 | class TextInput(Element): |
246 | type = "text" | |
247 | ||
b57004d7 MT |
248 | def initialize(self, prefix=None, suffix=None, placeholder=None): |
249 | self.prefix = prefix | |
250 | self.suffix = suffix | |
ac38be45 | 251 | |
b57004d7 | 252 | self.placeholder = placeholder or self.label |
f33e1d2f MT |
253 | |
254 | ||
f33e1d2f MT |
255 | class TextInputModule(base.BaseUIModule): |
256 | def render(self, input): | |
ac38be45 | 257 | assert isinstance(input, TextInput) |
f33e1d2f MT |
258 | |
259 | return self.render_string("modules/forms/inputs/text.html", input=input) | |
260 | ||
261 | ||
ac38be45 MT |
262 | class PasswordInput(TextInput): |
263 | type = "password" | |
264 | ||
265 | ||
266 | class MultilineTextInput(TextInput): | |
267 | type = "multiline-text" | |
268 | ||
b57004d7 MT |
269 | DEFAULT_LINES = 3 |
270 | ||
271 | def initialize(self, lines=DEFAULT_LINES, **kwargs): | |
272 | super().initialize(**kwargs) | |
273 | ||
274 | self.lines = lines | |
ac38be45 | 275 | |
ac38be45 MT |
276 | |
277 | ||
f33e1d2f MT |
278 | class MultilineTextInputModule(base.BaseUIModule): |
279 | def render(self, input): | |
ac38be45 | 280 | assert isinstance(input, MultilineTextInput) |
f33e1d2f MT |
281 | |
282 | return self.render_string("modules/forms/inputs/textarea.html", input=input) | |
283 | ||
284 | ||
ac38be45 MT |
285 | class YesNoInput(Element): |
286 | type = "yesno" | |
287 | ||
b57004d7 MT |
288 | def initialize(self, description=None): |
289 | self.description = description | |
ac38be45 MT |
290 | |
291 | def get_value(self): | |
292 | value = Element.get_value(self) | |
293 | ||
294 | # This only returns true or false | |
295 | if value == "on": | |
296 | return True | |
297 | ||
298 | return False | |
299 | ||
300 | ||
f33e1d2f MT |
301 | class YesNoInputModule(base.BaseUIModule): |
302 | def render(self, input): | |
ac38be45 | 303 | assert isinstance(input, YesNoInput) |
f33e1d2f MT |
304 | |
305 | return self.render_string("modules/forms/inputs/checkbox.html", input=input) |