diff --git a/fields/string.py b/fields/string.py index f840110..e4d4cf2 100644 --- a/fields/string.py +++ b/fields/string.py @@ -262,6 +262,7 @@ class String(Field): if letter.match(c): nv += str(ord(c.upper()) - 55) else: nv += c return int(nv) % 97 == 1 + @staticmethod def BIC(obj, value): '''Checks that p_value corresponds to a valid BIC number. BIC stands diff --git a/gen/__init__.py b/gen/__init__.py index 666b9f9..d7046f5 100644 --- a/gen/__init__.py +++ b/gen/__init__.py @@ -599,4 +599,6 @@ class Config: self.ogone = None # When using Google analytics, specify here the Analytics ID self.googleAnalyticsId = None + # Create a group for every global role? + self.groupsForGlobalRoles = False # ------------------------------------------------------------------------------ diff --git a/gen/generator.py b/gen/generator.py index 1da14e6..b896327 100644 --- a/gen/generator.py +++ b/gen/generator.py @@ -608,6 +608,7 @@ class ZopeGenerator(Generator): repls['ogone'] = repr(self.config.ogone) repls['googleAnalyticsId'] = repr(self.config.googleAnalyticsId) repls['activateForgotPassword'] = self.config.activateForgotPassword + repls['groupsForGlobalRoles'] = self.config.groupsForGlobalRoles self.copyFile('config.pyt', repls, destName='config.py') def generateInit(self): diff --git a/gen/installer.py b/gen/installer.py index 9989f31..2f71545 100644 --- a/gen/installer.py +++ b/gen/installer.py @@ -250,14 +250,16 @@ class ZopeInstaller: appyTool.log('Group "admins" created.') # Create a group for every global role defined in the application - for role in self.config.applicationGlobalRoles: - relatedGroup = '%s_group' % role - if appyTool.count('Group', noSecurity=True, login=relatedGroup): - continue - appyTool.create('groups', noSecurity=True, login=relatedGroup, - title=relatedGroup, roles=[role]) - appyTool.log('Group "%s", related to global role "%s", was ' \ - 'created.' % (relatedGroup, role)) + # (if required). + if self.app.config.getProductConfig().groupsForGlobalRoles: + for role in self.config.applicationGlobalRoles: + groupId = role.lower() + if appyTool.count('Group', noSecurity=True, login=groupId): + continue + appyTool.create('groups', noSecurity=True, login=groupId, + title=role, roles=[role]) + appyTool.log('Group "%s", related to global role "%s", was ' \ + 'created.' % (groupId, role)) # Create POD templates within the tool if required for contentType in self.config.attributes.iterkeys(): @@ -307,11 +309,8 @@ class ZopeInstaller: id=language, title=title) appyTool.log('Translation object created for "%s".' % language) - # Execute custom installation code if any - if hasattr(appyTool, 'onInstall'): appyTool.onInstall() - - # Now, if required, we synchronise every Translation object with the - # corresponding "po" file on disk. + # Synchronize, if required, synchronise every Translation object with + # the corresponding "po" file on disk. if appyTool.loadTranslationsAtStartup: appFolder = self.config.diskFolder appName = self.config.PROJECTNAME @@ -325,6 +324,9 @@ class ZopeInstaller: appyTool.log('Translation "%s" updated from "%s".' % \ (translation.id, poName)) + # Execute custom installation code if any. + if hasattr(appyTool, 'onInstall'): appyTool.onInstall() + def configureSessions(self): '''Configure the session machinery.''' # Register a function warning us when a session object is deleted. When diff --git a/gen/mixins/__init__.py b/gen/mixins/__init__.py index cd7273b..d0e5b99 100644 --- a/gen/mixins/__init__.py +++ b/gen/mixins/__init__.py @@ -57,7 +57,15 @@ class BaseMixin: if created and rq: # Create the final object and put it at the right place. tool = self.getTool() - id = tool.generateUid(obj.portal_type) + # The app may define a method klass.generateUid for producing an UID + # for instance of this class. If no such method is found, we use the + # standard Appy method to produce an UID. + id = None + klass = tool.getAppyClass(obj.portal_type) + if hasattr(klass, 'generateUid'): + id = klass.generateUid(obj.REQUEST) + if not id: + id = tool.generateUid(obj.portal_type) if not initiator: folder = tool.getPath('/data') else: diff --git a/gen/templates/config.pyt b/gen/templates/config.pyt index 6d1296c..85b389b 100644 --- a/gen/templates/config.pyt +++ b/gen/templates/config.pyt @@ -52,6 +52,7 @@ enableSessionTimeout = discreetLogin = ogone = googleAnalyticsId = +groupsForGlobalRoles = # When Zope is starting or runs in test mode, there is no request object. We # create here a fake one for storing Appy wrappers. diff --git a/gen/wrappers/GroupWrapper.py b/gen/wrappers/GroupWrapper.py index 41dae73..5c5ca95 100644 --- a/gen/wrappers/GroupWrapper.py +++ b/gen/wrappers/GroupWrapper.py @@ -9,7 +9,7 @@ class GroupWrapper(AbstractWrapper): def showLogin(self): '''When must we show the login field?''' if self.o.isTemporary(): return 'edit' - return 'view' + return ('view', 'result') def showGroups(self): '''Only the admin can view or edit roles.''' diff --git a/gen/wrappers/UserWrapper.py b/gen/wrappers/UserWrapper.py index 7f01172..c195724 100644 --- a/gen/wrappers/UserWrapper.py +++ b/gen/wrappers/UserWrapper.py @@ -134,7 +134,11 @@ class UserWrapper(AbstractWrapper): return self._callCustom('validate', new, errors) def onEdit(self, created): - self.title = self.login + # Set a title for this user. + if self.firstName and self.name: + self.title = '%s %s' % (self.name, self.firstName) + else: + self.title = self.login aclUsers = self.o.acl_users login = self.login if created: diff --git a/shared/dav.py b/shared/dav.py index 9641103..1c123e7 100644 --- a/shared/dav.py +++ b/shared/dav.py @@ -68,7 +68,7 @@ class SoapDataEncoder: # ------------------------------------------------------------------------------ class HttpResponse: '''Stores information about a HTTP response.''' - def __init__(self, response, body, duration=None): + def __init__(self, response, body, duration=None, utf8=True): self.code = response.status # The return code, ie 404, 200, 500... self.text = response.reason # Textual description of the code self.headers = response.msg # A dict-like object containing the headers @@ -76,6 +76,7 @@ class HttpResponse: # p_duration, if given, is the time, in seconds, we have waited, before # getting this response after having sent the request. self.duration = duration + self.utf8 = utf8 # The following attribute may contain specific data extracted from # the previous fields. For example, when response if 302 (Redirect), # self.data contains the URI where we must redirect the user to. @@ -108,7 +109,7 @@ class HttpResponse: # Return an unmarshalled version of the XML content, for # easy use in Python. try: - return XmlUnmarshaller().parse(self.body) + return XmlUnmarshaller(utf8=self.utf8).parse(self.body) except xml.sax.SAXParseException, se: raise ResourceError('Invalid XML response (%s)'%str(se)) @@ -120,7 +121,8 @@ class Resource: '''Every instance of this class represents some web resource accessible through HTTP.''' - def __init__(self, url, username=None, password=None, measure=False): + def __init__(self, url, username=None, password=None, measure=False, + utf8=True): self.username = username self.password = password self.url = url @@ -142,6 +144,7 @@ class Resource: # If some headers must be sent with any request sent through this # resource (like a cookie), you can store them in the following dict. self.headers = {'Host': self.host} + self.utf8 = utf8 def __repr__(self): return '' % self.url @@ -190,7 +193,7 @@ class Resource: if self.measure: duration = endTime - startTime self.serverTime += duration - return HttpResponse(response, body, duration=duration) + return HttpResponse(response, body, duration=duration, utf8=self.utf8) def mkdir(self, name): '''Creates a folder named p_name in this resource.'''