Improved class appy.Hack to be able to hack static methods as well; [gen] pod field can now accept a string in param 'showTemplate' if a single format is accepted; when showing linked objects in a ref field, we show a message and no data for every object the currently logged user is not allowed to read; improved the default UI layout (less table borders).

This commit is contained in:
Gaetan Delannay 2014-05-03 15:18:41 +02:00
parent 0a174098f1
commit b2dbef2bc4
16 changed files with 79 additions and 22 deletions

View file

@ -49,21 +49,33 @@ class Hack:
'''This class proposes methods for patching some existing code with '''This class proposes methods for patching some existing code with
alternative methods.''' alternative methods.'''
@staticmethod @staticmethod
def patch(method, replacement): def patch(method, replacement, klass=None):
'''This method replaces m_method with a p_replacement method, but '''This method replaces m_method with a p_replacement method, but
keeps p_method on its class under name keeps p_method on its class under name
"_base_<initial_method_name>_". In the patched method, one may use "_base_<initial_method_name>_". In the patched method, one may use
Hack.base to call the base method.''' Hack.base to call the base method. If p_method is static, you must
specify its class in p_klass.'''
# Get the class on which the surgery will take place. # Get the class on which the surgery will take place.
klass = method.im_class isStatic = klass
klass = klass or method.im_class
# On this class, store m_method under its "base" name. # On this class, store m_method under its "base" name.
name = method.im_func.__name__ name = isStatic and method.func_name or method.im_func.__name__
baseName = '_base_%s_' % name baseName = '_base_%s_' % name
setattr(klass, baseName, method) setattr(klass, baseName, method)
# Store the replacement method on klass. # Store the replacement method on klass. When doing so, even when
# m_method is static, it is wrapped in a method. This is why, in
# m_base below, when the method is static, we return method.im_func to
# retrieve the original static method.
setattr(klass, name, replacement) setattr(klass, name, replacement)
@staticmethod @staticmethod
def base(method): def base(method, klass=None):
return getattr(method.im_class, '_base_%s_' % method.im_func.__name__) '''Allows to call the base (replaced) method. If p_method is static,
you must specify its p_klass.'''
isStatic = klass
klass = klass or method.im_class
name = isStatic and method.func_name or method.im_func.__name__
res = getattr(klass, '_base_%s_' % name)
if isStatic: res = res.im_func
return res
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View file

@ -249,6 +249,7 @@ class Pod(Field):
formats = self.showTemplate(obj, template) formats = self.showTemplate(obj, template)
if not formats: continue if not formats: continue
formats = isManager and self.getAllFormats(template) or formats formats = isManager and self.getAllFormats(template) or formats
if isinstance(formats, basestring): formats = (formats,)
res.append(Object(template=template, formats=formats, res.append(Object(template=template, formats=formats,
freezeFormats=self.getFreezeFormats(obj, template))) freezeFormats=self.getFreezeFormats(obj, template)))
return res return res

View file

@ -214,7 +214,8 @@ class Ref(Field):
<x>:tool.pxNavigate</x> <x>:tool.pxNavigate</x>
<!-- No object is present --> <!-- No object is present -->
<p class="discreet" if="not objects and not showPlusIcon">:_('no_ref')</p> <p class="discreet"
if="not objects and (innerRef and showPlusIcon)">:_('no_ref')</p>
<!-- Linked objects --> <!-- Linked objects -->
<table if="objects" class=":not innerRef and 'list' or ''" <table if="objects" class=":not innerRef and 'list' or ''"
@ -240,23 +241,29 @@ class Ref(Field):
<tr for="tied in objects" valign="top" <tr for="tied in objects" valign="top"
class=":loop.tied.odd and 'even' or 'odd'" class=":loop.tied.odd and 'even' or 'odd'"
var2="tiedUid=tied.o.id; var2="tiedUid=tied.o.id;
objectIndex=field.getIndexOf(zobj, tiedUid)|None"> objectIndex=field.getIndexOf(zobj, tiedUid)|None;
mayView=tied.allows('read')">
<td if="not inPickList and numbered">:field.pxNumber</td> <td if="not inPickList and numbered">:field.pxNumber</td>
<td for="column in columns" width=":column.width" align=":column.align" <td for="column in columns" width=":column.width" align=":column.align"
var2="refField=column.field"> var2="refField=column.field">
<!-- The "title" field --> <!-- The "title" field -->
<x if="refField.name == 'title'"> <x if="refField.name == 'title'">
<x>:field.pxObjectTitle</x> <x if="mayView">
<div if="tied.o.mayAct()">:field.pxObjectActions</div> <x>:field.pxObjectTitle</x>
<div if="tied.o.mayAct()">:field.pxObjectActions</div>
</x>
<div if="not mayView">
<img src=":url('fake')" style="margin-right: 5px"/>
<x>:_('unauthorized')</x></div>
</x> </x>
<!-- Any other field --> <!-- Any other field -->
<x if="refField.name != 'title'"> <x if="(refField.name != 'title') and mayView">
<x var="zobj=tied.o; obj=tied; layoutType='cell'; <x var="zobj=tied.o; obj=tied; layoutType='cell';
innerRef=True; field=refField" innerRef=True; field=refField"
if="field.isShowable(zobj, 'result')">:field.pxRender</x> if="field.isShowable(zobj, 'result')">:field.pxRender</x>
</x> </x>
</td> </td>
<td if="checkboxes" class="cbCell"> <td if="checkboxes and mayView" class="cbCell">
<input type="checkbox" name=":ajaxHookId" checked="checked" <input type="checkbox" name=":ajaxHookId" checked="checked"
value=":tiedUid" onclick="toggleRefCb(this)"/> value=":tiedUid" onclick="toggleRefCb(this)"/>
</td> </td>

