Attachment 'Form-0.12alpha.py'
Download 1 # -*- coding: iso-8859-1 -*-
2 """
3 Form.py - MoinMoin macro to display database based forms
4
5 @copyright: 2008 Wolfgang Fischer
6 @license: GNU GPL, see COPYING for details.
7
8 Information on prerequisites, installation and usage can be found at
9 http://moinmo.in/MacroMarket/Form
10
11 2008-07-29 RudolfReuter, Version 0.9alpha because of moin-1.7.1 changes
12 Attention: Python >= 2.5 needed for SQLite access
13 debug help for tracing, insert the following line
14 import pdb; pdb.set_trace() #debug
15 changes in moin-1.7.1:
16 1. MoinMoin/wikidicts.py - Function get_dict was dropped
17 2. MoinMoin/wikiutil.py - Function renderText, 4th parameter dropped
18 Added ".type" check in first Form element. 2008-07-31
19 2008-12-28 RudolfReuter, Version 0.10alpha because of moin-1.8.1 changes
20 added dict check xxx/FormsDict for Linux version at #Get form dict
21 2010-01-28 RudolfReuter, adopted to 1.9.1 (Werkzeug), version 0.11alpha
22 2014-06-11 TestTest, adopted to 1.9.7, version 0.12alpha
23 """
24 Dependencies = ['external'] # Can't be cached as it depends on external data
25
26 def execute(macro, args):
27 args = [ arg.strip() for arg in args.split(',') ]
28 request = macro.request
29 if not hasattr(request, 'form_macro_form'):
30 request.form_macro_form = Form(request)
31 return request.form_macro_form.render(args)
32
33
34 class Form:
35 import datetime
36 from MoinMoin import wikiutil
37
38 # fake _ function to get gettext recognize those texts:
39 _ = lambda x: x
40 ui_text = {
41 # TODO: Button labels should be defined here
42 }
43 del _
44
45 datetime_types = [ datetime.datetime, datetime.date, datetime.time ]
46
47
48 def __init__(self, request):
49 self.request = request
50 self.formatter = request.formatter
51 self.post_args = request.values
52 self.date_format = request.user.date_fmt or request.cfg.date_fmt
53 self.datetime_format = request.user.datetime_fmt or request.cfg.datetime_fmt
54 today = self.datetime.date.today()
55 self.variables = {
56 'PAGE': request.page.page_name,
57 'NOW' : self.datetime.datetime.now(),
58 'TODAY' : today,
59 'TODAY_DATETIME' : self.datetime.datetime.fromordinal(today.toordinal()),
60 'ME': request.user.name,
61 }
62 self.forms = {}
63 self.dict_name_stack = []
64 self.last_form_key = ''
65
66 from MoinMoin import version
67 self.is_moin_1_7 = version.release.startswith('1.7')
68 self.is_moin_1_8 = version.release.startswith('1.8')
69 self.is_moin_1_9 = version.release.startswith('1.9')
70
71 # Extract information on post action
72 self.action = u''
73 self.action_form = u''
74 self.action_index = 0
75 for k in self.post_args.keys():
76 if k.startswith('_Action_') and len(k) > 18:
77 self.action = k[8:11]
78 self.action_index = int(k[12:16])
79 self.action_form = k[17:]
80
81
82
83 # =============================
84 # Render form elements
85 # =============================
86
87 def render(self, args):
88 if not args:
89 return self.error_message('Missing arguments')
90 element = args[0].lower()
91 if element == 'start':
92 return self.render_start()
93 elif element == 'end':
94 return self.render_end()
95 request = self.request
96 post_args = self.post_args
97
98 if len(args) > 1: # key specified
99 key = self.key = args[1]
100 else:
101 key = u''
102 self.dict_name, self.form_name, self.field_name = self.parse_element_key(key, element)
103 dict_name = self.dict_name
104 form_name = self.form_name
105 self.form_key = form_key = u'%s.%s' % (dict_name, form_name)
106 self.last_form_key = self.form_key
107 if form_key not in self.forms: # First element of this form
108 form = self.form = self.forms[form_key] = {}
109 form['form_name'] = form_name
110 form['dict_name'] = dict_name
111 # Get form dict
112 if self.is_moin_1_7 or self.is_moin_1_8:
113 if not request.dicts.has_dict(dict_name): # fix for Linux, RudolfReuter 2008-12-28
114 request.dicts.adddict(request, dict_name)
115
116 if self.is_moin_1_7 or self.is_moin_1_8:
117 form_dict_obj = request.dicts.dict(dict_name)
118 elif self.is_moin_1_9:
119 form_dict_obj = request.dicts[dict_name]
120 #if not hasattr(form_dict_obj, 'get_dict'): # 'get_dict' was dropped in moin-1.7.1
121 # form['dict'] = { }
122 # return self.error_message('The form dictionary "%s" does not exist or is not valid' % dict_name)
123 if not form_dict_obj.has_key('.type'):
124 form['dict'] = { }
125 return self.error_message('The form dictionary "%s" does not exist or is not valid' % dict_name)
126 #form['dict'] = form_dict_obj.get_dict()
127 form['dict'] = form_dict_obj
128 form['addonly'] = self.get_stripped_from_form_dict('addonly') == u'1'
129 # Connect to data source
130 error = self.connect()
131 if error:
132 return self.error_message(error)
133
134 # Fetch data
135 error = self.fetch_data()
136 if error:
137 return self.error_message(error)
138
139 if form_key == self.action_form:
140 # Do form action
141 self.do_action()
142 # Fetch updated data
143 self.fetch_data()
144 # Close connection
145 self.connection.close()
146
147 records = form['records']
148 form['add_mode'] = (post_args.has_key('_Action_New_0000_%s' % form_key) or not records) and self.get_stripped_from_form_dict('insert')
149 # Set index of current record
150 self.set_record_index()
151 # Add record values to variables dict
152 # if form_key not in post_args.get('form', []):
153 index = form['index']
154 if 0 < index <= len(records):
155 record = records[index-1]
156 else:
157 record = value = None
158 for (field, field_data) in form['fields'].iteritems():
159 field_type = field_data[1]
160 if record:
161 value = record[field_data[0]]
162 if field_type not in self.datetime_types and field_type:
163 value = field_type(value)
164 self.variables['%s.%s' % (form_key, field)] = value
165
166 self.form = self.forms[form_key]
167
168 if element == 'form':
169 return self.render_form()
170 elif element == 'buttons':
171 return self.render_buttons()
172 elif element == 'navigation':
173 return self.render_navigation()
174 elif element == 'filter':
175 return self.render_filter()
176 elif element == 'next':
177 return self.render_next()
178 else:
179 return self.render_field(element, args)
180
181
182 def render_form(self):
183 # TODO: handle recursion
184 # TODO: handle form body doesn't exist
185 form = self.form
186 request = self.request
187 form_text = [ self.get_stripped_from_form_dict('header').replace('\\n', '\n') % self.variables ]
188 form_body = self.get_stripped_from_form_dict('body').replace('\\n', '\n') % self.variables
189 if self.get_stripped_from_form_dict('all'):
190 records = form['records']
191 count = [len(records), len(records) + 1][self.get_stripped_from_form_dict('insert') != u'']
192 for i in range(count):
193 form_text.append(form_body)
194 else:
195 form_text.append(form_body)
196 form_text.append(self.get_stripped_from_form_dict('footer').replace('\\n', '\n') % self.variables)
197 # Load the parser
198 Parser = self.wikiutil.searchAndImportPlugin(request.cfg, "parser", 'wiki')
199 self.dict_name_stack.append(self.dict_name)
200 # change in moin-1.7.1 MoinMoin/wikiutil.py line 2476, 4th parameter dropped
201 #result = self.wikiutil.renderText(request, Parser, u''.join(form_text), line_anchors=False)
202 result = self.wikiutil.renderText(request, Parser, u''.join(form_text))
203 self.dict_name_stack.pop()
204 return result
205
206
207 def render_start(self):
208 return self.formatter.rawHTML('<span><form action="" method="POST" enctype="multipart/form-data">')
209
210
211 def render_end(self):
212 return self.formatter.rawHTML('</form></span>')
213
214
215 def render_next(self):
216 form = self.form
217 form['index'] += 1
218 form['add_mode'] = form['index'] > len(form['records'])
219 return u''
220
221
222 def render_field(self, element, args):
223 form = self.form
224 field_name = self.field_name
225 field_key = '%s.%s' % (self.form_key, field_name)
226 value = self.wikiutil.escape(self.get_field_value(self.form_key, field_name, field_key) or '', 1)
227
228 if element == 'value':
229 return self.formatter.rawHTML(u'%s<input type="hidden" name="%s" value="%s">' % (value, field_key, value))
230
231 elif element == 'textarea':
232 if len(args) > 2:
233 cols = args[2]
234 else:
235 cols = u''
236 if len(args) > 3:
237 rows = args[3]
238 else:
239 rows = u''
240 return self.formatter.rawHTML(u'<textarea name="%s" cols="%s" rows="%s">%s</textarea>' % (field_key, cols, rows, value))
241
242 elif element == 'hidden':
243 return self.formatter.rawHTML(u'<input type="hidden" name="%s" value="%s">' % (field_key, value))
244
245 else:
246 if len(args) > 2:
247 size = args[2]
248 else:
249 size = u''
250 return self.formatter.rawHTML(u'<input type="text" name="%s" size="%s" value="%s">' % (field_key, size, value))
251
252
253 def get_field_value(self, form_key, field_name, field_key, for_action = 0):
254 if field_name.startswith('@'):
255 value = self.post_args.get(field_key, None)
256 elif not for_action:
257 form = self.forms[form_key]
258 index = form.get('index', 0)
259 records = form.get('records', [])
260 if index > len(records):
261 value = None
262 else:
263 value = records[index-1][form['fields'][field_name][0]]
264 else:
265 if form_key == self.action_form:
266 index = self.action_index
267 else:
268 index = 0
269 value = self.post_args.getlist(field_key)[index]
270 form = self.forms[form_key]
271 field_type = form['fields'][field_name][1]
272 if field_type:
273 value = field_type(value)
274 if value == '':
275 value = None
276 return value
277
278
279 def render_navigation(self):
280 form = self.form
281 form_key = self.form_key
282 return self.formatter.rawHTML("""
283 <input type="submit" name="First_%s" value="|<">
284 <input type="submit" name="Previous_%s" value="<">
285 <input type="text" name="GoTo_%s" size="3" value="%s">
286 <input type="submit" name="Next_%s" value=">">
287 <input type="submit" name="Last_%s" value=">|"> of %s
288 """ % (form_key, form_key, form_key, str(form['index']), form_key, form_key, len(form['records'])))
289
290
291 def render_filter(self):
292 formatter = self.formatter
293 return formatter.rawHTML('<input type="submit" name="_Action_App_0000" value="Filter"><input type="reset">')
294
295
296 def render_buttons(self):
297 formatter = self.formatter
298 if self.get_stripped_from_form_dict('all'):
299 action_index = self.form['index'] - 1
300 else:
301 action_index = 0
302 action_key = ('0000%i_' % action_index)[-5:]
303 action_key += self.form_key
304 result = []
305 # if self.form['unbound']:
306 # result.append(formatter.rawHTML('<input type="submit" name="_Action_App_%s" value="Filter"><input type="reset">' % (action_key)))
307 if self.form['add_mode']:
308 result.append(formatter.rawHTML('<input type="submit" name="_Action_Add_%s" value="Add"><input type="submit" name="_Action_Can_%s" value="Cancel">' % (action_key, action_key)))
309 else:
310 if self.get_stripped_from_form_dict('update'):
311 result.append(formatter.rawHTML('<input type="submit" name="_Action_Sav_%s" value="Save">' % action_key))
312 if self.get_stripped_from_form_dict('delete'):
313 result.append(formatter.rawHTML('<input type="submit" name="_Action_Del_%s" value="Delete">' % action_key))
314 if self.get_stripped_from_form_dict('insert') and not self.get_stripped_from_form_dict('all'):
315 result.append(formatter.rawHTML('<input type="submit" name="_Action_New_%s" value="New">' % action_key))
316 return u''.join(result)
317
318
319
320 # =============================
321 # DataBase communication
322 # =============================
323
324 def fetch_data(self):
325 select_query = self.get_stripped_from_form_dict('select')
326 if select_query:
327 parameters = self.get_query_parameters('select')
328 else:
329 return 'The form "%s" does not contain a select query' % self.form_key
330 cursor = self.connection.cursor()
331 cursor.execute(select_query, parameters)
332 description = cursor.description
333 form = self.form
334 form['fields'] = dict([(description[i][0], [i, description[i][1]]) for i in range(len(description))])
335 form['records'] = [cursor.fetchall(), []][form['addonly']]
336 cursor.close()
337 return None
338
339
340 def set_record_index(self):
341 form = self.form
342 form_key = self.form_key
343 post_args = self.post_args
344 records = form['records']
345 # Calculate index of current record (first record is 1)
346 if form['add_mode']:
347 index = len(records) + 1
348 elif 'GoTo_%s' % form_key in post_args:
349 index = int(post_args['GoTo_%s' % form_key][0])
350 if 'First_%s' % form_key in post_args:
351 index = 1
352 elif 'Previous_%s' % form_key in post_args:
353 index -= 1
354 elif 'Next_%s' % form_key in post_args:
355 index +=1
356 elif 'Last_%s' % form_key in post_args:
357 index = len(records)
358 if index < 1:
359 index = 1
360 elif index > len(records):
361 index = len(records)
362 else:
363 goto_fields = self.get_stripped_from_form_dict('goto_fields')
364 if goto_fields:
365 fields = form['fields']
366 goto_fields = [ field.strip() for field in goto_fields.split(',') ]
367 variables = self.variables
368 def get_value(value, type, variables):
369 value = value.strip().strip('"\'')
370 if value in variables:
371 return variables[value]
372 else:
373 value = value % variables
374 if type == self.datetime.datetime:
375 return self.datetime.datetime.strptime(value, self.datetime_format)
376 elif type == self.datetime.date:
377 return self.datetime.datetime.strptime(value, self.date_format)
378 return type(value)
379 goto_values_list = self.get_stripped_from_form_dict('goto_values').split(',')
380 goto_values = [ (get_value(goto_values_list[i], fields[goto_fields[i]][1], variables), fields[goto_fields[i]][0]) for i in range(len(goto_values_list)) ]
381 index = 0
382 for record_index in range(len(records)):
383 equal = True
384 for value in goto_values:
385 equal = value[0] == records[record_index][value[1]]
386 if not equal:
387 break
388 if equal:
389 index = record_index + 1
390 break
391 if not index: # No matching record found
392 index = 1 # Set to first record (default)
393 else:
394 index = 1 # Set to first record (default)
395 form['index'] = index
396
397
398 def do_action(self):
399 cursor = self.connection.cursor()
400 action = self.action
401 action_query_map = {'Add' : 'insert', 'Sav' : 'update', 'Del' : 'delete'}
402 if action in action_query_map:
403 query_type =action_query_map[action]
404 query = self.get_stripped_from_form_dict(query_type)
405 if query:
406 parameters = self.get_query_parameters(query_type, for_action = 1)
407 count = cursor.execute(query, parameters)
408 self.connection.commit()
409 cursor.close()
410
411
412 def connect(self):
413 dict_name, self.connection_name = self.parse_connection_key(self.get_stripped_from_form_dict('connection'))
414 if self.is_moin_1_7 or self.is_moin_1_8:
415 connection_dict_obj = self.request.dicts.dict(dict_name)
416 elif self.is_moin_1_9:
417 connection_dict_obj = self.request.dicts[dict_name]
418 #if hasattr(connection_dict_obj, 'get_dict'): # 'get_dict' was dropped in moin-1.7.1
419 self.connection_dict = connection_dict = connection_dict_obj
420 #else:
421 #return 'The connection dictionary "%s" does not exist or is not valid' % dict_name
422
423 connection_name_prefix = '%s.' % self.connection_name
424 connection_type = self.get_stripped_from_connection_dict('type').lower()
425
426 if connection_type == 'sqlite':
427 import sqlite3
428 connection_string = self.get_stripped_from_connection_dict('file')
429 if connection_string.startswith('attachment:'):
430 from MoinMoin.action import AttachFile
431 pagename, filename = AttachFile.absoluteName(connection_string[11:], dict_name)
432 connection_string = AttachFile.getFilename(self.request, pagename, filename)
433 self.connection = sqlite3.connect(connection_string)
434
435 elif connection_type == 'odbc':
436 import pyodbc
437 connection_string = self.get_stripped_from_connection_dict('connection_string')
438 self.connection = pyodbc.connect(connection_string)
439
440 elif connection_type == 'mysql':
441 import MySQLdb
442 port = connection_dict.get('port', '3306').strip('`')
443 self.connection = MySQLdb.connect(host = self.get_stripped_from_connection_dict('server'), port = port, user = self.get_stripped_from_connection_dict('uid'), passwd = self.get_stripped_from_connection_dict('pwd'),db = self.get_stripped_from_connection_dict('database'))
444
445 elif connection_type == 'oracle':
446 import cx_Oracle
447 if 'port' in parameters:
448 port = parameters['%sport' % connection_name_prefix]
449 else:
450 port = 1521
451 self.connection = cx_Oracle.connect(self.get_stripped_from_connection_dict('uid'), self.get_stripped_from_connection_dict('pwd'), cx_Oracle.makedsn(self.get_stripped_from_connection_dict('server'), port, self.get_stripped_from_connection_dict('database')))
452
453 else:
454 return 'Connection type "%s" specified in "%s" is unknown' % (connection_type, dict_name)
455
456 return None
457
458
459
460 # =============================
461 # Handling Form Dictionaries
462 # =============================
463
464 def parse_element_key(self, key, element):
465 key_parts = key.split('.')
466 if element in ['form', 'buttons', 'navigation', 'filter', 'next']:
467 key_parts.extend([u'', u'', u''])
468 else:
469 if len(key_parts) == 2:
470 key_parts.insert(0, u'')
471 elif len(key_parts) < 2:
472 pass # TODO: raise error - field not specified correctly
473 dict_name, form_name, field_name = key_parts[:3]
474 if not (form_name or dict_name):
475 if self.last_form_key:
476 form_key = self.last_form_key
477 form = self.forms[form_key]
478 dict_name, form_name = form['dict_name'], form['form_name']
479 else:
480 pass # TODO: raise error - unspecified form
481 elif not dict_name:
482 if self.dict_name_stack:
483 dict_name = self.dict_name_stack[-1]
484 else:
485 dict_name = self.request.page.page_name
486 return dict_name, form_name, field_name
487
488
489 def parse_connection_key(self, key):
490 key_parts = key.split('.')
491 key_parts.extend([u'', u''])
492 dict_name, connection_name = key_parts[:2]
493 if not dict_name:
494 dict_name = self.dict_name
495 return dict_name, connection_name
496
497
498 def get_query_parameters(self, key, for_action = 0):
499 # In this order:
500 # 1. variables values
501 # 2. form values (if not current)
502 # 3. post values
503 form_key = self.form_key
504 variables = self.variables
505 result = []
506 parameters = self.get_stripped_from_form_dict('%s_parameters' % key)
507 if parameters:
508 for par in parameters.split(','):
509 par = par.strip()
510 if variables.has_key(par):
511 value = variables[par]
512 else:
513 key_parts = par.strip().split('.')
514 if len(key_parts) == 2:
515 key_parts.insert(0, u'')
516 elif len(key_parts) < 2:
517 pass # TODO: raise error - parameter not specified correctly
518 dict_name, form_name, field_name = key_parts[:3]
519 if not (form_name or dict_name):
520 dict_name, form_name = self.dict_name, self.form_name
521 elif not dict_name:
522 dict_name = self.dict_name
523 parameter_form_key = u'%s.%s' % (dict_name, form_name)
524 parameter_key = u'%s.%s' % (parameter_form_key, field_name)
525 if self.forms.has_key(parameter_form_key):
526 value = self.get_field_value(parameter_form_key, field_name, parameter_key, for_action = for_action)
527 result.append(value)
528 return result
529
530
531
532 def get_stripped_from_form_dict(self, key):
533 return self.form['dict'].get('%s.%s' % (self.form_name, key), u'').strip('`')
534
535
536 def get_stripped_from_connection_dict(self, key):
537 return self.connection_dict.get('%s.%s' % (self.connection_name, key), u'').strip('`')
538
539
540
541 # =============================
542 # Error Message
543 # =============================
544
545 def error_message(self, message):
546 formatter = self.formatter
547 result = [ '', formatter.preformatted(True) ]
548 message = self.wikiutil.escape(message, 1)
549 result.append(formatter.rawHTML("Form macro error: %s" % message))
550 result.append(formatter.preformatted(False))
551 return u'\n'.join(result)
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.