def getState(self, obj, is_validator): id = obj.id controller_state = self.REQUEST.get('controller_state', None) # The variable 'env' is a dictionary that is passed # along using record variables on forms so that you can keep # some state between different forms. env = self.REQUEST.get('form_env', {}) # Make sure controller_state is something generated by us, not something submitted via POST or GET if controller_state and getattr(controller_state, '__class__', None) != ControllerState: controller_state = None if not is_validator: # Construct a new controller state object or clear out the existing # one unless we are in a validator script. if controller_state is None: # XXX - errors={} shouldn't need to be set here, but without it # I encountered what looks like a weird Zope caching bug. # To reproduce, install CMFFormControllerDemo, go to portal_skins # then to CMFFormControllerDemo, then click on test_form and # click the Test tab. Submit the form with an empty text box. # Now back up to the ZMI and click the Test tab again. If # errors={} is left out in the line below, ControllerState.__init__() # gets called with errors set to a non-empty value (more # precisely, it is set to the value that was in REQUEST.controller_state. # Yikes! controller_state = ControllerState(errors={}) else: # clear out values we don't want to carry over from previous states. controller_state.setStatus('success') controller_state.setNextAction(None) #controller_state.setButton(None) controller_state.set(id=id, context=obj.__parent__) else: if controller_state is None: raise ValueError, 'No controller state available. ' + \ 'This commonly occurs when a ControllerValidator (.vpy) ' + \ 'script is invoked via the validation mechanism in the ' + \ 'portal_form tool. If you are using a package designed to ' + \ 'be used with portal_form, you are probably inadvertently ' + \ 'invoking a validator designed for use with CMFFormController (e.g. validate_id). ' + \ 'If you are using a package designed to be used with CMFFormController, you probably ' + \ 'have a "portal_form" in your URL that needs to be removed.' controller_state._setValidating(is_validator) # Pass environment along, with care so we don't override # existing variables. for k, v in env.items(): controller_state.kwargs.setdefault(k, v) self.REQUEST.set('controller_state', controller_state) return controller_state
def testIntegers(self): """Test validation fails if an integer field is a string""" sdq1 = getattr(self.s1, 'sdq1') self.layer['request'].form['startingYear'] = 'string' self.layer['request'].form['endingYear'] = 'string' self.layer['request'].form['futureYears'] = 'string' dummy_controller_state = ControllerState( id='base_edit', context=sdq1, button='submit', status='success', errors={}, next_action=None, ) controller = self.portal.portal_form_controller controller_state = controller.validate(dummy_controller_state, self.layer['request'], [ 'validate_base', ]) errors = controller_state.getErrors() errors = sdq1.post_validate(self.layer['request'], errors) assert errors != {}, "Validation error not raised" assert 'startingYear' in errors assert 'endingYear' in errors assert 'futureYears' in errors
def testMultipleSelectionsAnswersValidates(self): s1 = getattr(self, 's1') sm1 = getattr(s1, 'sm1') sm1.setInputType("multipleSelect") # add your form variables self.layer['request'].form['sm1-smq1'] = ['5', '4'] # set up a dummy state object dummy_controller_state = ControllerState( id='survey_view', context=s1, button='submit', status='success', errors={}, next_action=None, ) # get the form controller controller = self.portal.portal_form_controller # send the validate script to the form controller # with the dummy state object controller_state = controller.validate(dummy_controller_state, self.layer['request'], [ 'validate_survey', ]) # Do any relevant tests assert controller_state.getErrors() == {}, controller_state.getErrors() userid = s1.getSurveyId() assert userid == "test_user_1_", "Not default test user" questions = s1.getAllQuestions() for question in questions: if question.portal_type == 'Survey Matrix Question': answer = question.getAnswerFor(userid) assert answer == [5, 4], \ "Answer not in question options: %s" % answer
def testMultipleLikertValidates(self): s1 = getattr(self, 's1') ssq1 = getattr(s1, 'ssq1') ssq1.setLikertOptions(1) # add your form variables self.layer['request'].form['ssq1'] = ['1', '2'] # set up a dummy state object dummy_controller_state = ControllerState( id='survey_view', context=s1, button='submit', status='success', errors={}, next_action=None, ) # get the form controller controller = self.portal.portal_form_controller # send the validate script to the form controller # with the dummy state object controller_state = controller.validate(dummy_controller_state, self.layer['request'], [ 'validate_survey', ]) # Do any relevant tests assert controller_state.getErrors() == {}, controller_state.getErrors() userid = s1.getSurveyId() questions = s1.getQuestions() for question in questions: if question.portal_type == 'Survey Select Question': answer = question.getAnswerFor(userid) assert answer == [1, 2], \ "Answer not saved correctly: %s" % answer
def testValidateLengthFails(self): s1 = getattr(self, 's1') stq1 = getattr(self.s1, 'stq1') stq1.setMaxLength(5) # add your form variables self.layer['request'].form['stq1'] = 'Text Answer' # set up a dummy state object dummy_controller_state = ControllerState( id='survey_view', context=s1, button='submit', status='success', errors={}, next_action=None, ) # get the form controller controller = self.portal.portal_form_controller # send the validate script to the form controller # with the dummy state object controller_state = controller.validate(dummy_controller_state, self.layer['request'], [ 'validate_survey', ]) # Do any relevant tests assert controller_state.getErrors() != {}, \ "Validation error not raised"
def testLikertOptions(self): s1 = getattr(self, 's1') ssq1 = getattr(s1, 'ssq1') ssq1.setLikertOptions(1) # add your form variables self.layer['request'].form['ssq1'] = '1' # set up a dummy state object dummy_controller_state = ControllerState( id='survey_view', context=s1, button='submit', status='success', errors={}, next_action=None, ) # get the form controller controller = self.portal.portal_form_controller # send the validate script to the form controller # with the dummy state object controller_state = controller.validate(dummy_controller_state, self.layer['request'], [ 'validate_survey', ]) # Do any relevant tests assert controller_state.getErrors() == {}, "Validation error raised" userid = s1.getSurveyId() assert userid == "test_user_1_", "Not default test user" questions = s1.getQuestions() for question in questions: if question.portal_type == 'Survey Select Question': assert question.getAnswerFor(userid) in \ question.getQuestionOptions(), \ "Answer not in question options"
def getState(self, obj, is_validator): id = obj.id controller_state = self.REQUEST.get('controller_state', None) # The variable 'env' is a dictionary that is passed # along using record variables on forms so that you can keep # some state between different forms. env = self.REQUEST.get('form_env', {}) # Make sure controller_state is something generated by us, not something submitted via POST or GET if controller_state and getattr(controller_state, '__class__', None) != ControllerState: controller_state = None if not is_validator: # Construct a new controller state object or clear out the existing # one unless we are in a validator script. if controller_state is None: # XXX - errors={} shouldn't need to be set here, but without it # I encountered what looks like a weird Zope caching bug. # To reproduce, install CMFFormControllerDemo, go to portal_skins # then to CMFFormControllerDemo, then click on test_form and # click the Test tab. Submit the form with an empty text box. # Now back up to the ZMI and click the Test tab again. If # errors={} is left out in the line below, ControllerState.__init__() # gets called with errors set to a non-empty value (more # precisely, it is set to the value that was in REQUEST.controller_state. # Yikes! controller_state = ControllerState(errors={}) else: # clear out values we don't want to carry over from previous states. controller_state.setStatus('success') controller_state.setNextAction(None) #controller_state.setButton(None) controller_state.set(id=id, context=obj.__parent__) else: if controller_state is None: raise ValueError('No controller state available. ' + \ 'This commonly occurs when a ControllerValidator (.vpy) ' + \ 'script is invoked via the validation mechanism in the ' + \ 'portal_form tool. If you are using a package designed to ' + \ 'be used with portal_form, you are probably inadvertently ' + \ 'invoking a validator designed for use with CMFFormController (e.g. validate_id). ' + \ 'If you are using a package designed to be used with CMFFormController, you probably ' + \ 'have a "portal_form" in your URL that needs to be removed.') controller_state._setValidating(is_validator) # Pass environment along, with care so we don't override # existing variables. for k, v in env.items(): controller_state.kwargs.setdefault(k, v) self.REQUEST.set('controller_state', controller_state) return controller_state
def testReadDoesNotWrite(self): s1 = getattr(self.portal, 's1') # commit, as we've added a question transaction.commit() original_size = s1._p_estimated_size respondent_size = s1.respondents._p_estimated_size assert s1.getRespondentsList() == [] logout() assert s1._p_changed is False assert s1.respondents._p_changed is False assert s1.stq1._p_changed is False # view the survey result = s1.survey_view(REQUEST=Request()) assert s1._p_changed is False assert s1.respondents._p_changed is False assert s1.stq1._p_changed is False transaction.commit() # XXX this should not cause an increase in the object size assert s1._p_estimated_size == original_size, \ "Survey size increased from %s to %s" % (original_size, s1._p_estimated_size) # submit a response self.layer['request'].form['stq1'] = 'An answer' dummy_controller_state = ControllerState( id='survey_view', context=s1, button='submit', status='success', errors={}, next_action=None, ) controller = self.portal.portal_form_controller controller_state = controller.validate( dummy_controller_state, self.layer['request'], ['validate_survey', ] ) assert controller_state.getErrors() == {}, controller_state.getErrors() assert len(s1.getRespondentsList()) == 1 assert s1._p_changed is False assert s1.respondents._p_changed is True assert s1.stq1._p_changed is False transaction.commit() # the survey itself should not increase in size assert s1._p_estimated_size == original_size, \ "Survey size increased from %s to %s" % (original_size, s1._p_estimated_size) # the respondents should increase in size assert s1.respondents._p_estimated_size > respondent_size, \ "Respondents size increased from %s to %s" % ( respondent_size, s1.respondents._p_estimated_size)
def testTwoRequests(self): s1 = getattr(self.portal, 's1') self.layer['request'].form['stq1'] = 'An answer' dummy_controller_state = ControllerState( id='survey_view', context=s1, button='submit', status='success', errors={}, next_action=None, ) controller = self.portal.portal_form_controller controller_state = controller.validate( dummy_controller_state, self.layer['request'], ['validate_survey', ] ) assert controller_state.getErrors() == {}, controller_state.getErrors() self.layer['request'].form['stq2'] = 'Another answer' self.layer['request'].form['survey_user_id'] = \ s1.getRespondentsList()[0] dummy_controller_state = ControllerState( id='survey_view', context=s1.ss1, button='submit', status='success', errors={}, next_action=None, ) controller = self.portal.portal_form_controller controller_state = controller.validate( dummy_controller_state, self.layer['request'], ['validate_survey', ] ) assert controller_state.getErrors() == {}, controller_state.getErrors() respondents = s1.getRespondentsList() assert len(respondents) == 1, respondents
def afterSetUp(self): self.loginAsPortalOwner() self.addProduct(PRODUCT_NAME) self.captcha_key = self.portal.captcha_key # Preparation for validator tests self.pfc = self.portal.portal_form_controller self.req = self.app.REQUEST # set up a dummy state object self.dummycs = ControllerState( id='prefs_captchas_setup_form', context=self.portal, button='submit', status='success', errors={}, ext_action=None, )
def testValidateScript(self): s1 = getattr(self, 's1') self.layer['request'].form['stq1'] = 'Text Answer' dummy_controller_state = ControllerState( id='survey_view', context=s1, button='submit', status='success', errors={}, next_action=None, ) controller = self.portal.portal_form_controller controller_state = controller.validate(dummy_controller_state, self.layer['request'], [ 'validate_survey', ]) assert controller_state.getErrors() == {}, "Validation error raised"
def testEmailValidationFails(self): s1 = getattr(self, 's1') self.layer['request'].form['stq1'] = 'Not an email address' dummy_controller_state = ControllerState( id='survey_view', context=s1, button='submit', status='success', errors={}, next_action=None, ) controller = self.portal.portal_form_controller controller_state = controller.validate(dummy_controller_state, self.layer['request'], [ 'validate_survey', ]) assert controller_state.getErrors() != {}, \ "Validation error not raised"
def testSecondPageRedirects(self): s1 = getattr(self.portal, 's1') self.layer['request'].form['stq2'] = 'Another answer' dummy_controller_state = ControllerState( id='survey_view', context=s1.ss1, button='submit', status='success', errors={}, next_action=None, ) controller = self.portal.portal_form_controller controller_state = controller.validate( dummy_controller_state, self.layer['request'], ['validate_survey', ] ) assert controller_state.getErrors() == {}, controller_state.getErrors()
def testValidationReCaptcha(self): """Test if captcha works""" self.layer['request'].form['stq1'] = 'test' dummy_controller_state = ControllerState( id='survey_view', context=self.s1, button='submit', status='success', errors={}, next_action=None, ) controller = self.portal.portal_form_controller controller_state = controller.validate(dummy_controller_state, self.layer['request'], [ 'validate_survey', ]) assert controller_state.getErrors() == {}, \ "Validation error raised: %s" % controller_state.getErrors()
def testFormValidates(self): """Test form validates correctly""" sdq1 = getattr(self.s1, 'sdq1') dummy_controller_state = ControllerState( id='base_edit', context=sdq1, button='submit', status='success', errors={}, next_action=None, ) controller = self.portal.portal_form_controller controller_state = controller.validate(dummy_controller_state, self.layer['request'], [ 'validate_base', ]) errors = controller_state.getErrors() errors = sdq1.post_validate(self.layer['request'], errors) assert errors == {}, \ "Validation error raised: %s" % controller_state.getErrors()
def testCantAddSpamToLikert(self): s1 = getattr(self, 's1') ssq1 = getattr(s1, 'ssq1') ssq1.setLikertOptions(1) self.layer['request'].form['ssq1'] = '99' dummy_controller_state = ControllerState( id='survey_view', context=s1, button='submit', status='success', errors={}, next_action=None, ) controller = self.portal.portal_form_controller controller_state = controller.validate(dummy_controller_state, self.layer['request'], [ 'validate_survey', ]) assert controller_state.getErrors() != {}, \ "Validation error not raised"
def testEndYear(self): """Test validation fails if end year is before start year""" sdq1 = getattr(self.s1, 'sdq1') self.layer['request'].form['endingYear'] = '1969' dummy_controller_state = ControllerState( id='base_edit', context=sdq1, button='submit', status='success', errors={}, next_action=None, ) controller = self.portal.portal_form_controller controller_state = controller.validate(dummy_controller_state, self.layer['request'], [ 'validate_base', ]) errors = controller_state.getErrors() errors = sdq1.post_validate(self.layer['request'], errors) assert errors != {}, "Validation error not raised" assert 'endingYear' in errors
def testFutureYear(self): """Test validation passes if future year, and no end year""" sdq1 = getattr(self.s1, 'sdq1') self.layer['request'].form['endingYear'] = '' self.layer['request'].form['futureYears'] = '5' dummy_controller_state = ControllerState( id='base_edit', context=sdq1, button='submit', status='success', errors={}, next_action=None, ) controller = self.portal.portal_form_controller controller_state = controller.validate(dummy_controller_state, self.layer['request'], [ 'validate_base', ]) errors = controller_state.getErrors() errors = sdq1.post_validate(self.layer['request'], errors) assert errors == {}, "Validation error raised: %s" % errors
def testValidationRequiredSplitDate(self): s1 = getattr(self, 's1') sdq1 = getattr(s1, 'sdq1') sdq1.setRequired(True) now = DateTime() now_value = str(now.year()) + \ '/' + str(now.month()) + \ '/' + str(now.day()) + \ ' ' + str(now.hour()) + \ ':' + str(now.minute()) + \ ':00 GMT' self.layer['request'].form['sdq1_ampm'] = '' self.layer['request'].form['sdq1_day'] = str(now.day()) self.layer['request'].form['sdq1_hour'] = str(now.hour()) self.layer['request'].form['sdq1_minute'] = str(now.minute()) self.layer['request'].form['sdq1_month'] = str(now.month()) self.layer['request'].form['sdq1_year'] = str(now.year()) dummy_controller_state = ControllerState( id='survey_view', context=s1, button='submit', status='success', errors={}, next_action=None, ) controller = self.portal.portal_form_controller controller_state = controller.validate(dummy_controller_state, self.layer['request'], [ 'validate_survey', ]) assert controller_state.getErrors() == {}, \ "Validation error raised: %s" % controller_state.getErrors() userid = s1.getSurveyId() assert userid == "test_user_1_", "Not default test user" questions = s1.getQuestions() for question in questions: if question.portal_type == 'Survey Date Question': assert question.getAnswerFor(userid) == now_value, \ "Answer not saved correctly: %s" \ % question.getAnswerFor(userid)
def testValidatesNoAnswerWithComment(self): s1 = getattr(self, 's1') ssq1 = getattr(s1, 'ssq1') userid = s1.getSurveyId() self.layer['request'].form['ssq1'] = '' self.layer['request'].form['ssq1_comments'] = 'Comment' dummy_controller_state = ControllerState( id='survey_view', context=s1, button='submit', status='success', errors={}, next_action=None, ) controller = self.portal.portal_form_controller controller.validate(dummy_controller_state, self.layer['request'], [ 'validate_survey', ]) assert ssq1.getAnswerFor(userid) == '', \ "Answer not saved correctly: %s" % ssq1.getAnswerFor(userid) assert ssq1.getCommentsFor(userid) == 'Comment', \ "Comment not saved correctly"
def testQuestionField(self): """Test validation fails if ymd and hm both unselected""" sdq1 = getattr(self.s1, 'sdq1') self.layer['request'].form['showYMD'] = False self.layer['request'].form['showHM'] = False dummy_controller_state = ControllerState( id='base_edit', context=sdq1, button='submit', status='success', errors={}, next_action=None, ) controller = self.portal.portal_form_controller controller_state = controller.validate(dummy_controller_state, self.layer['request'], [ 'validate_base', ]) errors = controller_state.getErrors() errors = sdq1.post_validate(self.layer['request'], errors) assert errors != {}, "Validation error not raised" assert 'showYMD' in errors assert 'showHM' in errors
def __call__(self, *args, **kwargs): """Handle form submission. The button names are taken directly from https://trac.rhaptos.org/trac/rhaptos/wiki/Express%20Edit%20or%20Reuse/Design """ context = aq_inner(self.context) form = self.request.form print str(form.items()) button = form['form.button'] if not form.get('form.submitted'): return self.macroContent('reuse_edit/macros/inner', **form) portal = getToolByName(self.context, 'portal_url').getPortalObject() # CONTINUE BUTTON # https://trac.rhaptos.org/trac/rhaptos/attachment/wiki/Express%20Edit%20or%20Reuse/Design/no-roles-derive-checkout-choice.jpg if button == 'continue': action = form['cannot_checkout_action'] if action == 'derive': return self.macroContent( 'reuse_edit/macros/inner', section='derive', **form ) elif action == 'checkout_anyway': return self.macroContent( 'reuse_edit/macros/inner', section='checkout_anyway', **form ) # CHECKOUT BUTTON # https://trac.rhaptos.org/trac/rhaptos/attachment/wiki/Express%20Edit%20or%20Reuse/Design/edit-reuse-choose-area.jpg if button == 'checkout': # Fetch the content content = portal.restrictedTraverse( 'content/%s/%s' % (form['contentId'], form['version']) ) # Fetch the area area = portal.restrictedTraverse(form['area_path']) if content.objectId not in area.objectIds(): print "Not in area - checkout" # Content is not in area - checkout area.invokeFactory(id=content.objectId, type_name=content.portal_type) obj = area._getOb(content.objectId) obj.setState('published') obj.checkout(content.objectId) else: # Content is already in area - inspect state and version obj = area._getOb(content.objectId) if obj.state == 'published': print "Published - check out published copy" # Check it out on top of the published copy obj.checkout(content.objectId) elif (obj.state == 'checkedout') and (obj.version == content.version): # Everything is OK print "All is OK" pass elif (obj.state == 'modified') or (obj.version != content.version): # Action needs more user input return self.macroContent( 'reuse_edit/macros/inner', section='already_have_copy', content_type=obj.portal_type, area=area, obj=obj, **form ) # Content was either checked out earlier in this method or it is # already checked out and the versions match. if len(form.get('reuse_edit_now', [])) > 1: # Redirect to edit page of the checked out content return "Redirect: %s" % obj.absolute_url() else: return "close: The %s is ready to edit in %s" \ % (obj.portal_type.lower(), area.Title()) # CREATE COPY BUTTON # https://trac.rhaptos.org/trac/rhaptos/attachment/wiki/Express%20Edit%20or%20Reuse/Design/derive-copy.jpg if button == 'create_copy': # User must agree to licence if not form.get('agree'): return self.macroContent( 'reuse_edit/macros/inner', section='derive', errors={'agree':_('You must agree to the conditions')}, **form ) # Fetch content and area content = self.content(form['contentId'], form['version']) area = portal.restrictedTraverse(form['area_path']) # There are going to be stale items later to_delete_id = '' # Content is not in area - create to_delete_id = area.generateUniqueId() area.invokeFactory(id=to_delete_id, type_name=content.portal_type) obj = area._getOb(to_delete_id) # Content must be checked out to area before a fork is possible obj.setState('published') obj.checkout(content.objectId) # Do the fork state = ControllerState() self.request.set('controller_state', state) fork = obj.forkContent(license=content.getDefaultLicense(), return_context=True, ) fork.setState('created') # For some reason setGoogleAnalyticsTrackingCode causes an # Unauthorized error in forkContent. It is supressed by the # return_context parameter allowing us to set it here. fork.setGoogleAnalyticsTrackingCode(None) # Delete stale item if to_delete_id: area.manage_delObjects(ids=[to_delete_id]) if len(form.get('reuse_edit_now', [])) > 1: # Redirect to edit page of the checked out content return "Redirect: %s" % fork.absolute_url() else: # Close the popup by redirecting to the context return "close: The %s is ready to edit in %s" \ % (obj.portal_type.lower(), area.Title()) # EDIT EXISTING COPY BUTTON # https://trac.rhaptos.org/trac/rhaptos/attachment/wiki/Express%20Edit%20or%20Reuse/Design/conflict-message-chose-edit-originally.jpg # and # https://trac.rhaptos.org/trac/rhaptos/attachment/wiki/Express%20Edit%20or%20Reuse/Design/conflict-message-chose-noedit-originally.jpg if button == 'edit_existing_copy': print "REDIR 1" # Redirect to edit page of the checked out content obj = portal.restrictedTraverse(form['obj_path']) return "Redirect: %s" % obj.absolute_url()
def afterSetUp(self): manage_addControllerPageTemplate(self.portal, 'base_controller') self.base_controller = self.portal.base_controller self.base_state = ControllerState(id="my_id", context=self.portal, button="default")