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:
		
							parent
							
								
									cbb8d5cd12
								
							
						
					
					
						commit
						f6a828bc13
					
				
					 12 changed files with 67 additions and 32 deletions
				
			
		| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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?'''
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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.'
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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')"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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():
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue