[gen] A page can now be visible on edit but not on view (ie, the new User page containing only fields 'password' and 'retype password'. Default User class has now 2 pages: the 2 password fields are on a separate page. Zone containing user name in the user strip has evolved.

This commit is contained in:
Gaetan Delannay 2014-10-24 15:55:45 +02:00
parent c316ab896b
commit 412d9f939f
20 changed files with 150 additions and 106 deletions

View file

@ -21,6 +21,7 @@ from appy import Object
class Page: class Page:
'''Used for describing a page, its related phase, show condition, etc.''' '''Used for describing a page, its related phase, show condition, etc.'''
subElements = ('save', 'cancel', 'previous', 'next', 'edit') subElements = ('save', 'cancel', 'previous', 'next', 'edit')
def __init__(self, name, phase='main', show=True, showSave=True, def __init__(self, name, phase='main', show=True, showSave=True,
showCancel=True, showPrevious=True, showNext=True, showCancel=True, showPrevious=True, showNext=True,
showEdit=True, label=None): showEdit=True, label=None):
@ -61,9 +62,8 @@ class Page:
res = Page(pageData[0], phase=pageData[1]) res = Page(pageData[0], phase=pageData[1])
return res return res
def isShowable(self, obj, elem='page'): def isShowable(self, obj, layoutType, elem='page'):
'''Must this page be shown for p_obj? The method can return True, False '''Is this page showable for p_obj on p_layoutType ("view" or "edit")?
or 'view' (page is available only in "view" mode).
If p_elem is not "page", this method returns the fact that a If p_elem is not "page", this method returns the fact that a
sub-element is viewable or not (buttons "save", "cancel", etc).''' sub-element is viewable or not (buttons "save", "cancel", etc).'''
@ -72,16 +72,14 @@ class Page:
# Get the value of the show attribute as identified above. # Get the value of the show attribute as identified above.
res = getattr(self, attr) res = getattr(self, attr)
if callable(res): res = res(obj.appy()) if callable(res): res = res(obj.appy())
if isinstance(res, str): return res == layoutType
return res return res
def getInfo(self, obj, layoutType): def getInfo(self, obj, layoutType):
'''Gets information about this page, for p_obj, as an object.''' '''Gets information about this page, for p_obj, as an object.'''
res = Object() res = Object()
for elem in Page.subElements: for elem in Page.subElements:
showable = self.isShowable(obj, elem) showable = self.isShowable(obj, layoutType, elem)
# "showable" can be True, False or "view"
if layoutType == 'edit': showable = showable==True
else: showable = bool(showable)
setattr(res, 'show%s' % elem.capitalize(), showable) setattr(res, 'show%s' % elem.capitalize(), showable)
return res return res

View file

@ -33,8 +33,11 @@ class Phase:
class=":(aPage == page) and 'currentPage' or ''"> class=":(aPage == page) and 'currentPage' or ''">
<!-- First line: page name and icons --> <!-- First line: page name and icons -->
<span if="not (singlePhase and singlePage)"> <span if="not (singlePhase and singlePage)">
<a href=":zobj.getUrl(page=aPage, \ <x var="label=aPageInfo.page.getLabel(zobj)">
inPopup=inPopup)">::aPageInfo.page.getLabel(zobj)</a> <a if="aPageInfo.showOnView"
href=":zobj.getUrl(page=aPage, inPopup=inPopup)">::label</a>
<x if="not aPageInfo.showOnView">:label</x>
</x>
<x var="locked=zobj.isLocked(user, aPage); <x var="locked=zobj.isLocked(user, aPage);
editable=mayEdit and aPageInfo.showOnEdit and \ editable=mayEdit and aPageInfo.showOnEdit and \
aPageInfo.showEdit"> aPageInfo.showEdit">
@ -136,13 +139,14 @@ class Phase:
if (field.page.name in self.pages) or \ if (field.page.name in self.pages) or \
(field.page.name in self.hiddenPages): return (field.page.name in self.hiddenPages): return
# Add the page only if it must be shown. # Add the page only if it must be shown.
showable = field.page.isShowable(obj) showOnView = field.page.isShowable(obj, 'view')
if showable: showOnEdit = field.page.isShowable(obj, 'edit')
if showOnView or showOnEdit:
# The page must be added # The page must be added
self.pages.append(field.page.name) self.pages.append(field.page.name)
# Create the dict about page information and add it in self.pageInfo # Create the dict about page information and add it in self.pageInfo
pageInfo = Object(page=field.page, showOnView=bool(showable), pageInfo = Object(page=field.page, showOnView=showOnView,
showOnEdit=showable==True, links=None) showOnEdit=showOnEdit, links=None)
pageInfo.update(field.page.getInfo(obj, layoutType)) pageInfo.update(field.page.getInfo(obj, layoutType))
self.pagesInfo[field.page.name] = pageInfo self.pagesInfo[field.page.name] = pageInfo
else: else:
@ -203,4 +207,18 @@ class Phase:
return res, nextPhase.pagesInfo[res] return res, nextPhase.pagesInfo[res]
else: else:
return None, None return None, None
def getPageInfo(self, page, layoutType):
'''Return the page info corresponding to the given p_page. If this page
cannot be shown on p_layoutType, this method returns page info about
the first showable page on p_layoutType, or None if no page is
showable at all.'''
res = self.pagesInfo[page]
showAttribute = 'showOn%s' % layoutType.capitalize()
if getattr(res, showAttribute): return res
# Find the first showable page in this phase on p_layoutType.
for pageName in self.pages:
if pageName == page: continue
pageInfo = self.pagesInfo[pageName]
if getattr(pageInfo, showAttribute): return pageInfo
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View file