View file

@ -395,7 +395,14 @@ class Transition:
self.trigger(name, obj, wf, rq.get('comment', ''), reindex=False) self.trigger(name, obj, wf, rq.get('comment', ''), reindex=False)
# Reindex obj if required. # Reindex obj if required.
if not obj.isTemporary(): obj.reindex() if not obj.isTemporary(): obj.reindex()
return tool.goto(obj.getUrl(rq['HTTP_REFERER'])) # If we are viewing the object and if the logged user looses the
# permission to view it, redirect the user to its home page.
if not obj.allows('read') and \
(obj.absolute_url_path() in rq['HTTP_REFERER']):
back = tool.getHomePage()
else:
back = obj.getUrl(rq['HTTP_REFERER'])
return tool.goto(back)
@staticmethod @staticmethod
def getBack(workflow, transition): def getBack(workflow, transition):

View file

@ -710,3 +710,7 @@ msgstr ""
#. Default: "In some situations, by leaving this page this way, you may lose encoded data or prevent other users from editing it afterwards. Please use buttons instead." #. Default: "In some situations, by leaving this page this way, you may lose encoded data or prevent other users from editing it afterwards. Please use buttons instead."
msgid "warn_leave_form" msgid "warn_leave_form"
msgstr "" msgstr ""
#. Default: "You are not allowed to consult this."
msgid "unauthorized"
msgstr ""

View file

@ -710,3 +710,7 @@ msgstr ""
#. Default: "In some situations, by leaving this page this way, you may lose encoded data or prevent other users from editing it afterwards. Please use buttons instead." #. Default: "In some situations, by leaving this page this way, you may lose encoded data or prevent other users from editing it afterwards. Please use buttons instead."
msgid "warn_leave_form" msgid "warn_leave_form"
msgstr "" msgstr ""
#. Default: "You are not allowed to consult this."
msgid "unauthorized"
msgstr ""

View file

@ -710,3 +710,7 @@ msgstr ""
#. Default: "In some situations, by leaving this page this way, you may lose encoded data or prevent other users from editing it afterwards. Please use buttons instead." #. Default: "In some situations, by leaving this page this way, you may lose encoded data or prevent other users from editing it afterwards. Please use buttons instead."
msgid "warn_leave_form" msgid "warn_leave_form"
msgstr "" msgstr ""
#. Default: "You are not allowed to consult this."
msgid "unauthorized"
msgstr ""

View file

@ -711,3 +711,7 @@ msgstr "${date} - This page is locked by ${user}."
#. Default: "In some situations, by leaving this page this way, you may lose encoded data or prevent other users from editing it afterwards. Please use buttons instead." #. Default: "In some situations, by leaving this page this way, you may lose encoded data or prevent other users from editing it afterwards. Please use buttons instead."
msgid "warn_leave_form" msgid "warn_leave_form"
msgstr "In some situations, by leaving this page this way, you may lose encoded data or prevent other users from editing it afterwards. Please use buttons instead." msgstr "In some situations, by leaving this page this way, you may lose encoded data or prevent other users from editing it afterwards. Please use buttons instead."
#. Default: "You are not allowed to consult this."
msgid "unauthorized"
msgstr "You are not allowed to consult this."

View file

