[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
					
				
					 20 changed files with 150 additions and 106 deletions
				
			
		|  | @ -21,6 +21,7 @@ from appy import Object | |||
| class Page: | ||||
|     '''Used for describing a page, its related phase, show condition, etc.''' | ||||
|     subElements = ('save', 'cancel', 'previous', 'next', 'edit') | ||||
| 
 | ||||
|     def __init__(self, name, phase='main', show=True, showSave=True, | ||||
|                  showCancel=True, showPrevious=True, showNext=True, | ||||
|                  showEdit=True, label=None): | ||||
|  | @ -61,9 +62,8 @@ class Page: | |||
|                 res = Page(pageData[0], phase=pageData[1]) | ||||
|         return res | ||||
| 
 | ||||
|     def isShowable(self, obj, elem='page'): | ||||
|         '''Must this page be shown for p_obj? The method can return True, False | ||||
|            or 'view' (page is available only in "view" mode). | ||||
|     def isShowable(self, obj, layoutType, elem='page'): | ||||
|         '''Is this page showable for p_obj on p_layoutType ("view" or "edit")? | ||||
| 
 | ||||
|            If p_elem is not "page", this method returns the fact that a | ||||
|            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. | ||||
|         res = getattr(self, attr) | ||||
|         if callable(res): res = res(obj.appy()) | ||||
|         if isinstance(res, str): return res == layoutType | ||||
|         return res | ||||
| 
 | ||||
|     def getInfo(self, obj, layoutType): | ||||
|         '''Gets information about this page, for p_obj, as an object.''' | ||||
|         res = Object() | ||||
|         for elem in Page.subElements: | ||||
|             showable = self.isShowable(obj, elem) | ||||
|             # "showable" can be True, False or "view" | ||||
|             if layoutType == 'edit': showable = showable==True | ||||
|             else: showable = bool(showable) | ||||
|             showable = self.isShowable(obj, layoutType, elem) | ||||
|             setattr(res, 'show%s' % elem.capitalize(), showable) | ||||
|         return res | ||||
| 
 | ||||
|  |  | |||
|  | @ -33,8 +33,11 @@ class Phase: | |||
|            class=":(aPage == page) and 'currentPage' or ''"> | ||||
|         <!-- First line: page name and icons --> | ||||
|         <span if="not (singlePhase and singlePage)"> | ||||
|          <a href=":zobj.getUrl(page=aPage, \ | ||||
|                            inPopup=inPopup)">::aPageInfo.page.getLabel(zobj)</a> | ||||
|          <x var="label=aPageInfo.page.getLabel(zobj)"> | ||||
|           <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); | ||||
|                  editable=mayEdit and aPageInfo.showOnEdit and \ | ||||
|                           aPageInfo.showEdit"> | ||||
|  | @ -136,13 +139,14 @@ class Phase: | |||
|         if (field.page.name in self.pages) or \ | ||||
|            (field.page.name in self.hiddenPages): return | ||||
|         # Add the page only if it must be shown. | ||||
|         showable = field.page.isShowable(obj) | ||||
|         if showable: | ||||
|         showOnView = field.page.isShowable(obj, 'view') | ||||
|         showOnEdit = field.page.isShowable(obj, 'edit') | ||||
|         if showOnView or showOnEdit: | ||||
|             # The page must be added | ||||
|             self.pages.append(field.page.name) | ||||
|             # Create the dict about page information and add it in self.pageInfo | ||||
|             pageInfo = Object(page=field.page, showOnView=bool(showable), | ||||
|                               showOnEdit=showable==True, links=None) | ||||
|             pageInfo = Object(page=field.page, showOnView=showOnView, | ||||
|                               showOnEdit=showOnEdit, links=None) | ||||
|             pageInfo.update(field.page.getInfo(obj, layoutType)) | ||||
|             self.pagesInfo[field.page.name] = pageInfo | ||||
|         else: | ||||
|  | @ -203,4 +207,18 @@ class Phase: | |||
|                 return res, nextPhase.pagesInfo[res] | ||||
|             else: | ||||
|                 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 | ||||
| from group import Group | ||||
| from appy.px import Px | ||||
| from appy.gen.utils import User | ||||
| from appy.gen.mail import sendNotification | ||||
| 
 | ||||
