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
|
@ -13,13 +13,9 @@ from appy.shared.data import languages
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
homePage = '''
|
homePage = '''
|
||||||
<tal:main define="tool python: context.config">
|
<tal:hp define="tool python: context.config;
|
||||||
<html metal:use-macro="context/ui/template/macros/main">
|
dummy python: request.RESPONSE.redirect(tool.getHomePage())">
|
||||||
<div metal:fill-slot="content">
|
</tal:hp>
|
||||||
<span tal:replace="structure python: tool.translate('front_page_text')"/>
|
|
||||||
</div>
|
|
||||||
</html>
|
|
||||||
</tal:main>
|
|
||||||
'''
|
'''
|
||||||
errorPage = '''
|
errorPage = '''
|
||||||
<tal:main define="tool python: context.config"
|
<tal:main define="tool python: context.config"
|
||||||
|
@ -98,9 +94,15 @@ class ZopeInstaller:
|
||||||
zopeContent = self.app.objectIds()
|
zopeContent = self.app.objectIds()
|
||||||
if 'ui' in zopeContent: self.app.manage_delObjects(['ui'])
|
if 'ui' in zopeContent: self.app.manage_delObjects(['ui'])
|
||||||
manage_addFolder(self.app, '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
|
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):
|
for root, dirs, files in os.walk(ui):
|
||||||
folderName = root[len(ui):]
|
folderName = root[len(ui):]
|
||||||
# Get the Zope folder that corresponds to this name
|
# 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):
|
for name in folderName.strip(os.sep).split(os.sep):
|
||||||
zopeFolder = zopeFolder._getOb(name)
|
zopeFolder = zopeFolder._getOb(name)
|
||||||
# Create sub-folders at this level
|
# 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
|
# Create files at this level
|
||||||
for name in files:
|
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))
|
f = file(j(root, name))
|
||||||
if ext in gen.File.imageExts:
|
if ext in gen.File.imageExts:
|
||||||
manage_addImage(zopeFolder, name, f)
|
manage_addImage(zopeFolder, zopeName, f)
|
||||||
elif ext == '.pt':
|
elif ext == '.pt':
|
||||||
manage_addPageTemplate(zopeFolder, baseName, '', f.read())
|
manage_addPageTemplate(zopeFolder,zopeName,'',f.read())
|
||||||
elif ext == '.py':
|
elif ext == '.py':
|
||||||
obj = PythonScript(baseName)
|
obj = PythonScript(zopeName)
|
||||||
zopeFolder._setObject(baseName, obj)
|
zopeFolder._setObject(zopeName, obj)
|
||||||
zopeFolder._getOb(baseName).write(f.read())
|
zopeFolder._getOb(zopeName).write(f.read())
|
||||||
else:
|
else:
|
||||||
manage_addFile(zopeFolder, name, f)
|
manage_addFile(zopeFolder, zopeName, f)
|
||||||
f.close()
|
f.close()
|
||||||
# Update the home page
|
# Update the home page
|
||||||
if 'index_html' in zopeContent:
|
if 'index_html' in zopeContent:
|
||||||
|
|
|
@ -33,6 +33,21 @@ class ToolMixin(BaseMixin):
|
||||||
if res in ('User', 'Group', 'Translation'): res = appName + res
|
if res in ('User', 'Group', 'Translation'): res = appName + res
|
||||||
return 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):
|
def getCatalog(self):
|
||||||
'''Returns the catalog object.'''
|
'''Returns the catalog object.'''
|
||||||
return self.getParentNode().catalog
|
return self.getParentNode().catalog
|
||||||
|
@ -829,20 +844,13 @@ class ToolMixin(BaseMixin):
|
||||||
user = self.acl_users.validate(rq)
|
user = self.acl_users.validate(rq)
|
||||||
if self.userIsAnon():
|
if self.userIsAnon():
|
||||||
rq.RESPONSE.expireCookie('__ac', path='/')
|
rq.RESPONSE.expireCookie('__ac', path='/')
|
||||||
msg = 'Login failed' # XXX to translate
|
msg = 'Login failed.' # XXX to translate
|
||||||
logMsg = 'Authentication failed (tried with login "%s")' % login
|
logMsg = 'Authentication failed (tried with login "%s").' % login
|
||||||
else:
|
else:
|
||||||
msg = 'Welcome! You are now logged in.' # XXX to translate
|
msg = 'Welcome! You are now logged in.' # XXX to translate
|
||||||
logMsg = 'User "%s" has been logged in.' % login
|
logMsg = 'User "%s" has been logged in.' % login
|
||||||
self.log(logMsg)
|
self.log(logMsg)
|
||||||
# Bring Managers to the config, leave others on the main page.
|
return self.goto(self.getApp().absolute_url(), msg)
|
||||||
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
|
|
||||||
|
|
||||||
def performLogout(self):
|
def performLogout(self):
|
||||||
'''Logs out the current user when he clicks on "disconnect".'''
|
'''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; }
|
border-style: solid; border-width: 1px; border-color: grey; }
|
||||||
.top { height: 75px; margin-left: 3em; vertical-align: top;}
|
.top { height: 75px; margin-left: 3em; vertical-align: top;}
|
||||||
.lang { margin-right: 3px; }
|
.lang { margin-right: 3px; }
|
||||||
.userStrip { background-color: #a2a2a2; height: 30px;
|
.userStrip { background-color: #89A6B1; height: 30px;
|
||||||
border-top: 3px solid #525252; border-bottom: 2px solid #9b0000; }
|
border-top: 3px solid #405A64; border-bottom: 2px solid #5F7983; }
|
||||||
.login { margin-top: 2px; margin-bottom: 2px; color: white;}
|
.login { margin-top: 2px; margin-bottom: 2px; color: white;}
|
||||||
.buttons { margin-left: 4px;}
|
.buttons { margin-left: 4px;}
|
||||||
.message { color: #9b0000; font-style: italic;
|
.message { color: #fd9c03; position: absolute; top: -55px; left: 100px;
|
||||||
position: absolute; top: -15px; right: 5px}
|
width: 700px; border: 1px black dashed; padding: 2px 6px;
|
||||||
|
background-color: #f4f5f6}
|
||||||
.discreet { font-size: 90%; }
|
.discreet { font-size: 90%; }
|
||||||
.portlet { width: 150px; padding: 12px 9px 9px 9px;
|
.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;}
|
.portletTitle { font-weight: bold; font-size: 110%; margin-bottom: 4px;}
|
||||||
.portletCurrent { font-weight: bold; }
|
.portletCurrent { font-weight: bold; }
|
||||||
.portletSep { border-top: 1px solid grey; margin-top: 9px; padding-top: 9px;}
|
.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;
|
.portletGroup { font-variant: small-caps; font-weight: bold; font-style: normal;
|
||||||
margin: 0.4em 0 0.2em 0; }
|
margin: 0.4em 0 0.2em 0; }
|
||||||
.phase { border-style: dashed; border-width: thin; padding: 0 0.6em 5px 1em;}
|
.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;}
|
.content { padding: 14px 3px 9px 15px;}
|
||||||
.grey { display: none; position: absolute; left: 0px; top: 0px;
|
.grey { display: none; position: absolute; left: 0px; top: 0px;
|
||||||
background:grey; opacity:0.5; -moz-opacity:0.5; -khtml-opacity:0.5;
|
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 { border: 1px solid grey; margin-bottom: 3px;}
|
||||||
.list td, .list th { border: 1px solid grey;
|
.list td, .list th { border: 1px solid grey;
|
||||||
padding-left: 5px; padding-right: 5px; padding-top: 3px;}
|
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;
|
.grid th { font-style: italic; font-weight: normal;
|
||||||
border-bottom: 2px solid grey; padding: 2px 2px;}
|
border-bottom: 2px solid grey; padding: 2px 2px;}
|
||||||
.grid td { padding-right: 5px; }
|
.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;
|
.section3 { font-size: 100%; font-style: italic; margin: 0.45em 0em 0.1em 0;
|
||||||
background-color: #efeae8; text-align: center; color: grey; }
|
background-color: #efeae8; text-align: center; color: grey; }
|
||||||
.odd { background-color: white; }
|
.odd { background-color: white; }
|
||||||
.even { background-color: #ededed; }
|
.even { background-color: #F4F5F6; }
|
||||||
.summary {margin-bottom: 5px;}
|
.summary {margin-bottom: 5px;}
|
||||||
.objectTitle { font-size: 11pt; border-bottom: 3px solid grey;
|
.objectTitle { font-size: 11pt; border-bottom: 3px solid grey;
|
||||||
font-weight: bold;}
|
font-weight: bold;}
|
||||||
.by { padding-top: 5px;}
|
.by { padding-top: 5px;}
|
||||||
.workflow { text-align: center; border-top: 1px solid grey;
|
.workflow { text-align: center; border-top: 1px solid grey;
|
||||||
background-color: #f8f8f8;}
|
background-color: #f8f8f8;}
|
||||||
.underTitle { background-color: #ededed;}
|
.underTitle { background-color: #F4F5F6;}
|
||||||
.objectNavigate { margin-top: 3px;}
|
.objectNavigate { margin-top: 3px;}
|
||||||
.underline { border-bottom: 1px dotted grey;}
|
.underline { border-bottom: 1px dotted grey;}
|
||||||
.state { font-weight: bold; border-bottom: 1px dashed 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">
|
<html metal:use-macro="context/ui/template/macros/main">
|
||||||
<metal:fill fill-slot="content"
|
<metal:fill fill-slot="content"
|
||||||
tal:define="className request/className;
|
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">
|
<html metal:use-macro="context/ui/template/macros/main">
|
||||||
<metal:fill fill-slot="content"
|
<metal:fill fill-slot="content"
|
||||||
tal:define="className request/className;
|
tal:define="className request/className;
|
||||||
|
|
|
@ -14,19 +14,22 @@
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title tal:content="tool/getAppName"></title>
|
<title tal:content="tool/getAppName"></title>
|
||||||
<link rel="stylesheet" type="text/css" tal:attributes="href string:$appUrl/ui/appy.css"/>
|
<tal:link repeat="name python: app.ui.objectIds('File')">
|
||||||
<script type="text/javascript" tal:attributes="src string:$appUrl/ui/appy.js"></script>
|
<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>
|
</head>
|
||||||
|
|
||||||
<body tal:on-error="structure python: tool.manageError(error)">
|
<body tal:on-error="structure python: tool.manageError(error)">
|
||||||
<table class="main" align="center" cellpadding="0">
|
<table class="main" align="center" cellpadding="0">
|
||||||
<tal:comment replace="nothing">Top banner</tal:comment>
|
<tal:comment replace="nothing">Top banner</tal:comment>
|
||||||
<tr class="top" metal:define-slot="top">
|
<tr class="top" metal:define-slot="top">
|
||||||
<td>
|
<td tal:attributes="style python: 'background-image: url(%s/ui/banner.jpg)' % appUrl">
|
||||||
<table width="100%">
|
<table width="100%">
|
||||||
<tr valign="top">
|
<tr valign="top">
|
||||||
<tal:comment replace="nothing">Logo</tal:comment>
|
<td></td>
|
||||||
<td><a tal:attributes="href appUrl"><img tal:attributes="src string: $appUrl/ui/logo.jpg"/></a></td>
|
|
||||||
<tal:comment replace="nothing">Language selector (links or listbox)</tal:comment>
|
<tal:comment replace="nothing">Language selector (links or listbox)</tal:comment>
|
||||||
<td align="right"
|
<td align="right"
|
||||||
tal:define="languages tool/getLanguages;
|
tal:define="languages tool/getLanguages;
|
||||||
|
@ -59,7 +62,7 @@
|
||||||
<tal:comment replace="nothing">The message strip</tal:comment>
|
<tal:comment replace="nothing">The message strip</tal:comment>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<div style="position: relative" align="right">
|
<div style="position: relative">
|
||||||
<metal:msg use-macro="app/ui/page/macros/message"/>
|
<metal:msg use-macro="app/ui/page/macros/message"/>
|
||||||
</div>
|
</div>
|
||||||
<tal:comment replace="nothing">Grey background shown when popups are shown</tal:comment>
|
<tal:comment replace="nothing">Grey background shown when popups are shown</tal:comment>
|
||||||
|
|
Loading…
Reference in a new issue