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