| # Default Appy permissions ----------------------------------------------------- | ||||
|  | @ -568,6 +569,11 @@ class WorkflowOwner: | |||
|     active = State({r:(ma, o), w:(ma, o), d:ma}, initial=True) | ||||
|     inactive = State({r:(ma, o), w:ma, d:ma}) | ||||
|     # 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) | ||||
| # ------------------------------------------------------------------------------ | ||||
|  |  | |||
|  | @ -38,20 +38,13 @@ from appy.fields.page import Page | |||
| from appy.fields.phase import Phase | ||||
| from appy.fields.workflow import * | ||||
| 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 | ||||
| # of their PXs (defined as static attributes). | ||||
| from appy.gen.wrappers import AbstractWrapper as BaseObject | ||||
| 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: | ||||
|     '''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 ----- | ||||
| jsMessages = ('no_elem_selected', 'action_confirm', 'save_confirm', | ||||
|               '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): | ||||
|  | @ -67,6 +69,8 @@ class ToolMixin(BaseMixin): | |||
|         if not url: | ||||
|             # Bring Managers to the config, lead others to pxHome. | ||||
|             user = self.getUser() | ||||
|             if not user: | ||||
|                 raise Exception(USER_NOT_FOUND % self.identifyUser()[0]) | ||||
|             if user.has_role('Manager'): | ||||
|                 url = self.goto(self.absolute_url()) | ||||
|             else: | ||||
|  | @ -1043,22 +1047,6 @@ class ToolMixin(BaseMixin): | |||
|     from AccessControl.User import BasicUserFolder | ||||
|     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): | ||||
|         '''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 | ||||
|  |  | |||
|  | @ -383,23 +383,23 @@ class BaseMixin: | |||
|         errorMessage = self.translate('validation_error') | ||||
|         isNew = self.isTemporary() | ||||
|         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() | ||||
|         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') | ||||
|         if buttonClicked == 'cancel': | ||||
|             if inPopup: | ||||
|                 back = tool.backFromPopup() | ||||
|             elif initiator: | ||||
|                 # Go back to the initiator page. | ||||
|             if inPopup: back = tool.backFromPopup() | ||||
|             elif initiator: # Go back to the initiator page | ||||
|                 urlBack = initiator.getUrl(page=initiatorPage, nav='') | ||||
|             else: | ||||
|                 if isNew: | ||||
|                    # Go back to the root of the site. | ||||
|                    urlBack = tool.getSiteUrl() | ||||
|                 if isNew: urlBack = tool.getHomePage() # Go back to home page | ||||
|                 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.removeLock(rq['page']) | ||||
|             if inPopup: return back | ||||
|  | @ -452,24 +452,27 @@ class BaseMixin: | |||
|             if inPopup: return tool.backFromPopup() | ||||
|             if isNew and initiator: | ||||
|                 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'] | ||||
|         # can be changed by m_getAppyPhases called below. | ||||
|         pageName = rq['page'] | ||||
|         if buttonClicked == 'previous': | ||||
|             # Go to the previous page for this object. | ||||
|             # We recompute the list of phases and pages because things | ||||
|             # may have changed since the object has been updated (ie, | ||||
|             # additional pages may be shown or hidden now, so the next and | ||||
|             # previous pages may have changed). Moreover, previous and next | ||||
|             # pages may not be available in "edit" mode, so we return the edit | ||||
|             # or view pages depending on page.show. | ||||
|             phaseObj = self.getAppyPhases(currentOnly=True, layoutType='edit') | ||||
|             pageName, pageInfo = phaseObj.getPreviousPage(pageName) | ||||
|         if buttonClicked in ('previous', 'next'): | ||||
|             # Go to the previous or next page for this object. We recompute the | ||||
|             # list of phases and pages because things may have changed since the | ||||
|             # object has been updated (ie, additional pages may be shown or | ||||
|             # hidden now, so the next and previous pages may have changed). | ||||
|             # Moreover, previous and next pages may not be available in "edit" | ||||
|             # mode, so we return the edit or view pages depending on page.show. | ||||
|             phaseObj = self.getAppyPhases(True, 'edit') | ||||
|             methodName = 'get%sPage' % buttonClicked.capitalize() | ||||
|             pageName, pageInfo = getattr(phaseObj, methodName)(pageName) | ||||
|             if pageName: | ||||
|                 # Return to the edit or view page? | ||||
|                 if pageInfo.showOnEdit: | ||||
|                     rq.set('page', pageName) | ||||
|                     # I do not use gotoEdit here because I really need to | ||||
|                     # redirect the user to the edit page. Indeed, the object | ||||
|                     # edit URL may have moved from temp_folder to another place. | ||||
|  | @ -480,21 +483,6 @@ class BaseMixin: | |||
|             else: | ||||
|                 obj.say(msg) | ||||
|                 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() | ||||
| 
 | ||||
