[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:
parent
c316ab896b
commit
412d9f939f
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -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)
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
50
gen/model.py
50
gen/model.py
|
@ -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
|
pShow = ''
|
||||||
|
for attr in ('',) + page.subElements:
|
||||||
|
attrName = 'show%s' % attr.capitalize()
|
||||||
|
pageShow = getattr(page, attrName)
|
||||||
if isinstance(pageShow, basestring): pageShow='"%s"' % pageShow
|
if isinstance(pageShow, basestring): pageShow='"%s"' % pageShow
|
||||||
elif callable(pageShow):
|
elif callable(pageShow):
|
||||||
pageShow = '%s.%s' % (wrapperName, pageShow.__name__)
|
pageShow = '%s.%s' % (wrapperName, pageShow.__name__)
|
||||||
pShow = ''
|
|
||||||
if pageShow != True:
|
if pageShow != True:
|
||||||
pShow = ', show=%s' % pageShow
|
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):
|
||||||
|
|
|
@ -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 ""
|
||||||
|
|
|
@ -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 ""
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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."
|
||||||
|
|
|
@ -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."
|
||||||
|
|
|
@ -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é."
|
||||||
|
|
|
@ -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 ""
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
BIN
gen/ui/user.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 643 B |
|
@ -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.'''
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue