# ------------------------------------------------------------------------------ import re, os, os.path, base64, urllib from appy.shared import utils as sutils # Function for creating a Zope object ------------------------------------------ def createObject(folder, id, className, appName, wf=True, noSecurity=False): '''Creates, in p_folder, object with some p_id. Object will be an instance of p_className from application p_appName. In a very special case (the creation of the config object), computing workflow-related info is not possible at this time. This is why this function can be called with p_wf=False.''' exec 'from Products.%s.%s import %s as ZopeClass' % \ (appName, className, className) # Get the tool. It may not be present yet, maybe are we creating it now. if folder.meta_type.endswith('Folder'): # p_folder is a standard Zope (temp) folder. tool = getattr(folder, 'config', None) else: # p_folder is an instance of a gen-class. tool = folder.getTool() # Get the currently logged user user = None if tool: user = tool.getUser() # Checks whether the user an create this object if security is enabled. if not noSecurity: klass = ZopeClass.wrapperClass.__bases__[-1] if not tool.userMayCreate(klass): from AccessControl import Unauthorized raise Unauthorized("User can't create instances of %s" % \ klass.__name__) # Create the object obj = ZopeClass(id) folder._objects = folder._objects + ({'id':id, 'meta_type':className},) folder._setOb(id, obj) obj = folder._getOb(id) # Important. Else, obj is not really in the folder. obj.portal_type = className obj.id = id obj._at_uid = id # If no user object is there, we are at startup, before default User # instances are created. userId = user and user.login or 'system' obj.creator = userId from DateTime import DateTime obj.created = DateTime() obj.modified = obj.created from persistent.mapping import PersistentMapping obj.__ac_local_roles__ = PersistentMapping({ userId: ['Owner'] }) if wf: obj.initializeWorkflow() return obj # ------------------------------------------------------------------------------ upperLetter = re.compile('[A-Z]') def produceNiceMessage(msg): '''Transforms p_msg into a nice msg.''' res = '' if msg: res = msg[0].upper() for c in msg[1:]: if c == '_': res += ' ' elif upperLetter.match(c): res += ' ' + c.lower() else: res += c return res # ------------------------------------------------------------------------------ class SomeObjects: '''Represents a bunch of objects retrieved from a reference or a query in the catalog.''' def __init__(self, objects=None, batchSize=None, startNumber=0, noSecurity=False): self.objects = objects or [] # The objects self.totalNumber = len(self.objects) # self.objects may only represent a # part of all available objects. self.batchSize = batchSize or self.totalNumber # The max length of # self.objects. self.startNumber = startNumber # The index of first object in # self.objects in the whole list. self.noSecurity = noSecurity def brainsToObjects(self): '''self.objects has been populated from brains from the catalog, not from True objects. This method turns them (or some of them depending on batchSize and startNumber) into real objects. If self.noSecurity is True, it gets the objects even if the logged user does not have the right to get them.''' start = self.startNumber brains = self.objects[start:start + self.batchSize] if self.noSecurity: getMethod = '_unrestrictedGetObject' else: getMethod = 'getObject' self.objects = [getattr(b, getMethod)() for b in brains] # ------------------------------------------------------------------------------ def splitIntoWords(text, ignore=2): '''Split the cleaned index value p_text into words (returns a list of words). Words whose length is below p_ignore are ignored, excepted digits which are always kept. Duplicate words are removed (result is a set and not a list).''' # Split p_text into words res = text.split() # Remove shorter words not being figures i = len(res) - 1 while i > -1: if (len(res[i]) <= ignore) and not res[i].isdigit(): del res[i] i -= 1 # Remove duplicates return set(res) # ------------------------------------------------------------------------------ class Keywords: '''This class allows to handle keywords that a user enters and that will be used as basis for performing requests in a TextIndex/XhtmlIndex.''' toRemove = '?-+*()' def __init__(self, keywords, operator='AND', ignore=2): # Clean the p_keywords that the user has entered words = sutils.normalizeText(keywords) if words == '*': words = '' for c in self.toRemove: words = words.replace(c, ' ') self.keywords = splitIntoWords(words, ignore=ignore) # Store the operator to apply to the keywords (AND or OR) self.operator = operator def merge(self, other, append=False): '''Merges our keywords with those from p_other. If p_append is True, p_other keywords are appended at the end; else, keywords are appended at the begin.''' for word in other.keywords: if word not in self.keywords: if append: self.keywords.append(word) else: self.keywords.insert(0, word) def get(self): '''Returns the keywords as needed by the TextIndex.''' if self.keywords: op = ' %s ' % self.operator return op.join(self.keywords)+'*' return '' # ------------------------------------------------------------------------------ def getClassName(klass, appName=None): '''Generates, from appy-class p_klass, the name of the corresponding Zope class. For some classes, name p_appName is required: it is part of the class name.''' moduleName = klass.__module__ if (moduleName == 'appy.gen.model') or moduleName.endswith('.wrappers'): # This is a model (generation time or run time) res = appName + klass.__name__ elif klass.__bases__ and (klass.__bases__[-1].__module__=='appy.gen.utils'): # This is a customized class (inherits from appy.gen.Tool, User,...) res = appName + klass.__bases__[-1].__name__ else: # This is a standard class res = klass.__module__.replace('.', '_') + '_' + klass.__name__ return res # ------------------------------------------------------------------------------ def callMethod(obj, method, klass=None, cache=True): '''This function is used to call a p_method on some Appy p_obj. m_method can be an instance method on p_obj; it can also be a static method. In this latter case, p_obj is the tool and the static method, defined in p_klass, will be called with the tool as unique arg. A method cache is implemented on the request object (available at p_obj.request). So while handling a single request from the ui, every method is called only once. Some method calls must not be cached (ie, values of Computed fields). In this case, p_cache will be False.''' rq = obj.request # Create the method cache if it does not exist on the request if not hasattr(rq, 'methodCache'): rq.methodCache = {} # If m_method is a static method or an instance method, unwrap the true # Python function object behind it. methodType = method.__class__.__name__ if methodType == 'staticmethod': method = method.__get__(klass) elif methodType == 'instancemethod': method = method.im_func # Call the method if cache is not needed. if not cache: return method(obj) # If first arg of method is named "tool" instead of the traditional "self", # we cheat and will call the method with the tool as first arg. This will # allow to consider this method as if it was a static method on the tool. # Every method call, even on different instances, will be cached in a unique # key. cheat = False if not klass and (method.func_code.co_varnames[0] == 'tool'): prefix = obj.klass.__name__ obj = obj.tool cheat = True # Build the key of this method call in the cache. # First part of the key: the p_obj's uid (if p_method is an instance method) # or p_className (if p_method is a static method). if not cheat: if klass: prefix = klass.__name__ else: prefix = obj.uid # Second part of the key: p_method name key = '%s:%s' % (prefix, method.func_name) # Return the cached value if present in the method cache. if key in rq.methodCache: return rq.methodCache[key] # No cached value: call the method, cache the result and return it res = method(obj) rq.methodCache[key] = res return res # Functions for manipulating the authentication cookie ------------------------- def readCookie(request): '''Returns the tuple (login, password) read from the authentication cookie received in p_request. If no user is logged, its returns (None, None).''' cookie = request.get('_appy_', None) if not cookie: return None, None cookieValue = base64.decodestring(urllib.unquote(cookie)) if ':' in cookieValue: return cookieValue.split(':') return None, None def writeCookie(login, password, request): '''Encode p_login and p_password into the cookie set in the p_request.''' cookieValue = base64.encodestring('%s:%s' % (login, password)).rstrip() cookieValue = urllib.quote(cookieValue) request.RESPONSE.setCookie('_appy_', cookieValue, path='/') # ------------------------------------------------------------------------------ def initMasterValue(v): '''Standardizes p_v as a list of strings, excepted if p_v is a method.''' if callable(v): return v if not isinstance(v, bool) and not v: res = [] elif type(v) not in sutils.sequenceTypes: res = [v] else: res = v return [str(v) for v in res] # ------------------------------------------------------------------------------ class No: '''When you write a workflow condition method and you want to return False but you want to give to the user some explanations about why a transition can't be triggered, do not return False, return an instance of No instead. When creating such an instance, you can specify an error message.''' def __init__(self, msg): self.msg = msg def __nonzero__(self): return False def __repr__(self): return '' % self.msg # ------------------------------------------------------------------------------ class Model: pass class Tool(Model): '''Subclass me to extend or modify the Tool class.''' class User(Model): '''Subclass me to extend or modify the User class.''' # ------------------------------------------------------------------------------