appy.gen: allow to define a method Tool.getHomePage that returns the URL of the home page for any user (authenticated, anonymous, manager, or any other code-based distinction); allow an app to define a 'ui' folder for storing new UI-related elements (pages, images, etc) or overriding standard Appy UI elements; logo.jog is replaced with banner.jpg (size 900 x 75px).
This commit is contained in:
		
							parent
							
								
									a80ef513ff
								
							
						
					
					
						commit
						9394490d33
					
				
					 7 changed files with 81 additions and 60 deletions
				
			
		|  | @ -13,13 +13,9 @@ from appy.shared.data import languages | |||
| 
 | ||||
| # ------------------------------------------------------------------------------ | ||||
| homePage = ''' | ||||
| <tal:main define="tool python: context.config"> | ||||
|  <html metal:use-macro="context/ui/template/macros/main"> | ||||
|   <div metal:fill-slot="content"> | ||||
|    <span tal:replace="structure python: tool.translate('front_page_text')"/> | ||||
|   </div> | ||||
|  </html> | ||||
| </tal:main> | ||||
| <tal:hp define="tool python: context.config; | ||||
|                 dummy python: request.RESPONSE.redirect(tool.getHomePage())"> | ||||
| </tal:hp> | ||||
| ''' | ||||
| errorPage = ''' | ||||
| <tal:main define="tool python: context.config" | ||||
|  | @ -98,9 +94,15 @@ class ZopeInstaller: | |||
|         zopeContent = self.app.objectIds() | ||||
|         if 'ui' in zopeContent: self.app.manage_delObjects(['ui']) | ||||
|         manage_addFolder(self.app, 'ui') | ||||
|         # Browse the physical folder and re-create it in the Zope folder | ||||
|         # Browse the physical ui folders (the Appy one and an app-specific, if | ||||
|         # the app defines one) and create the corresponding objects in the Zope | ||||
|         # folder. In the case of files having the same name in both folders, | ||||
|         # the one from the app-specific folder is chosen. | ||||
|         j = os.path.join | ||||
|         ui = j(j(appy.getPath(), 'gen'), 'ui') | ||||
|         uiFolders = [j(j(appy.getPath(), 'gen'), 'ui')] | ||||
|         appUi = j(self.config.diskFolder, 'ui') | ||||
|         if os.path.exists(appUi): uiFolders.insert(0, appUi) | ||||
|         for ui in uiFolders: | ||||
|             for root, dirs, files in os.walk(ui): | ||||
|                 folderName = root[len(ui):] | ||||
|                 # Get the Zope folder that corresponds to this name | ||||
|  | @ -109,21 +111,28 @@ class ZopeInstaller: | |||
|                     for name in folderName.strip(os.sep).split(os.sep): | ||||
|                         zopeFolder = zopeFolder._getOb(name) | ||||
|                 # Create sub-folders at this level | ||||
|             for name in dirs: manage_addFolder(zopeFolder, name) | ||||
|                 for name in dirs: | ||||
|                     if not hasattr(zopeFolder.aq_base, name): | ||||
|                         manage_addFolder(zopeFolder, name) | ||||
|                 # Create files at this level | ||||
|                 for name in files: | ||||
|                 baseName, ext = os.path.splitext(name) | ||||
|                     zopeName, ext = os.path.splitext(name) | ||||
|                     if ext not in ('.pt', '.py'): | ||||
|                         # In the ZODB, pages and scripts have their name without | ||||
|                         # their extension. | ||||
|                         zopeName = name | ||||
|                     if hasattr(zopeFolder.aq_base, zopeName): continue | ||||
|                     f = file(j(root, name)) | ||||
|                     if ext in gen.File.imageExts: | ||||
|                     manage_addImage(zopeFolder, name, f) | ||||
|                         manage_addImage(zopeFolder, zopeName, f) | ||||
|                     elif ext == '.pt': | ||||
|                     manage_addPageTemplate(zopeFolder, baseName, '', f.read()) | ||||
|                         manage_addPageTemplate(zopeFolder,zopeName,'',f.read()) | ||||
|                     elif ext == '.py': | ||||
|                     obj = PythonScript(baseName) | ||||
|                     zopeFolder._setObject(baseName, obj) | ||||
|                     zopeFolder._getOb(baseName).write(f.read()) | ||||
|                         obj = PythonScript(zopeName) | ||||
|                         zopeFolder._setObject(zopeName, obj) | ||||
|                         zopeFolder._getOb(zopeName).write(f.read()) | ||||
|                     else: | ||||
|                     manage_addFile(zopeFolder, name, f) | ||||
|                         manage_addFile(zopeFolder, zopeName, f) | ||||
|                     f.close() | ||||
|         # Update the home page | ||||
|         if 'index_html' in zopeContent: | ||||
|  |  | |||
|  | @ -33,6 +33,21 @@ class ToolMixin(BaseMixin): | |||
|         if res in ('User', 'Group', 'Translation'): res = appName + res | ||||
|         return res | ||||
| 
 | ||||
|     def getHomePage(self): | ||||
|         '''Return the home page when a user hits the app.''' | ||||
|         # If the app defines a method "getHomePage", call it. | ||||
|         appyTool = self.appy() | ||||
|         try: | ||||
|             url = appyTool.getHomePage() | ||||
|         except AttributeError: | ||||
|             # Bring Managers to the config, lead others to home.pt. | ||||
|             user = self.getUser() | ||||
|             if user.has_role('Manager'): | ||||
|                 url = self.goto(self.absolute_url()) | ||||
|             else: | ||||
|                 url = self.goto(self.getApp().ui.home.absolute_url()) | ||||
|         return url | ||||
| 
 | ||||
|     def getCatalog(self): | ||||
|         '''Returns the catalog object.''' | ||||
|         return self.getParentNode().catalog | ||||
|  | @ -829,20 +844,13 @@ class ToolMixin(BaseMixin): | |||
|         user = self.acl_users.validate(rq) | ||||
|         if self.userIsAnon(): | ||||
|             rq.RESPONSE.expireCookie('__ac', path='/') | ||||
|             msg = 'Login failed' # XXX to translate | ||||
|             logMsg = 'Authentication failed (tried with login "%s")' % login | ||||
|             msg = 'Login failed.' # XXX to translate | ||||
|             logMsg = 'Authentication failed (tried with login "%s").' % login | ||||
|         else: | ||||
|             msg = 'Welcome! You are now logged in.' # XXX to translate | ||||
|             logMsg = 'User "%s" has been logged in.' % login | ||||
|         self.log(logMsg) | ||||
|         # Bring Managers to the config, leave others on the main page. | ||||
|         user = self.getUser() | ||||
|         if user.has_role('Manager'): | ||||
|             # Bring the user to the configuration | ||||
|             url = self.goto(self.absolute_url(), msg) | ||||
|         else: | ||||
|             url = self.goto(rq['HTTP_REFERER'], msg) | ||||
|         return url | ||||
|         return self.goto(self.getApp().absolute_url(), msg) | ||||
| 
 | ||||
|     def performLogout(self): | ||||
|         '''Logs out the current user when he clicks on "disconnect".''' | ||||
|  |  | |||
|  | @ -40,15 +40,16 @@ 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: #a2a2a2; height: 30px; | ||||
|              border-top: 3px solid #525252; border-bottom: 2px solid #9b0000; } | ||||
| .userStrip { background-color: #89A6B1; height: 30px; | ||||
|              border-top: 3px solid #405A64; border-bottom: 2px solid #5F7983; } | ||||
| .login { margin-top: 2px; margin-bottom: 2px; color: white;} | ||||
| .buttons { margin-left: 4px;} | ||||
| .message { color: #9b0000; font-style: italic; | ||||
|            position: absolute; top: -15px; right: 5px} | ||||
| .message { color: #fd9c03; position: absolute; top: -55px; left: 100px; | ||||
|            width: 700px; border: 1px black dashed; padding: 2px 6px; | ||||
|            background-color: #f4f5f6} | ||||
| .discreet { font-size: 90%; } | ||||
| .portlet { width: 150px; padding: 12px 9px 9px 9px; | ||||
|            border-right: 1px solid #9b0000;} | ||||
|            border-right: 2px solid #5F7983;} | ||||
| .portletTitle { font-weight: bold; font-size: 110%; margin-bottom: 4px;} | ||||
| .portletCurrent { font-weight: bold; } | ||||
| .portletSep { border-top: 1px solid grey; margin-top: 9px; padding-top: 9px;} | ||||
|  | @ -56,7 +57,7 @@ img {border: 0} | |||
| .portletGroup { font-variant: small-caps; font-weight: bold; font-style: normal; | ||||
|                 margin: 0.4em 0 0.2em 0; } | ||||
| .phase { border-style: dashed; border-width: thin; padding: 0 0.6em 5px 1em;} | ||||
| .phaseSelected { background-color: #EDEDED; } | ||||
| .phaseSelected { background-color: #F4F5F6; } | ||||
| .content { padding: 14px 3px 9px 15px;} | ||||
| .grey { display: none; position: absolute; left: 0px; top: 0px; | ||||
|         background:grey; opacity:0.5; -moz-opacity:0.5; -khtml-opacity:0.5; | ||||
|  | @ -67,7 +68,7 @@ img {border: 0} | |||
| .list { border: 1px solid grey; margin-bottom: 3px;} | ||||
| .list td, .list th { border: 1px solid grey; | ||||
|                      padding-left: 5px; padding-right: 5px; padding-top: 3px;} | ||||
| .list th { background-color: #D4D4D4; font-style: italic; font-weight: normal;} | ||||
| .list th { background-color: #c8d7e1; font-style: italic; font-weight: normal;} | ||||
| .grid th { font-style: italic; font-weight: normal; | ||||
|            border-bottom: 2px solid grey; padding: 2px 2px;} | ||||
| .grid td { padding-right: 5px; } | ||||
|  | @ -83,14 +84,14 @@ img {border: 0} | |||
| .section3 { font-size: 100%; font-style: italic; margin: 0.45em 0em 0.1em 0; | ||||
|             background-color: #efeae8; text-align: center; color: grey; } | ||||
| .odd { background-color: white; } | ||||
| .even { background-color: #ededed; } | ||||
| .even { background-color: #F4F5F6; } | ||||
| .summary {margin-bottom: 5px;} | ||||
| .objectTitle { font-size: 11pt; border-bottom: 3px solid grey; | ||||
|                font-weight: bold;} | ||||
| .by { padding-top: 5px;} | ||||
| .workflow { text-align: center; border-top: 1px solid grey; | ||||
|             background-color: #f8f8f8;} | ||||
| .underTitle { background-color: #ededed;} | ||||
| .underTitle { background-color: #F4F5F6;} | ||||
| .objectNavigate { margin-top: 3px;} | ||||
| .underline { border-bottom: 1px dotted grey;} | ||||
| .state { font-weight: bold; border-bottom: 1px dashed grey;} | ||||
|  |  | |||
							
								
								
									
										
											BIN
										
									
								
								gen/ui/logo.jpg
									
										
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								gen/ui/logo.jpg
									
										
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 4.3 KiB | 
|  | @ -1,4 +1,4 @@ | |||
| <tal:main define="tool context/getParentNode"> | ||||
| <tal:main define="tool python: context.config"> | ||||
| <html metal:use-macro="context/ui/template/macros/main"> | ||||
|  <metal:fill fill-slot="content" | ||||
|    tal:define="className request/className; | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| <tal:main define="tool context/getParentNode"> | ||||
| <tal:main define="tool python: context.config"> | ||||
| <html metal:use-macro="context/ui/template/macros/main"> | ||||
|  <metal:fill fill-slot="content" | ||||
|    tal:define="className request/className; | ||||
|  |  | |||
|  | @ -14,19 +14,22 @@ | |||
| 
 | ||||
| <head> | ||||
|   <title tal:content="tool/getAppName"></title> | ||||
|   <link rel="stylesheet" type="text/css" tal:attributes="href string:$appUrl/ui/appy.css"/> | ||||
|   <script type="text/javascript" tal:attributes="src string:$appUrl/ui/appy.js"></script> | ||||
|   <tal:link repeat="name python: app.ui.objectIds('File')"> | ||||
|     <link tal:condition="python: name.endswith('.css')" | ||||
|           rel="stylesheet" type="text/css" tal:attributes="href string:$appUrl/ui/$name"/> | ||||
|     <script tal:condition="python: name.endswith('.js')" | ||||
|           type="text/javascript" tal:attributes="src string:$appUrl/ui/$name"></script> | ||||
|   </tal:link> | ||||
| </head> | ||||
| 
 | ||||
| <body tal:on-error="structure python: tool.manageError(error)"> | ||||
| <table class="main" align="center" cellpadding="0"> | ||||
|  <tal:comment replace="nothing">Top banner</tal:comment> | ||||
|  <tr class="top" metal:define-slot="top"> | ||||
|   <td> | ||||
|   <td tal:attributes="style python: 'background-image: url(%s/ui/banner.jpg)' % appUrl"> | ||||
|    <table width="100%"> | ||||
|     <tr valign="top"> | ||||
|      <tal:comment replace="nothing">Logo</tal:comment> | ||||
|      <td><a tal:attributes="href appUrl"><img tal:attributes="src string: $appUrl/ui/logo.jpg"/></a></td> | ||||
|      <td></td> | ||||
|      <tal:comment replace="nothing">Language selector (links or listbox)</tal:comment> | ||||
|      <td align="right" | ||||
|          tal:define="languages tool/getLanguages; | ||||
|  | @ -59,7 +62,7 @@ | |||
|  <tal:comment replace="nothing">The message strip</tal:comment> | ||||
|  <tr> | ||||
|   <td> | ||||
|    <div style="position: relative" align="right"> | ||||
|    <div style="position: relative"> | ||||
|     <metal:msg use-macro="app/ui/page/macros/message"/> | ||||
|    </div> | ||||
|    <tal:comment replace="nothing">Grey background shown when popups are shown</tal:comment> | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Gaetan Delannay
						Gaetan Delannay