From 9c5f92337b5017adcdc080fb99458a9b38171700 Mon Sep 17 00:00:00 2001 From: Gaetan Delannay Date: Tue, 21 Feb 2012 12:09:42 +0100 Subject: [PATCH] appy.gen: improvements in user management. --- gen/installer.py | 3 +-- gen/mixins/ToolMixin.py | 28 ++++++++++++++++++++++++---- gen/model.py | 15 ++++++++++----- gen/ui/appy.css | 2 +- gen/ui/banner.jpg | Bin 0 -> 4470 bytes gen/ui/home.pt | 7 +++++++ gen/ui/template.pt | 13 ++++++------- gen/wrappers/GroupWrapper.py | 4 ++++ gen/wrappers/UserWrapper.py | 24 ++++++++++++++++++++++-- shared/packaging.py | 12 ++++++++++-- 10 files changed, 85 insertions(+), 23 deletions(-) create mode 100644 gen/ui/banner.jpg create mode 100644 gen/ui/home.pt diff --git a/gen/installer.py b/gen/installer.py index e38879d..119b60d 100644 --- a/gen/installer.py +++ b/gen/installer.py @@ -335,8 +335,7 @@ class ZopeInstaller: (translation.id, poName)) # Execute custom installation code if any - if hasattr(appyTool, 'install'): - tool.executeAppyAction('install', reindex=False) + if hasattr(appyTool, 'onInstall'): appyTool.onInstall() def configureSessions(self): '''Configure the session machinery.''' diff --git a/gen/mixins/ToolMixin.py b/gen/mixins/ToolMixin.py index 8a19817..7c964cb 100644 --- a/gen/mixins/ToolMixin.py +++ b/gen/mixins/ToolMixin.py @@ -102,6 +102,16 @@ class ToolMixin(BaseMixin): for elem in path.split('/'): res = res._getOb(elem) return res + def showLanguageSelector(self): + '''We must show the language selector if the app config requires it and + it there is more than 2 supported languages. Moreover, on some pages, + switching the language is not allowed.''' + cfg = self.getProductConfig() + if not cfg.languageSelector: return + if len(cfg.languages) < 2: return + page = self.REQUEST.get('ACTUAL_URL').split('/')[-1] + return page not in ('edit', 'query', 'search') + def getLanguages(self): '''Returns the supported languages. First one is the default.''' return self.getProductConfig().languages @@ -111,6 +121,14 @@ class ToolMixin(BaseMixin): p_code.''' return languages.get(code)[2] + def getCssJs(self): + '''Returns the list of CSS and JS files to include in the main template. + The method ensures that appy.css and appy.js come first.''' + names = self.getPhysicalRoot().ui.objectIds('File') + names.remove('appy.js'); names.insert(0, 'appy.js') + names.remove('appy.css'); names.insert(0, 'appy.css') + return names + def consumeMessages(self): '''Returns the list of messages to show to a web page and clean it in the session.''' @@ -825,6 +843,11 @@ class ToolMixin(BaseMixin): # -------------------------------------------------------------------------- # Authentication-related methods # -------------------------------------------------------------------------- + def _updateCookie(self, login, password): + cookieValue = base64.encodestring('%s:%s' % (login, password)).rstrip() + cookieValue = urllib.quote(cookieValue) + self.REQUEST.RESPONSE.setCookie('__ac', cookieValue, path='/') + def performLogin(self): '''Logs the user in.''' rq = self.REQUEST @@ -837,10 +860,7 @@ class ToolMixin(BaseMixin): return self.goto(urlBack, msg) # Perform the Zope-level authentication login = rq.get('__ac_name', '') - password = rq.get('__ac_password', '') - cookieValue = base64.encodestring('%s:%s' % (login, password)).rstrip() - cookieValue = urllib.quote(cookieValue) - rq.RESPONSE.setCookie('__ac', cookieValue, path='/') + self._updateCookie(login, rq.get('__ac_password', '')) user = self.acl_users.validate(rq) if self.userIsAnon(): rq.RESPONSE.expireCookie('__ac', path='/') diff --git a/gen/model.py b/gen/model.py index 50d136f..602e82b 100644 --- a/gen/model.py +++ b/gen/model.py @@ -85,7 +85,10 @@ class ModelClass: elif isinstance(value, gen.Page): value = 'pages["%s"]' % value.name elif callable(value): - value = '%s.%s' % (wrapperName, value.__name__) + className = wrapperName + if (appyType.type == 'Ref') and appyType.isBack: + className = appyType.back.klass.__name__ + value = '%s.%s' % (className, value.__name__) typeArgs += '%s=%s,' % (name, value) return '%s(%s)' % (appyType.__class__.__name__, typeArgs) @@ -144,10 +147,12 @@ class User(ModelClass): password1 = gen.String(format=gen.String.PASSWORD, show=showPassword, validator=validatePassword, **gm) password2 = gen.String(format=gen.String.PASSWORD, show=showPassword, **gm) - email = gen.String(group='main', width=25) + def showEmail(self): pass + email = gen.String(show=showEmail, group='main', width=25) gm['multiplicity'] = (0, None) - roles = gen.String(validator=gen.Selection('getGrantableRoles'), - indexed=True, **gm) + def showRoles(self): pass + roles = gen.String(show=showRoles, indexed=True, + validator=gen.Selection('getGrantableRoles'), **gm) # The Group class -------------------------------------------------------------- class Group(ModelClass): @@ -163,7 +168,7 @@ class Group(ModelClass): roles = gen.String(validator=gen.Selection('getGrantableRoles'), multiplicity=(0,None), **m) users = gen.Ref(User, multiplicity=(0,None), add=False, link=True, - back=gen.Ref(attribute='groups', show=True), + back=gen.Ref(attribute='groups', show=User.showRoles), showHeaders=True, shownInfo=('title', 'login')) # The Translation class -------------------------------------------------------- diff --git a/gen/ui/appy.css b/gen/ui/appy.css index 26b977b..fdd5176 100644 --- a/gen/ui/appy.css +++ b/gen/ui/appy.css @@ -40,7 +40,7 @@ img {border: 0} border-style: solid; border-width: 1px; border-color: grey; } .top { height: 75px; margin-left: 3em; vertical-align: top;} .lang { margin-right: 3px; } -.userStrip { background-color: #89A6B1; height: 30px; +.userStrip { background-color: #89A6B1; height: 40px; border-top: 3px solid #405A64; border-bottom: 2px solid #5F7983; } .login { margin-top: 2px; margin-bottom: 2px; color: white;} .buttons { margin-left: 4px;} diff --git a/gen/ui/banner.jpg b/gen/ui/banner.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d6af136852aefae7337a79da66a469f7be76ab1b GIT binary patch literal 4470 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 + +
+ +
+ + diff --git a/gen/ui/template.pt b/gen/ui/template.pt index 32c745d..5b55272 100644 --- a/gen/ui/template.pt +++ b/gen/ui/template.pt @@ -14,7 +14,7 @@ - +