[px] Added the possibility to have several PX actions in the same XHTML tag. If several PX action are defined, they are evaluated in this order: var, for, if.
This commit is contained in:
parent
1bc2a2f890
commit
5ece5c9831
|
@ -24,6 +24,8 @@ jsMessages = ('no_elem_selected', 'delete_confirm', 'unlink_confirm',
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class ToolMixin(BaseMixin):
|
class ToolMixin(BaseMixin):
|
||||||
_appy_meta_type = 'Tool'
|
_appy_meta_type = 'Tool'
|
||||||
|
xhtmlEncoding = 'text/html;charset=UTF-8'
|
||||||
|
|
||||||
def getPortalType(self, metaTypeOrAppyClass):
|
def getPortalType(self, metaTypeOrAppyClass):
|
||||||
'''Returns the name of the portal_type that is based on
|
'''Returns the name of the portal_type that is based on
|
||||||
p_metaTypeOrAppyType.'''
|
p_metaTypeOrAppyType.'''
|
||||||
|
@ -40,7 +42,7 @@ class ToolMixin(BaseMixin):
|
||||||
def home(self):
|
def home(self):
|
||||||
'''Returns the content of px ToolWrapper.pxHome.'''
|
'''Returns the content of px ToolWrapper.pxHome.'''
|
||||||
tool = self.appy()
|
tool = self.appy()
|
||||||
return tool.pxHome({'self': tool}).encode('utf-8')
|
return tool.pxHome({'self': tool})
|
||||||
|
|
||||||
def getHomePage(self):
|
def getHomePage(self):
|
||||||
'''Return the home page when a user hits the app.'''
|
'''Return the home page when a user hits the app.'''
|
||||||
|
|
|
@ -21,8 +21,13 @@ NOT_UNO_ENABLED_PYTHON = '"%s" is not a UNO-enabled Python interpreter. ' \
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class ToolWrapper(AbstractWrapper):
|
class ToolWrapper(AbstractWrapper):
|
||||||
|
|
||||||
pxHome = Px('''<p>Hello home</p>''',
|
pxHome = Px('''
|
||||||
template=AbstractWrapper.pxTemplate, hook='content')
|
<table width="300px" height="240px" align="center">
|
||||||
|
<tr valign="middle">
|
||||||
|
<td align="center">:_('front_page_text')</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
''', template=AbstractWrapper.pxTemplate, hook='content')
|
||||||
|
|
||||||
def validPythonWithUno(self, value):
|
def validPythonWithUno(self, value):
|
||||||
'''This method represents the validator for field unoEnabledPython.'''
|
'''This method represents the validator for field unoEnabledPython.'''
|
||||||
|
|
|
@ -28,9 +28,211 @@ class AbstractWrapper(object):
|
||||||
instance of this class.'''
|
instance of this class.'''
|
||||||
|
|
||||||
pxTemplate = Px('''
|
pxTemplate = Px('''
|
||||||
<html>
|
<html var="tool=self.tool; ztool=tool.o; user=tool.user;
|
||||||
<head><title>:self.title</title></head>
|
isAnon=ztool.userIsAnon(); app=ztool.getApp();
|
||||||
|
appUrl=app.absolute_url(); appFolder=app.data;
|
||||||
|
appName=ztool.getAppName(); _=ztool.translate;
|
||||||
|
req=ztool.REQUEST; resp=req.RESPONSE;
|
||||||
|
lang=ztool.getUserLanguage();
|
||||||
|
layoutType=ztool.getLayoutType();
|
||||||
|
contextObj=ztool.getPublishedObject(layoutType);
|
||||||
|
dir=ztool.getLanguageDirection(lang);
|
||||||
|
discreetLogin=ztool.getAttr('discreetLogin', source='config');
|
||||||
|
dleft=(dir == 'ltr') and 'left' or 'right';
|
||||||
|
dright=(dir == 'ltr') and 'right' or 'left';
|
||||||
|
x=resp.setHeader('Content-type', ztool.xhtmlEncoding);
|
||||||
|
x=resp.setHeader('Expires', 'Thu, 11 Dec 1975 12:05:00 GMT+2');
|
||||||
|
x=resp.setHeader('Content-Language', lang)"
|
||||||
|
dir=":ztool.getLanguageDirection(lang)">
|
||||||
|
<head>
|
||||||
|
<title>:_('app_name')</title>
|
||||||
|
<link rel="icon" type="image/x-icon" href="/favicon.ico"/>
|
||||||
|
<x for="name in ztool.getGlobalCssJs()">
|
||||||
|
<link if="name.endswith('.css') and \
|
||||||
|
not ((dir == 'ltr') and (name == 'appyrtl.css'))"
|
||||||
|
rel="stylesheet" type="text/css"
|
||||||
|
href=":'%s/ui/%s' % (appUrl, name)"/>
|
||||||
|
<script if="name.endswith('.js')" type="text/javascript"
|
||||||
|
src=":'%s/ui/%s' % (appUrl, name)"></script>
|
||||||
|
</x>
|
||||||
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<!-- Google Analytics stuff, if enabled -->
|
||||||
|
<script var="gaCode=ztool.getGoogleAnalyticsCode()" if="gaCode"
|
||||||
|
type="text/javascript">:gaCode</script>
|
||||||
|
|
||||||
|
<!-- Grey background shown when popups are shown -->
|
||||||
|
<div id="grey" class="grey"></div>
|
||||||
|
|
||||||
|
<!-- Popup for confirming an action -->
|
||||||
|
<div id="confirmActionPopup" class="popup">
|
||||||
|
<form id="confirmActionForm" method="post">
|
||||||
|
<div align="center">
|
||||||
|
<p id="appyConfirmText"></p>
|
||||||
|
<input type="hidden" name="actionType"/>
|
||||||
|
<input type="hidden" name="action"/>
|
||||||
|
<div id="commentArea" align=":dleft"><br/>
|
||||||
|
<span class="discreet">:_('workflow_comment')</span>
|
||||||
|
<textarea name="comment" cols="30" rows="3"></textarea>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
<input type="button" onclick="doConfirm()" value="_('yes')"/>
|
||||||
|
<input type="button" onclick="closePopup('confirmActionPopup')"
|
||||||
|
value="_('no')"/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Popup for reinitializing the password -->
|
||||||
|
<div id="askPasswordReinitPopup" class="popup"
|
||||||
|
if="isAnon and ztool.showForgotPassword()">
|
||||||
|
<form id="askPasswordReinitForm" method="post"
|
||||||
|
action=":ztool.absolute_url() + '/askPasswordReinit'">
|
||||||
|
<div align="center">
|
||||||
|
<p>:_('app_login')</p>
|
||||||
|
<input type="text" size="35" name="login" id="login" value=""/>
|
||||||
|
<br/><br/>
|
||||||
|
<input type="button" onclick="doAskPasswordReinit()"
|
||||||
|
value=":_('ask_password_reinit')"/>
|
||||||
|
<input type="button" onclick="closePopup('askPasswordReinitPopup')"
|
||||||
|
value="_('object_cancel')"/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<table class="main" align="center" cellpadding="0">
|
||||||
|
<tal:comment replace="nothing">Top banner</tal:comment>
|
||||||
|
<tr class="top" metal:define-slot="top">
|
||||||
|
<td tal:define="bannerName python: (dir == 'ltr') and 'banner' or 'bannerrtl'"
|
||||||
|
tal:attributes="style python: 'background-image: url(%s/ui/%s.jpg)' % (appUrl, bannerName)">
|
||||||
|
<tal:comment replace="nothing">Top links</tal:comment>
|
||||||
|
<div style="margin-top: 4px"
|
||||||
|
tal:define="pages tool/getMainPages" tal:attributes="align dright">
|
||||||
|
<tal:comment replace="nothing">Icon "home"</tal:comment>
|
||||||
|
<a class="pageLink" tal:attributes="href appUrl; title python: _('app_home')">
|
||||||
|
<img tal:attributes="src string: $appUrl/ui/home.gif" style="margin-right: 3px"/>
|
||||||
|
</a>
|
||||||
|
<tal:comment replace="nothing">Additional links (or icons) from icons.pt</tal:comment>
|
||||||
|
<metal:call use-macro="app/ui/icons/macros/links"/>
|
||||||
|
<tal:comment replace="nothing">Top-level pages</tal:comment>
|
||||||
|
<a tal:repeat="page pages" class="pageLink"
|
||||||
|
tal:content="page/title" tal:attributes="href page/absolute_url"></a>
|
||||||
|
<tal:comment replace="nothing">Connect link if discreet login</tal:comment>
|
||||||
|
<a id="loginLink" name="loginLink" onclick="showLoginForm()" class="pageLink" style="cursor:pointer"
|
||||||
|
tal:condition="python: isAnon and discreetLogin" tal:content="python: _('app_connect')">
|
||||||
|
</a>
|
||||||
|
<tal:comment replace="nothing">Language selector</tal:comment>
|
||||||
|
<tal:lg condition="tool/showLanguageSelector">
|
||||||
|
<select class="pageLink"
|
||||||
|
tal:define="languages tool/getLanguages;
|
||||||
|
defaultLanguage python: languages[0]"
|
||||||
|
tal:attributes="onchange string:window.location='$appUrl/config/changeLanguage?language=' + this.options[this.selectedIndex].value">
|
||||||
|
<option tal:repeat="lg languages"
|
||||||
|
tal:content="python: tool.getLanguageName(lg)"
|
||||||
|
tal:attributes="selected python:lang == lg; value lg">
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</tal:lg>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tal:comment replace="nothing">The message strip</tal:comment>
|
||||||
|
<tr valign="top">
|
||||||
|
<td>
|
||||||
|
<div style="position: relative">
|
||||||
|
<metal:msg use-macro="app/ui/page/macros/message"/>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tal:comment replace="nothing">The user strip</tal:comment>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<table class="userStrip" width="100%">
|
||||||
|
<tr>
|
||||||
|
<tal:comment replace="nothing">The user login form for anonymous users</tal:comment>
|
||||||
|
<td align="center"
|
||||||
|
tal:condition="python: isAnon and ('/temp_folder/' not in req['ACTUAL_URL'])">
|
||||||
|
<form id="loginForm" name="loginForm" method="post" class="login"
|
||||||
|
tal:attributes="action python: tool.absolute_url() + '/performLogin'">
|
||||||
|
<input type="hidden" name="js_enabled" id="js_enabled" value="0"/>
|
||||||
|
<input type="hidden" name="cookies_enabled" id="cookies_enabled" value=""/>
|
||||||
|
<input type="hidden" name="login_name" id="login_name" value=""/>
|
||||||
|
<input type="hidden" name="pwd_empty" id="pwd_empty" value="0"/>
|
||||||
|
<tal:comment replace="nothing">The login fields, directly shown or not (depends on discreetLogin)</tal:comment>
|
||||||
|
<span id="loginFields" name="loginFields"
|
||||||
|
tal:attributes="style python: discreetLogin and 'display:none' or 'display:block'">
|
||||||
|
<span class="userStripText" tal:content="python: _('app_login')"/>
|
||||||
|
<input type="text" name="__ac_name" id="__ac_name" value="" style="width: 142px"/>
|
||||||
|
<span class="userStripText" tal:content="python: _('app_password')"/>
|
||||||
|
<input type="password" name="__ac_password" id="__ac_password" style="width: 142px"/>
|
||||||
|
<input type="submit" name="submit" onclick="setLoginVars()"
|
||||||
|
tal:define="label python: _('app_connect')" tal:attributes="value label; alt label"/>
|
||||||
|
<tal:comment replace="nothing">Forgot password?</tal:comment>
|
||||||
|
<a class="lostPassword" href="javascript: openPopup('askPasswordReinitPopup')"
|
||||||
|
tal:condition="tool/showForgotPassword"
|
||||||
|
tal:content="python: _('forgot_password')">
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
<tal:comment replace="nothing">User info and controls for authenticated users</tal:comment>
|
||||||
|
<td tal:condition="not: isAnon">
|
||||||
|
<table class="buttons" width="99%">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<tal:comment replace="nothing">Config</tal:comment>
|
||||||
|
<a tal:condition="python: user.has_role('Manager')"
|
||||||
|
tal:attributes="href python: tool.getUrl(nav='');
|
||||||
|
title python: _('%sTool' % appName)">
|
||||||
|
<img tal:attributes="src string:$appUrl/ui/appyConfig.gif"/>
|
||||||
|
</a>
|
||||||
|
<tal:comment replace="nothing">Additional icons from icons.pt</tal:comment>
|
||||||
|
<metal:call use-macro="app/ui/icons/macros/icons"/>
|
||||||
|
<tal:comment replace="nothing">Log out</tal:comment>
|
||||||
|
<a tal:attributes="href python: tool.absolute_url() + '/performLogout';
|
||||||
|
title python: _('app_logout')">
|
||||||
|
<img tal:attributes="src string: $appUrl/ui/logout.gif"/>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td class="userStripText" tal:define="userInfo tool/getUserLine" tal:attributes="align dright">
|
||||||
|
<span tal:content="python: userInfo[0]"></span>
|
||||||
|
<a tal:condition="python: userInfo[1]"
|
||||||
|
tal:attributes="href python: userInfo[1]">
|
||||||
|
<img tal:attributes="src string: $appUrl/ui/edit.png"/>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tal:comment replace="nothing">The navigation strip</tal:comment>
|
||||||
|
<tr tal:condition="python: contextObj and (layoutType == 'view')">
|
||||||
|
<td><metal:navigate use-macro="app/ui/navigate/macros/navigationStrip"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<table width="100%" cellpadding="0" cellspacing="0">
|
||||||
|
<tr valign="top">
|
||||||
|
<tal:comment replace="nothing">Portlet</tal:comment>
|
||||||
|
<td tal:condition="python: tool.showPortlet(context, layoutType)" class="portlet">
|
||||||
|
<metal:portlet use-macro="app/ui/portlet/macros/portlet"/>
|
||||||
|
</td>
|
||||||
|
<tal:comment replace="nothing">Page content</tal:comment>
|
||||||
|
<td class="content"><span metal:define-slot="content"></span></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr><tal:comment replace="nothing">Footer</tal:comment>
|
||||||
|
<td><metal:call use-macro="app/ui/footer/macros/footer"/></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
-->
|
||||||
<x>:content</x>
|
<x>:content</x>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -48,6 +48,9 @@ class BufferAction:
|
||||||
self.fromExpr = fromExpr
|
self.fromExpr = fromExpr
|
||||||
# When an error occurs, must we raise it or write it into the buffer?
|
# When an error occurs, must we raise it or write it into the buffer?
|
||||||
self.raiseErrors = not self.buffer.pod
|
self.raiseErrors = not self.buffer.pod
|
||||||
|
# Several actions may co-exist for the same buffer, as a chain of
|
||||||
|
# BufferAction instances, defined via the following attribute.
|
||||||
|
self.subAction = None
|
||||||
|
|
||||||
def getExceptionLine(self, e):
|
def getExceptionLine(self, e):
|
||||||
'''Gets the line describing exception p_e, containing the pathname of
|
'''Gets the line describing exception p_e, containing the pathname of
|
||||||
|
@ -120,6 +123,13 @@ class BufferAction:
|
||||||
if not error:
|
if not error:
|
||||||
result.write(feRes)
|
result.write(feRes)
|
||||||
|
|
||||||
|
def addSubAction(self, action):
|
||||||
|
'''Adds p_action as a sub-action of this action.'''
|
||||||
|
if not self.subAction:
|
||||||
|
self.subAction = action
|
||||||
|
else:
|
||||||
|
self.subAction.addSubAction(action)
|
||||||
|
|
||||||
class IfAction(BufferAction):
|
class IfAction(BufferAction):
|
||||||
'''Action that determines if we must include the content of the buffer in
|
'''Action that determines if we must include the content of the buffer in
|
||||||
the result or not.'''
|
the result or not.'''
|
||||||
|
@ -227,7 +237,12 @@ class ForAction(BufferAction):
|
||||||
result.dumpEndElement(Row.OD.elem)
|
result.dumpEndElement(Row.OD.elem)
|
||||||
result.dumpStartElement(Row.OD.elem, rowAttributes)
|
result.dumpStartElement(Row.OD.elem, rowAttributes)
|
||||||
currentColIndex = 0
|
currentColIndex = 0
|
||||||
self.evaluateBuffer(result, context)
|
# If a sub-action is defined, execute it.
|
||||||
|
if self.subAction:
|
||||||
|
self.subAction.execute(result, context)
|
||||||
|
else:
|
||||||
|
# Evaluate the buffer directly.
|
||||||
|
self.evaluateBuffer(result, context)
|
||||||
# Cell: increment the current column index
|
# Cell: increment the current column index
|
||||||
if isCell:
|
if isCell:
|
||||||
currentColIndex += 1
|
currentColIndex += 1
|
||||||
|
@ -307,8 +322,12 @@ class VariablesAction(BufferAction):
|
||||||
hidden[name] = context[name]
|
hidden[name] = context[name]
|
||||||
# Store the result into the context
|
# Store the result into the context
|
||||||
context[name] = vRes
|
context[name] = vRes
|
||||||
# Evaluate the buffer
|
# If a sub-action is defined, execute it.
|
||||||
self.evaluateBuffer(result, context)
|
if self.subAction:
|
||||||
|
self.subAction.execute(result, context)
|
||||||
|
else:
|
||||||
|
# Evaluate the buffer directly.
|
||||||
|
self.evaluateBuffer(result, context)
|
||||||
# Restore hidden variables if any
|
# Restore hidden variables if any
|
||||||
if hidden: context.update(hidden)
|
if hidden: context.update(hidden)
|
||||||
# Delete not-hidden variables
|
# Delete not-hidden variables
|
||||||
|
|
|
@ -471,6 +471,9 @@ class MemoryBuffer(Buffer):
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def createPxAction(self, elem, actionType, statement):
|
def createPxAction(self, elem, actionType, statement):
|
||||||
|
'''Creates a PX action and link it to this buffer. If an action is
|
||||||
|
already linked to this buffer (in self.action), this action is
|
||||||
|
chained behind the last action via self.action.subAction.'''
|
||||||
res = 0
|
res = 0
|
||||||
statement = statement.strip()
|
statement = statement.strip()
|
||||||
if actionType == 'for':
|
if actionType == 'for':
|
||||||
|
@ -478,15 +481,19 @@ class MemoryBuffer(Buffer):
|
||||||
if not forRes:
|
if not forRes:
|
||||||
raise ParsingError(BAD_FOR_EXPRESSION % statement)
|
raise ParsingError(BAD_FOR_EXPRESSION % statement)
|
||||||
iter, subExpr = forRes.groups()
|
iter, subExpr = forRes.groups()
|
||||||
self.action = ForAction('for', self, subExpr, elem, False, iter,
|
action = ForAction('for', self, subExpr, elem, False, iter,
|
||||||
'buffer', None)
|
'buffer', None)
|
||||||
elif actionType == 'if':
|
elif actionType == 'if':
|
||||||
self.action = IfAction('if', self, statement, elem, False,
|
action= IfAction('if', self, statement, elem, False, 'buffer', None)
|
||||||
'buffer', None)
|
|
||||||
elif actionType == 'var':
|
elif actionType == 'var':
|
||||||
variables = self._getVariables(statement)
|
variables = self._getVariables(statement)
|
||||||
self.action = VariablesAction('var', self, elem, False, variables,
|
action = VariablesAction('var', self, elem, False, variables,
|
||||||
'buffer', None)
|
'buffer', None)
|
||||||
|
# Is it the first action for this buffer or not?
|
||||||
|
if not self.action:
|
||||||
|
self.action = action
|
||||||
|
else:
|
||||||
|
self.action.addSubAction(action)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def cut(self, index, keepFirstPart):
|
def cut(self, index, keepFirstPart):
|
||||||
|
|
|
@ -46,7 +46,7 @@ class PxParser(XmlParser):
|
||||||
'''PX parser that is specific for parsing PX data.'''
|
'''PX parser that is specific for parsing PX data.'''
|
||||||
pxAttributes = ('var', 'for', 'if')
|
pxAttributes = ('var', 'for', 'if')
|
||||||
# No-end tags
|
# No-end tags
|
||||||
noEndTags = ('br', 'img')
|
noEndTags = ('br', 'img', 'link', 'input')
|
||||||
|
|
||||||
def __init__(self, env, caller=None):
|
def __init__(self, env, caller=None):
|
||||||
XmlParser.__init__(self, env, caller)
|
XmlParser.__init__(self, env, caller)
|
||||||
|
@ -56,13 +56,16 @@ class PxParser(XmlParser):
|
||||||
e = self.env
|
e = self.env
|
||||||
self.currentElem = elem
|
self.currentElem = elem
|
||||||
# See if we have a PX attribute among p_attrs.
|
# See if we have a PX attribute among p_attrs.
|
||||||
|
found = False
|
||||||
for name in self.pxAttributes:
|
for name in self.pxAttributes:
|
||||||
if attrs.has_key(name):
|
if attrs.has_key(name):
|
||||||
# Dump the element in a new sub-buffer
|
if not found:
|
||||||
e.addSubBuffer()
|
# This is the first PX attr we find.
|
||||||
# Create the action for this buffer
|
# Create a sub-buffer with an action.
|
||||||
|
e.addSubBuffer()
|
||||||
|
found = True
|
||||||
|
# Add the action.
|
||||||
e.currentBuffer.createPxAction(elem, name, attrs[name])
|
e.currentBuffer.createPxAction(elem, name, attrs[name])
|
||||||
break
|
|
||||||
if e.isActionElem(elem):
|
if e.isActionElem(elem):
|
||||||
# Add a temp element in the buffer (that will be unreferenced
|
# Add a temp element in the buffer (that will be unreferenced
|
||||||
# later). This way, when encountering the corresponding end element,
|
# later). This way, when encountering the corresponding end element,
|
||||||
|
|
Loading…
Reference in a new issue