@ -17,6 +17,7 @@
import types, string import types, string
from group import Group from group import Group
from appy.px import Px from appy.px import Px
from appy.gen.utils import User
from appy.gen.mail import sendNotification from appy.gen.mail import sendNotification
# Default Appy permissions ----------------------------------------------------- # Default Appy permissions -----------------------------------------------------
@ -568,6 +569,11 @@ class WorkflowOwner:
active = State({r:(ma, o), w:(ma, o), d:ma}, initial=True) active = State({r:(ma, o), w:(ma, o), d:ma}, initial=True)
inactive = State({r:(ma, o), w:ma, d:ma}) inactive = State({r:(ma, o), w:ma, d:ma})
# Transitions # Transitions
deactivate = Transition( (active, inactive), condition=ma) def doDeactivate(self, obj):
'''Prevent user "admin" from being deactivated.'''
if isinstance(obj, User) and (obj.login == 'admin'):
raise Exception('Cannot deactivate admin.')
deactivate = Transition( (active, inactive), condition=ma,
action=doDeactivate)
reactivate = Transition( (inactive, active), condition=ma) reactivate = Transition( (inactive, active), condition=ma)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View file

@ -38,20 +38,13 @@ from appy.fields.page import Page
from appy.fields.phase import Phase from appy.fields.phase import Phase
from appy.fields.workflow import * from appy.fields.workflow import *
from appy.gen.layout import Table from appy.gen.layout import Table
from appy.gen.utils import No from appy.gen.utils import No, Tool, User
# Make the following classes available here: people may need to override some # Make the following classes available here: people may need to override some
# of their PXs (defined as static attributes). # of their PXs (defined as static attributes).
from appy.gen.wrappers import AbstractWrapper as BaseObject from appy.gen.wrappers import AbstractWrapper as BaseObject
from appy.gen.wrappers.ToolWrapper import ToolWrapper as BaseTool from appy.gen.wrappers.ToolWrapper import ToolWrapper as BaseTool
# ------------------------------------------------------------------------------
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.'''
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
class Config: class Config:
'''If you want to specify some configuration parameters for appy.gen and '''If you want to specify some configuration parameters for appy.gen and

View file

@ -21,6 +21,8 @@ except ImportError:
# Global JS internationalized messages that will be computed in every page ----- # Global JS internationalized messages that will be computed in every page -----
jsMessages = ('no_elem_selected', 'action_confirm', 'save_confirm', jsMessages = ('no_elem_selected', 'action_confirm', 'save_confirm',
'warn_leave_form') 'warn_leave_form')
USER_NOT_FOUND = 'User %s not found. Probably a problem implying several ' \
'Appy apps put behind the same domain name or dev machine.'
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
class ToolMixin(BaseMixin): class ToolMixin(BaseMixin):
@ -67,6 +69,8 @@ class ToolMixin(BaseMixin):
if not url: if not url:
# Bring Managers to the config, lead others to pxHome. # Bring Managers to the config, lead others to pxHome.
user = self.getUser() user = self.getUser()
if not user:
raise Exception(USER_NOT_FOUND % self.identifyUser()[0])
if user.has_role('Manager'): if user.has_role('Manager'):
url = self.goto(self.absolute_url()) url = self.goto(self.absolute_url())
else: else:
@ -1043,22 +1047,6 @@ class ToolMixin(BaseMixin):
from AccessControl.User import BasicUserFolder from AccessControl.User import BasicUserFolder
BasicUserFolder.validate = validate BasicUserFolder.validate = validate
def getUserLine(self):
'''Returns info about the currently logged user as a 2-tuple: first
elem is the one-line user info as shown on every page; second line is
the URL to edit user info.'''
user = self.getUser()
info = [user.title]
showable = [r for r in user.getRoles() if r != 'Authenticated']
if showable:
info.append(', '.join([self.translate('role_%s' % r) \
for r in showable]))
# Edit URL for the user.
url = None
if user.o.mayEdit():
url = user.o.getUrl(mode='edit', page='main', nav='')
return (' | '.join(info), url)
def getUserName(self, login=None, normalized=False): def getUserName(self, login=None, normalized=False):
'''Gets the user name corresponding to p_login (or the currently logged '''Gets the user name corresponding to p_login (or the currently logged
user if None), or the p_login itself if the user does not exist user if None), or the p_login itself if the user does not exist

View file

@ -383,23 +383,23 @@ class BaseMixin:
errorMessage = self.translate('validation_error') errorMessage = self.translate('validation_error')
isNew = self.isTemporary() isNew = self.isTemporary()
inPopup = rq.get('popup') == '1' inPopup = rq.get('popup') == '1'
# If this object is created from an initiator, get info about him. # If this object is created from an initiator, get info about him
initiator, initiatorField = self.getInitiatorInfo() initiator, initiatorField = self.getInitiatorInfo()
initiatorPage = initiatorField and initiatorField.pageName or None initiatorPage = initiatorField and initiatorField.pageName or None
# If the user clicked on 'Cancel', go back to the previous page. # If the user clicked on 'Cancel', go back to the previous page
buttonClicked = rq.get('button') buttonClicked = rq.get('button')
if buttonClicked == 'cancel': if buttonClicked == 'cancel':
if inPopup: if inPopup: back = tool.backFromPopup()
back = tool.backFromPopup() elif initiator: # Go back to the initiator page
elif initiator:
# Go back to the initiator page.
urlBack = initiator.getUrl(page=initiatorPage, nav='') urlBack = initiator.getUrl(page=initiatorPage, nav='')
else: else:
if isNew: if isNew: urlBack = tool.getHomePage() # Go back to home page
# Go back to the root of the site.
urlBack = tool.getSiteUrl()
else: else:
urlBack = self.getUrl() # Return to the same page, excepted if unshowable on view.
phaseObj = self.getAppyPhases(True, 'view')
pageInfo = phaseObj.getPageInfo(rq['page'], 'view')
if not pageInfo: urlBack = tool.getHomePage()
else: urlBack = self.getUrl(page=pageInfo.page.name)
self.say(self.translate('object_canceled')) self.say(self.translate('object_canceled'))
self.removeLock(rq['page']) self.removeLock(rq['page'])
if inPopup: return back if inPopup: return back
@ -452,24 +452,27 @@ class BaseMixin:
if inPopup: return tool.backFromPopup() if inPopup: return tool.backFromPopup()
if isNew and initiator: if isNew and initiator:
return self.goto(initiator.getUrl(page=initiatorPage, nav='')) return self.goto(initiator.getUrl(page=initiatorPage, nav=''))
return self.goto(obj.getUrl()) # Return to the same page, if showable on view
phaseObj = self.getAppyPhases(True, 'view')
pageInfo = phaseObj.getPageInfo(rq['page'], 'view')
if not pageInfo: return self.goto(tool.getHomePage(), msg)
return self.goto(obj.getUrl(page=pageInfo.page.name))
# Get the current page name. We keep it in "pageName" because rq['page'] # Get the current page name. We keep it in "pageName" because rq['page']
# can be changed by m_getAppyPhases called below. # can be changed by m_getAppyPhases called below.
pageName = rq['page'] pageName = rq['page']
if buttonClicked == 'previous': if buttonClicked in ('previous', 'next'):
# Go to the previous page for this object. # Go to the previous or next page for this object. We recompute the
# We recompute the list of phases and pages because things # list of phases and pages because things may have changed since the
# may have changed since the object has been updated (ie, # object has been updated (ie, additional pages may be shown or
# additional pages may be shown or hidden now, so the next and # hidden now, so the next and previous pages may have changed).
# previous pages may have changed). Moreover, previous and next # Moreover, previous and next pages may not be available in "edit"
# pages may not be available in "edit" mode, so we return the edit # mode, so we return the edit or view pages depending on page.show.
# or view pages depending on page.show. phaseObj = self.getAppyPhases(True, 'edit')
phaseObj = self.getAppyPhases(currentOnly=True, layoutType='edit') methodName = 'get%sPage' % buttonClicked.capitalize()
pageName, pageInfo = phaseObj.getPreviousPage(pageName) pageName, pageInfo = getattr(phaseObj, methodName)(pageName)
if pageName: if pageName:
# Return to the edit or view page? # Return to the edit or view page?
if pageInfo.showOnEdit: if pageInfo.showOnEdit:
rq.set('page', pageName)
# I do not use gotoEdit here because I really need to # I do not use gotoEdit here because I really need to
# redirect the user to the edit page. Indeed, the object # redirect the user to the edit page. Indeed, the object
# edit URL may have moved from temp_folder to another place. # edit URL may have moved from temp_folder to another place.
@ -480,21 +483,6 @@ class BaseMixin:
else: else:
obj.say(msg) obj.say(msg)
return self.goto(obj.getUrl(inPopup=inPopup)) return self.goto(obj.getUrl(inPopup=inPopup))
if buttonClicked == 'next':
# Go to the next page for this object.
phaseObj = self.getAppyPhases(currentOnly=True, layoutType='edit')
pageName, pageInfo = phaseObj.getNextPage(pageName)
if pageName:
# Return to the edit or view page?
if pageInfo.showOnEdit:
# Same remark as above (click on "previous").
return self.goto(obj.getUrl(mode='edit', page=pageName,
inPopup=inPopup))
else:
return self.goto(obj.getUrl(page=pageName, inPopup=inPopup))
else:
obj.say(msg)
return self.goto(obj.getUrl(inPopup=inPopup))
return obj.gotoEdit() return obj.gotoEdit()
def reindex(self, indexes=None, unindex=False): def reindex(self, indexes=None, unindex=False):

View file

@ -123,14 +123,16 @@ class ModelClass:
pages[appyType.page.name] = appyType.page pages[appyType.page.name] = appyType.page
res += ' pges = {' res += ' pges = {'
for page in pages.itervalues(): for page in pages.itervalues():
# Determine page show # Determine page "show" attributes
pageShow = page.show
if isinstance(pageShow, basestring): pageShow='"%s"' % pageShow
elif callable(pageShow):
pageShow = '%s.%s' % (wrapperName, pageShow.__name__)
pShow = '' pShow = ''
if pageShow != True: for attr in ('',) + page.subElements:
pShow = ', show=%s' % pageShow attrName = 'show%s' % attr.capitalize()
pageShow = getattr(page, attrName)
if isinstance(pageShow, basestring): pageShow='"%s"' % pageShow
elif callable(pageShow):
pageShow = '%s.%s' % (wrapperName, pageShow.__name__)
if pageShow != True:
pShow += ', %s=%s' % (attrName, pageShow)
# For translation pages, fixed labels are used. # For translation pages, fixed labels are used.
label = '' label = ''
if className == 'Translation': if className == 'Translation':
@ -148,34 +150,38 @@ class ModelClass:
# The User class --------------------------------------------------------------- # The User class ---------------------------------------------------------------
class User(ModelClass): class User(ModelClass):
_appy_attributes = ['title', 'name', 'firstName', 'login', 'password1', _appy_attributes = ['password1', 'password2', 'title', 'name', 'firstName',
'password2', 'email', 'roles', 'source', 'groups', 'login', 'email', 'roles', 'source', 'groups', 'toTool']
'toTool'] # Passwords are on a specific page.
def showPassword(self): pass
def validatePassword(self): pass
pp = {'page': gen.Page('passwords', showNext=False, show=showPassword),
'width': 34, 'multiplicity': (1,1), 'format': gen.String.PASSWORD,
'show': showPassword}
password1 = gen.String(validator=validatePassword, **pp)
password2 = gen.String(**pp)
# All methods defined below are fake. Real versions are in the wrapper. # All methods defined below are fake. Real versions are in the wrapper.
title = gen.String(show=False, indexed=True) pm = {'page': gen.Page('main', showPrevious=False), 'group': 'main',
gm = {'group': 'main', 'width': 34} 'width': 34}
title = gen.String(show=False, indexed=True, **pm)
def showName(self): pass def showName(self): pass
name = gen.String(show=showName, **gm) name = gen.String(show=showName, **pm)
firstName = gen.String(show=showName, **gm) firstName = gen.String(show=showName, **pm)
def showEmail(self): pass def showEmail(self): pass
email = gen.String(show=showEmail, **gm) email = gen.String(show=showEmail, **pm)
# Where is this user stored? By default, in the ZODB. But the user can be # Where is this user stored? By default, in the ZODB. But the user can be
# stored in an external LDAP (source='ldap'). # stored in an external LDAP (source='ldap').
source = gen.String(show=False, default='zodb', layouts='f', **gm) source = gen.String(show=False, default='zodb', layouts='f', **pm)
gm['multiplicity'] = (1,1) pm['multiplicity'] = (1,1)
def showLogin(self): pass def showLogin(self): pass
def validateLogin(self): pass def validateLogin(self): pass
login = gen.String(show=showLogin, validator=validateLogin, login = gen.String(show=showLogin, validator=validateLogin,
indexed=True, **gm) indexed=True, **pm)
def showPassword(self): pass pm['multiplicity'] = (0, None)
def validatePassword(self): pass
password1 = gen.String(format=gen.String.PASSWORD, show=showPassword,
validator=validatePassword, **gm)
password2 = gen.String(format=gen.String.PASSWORD, show=showPassword, **gm)
gm['multiplicity'] = (0, None)
def showRoles(self): pass def showRoles(self): pass
roles = gen.String(show=showRoles, indexed=True, roles = gen.String(show=showRoles, indexed=True,
validator=gen.Selection('getGrantableRoles'), **gm) validator=gen.Selection('getGrantableRoles'), **pm)
# The Group class -------------------------------------------------------------- # The Group class --------------------------------------------------------------
class Group(ModelClass): class Group(ModelClass):

View file

@ -379,6 +379,10 @@ msgstr ""
msgid "app_home" msgid "app_home"
msgstr "" msgstr ""
#. Default: "Change password"
msgid "change_password"
msgstr ""
#. Default: "This login is reserved." #. Default: "This login is reserved."
msgid "login_reserved" msgid "login_reserved"
msgstr "" msgstr ""

View file

@ -379,6 +379,10 @@ msgstr "كلمة السر"
msgid "app_home" msgid "app_home"
msgstr "" msgstr ""
#. Default: "Change password"
msgid "change_password"
msgstr ""
#. Default: "This login is reserved." #. Default: "This login is reserved."
msgid "login_reserved" msgid "login_reserved"
msgstr "" msgstr ""

View file

@ -379,6 +379,10 @@ msgstr "Passwort"
msgid "app_home" msgid "app_home"
msgstr "" msgstr ""
#. Default: "Change password"
msgid "change_password"
msgstr ""
#. Default: "This login is reserved." #. Default: "This login is reserved."
msgid "login_reserved" msgid "login_reserved"
msgstr "Dieses Login ist reserviert" msgstr "Dieses Login ist reserviert"

View file

@ -380,6 +380,10 @@ msgstr "Password"
msgid "app_home" msgid "app_home"
msgstr "Home" msgstr "Home"
#. Default: "Change password"
msgid "change_password"
msgstr "Change password"
#. Default: "This login is reserved." #. Default: "This login is reserved."
msgid "login_reserved" msgid "login_reserved"
msgstr "This login is reserved." msgstr "This login is reserved."

View file

@ -379,6 +379,10 @@ msgstr "Contraseña"
msgid "app_home" msgid "app_home"
msgstr "volver a mi página principal" msgstr "volver a mi página principal"
#. Default: "Change password"
msgid "change_password"
msgstr ""
#. Default: "This login is reserved." #. Default: "This login is reserved."
msgid "login_reserved" msgid "login_reserved"
msgstr "Este login está reservado." msgstr "Este login está reservado."

View file

@ -380,6 +380,10 @@ msgstr "Mot de passe"
msgid "app_home" msgid "app_home"
msgstr "Revenir à ma page principale" msgstr "Revenir à ma page principale"
#. Default: "Change password"
msgid "change_password"
msgstr "Changer le mot de passe"
#. Default: "This login is reserved." #. Default: "This login is reserved."
msgid "login_reserved" msgid "login_reserved"
msgstr "Ce login est réservé." msgstr "Ce login est réservé."

View file

@ -379,6 +379,10 @@ msgstr "Password"
msgid "app_home" msgid "app_home"
msgstr "Ritorno alla pagina di partenza" msgstr "Ritorno alla pagina di partenza"
#. Default: "Change password"
msgid "change_password"
msgstr ""
#. Default: "This login is reserved." #. Default: "This login is reserved."
msgid "login_reserved" msgid "login_reserved"
msgstr "" msgstr ""

View file

@ -379,6 +379,10 @@ msgstr "Wachtwoord"
msgid "app_home" msgid "app_home"
msgstr "Keer terug naar de hoofdpagina" msgstr "Keer terug naar de hoofdpagina"
#. Default: "Change password"
msgid "change_password"
msgstr ""
#. Default: "This login is reserved." #. Default: "This login is reserved."
msgid "login_reserved" msgid "login_reserved"
msgstr "Dit login is voorbehouden" msgstr "Dit login is voorbehouden"

View file

@ -55,6 +55,7 @@ img { border: 0; vertical-align: middle }
.userStripText { padding: 0 0.3em 0 0.3em; color: whitesmoke } .userStripText { padding: 0 0.3em 0 0.3em; color: whitesmoke }
.userStrip a { color: #e7e7e7 } .userStrip a { color: #e7e7e7 }
.userStrip a:visited { color: #e7e7e7 } .userStrip a:visited { color: #e7e7e7 }
.changePassword { color: #494949 !important; font-style:italic; font-size: 90% }
.breadcrumb { font-size: 11pt; padding-bottom: 6px } .breadcrumb { font-size: 11pt; padding-bottom: 6px }
.login { margin: 3px; color: black } .login { margin: 3px; color: black }
input.button { color: #666666; height: 20px; margin-bottom: 5px; margin-top:2px; input.button { color: #666666; height: 20px; margin-bottom: 5px; margin-top:2px;

BIN
gen/ui/user.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

View file

@ -228,4 +228,12 @@ class No:
def __init__(self, msg): self.msg = msg def __init__(self, msg): self.msg = msg
def __nonzero__(self): return False def __nonzero__(self): return False
def __repr__(self): return '<No: %s>' % self.msg def __repr__(self): return '<No: %s>' % 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.'''
# ------------------------------------------------------------------------------

View file

@ -1,4 +1,5 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from appy.px import Px
from appy.fields.string import String from appy.fields.string import String
from appy.gen import WorkflowOwner from appy.gen import WorkflowOwner
from appy.gen.layout import summaryPageLayouts from appy.gen.layout import summaryPageLayouts
@ -12,6 +13,16 @@ class UserWrapper(AbstractWrapper):
specialUsers = ('system', 'anon', 'admin') specialUsers = ('system', 'anon', 'admin')
layouts = summaryPageLayouts layouts = summaryPageLayouts
# Display, in the user strip, links to the User instance of the logged user.
pxUserLinks = Px('''
<td class="userStripText" align=":dright">
<a href=":user.url"><img src=":url('user')"/>
<span style="padding: 0 3px">:user.getTitle()</span></a>
<!-- Page for modifying the password -->
<a if="user.showPassword()" class="changePassword"
href=":'%s/edit?page=passwords' % user.url">:_('change_password')</a>
</td>''')
def getTitle(self, normalized=False): def getTitle(self, normalized=False):
'''Returns a nice name for this user, based on available information: '''Returns a nice name for this user, based on available information:
name/first name or title or login. If p_normalized is True, special name/first name or title or login. If p_normalized is True, special

View file

@ -264,12 +264,7 @@ class AbstractWrapper(object):
<a href=":tool.url + '/performLogout'" title=":_('app_logout')"> <a href=":tool.url + '/performLogout'" title=":_('app_logout')">
<img src=":url('logout.gif')"/></a> <img src=":url('logout.gif')"/></a>
</td> </td>
<td class="userStripText" var="userInfo=ztool.getUserLine()" <x>:user.pxUserLinks</x>
align=":dright">
<span>:userInfo[0]</span>
<a if="userInfo[1]"
href=":userInfo[1]"><img src=":url('edit')"/></a>
</td>
</tr> </tr>
</table> </table>
</td> </td>