|     def reindex(self, indexes=None, unindex=False): | ||||
|  |  | |||
							
								
								
									
										56
									
								
								gen/model.py
									
										
									
									
									
								
							
							
						
						
									
										56
									
								
								gen/model.py
									
										
									
									
									
								
							|  | @ -123,14 +123,16 @@ class ModelClass: | |||
|                 pages[appyType.page.name] = appyType.page | ||||
|         res += '    pges = {' | ||||
|         for page in pages.itervalues(): | ||||
|             # Determine page show | ||||
|             pageShow = page.show | ||||
|             if isinstance(pageShow, basestring): pageShow='"%s"' % pageShow | ||||
|             elif callable(pageShow): | ||||
|                 pageShow = '%s.%s' % (wrapperName, pageShow.__name__) | ||||
|             # Determine page "show" attributes | ||||
|             pShow = '' | ||||
|             if pageShow != True: | ||||
|                 pShow = ', show=%s' % pageShow | ||||
|             for attr in ('',) + page.subElements: | ||||
|                 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. | ||||
|             label = '' | ||||
|             if className == 'Translation': | ||||
|  | @ -148,34 +150,38 @@ class ModelClass: | |||
| 
 | ||||
| # The User class --------------------------------------------------------------- | ||||
| class User(ModelClass): | ||||
|     _appy_attributes = ['title', 'name', 'firstName', 'login', 'password1', | ||||
|                         'password2', 'email', 'roles', 'source', 'groups', | ||||
|                         'toTool'] | ||||
|     _appy_attributes = ['password1', 'password2', 'title', 'name', 'firstName', | ||||
|                         'login', 'email', 'roles', 'source', 'groups', '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. | ||||
|     title = gen.String(show=False, indexed=True) | ||||
|     gm = {'group': 'main', 'width': 34} | ||||
|     pm = {'page': gen.Page('main', showPrevious=False), 'group': 'main', | ||||
|           'width': 34} | ||||
|     title = gen.String(show=False, indexed=True, **pm) | ||||
|     def showName(self): pass | ||||
|     name = gen.String(show=showName, **gm) | ||||
|     firstName = gen.String(show=showName, **gm) | ||||
|     name = gen.String(show=showName, **pm) | ||||
|     firstName = gen.String(show=showName, **pm) | ||||
|     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 | ||||
|     # stored in an external LDAP (source='ldap'). | ||||
|     source = gen.String(show=False, default='zodb', layouts='f', **gm) | ||||
|     gm['multiplicity'] = (1,1) | ||||
|     source = gen.String(show=False, default='zodb', layouts='f', **pm) | ||||
|     pm['multiplicity'] = (1,1) | ||||
|     def showLogin(self): pass | ||||
|     def validateLogin(self): pass | ||||
|     login = gen.String(show=showLogin, validator=validateLogin, | ||||
|                        indexed=True, **gm) | ||||
|     def showPassword(self): pass | ||||
|     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) | ||||
|                        indexed=True, **pm) | ||||
|     pm['multiplicity'] = (0, None) | ||||
|     def showRoles(self): pass | ||||
|     roles = gen.String(show=showRoles, indexed=True, | ||||
|                        validator=gen.Selection('getGrantableRoles'), **gm) | ||||
|                        validator=gen.Selection('getGrantableRoles'), **pm) | ||||
| 
 | ||||
| # The Group class -------------------------------------------------------------- | ||||
| class Group(ModelClass): | ||||
|  |  | |||
|  | @ -379,6 +379,10 @@ msgstr "" | |||
| msgid "app_home" | ||||
| msgstr "" | ||||
| 
 | ||||
| #. Default: "Change password" | ||||
| msgid "change_password" | ||||
| msgstr "" | ||||
| 
 | ||||
| #. Default: "This login is reserved." | ||||
| msgid "login_reserved" | ||||
| msgstr "" | ||||
|  |  | |||
|  | @ -379,6 +379,10 @@ msgstr "كلمة السر" | |||
| msgid "app_home" | ||||
| msgstr "" | ||||
| 
 | ||||
| #. Default: "Change password" | ||||
| msgid "change_password" | ||||
| msgstr "" | ||||
| 
 | ||||
