From 699cc8346b94f6fd428498468e086550b029dbdb Mon Sep 17 00:00:00 2001 From: Gaetan Delannay Date: Wed, 18 Jul 2012 21:58:11 +0200 Subject: [PATCH] [gen] Field.indexed, instead of being a Boolean, can be a str, to represent the name of a Zope Index. This way, it is possible to bypass the standard Appy choice for index types, ie for Computed fields whose content may produce any type of value; added missing translation labels in the macro displaying object's history; added default fields Tool.dateFormat and Tool.hourFormat that give application-wide default formats for dates with/without hour; added a table in Config->Users that shows the connected users and the date/time of their last access to the app; added the missing search macro for a Computed field. --- gen/__init__.py | 6 ++++++ gen/generator.py | 8 ++++++++ gen/mixins/ToolMixin.py | 24 ++++++++++++++++++++++++ gen/model.py | 23 +++++++++++++++-------- gen/po.py | 8 ++++++++ gen/ui/page.pt | 23 ++++++++++++----------- gen/ui/widgets/computed.pt | 7 ++++++- gen/wrappers/ToolWrapper.py | 17 ++++++++++++++++- 8 files changed, 95 insertions(+), 21 deletions(-) diff --git a/gen/__init__.py b/gen/__init__.py index fab117b..6d22d64 100644 --- a/gen/__init__.py +++ b/gen/__init__.py @@ -786,6 +786,10 @@ class Type: def getIndexType(self): '''Returns the name of the technical, Zope-level index type for this field.''' + # Normally, self.indexed contains a Boolean. If a string value is given, + # we consider it to be an index type. It allows to bypass the standard + # way to decide what index type must be used. + if isinstance(self.indexed, str): return self.indexed return 'FieldIndex' def getIndexValue(self, obj, forSearch=False): @@ -2153,6 +2157,8 @@ class Action(Type): except Exception, e: res = (False, 'An error occurred. %s' % str(e)) obj.log(Traceback.get(), type='error') + #import transaction + #transaction.abort() return res def isShowable(self, obj, layoutType): diff --git a/gen/generator.py b/gen/generator.py index 163e73c..b509f3d 100644 --- a/gen/generator.py +++ b/gen/generator.py @@ -495,6 +495,14 @@ class ZopeGenerator(Generator): msg('new_password', '', msg.NEW_PASSWORD), msg('new_password_body', '', msg.NEW_PASSWORD_BODY), msg('new_password_sent', '', msg.NEW_PASSWORD_SENT), + msg('last_user_access', '', msg.LAST_USER_ACCESS), + msg('object_history', '', msg.OBJECT_HISTORY), + msg('object_created_by', '', msg.OBJECT_CREATED_BY), + msg('object_created_on', '', msg.OBJECT_CREATED_ON), + msg('object_action', '', msg.OBJECT_ACTION), + msg('object_author', '', msg.OBJECT_AUTHOR), + msg('action_date', '', msg.ACTION_DATE), + msg('action_comment', '', msg.ACTION_COMMENT), ] # Create a label for every role added by this application for role in self.getAllUsedRoles(): diff --git a/gen/mixins/ToolMixin.py b/gen/mixins/ToolMixin.py index ad38edb..8f54612 100644 --- a/gen/mixins/ToolMixin.py +++ b/gen/mixins/ToolMixin.py @@ -1020,6 +1020,30 @@ class ToolMixin(BaseMixin): url = appyUser.o.getUrl(mode='edit', page='main', nav='') return (' | '.join(info), url) + def getUserName(self, login): + '''Gets the user name corresponding to p_login, or the p_login itself + if the user does not exist anymore.''' + user = self.appy().search1('User', noSecurity=True, login=login) + if not user: return login + firstName = user.firstName + name = user.name + res = '' + if firstName: res += firstName + if name: + if res: res += ' ' + name + else: res = name + if not res: res = login + return res + + def formatDate(self, aDate, withHour=True): + '''Returns aDate formatted as specified by tool.dateFormat. + If p_withHour is True, hour is appended, with a format specified + in tool.hourFormat.''' + tool = self.appy() + res = aDate.strftime(tool.dateFormat) + if withHour: res += ' (%s)' % aDate.strftime(tool.hourFormat) + return res + def generateUid(self, className): '''Generates a UID for an instance of p_className.''' name = className.split('_')[-1] diff --git a/gen/model.py b/gen/model.py index eeb13af..21fa5f4 100644 --- a/gen/model.py +++ b/gen/model.py @@ -214,7 +214,8 @@ toolFieldPrefixes = ('defaultValue', 'podTemplate', 'formats', 'resultColumns', 'searchFields', 'optionalFields', 'showWorkflow', 'showAllStatesInPhase') defaultToolFields = ('title', 'mailHost', 'mailEnabled', 'mailFrom', - 'appyVersion', 'users', 'groups', 'translations', + 'appyVersion', 'dateFormat', 'hourFormat', 'users', + 'connectedUsers', 'groups', 'translations', 'loadTranslationsAtStartup', 'pages', 'unoEnabledPython', 'openOfficePort', 'numberOfResultsPerPage') @@ -226,18 +227,24 @@ class Tool(ModelClass): # Tool attributes def isManager(self): pass def isManagerEdit(self): pass - title = gen.String(show=False, page=gen.Page('main', show=False)) - mailHost = gen.String(default='localhost:25') - mailEnabled = gen.Boolean(default=False) - mailFrom = gen.String(default='info@appyframework.org') - appyVersion = gen.String(layouts='f') + lf = {'layouts':'f'} + title = gen.String(show=False, page=gen.Page('main', show=False), **lf) + mailHost = gen.String(default='localhost:25', **lf) + mailEnabled = gen.Boolean(default=False, **lf) + mailFrom = gen.String(default='info@appyframework.org', **lf) + appyVersion = gen.String(**lf) + dateFormat = gen.String(default='%d/%m/%Y', **lf) + hourFormat = gen.String(default='%H:%M', **lf) # Ref(User) will maybe be transformed into Ref(CustomUserClass). + userPage = gen.Page('users', show=isManager) users = gen.Ref(User, multiplicity=(0,None), add=True, link=False, - back=gen.Ref(attribute='toTool', show=False), - page=gen.Page('users', show=isManager), + back=gen.Ref(attribute='toTool', show=False), page=userPage, queryable=True, queryFields=('title', 'login'), showHeaders=True, shownInfo=('title', 'login', 'roles')) + def computeConnectedUsers(self): pass + connectedUsers = gen.Computed(method=computeConnectedUsers, page=userPage, + plainText=False) groups = gen.Ref(Group, multiplicity=(0,None), add=True, link=False, back=gen.Ref(attribute='toTool2', show=False), page=gen.Page('groups', show=isManager), diff --git a/gen/po.py b/gen/po.py index 62e71d9..59dd2c8 100644 --- a/gen/po.py +++ b/gen/po.py @@ -151,6 +151,14 @@ class PoMessage: 'requested for website ${siteUrl} is ${password}
' \ '
Best regards.' NEW_PASSWORD_SENT = 'Your new password has been sent to you by email.' + LAST_USER_ACCESS = 'Last access' + OBJECT_HISTORY = 'History' + OBJECT_CREATED_BY = 'By' + OBJECT_CREATED_ON = 'On' + OBJECT_ACTION = 'Action' + OBJECT_AUTHOR = 'Author' + ACTION_DATE = 'Date' + ACTION_COMMENT = 'Comment' def __init__(self, id, msg, default, fuzzy=False, comments=[], niceDefault=False): diff --git a/gen/ui/page.pt b/gen/ui/page.pt index 91a8181..53d5d00 100644 --- a/gen/ui/page.pt +++ b/gen/ui/page.pt @@ -72,10 +72,10 @@ - - - - + + + + Object history diff --git a/gen/ui/widgets/computed.pt b/gen/ui/widgets/computed.pt index aa1ae87..d0d1f87 100644 --- a/gen/ui/widgets/computed.pt +++ b/gen/ui/widgets/computed.pt @@ -34,4 +34,9 @@ Search macro for a Computed. - + +
   + +
diff --git a/gen/wrappers/ToolWrapper.py b/gen/wrappers/ToolWrapper.py index c1c7411..4f1b4b7 100644 --- a/gen/wrappers/ToolWrapper.py +++ b/gen/wrappers/ToolWrapper.py @@ -1,8 +1,9 @@ # ------------------------------------------------------------------------------ -import os.path +import os.path, time import appy from appy.shared.utils import executeCommand from appy.gen.wrappers import AbstractWrapper +from appy.gen.installer import loggedUsers # ------------------------------------------------------------------------------ _PY = 'Please specify a file corresponding to a Python interpreter ' \ @@ -38,6 +39,20 @@ class ToolWrapper(AbstractWrapper): '''Some pages on the tool can only be accessed by God, also in edit.''' if self.user.has_role('Manager'): return True + def computeConnectedUsers(self): + '''Computes a table showing users that are currently connected.''' + res = '
ActionByDateComment
- +   - History ||  + ||  Show document creator - - by - — - + + + Show creation date - + +
' \ + '' % self.translate('last_user_access') + rows = [] + for userId, lastAccess in loggedUsers.items(): + user = self.search1('User', noSecurity=True, login=userId) + if not user: continue # Could have been deleted in the meanwhile + fmt = '%s (%s)' % (self.dateFormat, self.hourFormat) + access = time.strftime(fmt, time.localtime(lastAccess)) + rows.append('' % \ + (user.o.absolute_url(), user.title,access)) + return res + '\n'.join(rows) + '
%s
%s%s
' + podOutputFormats = ('odt', 'pdf', 'doc', 'rtf') def getPodOutputFormats(self): '''Gets the available output formats for POD documents.'''