appy.gen: removed fields Tool.showWorkflowCommentFieldForxx (workflow comment may not be entered into the confirm popup); appy.gen: security-related bugfixes.

This commit is contained in:
Gaetan Delannay 2012-03-19 17:00:44 +01:00
parent cbb8d5cd12
commit f6a828bc13
12 changed files with 67 additions and 32 deletions

View file

@ -564,11 +564,6 @@ class ToolClassDescriptor(ClassDescriptor):
fieldType = gen.Boolean(default=defaultValue, page='userInterface', fieldType = gen.Boolean(default=defaultValue, page='userInterface',
group=groupName) group=groupName)
self.addField(fieldName, fieldType) self.addField(fieldName, fieldType)
# Adds the boolean field for showing or not the field "enter comments".
fieldName = 'showWorkflowCommentFieldFor%s' % className
fieldType = gen.Boolean(default=defaultValue, page='userInterface',
group=groupName)
self.addField(fieldName, fieldType)
# Adds the boolean field for showing all states in current state or not. # Adds the boolean field for showing all states in current state or not.
# If this boolean is True but the current phase counts only one state, # If this boolean is True but the current phase counts only one state,
# we will not show the state at all: the fact of knowing in what phase # we will not show the state at all: the fact of knowing in what phase

View file

@ -417,6 +417,8 @@ class ZopeInstaller:
self.configureSessions() self.configureSessions()
self.installBaseObjects() self.installBaseObjects()
self.installCatalog() self.installCatalog()
# The following line cleans and rebuilds the catalog entirely.
#self.app.config.appy().refreshCatalog()
self.installTool() self.installTool()
self.installUi() self.installUi()
# Perform migrations if required # Perform migrations if required

View file

@ -896,7 +896,11 @@ class BaseMixin:
self.log('Wrong workflow info for a "%s"; is not in state "%s".' % \ self.log('Wrong workflow info for a "%s"; is not in state "%s".' % \
(self.meta_type, stateName)) (self.meta_type, stateName))
# Update permission attributes on the object if required # Update permission attributes on the object if required
return state.updatePermissions(wf, self) updated = state.updatePermissions(wf, self)
if updated:
# Reindex the object because security-related info is indexed.
self.reindex()
return updated
def hasHistory(self): def hasHistory(self):
'''Has this object an history?''' '''Has this object an history?'''

View file

