Package orm2 :: Package ui :: Package ORMProcedureModule :: Module ORMProcedure
[hide private]
[frames] | no frames]

Source Code for Module orm2.ui.ORMProcedureModule.ORMProcedure

  1  #!/usr/bin/env python 
  2  # -*- coding: iso-8859-1 -*- 
  3   
  4  ##  This file is part of orm, The Object Relational Membrane Version 2. 
  5  ## 
  6  ##  Copyright 2002-2006 by Diedrich Vorberg <diedrich@tux4web.de> 
  7  ## 
  8  ##  All Rights Reserved 
  9  ## 
 10  ##  For more Information on orm see the README file. 
 11  ## 
 12  ##  This program is free software; you can redistribute it and/or modify 
 13  ##  it under the terms of the GNU General Public License as published by 
 14  ##  the Free Software Foundation; either version 2 of the License, or 
 15  ##  (at your option) any later version. 
 16  ## 
 17  ##  This program is distributed in the hope that it will be useful, 
 18  ##  but WITHOUT ANY WARRANTY; without even the implied warranty of 
 19  ##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 20  ##  GNU General Public License for more details. 
 21  ## 
 22  ##  You should have received a copy of the GNU General Public License 
 23  ##  along with this program; if not, write to the Free Software 
 24  ##  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA 
 25  ## 
 26  ##  I have added a copy of the GPL in the file gpl.txt. 
 27  # 
 28  # 
 29  # $Log: ORMProcedure.py,v $ 
 30  # Revision 1.8  2007/04/17 17:14:04  diedrich 
 31  # Added 'form' and 'formdata' to the formdata dict. 
 32  # 
 33  # Revision 1.7  2007/01/26 13:24:38  diedrich 
 34  # Better debug messages. 
 35  # 
 36  # Revision 1.6  2007/01/22 19:44:54  diedrich 
 37  # - ORMProcedure is a PropertyManager now. 
 38  # - Raise an exception if moduel or procedure is None (i.e. there was a syntax error) 
 39  # 
 40  # Revision 1.5  2006/10/10 15:49:14  diedrich 
 41  # - Opt in procedure parameters. (3) Oink! 
 42  # - Fixed datetime handling. OinkOink! 
 43  # 
 44  # Revision 1.4  2006/10/10 15:21:29  diedrich 
 45  # Opt in procedure parameters. (2) 
 46  # 
 47  # Revision 1.3  2006/10/10 15:19:43  diedrich 
 48  # Opt in procedure parameters. 
 49  # 
 50  # Revision 1.2  2006/10/08 15:27:01  diedrich 
 51  # Made security measures work. 
 52  # 
 53  # Revision 1.1  2006/10/07 22:07:34  diedrich 
 54  # Initial commit. 
 55  # 
 56  # 
 57  # 
 58   
 59   
 60   
 61  # Python 
 62  import sys, os, re, imp, urllib 
 63  from string import split, join 
 64  from types import * 
 65  from cStringIO import StringIO 
 66  from traceback import print_tb 
 67   
 68  # Zope 
 69  from Acquisition import Implicit, Acquired 
 70  from Persistence import Persistent 
 71  from AccessControl.Role import RoleManager 
 72  from AccessControl import ClassSecurityInfo 
 73  from OFS.SimpleItem import Item 
 74  from OFS.PropertyManager import PropertyManager 
 75  from Globals import MessageDialog, DevelopmentMode 
 76  from Products.PageTemplates.PageTemplateFile import PageTemplateFile 
 77  from App.Management import Navigation 
 78  from OFS.Cache import Cacheable 
 79  from OFS.Folder import Folder 
 80   
 81  # orm 
 82  from orm2.adapters.pgsql.datasource import zpsycopg_db_conn 
 83  import orm2.debug 
 84  from orm2.ui.procedure import procedure as orm2_ui_procedure 
 85   
 86  if DevelopmentMode: 
 87      orm2.debug.debug.verbose = True 
 88      orm2.debug.sqllog.verbose = True 
 89   
