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