@ -188,7 +188,7 @@ class Translation(ModelClass):
toolFieldPrefixes = ('defaultValue', 'podTemplate', 'formats', 'resultColumns', toolFieldPrefixes = ('defaultValue', 'podTemplate', 'formats', 'resultColumns',
'enableAdvancedSearch', 'numberOfSearchColumns', 'enableAdvancedSearch', 'numberOfSearchColumns',
'searchFields', 'optionalFields', 'showWorkflow', 'searchFields', 'optionalFields', 'showWorkflow',
'showWorkflowCommentField', 'showAllStatesInPhase') 'showAllStatesInPhase')
defaultToolFields = ('title', 'users', 'groups', 'translations', defaultToolFields = ('title', 'users', 'groups', 'translations',
'enableNotifications', 'unoEnabledPython','openOfficePort', 'enableNotifications', 'unoEnabledPython','openOfficePort',
'numberOfResultsPerPage', 'listBoxesMaximumWidth', 'numberOfResultsPerPage', 'listBoxesMaximumWidth',

View file

@ -38,9 +38,6 @@ class PoMessage:
MSG_searchFields = "Search fields" MSG_searchFields = "Search fields"
MSG_optionalFields = 'Optional fields' MSG_optionalFields = 'Optional fields'
MSG_showWorkflow = 'Show workflow-related information' MSG_showWorkflow = 'Show workflow-related information'
MSG_showWorkflowCommentField = 'Show field allowing to enter a ' \
'comment every time a transition is ' \
'triggered'
MSG_showAllStatesInPhase = 'Show all states in phase' MSG_showAllStatesInPhase = 'Show all states in phase'
POD_ASKACTION = 'Trigger related action' POD_ASKACTION = 'Trigger related action'
REF_NO = 'No object.' REF_NO = 'No object.'

View file

@ -9,6 +9,7 @@ table { font-size: 100%; border-spacing: 0px; border-collapse:collapse;}
form { margin: 0; padding: 0;} form { margin: 0; padding: 0;}
p { margin: 0;} p { margin: 0;}
acronym {cursor: help;} acronym {cursor: help;}
input { font: 92% Helvetica,Arial,sans-serif }
input[type=image] { border: 0; background: none; } input[type=image] { border: 0; background: none; }
input[type=checkbox] { border: 0; background: none; cursor: pointer;} input[type=checkbox] { border: 0; background: none; cursor: pointer;}
input[type=radio] { border: 0; background: none; cursor: pointer;} input[type=radio] { border: 0; background: none; cursor: pointer;}
@ -44,6 +45,8 @@ img {border: 0}
border-top: 1px solid #5F7983; border-bottom: 1px solid #5F7983; } border-top: 1px solid #5F7983; border-bottom: 1px 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;}
.fakeButton { border: 1px solid #D7DEE4; background-color: #fde8e0;
padding: 0px 8px 2px; font: italic 92% Helvetica,Arial,sans-serif}
.message { color: #fd9c03; position: absolute; top: -55px; left: 100px; .message { color: #fd9c03; position: absolute; top: -55px; left: 100px;
width: 700px; border: 1px black dashed; padding: 2px 6px; width: 700px; border: 1px black dashed; padding: 2px 6px;
background-color: #f4f5f6} background-color: #f4f5f6}

View file

@ -307,7 +307,7 @@ function triggerTransition(transitionId, msg) {
theForm.submit(); theForm.submit();
} }
else { // Ask the user to confirm. else { // Ask the user to confirm.
askConfirm('form', 'triggerTransitionForm', msg); askConfirm('form', 'triggerTransitionForm', msg, true);
} }
} }
@ -409,13 +409,17 @@ function closePopup(popupId) {
} }
// Function triggered when an action needs to be confirmed by the user // Function triggered when an action needs to be confirmed by the user
function askConfirm(actionType, action, msg) { function askConfirm(actionType, action, msg, showComment) {
/* Store the actionType (send a form, call an URL or call a script) and the /* Store the actionType (send a form, call an URL or call a script) and the
related action, and shows the confirm popup. If the user confirms, we related action, and shows the confirm popup. If the user confirms, we
will perform the action. */ will perform the action. If p_showComment is true, an input field allowing
to enter a comment will be shown in the popup. */
var confirmForm = document.getElementById('confirmActionForm'); var confirmForm = document.getElementById('confirmActionForm');
confirmForm.actionType.value = actionType; confirmForm.actionType.value = actionType;
confirmForm.action.value = action; confirmForm.action.value = action;
var commentArea = document.getElementById('commentArea');
if (showComment) commentArea.style.display = "block";
else commentArea.style.display = "none";
openPopup("confirmActionPopup", msg); openPopup("confirmActionPopup", msg);
} }
@ -427,8 +431,14 @@ function doConfirm() {
var actionType = confirmForm.actionType.value; var actionType = confirmForm.actionType.value;
var action = confirmForm.action.value; var action = confirmForm.action.value;
if (actionType == 'form') { if (actionType == 'form') {
// We must submit the form whose id is in "action" /* Submit the form whose id is in "action", and transmmit him the comment
document.getElementById(action).submit(); from the popup when relevant */
var theForm = document.getElementById(action);
if ((confirmForm.comment.style.display != 'none') &&
(confirmForm.comment.value)) {
theForm.comment.value = confirmForm.comment.value;
}
theForm.submit();
} }
else if (actionType == 'url') { else if (actionType == 'url') {
// We must go to the URL defined in "action" // We must go to the URL defined in "action"

View file

@ -88,8 +88,9 @@
<td tal:define="actorid python:event.get('actor');" tal:content="actorid"/> <td tal:define="actorid python:event.get('actor');" tal:content="actorid"/>
<td tal:content="event/time"/> <td tal:content="event/time"/>
<td tal:condition="not: isDataChange"> <td tal:condition="not: isDataChange">
<tal:comment condition="rhComments" tal:content="structure rhComments"/> <tal:c condition="rhComments"
<span tal:condition="not: rhComments">No comment.</span> content="structure python: contextObj.formatText(rhComments)"/>
<span tal:condition="not: rhComments">-</span>
</td> </td>
<td tal:condition="isDataChange"> <td tal:condition="isDataChange">
<tal:comment replace="nothing"> <tal:comment replace="nothing">
@ -152,21 +153,16 @@
<input type="hidden" name="workflow_action"/> <input type="hidden" name="workflow_action"/>
<table> <table>
<tr valign="middle"> <tr valign="middle">
<tal:comment replace="nothing">Input field allowing to enter a comment before triggering a transition</tal:comment> <tal:comment replace="nothing">Input field for storing comment</tal:comment>
<td tal:define="showCommentsField python:tool.getAttr('showWorkflowCommentFieldFor'+contextObj.meta_type)" <textarea id="comment" name="comment" cols="30" rows="3" style="display:none"></textarea>
align="right" tal:condition="showCommentsField">
<span tal:content="python: tool.translate('workflow_comment')" class="discreet"></span>
<input type="text" id="comment" name="comment" size="30"/>
</td>
<tal:comment replace="nothing">Buttons for triggering transitions</tal:comment> <tal:comment replace="nothing">Buttons for triggering transitions</tal:comment>
<td align="right" tal:repeat="transition transitions"> <td align="right" tal:repeat="transition transitions">
<tal:comment replace="nothing">Real button</tal:comment> <tal:comment replace="nothing">Real button</tal:comment>
<input type="button" class="appyButton" tal:condition="transition/may_trigger" <input type="button" tal:condition="transition/may_trigger"
tal:attributes="value transition/title; tal:attributes="value transition/title;
onClick python: 'triggerTransition(\'%s\',\'%s\')' % (transition['name'],transition['confirm']);"/> onClick python: 'triggerTransition(\'%s\',\'%s\')' % (transition['name'],transition['confirm']);"/>
<tal:comment replace="nothing">Fake button, explaining why the transition can't be triggered</tal:comment> <tal:comment replace="nothing">Fake button, explaining why the transition can't be triggered</tal:comment>
<div class="appyButton fakeButton" tal:condition="not: transition/may_trigger"> <div class="fakeButton" tal:condition="not: transition/may_trigger">
<acronym tal:content="transition/title" <acronym tal:content="transition/title"
tal:attributes="title transition/reason"></acronym> tal:attributes="title transition/reason"></acronym>
</div> </div>

View file

@ -74,6 +74,12 @@
<p id="appyConfirmText"></p> <p id="appyConfirmText"></p>
<input type="hidden" name="actionType"/> <input type="hidden" name="actionType"/>
<input type="hidden" name="action"/> <input type="hidden" name="action"/>
<div id="commentArea" align="left"><br/>
<span tal:content="python: tool.translate('workflow_comment')" class="discreet"></span>
<textarea name="comment" cols="30" rows="3"></textarea>
<br/>
</div>
<br/>
<input type="button" onClick="doConfirm()" <input type="button" onClick="doConfirm()"
tal:attributes="value python:_('yes')"/> tal:attributes="value python:_('yes')"/>
<input type="button" value="No" onClick="closePopup('confirmActionPopup')" <input type="button" value="No" onClick="closePopup('confirmActionPopup')"

View file

@ -95,10 +95,6 @@ class ToolWrapper(AbstractWrapper):
Stores the boolean field indicating if we must show workflow- Stores the boolean field indicating if we must show workflow-
related information for p_klass or not. related information for p_klass or not.
"showWorkflowCommentField"
Stores the boolean field indicating if we must show the field
allowing to enter a comment every time a transition is triggered.
"showAllStatesInPhase" "showAllStatesInPhase"
Stores the boolean field indicating if we must show all states Stores the boolean field indicating if we must show all states
linked to the current phase or not. If this field is False, we linked to the current phase or not. If this field is False, we
@ -133,4 +129,29 @@ class ToolWrapper(AbstractWrapper):
expression="ctx['nb'] += int(obj.o.refreshSecurity())") expression="ctx['nb'] += int(obj.o.refreshSecurity())")
msg = 'Security refresh: %d object(s) updated.' % context['nb'] msg = 'Security refresh: %d object(s) updated.' % context['nb']
self.log(msg) self.log(msg)
def refreshCatalog(self, startObject=None):
'''Reindex all Appy objects. For some unknown reason, method
catalog.refreshCatalog is not able to recatalog Appy objects.'''
if not startObject:
# This is a global refresh. Clear the catallog completely, and then
# reindex all Appy-managed objects, ie those in folders "config"
# and "data".
# First, clear the catalog.
app = self.o.getParentNode()
app.catalog._catalog.clear()
app.config.reindex()
nb = 1
for obj in app.config.objectValues():
nb += self.refreshCatalog(startObject=obj)
# Then, refresh objects in the "data" folder.
for obj in app.data.objectValues():
nb += self.refreshCatalog(startObject=obj)
print '%d object(s) were reindexed.' % nb
else:
startObject.reindex()
nb = 1
for obj in startObject.objectValues():
nb += self.refreshCatalog(startObject=obj)
return nb
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View file

@ -129,6 +129,7 @@ class ZopeUserPatches:
'''Returns the global roles that this user (or any of its groups) '''Returns the global roles that this user (or any of its groups)
possesses.''' possesses.'''
res = list(self.roles) res = list(self.roles)
if 'Anonymous' not in res: res.append('Authenticated')
# Add group global roles # Add group global roles
if not hasattr(aq_base(self), 'groups'): return res if not hasattr(aq_base(self), 'groups'): return res
for roles in self.groups.itervalues(): for roles in self.groups.itervalues():

View file

@ -375,10 +375,10 @@ class Cortexer:
Once the "cortex.admin" folder and its content has been generated, in Once the "cortex.admin" folder and its content has been generated, in
order to push the app definition into Cortex, go in the folder where order to push the app definition into Cortex, go in the folder where
"cortex.admin" lies and type (command-line tool "cortex-client" must "cortex.admin" lies and type (command-line tool "cortex" must
be installed): be installed):
cortex-client sync push --api http://<cortex-host-ip>/api cortex sync push --api http://<cortex-host-ip>/api
''' '''
def __init__(self, app, pythonVersions=('2.6',)): def __init__(self, app, pythonVersions=('2.6',)):
self.appName = os.path.basename(app) self.appName = os.path.basename(app)