90 -class ORMModeException(Exception): pass
91 -class NoSuch(ORMModeException): pass
92 -class NoSuchProcedure(ORMModeException): pass
93 94 # see __call__ 95 unwanted_request_keys = { 96 'AUTHENTICATION_PATH' : 0, 'BASE1' : 0, 'BASE2' : 0, 'BASE3' : 0, 97 'BASE4' : 0, 'GATEWAY_INTERFACE' : 0, 98 'HTTP_PRAGMA' : 0, 'PARENTS' : 0, 99 'PATH_INFO' : 0, 'PATH_TRANSLATED' : 0, 'PUBLISHED' : 0, 100 'RESPONSE' : 0, 'SCRIPT_NAME' : 0, 101 'SERVER_NAME' : 0, 'SERVER_PORT' : 0, 'SERVER_PROTOCOL' : 0, 102 'SERVER_SOFTWARE' : 0, 'SERVER_URL' : 0, 'SESSION' : 0, 103 'TraversalRequestNameStack' : 0, 'URL' : 0, 'URL1' : 0, 'URL2' : 0, 104 'URL3':0 } 105 106 charset_re=re.compile(r'text/[0-9a-z]+\s*;\s*charset=([-_0-9a-z]+' + 107 r')(?:(?:\s*;)|\Z)', re.IGNORECASE) 108 109 doc_string_param_re=re.compile(r'@param\s*(.*?)\s*:') 110
111 -def manage_addORMProcedure(self, id, 112 model_name, procedures_name, class_name, 113 db_connection_name, session_on=False, 114 REQUEST=None):
115 """ 116 Add an ORMMode Object to the current Zope instance 117 """ 118 id = str(id) 119 model_name = str(model_name) 120 procedures_name = str(procedures_name) 121 class_name = str(class_name) 122 db_connection = str(db_connection_name) 123 124 if str(session_on) == "on": 125 session_on = True 126 else: 127 session_on = False 128 129 obj = ORMProcedure(id, model_name, procedures_name, class_name, 130 db_connection_name, REQUEST) 131 self._setObject(id, obj) 132 133 if REQUEST is not None: 134 #print self, repr(self.absolute_url()) 135 REQUEST.RESPONSE.redirect(self.absolute_url() + "/%s/manage_form"%id) 136 return None
137 # return self.manage_form(self, REQUEST) 138
139 -class module_container(Persistent):
140 - def __init__(self, name):
141 self.module_name = str(name) 142 self.last_error = None
143
144 - def module(self):
145 if not hasattr(self, "_v_module") or DevelopmentMode: 146 imp.acquire_lock() 147 try: 148 parts = split(self.module_name, ".") 149 150 path = None 151 name = [] 152 for package in parts[:-1]: 153 name.append(package) 154 file, filename, description = imp.find_module(package, 155 path) 156 module = imp.load_module(join(name, "."), 157 file, filename, 158 description) 159 path = module.__path__ 160 161 file, filename, description = imp.find_module(parts[-1], 162 path) 163 164 self._v_module = imp.load_module(self.module_name, file, 165 filename, description) 166 167 sys.modules[self.module_name] = self._v_module 168 self.last_error = [] 169 170 except Exception, e: 171 self._v_module = None 172 exception, desc, traceback = sys.exc_info() 173 f = StringIO() 174 print_tb(traceback, file=f) 175 traceback = f.getvalue() 176 177 print >> sys.stderr, "-" * 60 178 print traceback 179 print exception 180 print desc 181 print >> sys.stderr, "-" * 60 182 183 # remove the first two lines of the traceback, which always 184 # point to line 140 above. To debug the import mechanism itself 185 # this must probably be commented out. 186 #lines = split(traceback, "\n") 187 #if len(lines) > 2: 188 # traceback = join(lines[2:], "\n") 189 # lines.append("") 190 191 self.last_error = [ exception, desc, traceback, ] 192 #except: 193 # imp.release_lock() 194 # raise 195 196 imp.release_lock() 197 198 if hasattr(self._v_module, "__reload__"): 199 self._v_module.__reload__() 200 201 if hasattr(self._v_module, "__relationships__"): 202 self._v_module.__relationships__(self._v_module) 203 204 return self._v_module
205 206 207 208
209 - def set_name(self, name):
210 self.module_name = name 211 self.last_error = None 212 213 if hasattr(self, "_v_module"): 214 del self._v_module
215 216 217 manage_addORMProcedureForm = PageTemplateFile( 218 "www/ORMProcedure.pt", globals()) 219
220 -class ORMProcedure(Item, Implicit, Cacheable, Persistent, PropertyManager):
221 """ 222 This object type allows you to call ORM 'mode' functions from within 223 your Zope application. 224 """ 225 226 meta_type = "ORM Procedure" 227 manage_form = PageTemplateFile("www/ORMProcedure.pt", globals()) 228 229 manage_options = ( 230 ( {"label": "Edit", "action": "manage_form"},) + 231 Item.manage_options + 232 Cacheable.manage_options + 233 RoleManager.manage_options + 234 PropertyManager.manage_options ) 235 236 ZopeTime=Acquired 237 HelpSys=Acquired 238 239 security = ClassSecurityInfo() 240
241 - def __init__(self, id, model_name, procedures_name, class_name, 242 db_connection_name, session_on, 243 REQUEST=None):
247 248 security.declareProtected("Change External Methods", 'manage_edit')
249 - def manage_edit(self, model_name, procedures_name, class_name, 250 db_connection_name, session_on, 251 REQUEST=None):
252 """ 253 Modify the ORMMode object. 254 """ 255 256 self._model = module_container(model_name) 257 self._procedures = module_container(procedures_name) 258 259 self._model.module() # To trigger any error message load the module 260 self._procedures.module() 261 262 self._class_name = class_name 263 264 self._db_connection_name = str(db_connection_name) 265 self._session_on = str(session_on) 266 267 if REQUEST is not None: 268 message="ORM Procedure updated." 269 return self.manage_form(self, REQUEST, manage_tabs_message=message)
270 271 272 security.declareProtected("View", 'index_html')
273 - def index_html(self, *args, **kw):
274 """ 275 Call the mode function and do cache management. 276 """ 277 result = self.ZCacheable_get(default=None) 278 279 if result is None: 280 data = self.om_exec(*args, **kw) 281 mime_type = self.REQUEST.RESPONSE.headers.get( 282 "content-type", "text/plain") 283 self.ZCacheable_set(data=(data, mime_type)) 284 else: 285 data, mime_type = result 286 self.REQUEST.RESPONSE.setHeader("Content-Type", mime_type) 287 288 return data
289 290 __call__ = index_html 291 292
293 - def om_exec(self, *args, **kw):
294 formdata = kw 295 296 # figure out which charset was used to post the formdata. 297 # This assumes, that the charset of the last REQUEST is the 298 # same as that of the RESPONSE. 299 # The data is actually sent url encoded and Zope converts it 300 # to a string with a specific charset somewhere. I was unable 301 # to figure out where and this is the best thing I came up 302 # with... :-( 303 304 # from ZPublisher/HTTPResponse.py 305 306 encoding = sys.getdefaultencoding() # reasonable default 307 308 # Try to figure out which encoding the request uses 309 if self.REQUEST.RESPONSE.headers.has_key('content-type'): 310 match = charset_re.match( 311 self.REQUEST.RESPONSE.headers['content-type']) 312 if match: 313 encoding = match.group(1) 314 315 # REQUEST contains tons of stuff that has not been passed by the 316 # browser but which needs to be calculated for each request. 317 # This is sorted out here. 318 for key in self.REQUEST.keys(): 319 if not unwanted_request_keys.has_key(key): 320 value = self.REQUEST[key] 321 322 # convert to Unicode 323 if type(value) == StringType: 324 try: 325 value = unicode(value, encoding) 326 except UnicodeDecodeError: 327 pass 328 329 formdata[key] = value 330 331 ds = self._ds() 332 333 # put together the stuff needed by the mode functions 334 formdata["ds"] = ds 335 formdata["base_url"] = self.absolute_url() 336 formdata["request"] = self.REQUEST 337 formdata["response"] = self.REQUEST.RESPONSE 338 formdata["REQUEST"] = self.REQUEST 339 formdata["RESPONSE"] = self.REQUEST.RESPONSE 340 formdata["context"] = self 341 formdata["formdata"] = formdata.copy() 342 formdata["form"] = formdata["formdata"] 343 344 if self.session_on(): 345 formdata["session"] = self.REQUEST.SESSION 346 347 #if self.mode_function_name(): 348 # mode = self.mode_function_name() 349 #else: 350 351 model = self._model.module() 352 formdata["model"] = model 353 354 procedure_class = self._procedure_class(formdata) 355 356 if procedure_class is None: 357 raise Exception("Illegal method name!") 358 359 procedure_instance = procedure_class(self, formdata) 360 361 if hasattr(procedure_class, "__call_param_names__"): 362 param_names = procedure_class.__call_param_names__ 363 elif procedure_instance.__call__.__doc__ is not None: 364 doc = procedure_instance.__call__.__doc__ 365 param_names = doc_string_param_re.findall(doc) 366 procedure_class.__call_param_names__ = param_names 367 else: 368 param_names = [] 369 370 kw = {} 371 if param_names != []: 372 for param_name in param_names: 373 if formdata.has_key(param_name): 374 kw[param_name] = formdata[param_name] 375 376 try: 377 ret = procedure_instance(**kw) 378 except: 379 if ds is not None: ds.rollback() 380 raise 381 382 # dispose any uncommitted transactions from the current ds 383 # that have not been commited. 384 if ds is not None: ds.rollback() 385 386 return ret
387
388 - def _procedure_class(self, formdata):
389 module = self._procedures.module() 390 if module is None: 391 raise Exception("module is None") 392 393 procedure_class = getattr(module, self._class_name, None) 394 if procedure_class is None: 395 msg = "Procedure_class is None (%s)." % repr(self._class_name) 396 raise Exception(msg) 397 398 return procedure_class
399 400
401 - def session_on(self):
402 """ 403 Return True if this adapter provied the mode functions with a 404 session object 405 """ 406 return self._session_on
407
408 - def model_name(self):
409 """ 410 Accessor. Return the name of the Python module where the 411 mode function resides. 412 """ 413 return self._model.module_name
414
415 - def model_error(self):
416 """ 417 Return the exception text of the last error loading the model module, 418 otherwise an empty string. 419 """ 420 return self._model.last_error
421
422 - def procedures_name(self):
423 """ 424 Accessor. Return the name of the Python module with the 425 procedures in it. 426 """ 427 return self._procedures.module_name
428
429 - def procedures_error(self):
430 """ 431 Return the exception text of the last error loading the procedures 432 module, otherwise an empty string. 433 """ 434 return self._procedures.last_error
435
436 - def class_name(self):
437 """ 438 Return the class name 439 """ 440 return self._class_name
441
442 - def db_connection_name(self):
443 """ 444 Return the name of the databse connection used for this 445 mode. 446 """ 447 return self._db_connection_name
448
449 - def _ds(self):
450 """ 451 Return an ORM datasource object or None if self._db_connection_name 452 is not set. 453 """ 454 if self._db_connection_name: 455 # FIXME: Currently this only works with PostgreSQL. 456 # We need to figure out a way of telling what kind of 457 # database connection we're dealing with so we know what 458 # kind of orm.datasource we need to create. 459 460 ds = zpsycopg_db_conn(self, self.db_connection_name()) 461 return ds 462 else: 463 return None
464 465
466 - def _module_mtime(self, module):
467 """ 468 Return the modification time of the module's source(!) file. 469 The time is returned as the number of seconds since the epoch. 470 """ 471 module_file = module.__file__ 472 473 # look at the .py file instead of .pyc 474 parts = split(module_file, ".") 475 fname = join(parts[:-1], ".") 476 try: 477 py_file = fname + ".py" 478 py_mtime = os.stat(py_file).st_mtime 479 except OSError: 480 py_mtime = 0 481 482 try: 483 pyc_file = fname + ".pyc" 484 pyc_mtime = os.stat(pyc_file).st_mtime 485 except OSError: 486 pyc_mtime = 0 487 488 return max(py_mtime, pyc_mtime)
489
490 - def index_html(self, *args, **kw):
491 "Just call self" 492 return self(*args, **kw)
493
494 - def procedure_url(self, procedure_name, **kw):
495 """ 496 Return an absolute_url to this object + procedure_name + params in kw 497 """ 498 if not hasattr(self._procedures.module(), procedure_name): 499 raise Exception("Illegal procedure name: %s" % procedure_name) 500 501 for name, value in kw.items(): 502 if type(value) == UnicodeType: 503 kw[name] = value.encode() 504 505 params = urllib.urlencode(kw) 506 507 if params: 508 params = "?" + params 509 else: 510 params = "" 511 512 url = "%s/%s%s" % ( self.absolute_url(), procedure_name, params, ) 513 514 return url
515