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.
  • [get | view] (2008-12-28 10:43:23, 22.1 KB) [[attachment:Form-0.10alpha.py]]
  • [get | view] (2010-01-29 18:51:04, 22.7 KB) [[attachment:Form-0.11alpha.py]]
  • [get | view] (2014-06-11 22:22:41, 22.8 KB) [[attachment:Form-0.12alpha.py]]
  • [get | view] (2008-02-04 07:06:07, 20.9 KB) [[attachment:Form-0.8alpha.py]]
  • [get | view] (2008-07-31 14:21:14, 21.8 KB) [[attachment:Form-0.9alpha.py]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.