@ -710,3 +710,7 @@ msgstr ""
#. Default: "In some situations, by leaving this page this way, you may lose encoded data or prevent other users from editing it afterwards. Please use buttons instead." #. Default: "In some situations, by leaving this page this way, you may lose encoded data or prevent other users from editing it afterwards. Please use buttons instead."
msgid "warn_leave_form" msgid "warn_leave_form"
msgstr "" msgstr ""
#. Default: "You are not allowed to consult this."
msgid "unauthorized"
msgstr ""

View file

@ -711,3 +711,7 @@ msgstr "${date} - Cette page est verrouillée par ${user}."
#. Default: "In some situations, by leaving this page this way, you may lose encoded data or prevent other users from editing it afterwards. Please use buttons instead." #. Default: "In some situations, by leaving this page this way, you may lose encoded data or prevent other users from editing it afterwards. Please use buttons instead."
msgid "warn_leave_form" msgid "warn_leave_form"
msgstr "Dans certaines situations, en quittant cette page de cette manière, vous pouvez perdre les données encodées ou empêcher d'autres utilisateurs de les éditer par la suite. Veuillez utilisez les boutons ad hoc." msgstr "Dans certaines situations, en quittant cette page de cette manière, vous pouvez perdre les données encodées ou empêcher d'autres utilisateurs de les éditer par la suite. Veuillez utilisez les boutons ad hoc."
#. Default: "You are not allowed to consult this."
msgid "unauthorized"
msgstr "Vous n'êtes pas autorisé à consulter ceci."

View file

@ -710,3 +710,7 @@ msgstr ""
#. Default: "In some situations, by leaving this page this way, you may lose encoded data or prevent other users from editing it afterwards. Please use buttons instead." #. Default: "In some situations, by leaving this page this way, you may lose encoded data or prevent other users from editing it afterwards. Please use buttons instead."
msgid "warn_leave_form" msgid "warn_leave_form"
msgstr "" msgstr ""
#. Default: "You are not allowed to consult this."
msgid "unauthorized"
msgstr ""

View file

@ -710,3 +710,7 @@ msgstr ""
#. Default: "In some situations, by leaving this page this way, you may lose encoded data or prevent other users from editing it afterwards. Please use buttons instead." #. Default: "In some situations, by leaving this page this way, you may lose encoded data or prevent other users from editing it afterwards. Please use buttons instead."
msgid "warn_leave_form" msgid "warn_leave_form"
msgstr "" msgstr ""
#. Default: "You are not allowed to consult this."
msgid "unauthorized"
msgstr ""

View file

@ -52,8 +52,7 @@ img { border: 0; vertical-align: middle }
.xhtml p { margin: 3px 0 7px 0 } .xhtml p { margin: 3px 0 7px 0 }
.clickable { cursor: pointer } .clickable { cursor: pointer }
.refLink { font-style: italic; padding-left: 5px; font-size: 90%; color: grey } .refLink { font-style: italic; padding-left: 5px; font-size: 90%; color: grey }
.main { width: 900px; height: 95%; box-shadow: 3px 3px 3px #A9A9A9; .main { width: 900px; height: 95%; box-shadow: 3px 3px 3px #A9A9A9 }
border-style: solid; border-width: 1px; border-color: grey}
.top { height: 89px; margin-left: 3em; vertical-align: top; .top { height: 89px; margin-left: 3em; vertical-align: top;
background-color: white } background-color: white }
.lang { margin-right: 6px } .lang { margin-right: 6px }

View file

@ -1,2 +1,2 @@
body { margin: 0 } body { margin: 0 }
.main {border: 0; width: 100%; height: 100%} .main { width: 100%; height: 100% }

View file

@ -225,8 +225,7 @@ class No:
can't be triggered, do not return False, return an instance of No can't be triggered, do not return False, return an instance of No
instead. When creating such an instance, you can specify an error instead. When creating such an instance, you can specify an error
message.''' message.'''
def __init__(self, msg): def __init__(self, msg): self.msg = msg
self.msg = msg def __nonzero__(self): return False
def __nonzero__(self): def __repr__(self): return '<No: %s>' % self.msg
return False
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View file

@ -394,7 +394,7 @@ class ToolWrapper(AbstractWrapper):
refInfo=ztool.getRefInfo(); refInfo=ztool.getRefInfo();
refObject=refInfo[0]; refObject=refInfo[0];
refField=refInfo[1]; refField=refInfo[1];
refUrlPart=refObject and ('&amp;ref=%s:%s' % (refObject.UID(), \ refUrlPart=refObject and ('&amp;ref=%s:%s' % (refObject.id, \
refField)) or ''; refField)) or '';
startNumber=req.get('startNumber', '0'); startNumber=req.get('startNumber', '0');
startNumber=int(startNumber); startNumber=int(startNumber);