| #. Default: "This login is reserved." | ||||
| msgid "login_reserved" | ||||
| msgstr "" | ||||
|  |  | |||
|  | @ -379,6 +379,10 @@ msgstr "Passwort" | |||
| msgid "app_home" | ||||
| msgstr "" | ||||
| 
 | ||||
| #. Default: "Change password" | ||||
| msgid "change_password" | ||||
| msgstr "" | ||||
| 
 | ||||
| #. Default: "This login is reserved." | ||||
| msgid "login_reserved" | ||||
| msgstr "Dieses Login ist reserviert" | ||||
|  |  | |||
|  | @ -380,6 +380,10 @@ msgstr "Password" | |||
| msgid "app_home" | ||||
| msgstr "Home" | ||||
| 
 | ||||
| #. Default: "Change password" | ||||
| msgid "change_password" | ||||
| msgstr "Change password" | ||||
| 
 | ||||
| #. Default: "This login is reserved." | ||||
| msgid "login_reserved" | ||||
| msgstr "This login is reserved." | ||||
|  |  | |||
|  | @ -379,6 +379,10 @@ msgstr "Contraseña" | |||
| msgid "app_home" | ||||
| msgstr "volver a mi página principal" | ||||
| 
 | ||||
| #. Default: "Change password" | ||||
| msgid "change_password" | ||||
| msgstr "" | ||||
| 
 | ||||
| #. Default: "This login is reserved." | ||||
| msgid "login_reserved" | ||||
| msgstr "Este login está reservado." | ||||
|  |  | |||
|  | @ -380,6 +380,10 @@ msgstr "Mot de passe" | |||
| msgid "app_home" | ||||
| msgstr "Revenir à ma page principale" | ||||
| 
 | ||||
| #. Default: "Change password" | ||||
| msgid "change_password" | ||||
| msgstr "Changer le mot de passe" | ||||
| 
 | ||||
| #. Default: "This login is reserved." | ||||
| msgid "login_reserved" | ||||
| msgstr "Ce login est réservé." | ||||
|  |  | |||
|  | @ -379,6 +379,10 @@ msgstr "Password" | |||
| msgid "app_home" | ||||
| msgstr "Ritorno alla pagina di partenza" | ||||
| 
 | ||||
| #. Default: "Change password" | ||||
| msgid "change_password" | ||||
| msgstr "" | ||||
| 
 | ||||
| #. Default: "This login is reserved." | ||||
| msgid "login_reserved" | ||||
| msgstr "" | ||||
|  |  | |||
|  | @ -379,6 +379,10 @@ msgstr "Wachtwoord" | |||
| msgid "app_home" | ||||
| msgstr "Keer terug naar de hoofdpagina" | ||||
| 
 | ||||
| #. Default: "Change password" | ||||
| msgid "change_password" | ||||
| msgstr "" | ||||
| 
 | ||||
| #. Default: "This login is reserved." | ||||
| msgid "login_reserved" | ||||
| 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 } | ||||
| .userStrip a { color: #e7e7e7 } | ||||
| .userStrip a:visited { color: #e7e7e7 } | ||||
| .changePassword { color: #494949 !important; font-style:italic; font-size: 90% } | ||||
| .breadcrumb { font-size: 11pt; padding-bottom: 6px } | ||||
| .login { margin: 3px; color: black } | ||||
| 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 __nonzero__(self): return False | ||||
|     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.gen import WorkflowOwner | ||||
| from appy.gen.layout import summaryPageLayouts | ||||
|  | @ -12,6 +13,16 @@ class UserWrapper(AbstractWrapper): | |||
|     specialUsers = ('system', 'anon', 'admin') | ||||
|     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): | ||||
|         '''Returns a nice name for this user, based on available information: | ||||
|            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')"> | ||||
|                 <img src=":url('logout.gif')"/></a> | ||||
|               </td> | ||||
|               <td class="userStripText" var="userInfo=ztool.getUserLine()" | ||||
|                   align=":dright"> | ||||
|                <span>:userInfo[0]</span> | ||||
|                <a if="userInfo[1]" | ||||
|                   href=":userInfo[1]"><img src=":url('edit')"/></a> | ||||
|               </td> | ||||
|               <x>:user.pxUserLinks</x> | ||||
|              </tr> | ||||
|             </table> | ||||
|            </td> | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Gaetan Delannay
						Gaetan Delannay