def test_canNotSendIfInNoPMCreatorGroup(self): """ If the user that wants to send the item in PloneMeeting is not a creator in PM, aka is not in a _creators suffixed group, a message is displayed to him. """ # remove pmCreator2 from the vendors_creators group # first check that the user is actually in a _creators group pmCreator2 = self.portal.portal_membership.getMemberById('pmCreator2') self.assertTrue([ group for group in self.portal.acl_users.source_groups. getGroupsForPrincipal(pmCreator2) if group.endswith('_creators') ]) self.portal.portal_groups.removePrincipalFromGroup( 'pmCreator2', self.vendors_creators) # pmCreator2 is no more in a _creators group self.assertFalse([ group for group in self.portal.acl_users.source_groups. getGroupsForPrincipal(pmCreator2) if group.endswith('_creators') ]) # try to send the item setCorrectSettingsConfig(self.portal) self.changeUser('pmCreator2') self.tool.getPloneMeetingFolder('plonemeeting-assembly', 'pmCreator2') transaction.commit() # create an element to send... document = createDocument(self.portal.Members.pmCreator2) messages = IStatusMessage(self.request) # if no item is created, _sendToPloneMeeting returns None self.assertFalse( self._sendToPloneMeeting(document, user='******', proposingGroup=self.vendors_uid)) msg = _(NO_PROPOSING_GROUP_ERROR, mapping={'userId': 'pmCreator2'}) self.assertEqual(messages.show()[-3].message, translate(msg))
def test_displayedDateRespectTimeZone(self): """ This confirm a bug in suds 0.4 corrected in suds-jurko 0.4.1.jurko.4 where returned date does not work when not using current time zone. For example, a GMT+1 date is wrongly displayed when we are in GMT+2, it miss 1 hour, so 2013/03/03 00:00 becomes 2013/03/02 23:00... Returned dates should always be UTC. """ self.changeUser('admin') document = createDocument(self.portal) item = self._sendToPloneMeeting(document) # create a meeting and present the item self.changeUser('pmManager') # use a date in GMT+1 meeting = self.create('Meeting', date=datetime(2013, 3, 3)) self.presentItem(item) transaction.commit() viewlet = PloneMeetingInfosViewlet(document, self.request, None, None) viewlet.update() meeting_date = viewlet.getPloneMeetingLinkedInfos()[0]['meeting_date'] # date TZ naive, uses UTC self.assertEqual(meeting_date.tzname(), 'UTC') self.assertEquals( (meeting_date.year, meeting_date.month, meeting_date.day, meeting_date.hour, meeting_date.minute), (2013, 3, 2, 23, 0)) # change meeting date, use a date in GMT+2 meeting.date = datetime(2013, 8, 8) transaction.commit() viewlet = PloneMeetingInfosViewlet(document, self.request, None, None) viewlet.update() meeting_date = viewlet.getPloneMeetingLinkedInfos()[0]['meeting_date'] self.assertEqual(meeting_date.tzname(), 'UTC') self.assertEquals( (meeting_date.year, meeting_date.month, meeting_date.day, meeting_date.hour, meeting_date.minute), (2013, 8, 7, 22, 0))
def test_renderTALExpression(self): """ Test the method that will render a TAL expression """ setRoles(self.portal, TEST_USER_ID, ('Manager', )) login(self.portal, TEST_USER_NAME) # create an element to use in the TAL expression... document = createDocument(self.portal) ws4pmSettings = getMultiAdapter((self.portal, self.request), name='ws4pmclient-settings') expr = u'python: None' # make sure None is never returned by the renderer as it breaks SOAP calls self.assertTrue( ws4pmSettings.renderTALExpression(document, self.portal, expr, {}) == u'') expr = u'object/Title' self.assertTrue( ws4pmSettings.renderTALExpression(document, self.portal, expr, {}) == u'Document title') expr = u'string:"My expr result"' self.assertTrue( ws4pmSettings.renderTALExpression(document, self.portal, expr, {}) == u'"My expr result"') # with a wrong expression, we raise expr = 'u object/wrongMethodCall' self.assertRaises(CompilerError, ws4pmSettings.renderTALExpression, document, self.portal, expr, {})
def test_canNotConnectTemporarily(self): """ Test a connection problem in PloneMeeting after an item has already been sent - connect to PM successfully - send the item successfully - break settings - check what is going on while not being able to show PM related informations """ self.changeUser('admin') document = createDocument(self.portal) viewlet = PloneMeetingInfosViewlet(document, self.request, None, None) viewlet.update() # send an element to PloneMeeting item = self._sendToPloneMeeting(document) # correctly sent self.assertTrue(viewlet.available() is True) self.assertTrue( viewlet.getPloneMeetingLinkedInfos()[0]['UID'] == item.UID()) setCorrectSettingsConfig(self.portal, **{'pm_url': u'http://wrong/url'}) cleanMemoize(self.request, viewlet) # no available # a message is returned in the viewlet by the viewlet.available method self.assertTrue(viewlet.available() == (UNABLE_TO_CONNECT_ERROR, 'error')) # the annotations on the document are still correct self.assertTrue( IAnnotations(document)[WS4PMCLIENT_ANNOTATION_KEY] == ['plonemeeting-assembly'])
def test_DownloadAnnexFromItemView(self, _soap_getItemInfos): """ Test the BrowserView that return the annex of an item """ # return an annex annex_id = 'my_annex' _soap_getItemInfos.return_value = [ type( 'ItemInfo', (object, ), { 'annexes': [ type( 'AnnexInfo', (object, ), { 'id': annex_id, 'filename': 'my_annex.pdf', 'file': base64.b64encode('Hello!') }) ] }) ] self.changeUser('admin') document = createDocument(self.portal) self.request.set('annex_id', annex_id) download_annex = document.restrictedTraverse( '@@download_annex_from_plonemeeting') # mock connexion to PloneMeeting download_annex.ws4pmSettings._soap_connectToPloneMeeting = Mock( return_value=True) annex = download_annex() self.assertEqual(annex, 'Hello!') self.assertEqual(self.request.RESPONSE.headers.get('content-type'), 'application/pdf') self.assertEqual( self.request.RESPONSE.headers.get('content-disposition'), 'inline;filename="my_annex.pdf"')
def test_available(self): """ """ self.changeUser('admin') document = createDocument(self.portal) # by default, no viewlet_display_condition TAL expression viewlet = PloneMeetingInfosViewlet(document, self.request, None, None) viewlet.update() settings = viewlet.ws4pmSettings.settings() settings.viewlet_display_condition = u'' # no items created/linked, so the viewlet is not displayed self.assertFalse(viewlet.available()) # viewlet is displayed depending on : # by default, the fact that it is linked to an item # or if a TAL expression is defined in the config and returns True # test with a defined TAL expression in the configuration settings.viewlet_display_condition = u'python: True' # if a TAL expression is defined, it take precedence # available is memoized so it is still False... self.assertFalse(viewlet.available()) # remove memoized informations cleanMemoize(self.request, viewlet) self.assertTrue(viewlet.available()) cleanMemoize(self.request, viewlet) # now remove the TAL expression and send the object to PloneMeeting settings.viewlet_display_condition = u'' self.assertFalse(viewlet.available()) cleanMemoize(self.request, viewlet) item = self._sendToPloneMeeting(document) # now that the element has been sent, the viewlet is available self.assertTrue(viewlet.available()) cleanMemoize(self.request, viewlet) # define a TAL expression taking care of the 'isLinked' settings.viewlet_display_condition = u'python: isLinked and object.portal_type == "wrong_value"' self.assertFalse(viewlet.available()) cleanMemoize(self.request, viewlet) settings.viewlet_display_condition = u'python: isLinked and object.portal_type == "Document"' self.assertTrue(viewlet.available()) cleanMemoize(self.request, viewlet) # if the TAL expression has errors, available is False and a message is displayed messages = IStatusMessage(self.request) # by default, 2 messages already exist, these are item creation related messages shownMessages = messages.show() self.assertTrue(len(shownMessages) == 1) self.assertTrue(shownMessages[0].message, CORRECTLY_SENT_TO_PM_INFO) settings.viewlet_display_condition = u'python: object.getUnexistingAttribute()' # in case there is a problem, a message is displayed in a tuple (msg, error_level) self.assertTrue(isinstance(viewlet.available(), tuple)) cleanMemoize(self.request, viewlet) # now check when the linked item is removed settings.viewlet_display_condition = u'' self.assertTrue(viewlet.available()) cleanMemoize(self.request, viewlet) item.aq_inner.aq_parent.manage_delObjects(ids=[ item.getId(), ]) transaction.commit() self.assertFalse(viewlet.available())
def test_sendItemToPloneMeeting(self): """Test that the item is actually sent to PloneMeeting.""" setCorrectSettingsConfig(self.portal) self.changeUser('pmCreator1') # create an element to send... document = createDocument(self.portal.Members.pmCreator1) self._configureRequestForView(document) view = document.restrictedTraverse(SEND_TO_PM_VIEW_NAME).form_instance # create an annex to send... annex = createAnnex(self.portal.Members.pmCreator1) # before sending, no item is linked to the document ws4pmSettings = getMultiAdapter((self.portal, self.request), name='ws4pmclient-settings') self.assertTrue( len( ws4pmSettings._soap_searchItems( {'externalIdentifier': document.UID()})) == 0) # create the 'pmCreator1' member area to be able to create an item self.tool.getPloneMeetingFolder('plonemeeting-assembly', 'pmCreator1') # we have to commit() here or portal used behing the SOAP call # does not have the freshly created member area... transaction.commit() # send to PloneMeeting # as form.button.Send is not in the form, nothing is done but returning the views's index # the form for sending an element is displayed form_action = '<form class="rowlike enableUnloadProtection kssattr-formname-send_to_plonemeeting_form"' \ ' action="{0}/Members/pmCreator1/document" method="post"' \ ' id="form" enctype="multipart/form-data">'.format(self.portal.absolute_url()) self.assertTrue(form_action in view()) self.assertTrue( len( ws4pmSettings._soap_searchItems( {'externalIdentifier': document.UID()})) == 0) # now send the element to PM view.proposingGroupId = 'developers' view.request.form['form.widgets.annexes'] = [annex.UID()] # view._doSendToPloneMeeting returns True if the element was actually sent self.assertTrue(view._doSendToPloneMeeting()) # while the element is sent, the view will return nothing... self.assertFalse(view()) # now that the element has been sent, an item is linked to the document items = ws4pmSettings._soap_searchItems( {'externalIdentifier': document.UID()}) self.assertTrue(len(items) == 1) # moreover, as defined in the configuration, 1 annex were added to the item itemInfos = ws4pmSettings._soap_getItemInfos({ 'UID': items[0]['UID'], 'showAnnexes': True })[0] self.assertTrue(len(itemInfos['annexes']) == 1)
def test_getPloneMeetingLinkedInfos(self): """ Test the getPloneMeetingLinkedInfos method : - if nothing found, an empty {} is returned - if elements are found, relevant data are returned """ self.changeUser('admin') document = createDocument(self.portal) viewlet = PloneMeetingInfosViewlet(document, self.request, None, None) viewlet.update() self.assertTrue(viewlet.available() is False) # now send an element to PloneMeeting and check again cleanMemoize(self.request, viewlet) item = self._sendToPloneMeeting(document) # we received informations about the created item self.assertTrue( viewlet.getPloneMeetingLinkedInfos()[0]['UID'] == item.UID())
def test_canNotExecuteWrongAction(self): """While calling the view to execute an action, we check if the user can actually execute the action regarding the parameters defined in settings.generated_actions.""" setCorrectSettingsConfig(self.portal) self.changeUser('pmCreator1') # create an element to send... document = createDocument(self.portal.Members.pmCreator1) # build an url that should not be accessible by the user # if the url does not correspond to an available wsclient linked action, # an Unauthorized is raised. This make sure the triggered action is # available to the user self.request.set('URL', document.absolute_url()) self.request.set( 'ACTUAL_URL', document.absolute_url() + '/%s' % SEND_TO_PM_VIEW_NAME) self.request.set('meetingConfigId', 'wrong-meeting-config-id') view = document.restrictedTraverse(SEND_TO_PM_VIEW_NAME).form_instance self.assertRaises(Unauthorized, view)
def test_DownloadAnnexFromItemView_with_no_result(self, _soap_getItemInfos): """ Test the BrowserView that return the annex of an item """ # return no annex _soap_getItemInfos.return_value = None messages = IStatusMessage(self.request) self.changeUser('admin') document = createDocument(self.portal) self.request.set('annex_id', 'my_annex') download_annex = document.restrictedTraverse( '@@download_annex_from_plonemeeting') # mock connexion to PloneMeeting download_annex.ws4pmSettings._soap_connectToPloneMeeting = Mock( return_value=True) download_annex() self.assertEqual(messages.show()[-1].message, MISSING_FILE_ERROR)
def test_canNotSeeLinkedInfos(self): """ If the element has been sent to PloneMeeting but current user can not see these items in PloneMeeting, a message is displayed """ self.changeUser('admin') document = createDocument(self.portal) viewlet = PloneMeetingInfosViewlet(document, self.request, None, None) viewlet.update() item = self._sendToPloneMeeting(document) # we received informations about the created item self.assertTrue( viewlet.getPloneMeetingLinkedInfos()[0]['UID'] == item.UID()) # admin will create the element inTheNameOf 'pmCreator1' self.changeUser('pmCreator1') cleanMemoize(self.request, viewlet) self.assertTrue( viewlet.getPloneMeetingLinkedInfos()[0]['UID'] == item.UID()) # 'pmCreator2' will not see the infos about items, just a message self.changeUser('pmCreator2') cleanMemoize(self.request, viewlet) self.assertTrue(viewlet.getPloneMeetingLinkedInfos() == ( CAN_NOT_SEE_LINKED_ITEMS_INFO, 'info'))
def test_checkAlreadySentToPloneMeeting(self): """Test in case we sent the element again to PloneMeeting, that should not happen... It check also that relevant annotation wipe out works correctly.""" ws4pmSettings = getMultiAdapter((self.portal, self.request), name='ws4pmclient-settings') setCorrectSettingsConfig(self.portal) settings = ws4pmSettings.settings() self.changeUser('pmCreator1') # create an element to send... document = createDocument(self.portal.Members.pmCreator1) self._configureRequestForView(document) view = document.restrictedTraverse(SEND_TO_PM_VIEW_NAME).form_instance view.proposingGroupId = 'developers' # create the 'pmCreator1' member area to be able to create an item self.tool.getPloneMeetingFolder('plonemeeting-assembly', 'pmCreator1') self.tool.getPloneMeetingFolder('plonegov-assembly', 'pmCreator1') # we have to commit() here or portal used behing the SOAP call # does not have the freshly created member area... transaction.commit() # before sending, the element is not linked annotations = IAnnotations(document) self.assertFalse(WS4PMCLIENT_ANNOTATION_KEY in annotations) self.assertFalse( view.ws4pmSettings.checkAlreadySentToPloneMeeting( document, self.request.get('meetingConfigId'))) # send the document self.assertTrue(view._doSendToPloneMeeting()) # is linked to one item self.assertTrue(annotations[WS4PMCLIENT_ANNOTATION_KEY] == [ self.request.get('meetingConfigId'), ]) self.assertTrue( view.ws4pmSettings.checkAlreadySentToPloneMeeting( document, (self.request.get('meetingConfigId'), ))) self.assertTrue( len( ws4pmSettings._soap_searchItems( {'externalIdentifier': document.UID()})) == 1) messages = IStatusMessage(self.request) # there is one message saying that the item was correctly sent shownMessages = messages.show() self.assertEquals(shownMessages[-1].message, CORRECTLY_SENT_TO_PM_INFO) # call form again, it will display relevant status messages # the rendered form is u'' self.assertTrue(settings.only_one_sending) self.assertTrue(view() == u'') # the item is not created again # is still linked to one item self.assertTrue(annotations[WS4PMCLIENT_ANNOTATION_KEY] == [ self.request.get('meetingConfigId'), ]) self.assertTrue( len( ws4pmSettings._soap_searchItems( {'externalIdentifier': document.UID()})) == 1) # a warning is displayed to the user self.request.response.status = 200 # if status in 300, messages are not deleted with show self.assertEquals(messages.show()[-1].message, ALREADY_SENT_TO_PM_ERROR) settings.only_one_sending = False self.assertFalse(settings.only_one_sending) view._finishedSent = False self.request.response.status = 200 # if status in 300, render is not called by z3cform self.assertIn('Send to PloneMeeting Assembly', view()) self.assertEqual(len(messages.show()), 0) # if we remove the item in PloneMeeting, the view is aware of it itemUID = str( ws4pmSettings._soap_searchItems( {'externalIdentifier': document.UID()})[0]['UID']) # avoid weird ConflictError while committing because of # self.portal._volatile_cache_keys PersistentMapping self.portal._volatile_cache_keys._p_changed = False transaction.commit() item = self.portal.uid_catalog(UID=itemUID)[0].getObject() # remove the item item.aq_inner.aq_parent.manage_delObjects(ids=[ item.getId(), ]) transaction.commit() # checkAlreadySentToPloneMeeting will wipe out inconsistent annotations # for now, annotations are inconsistent self.assertTrue(annotations[WS4PMCLIENT_ANNOTATION_KEY] == [ self.request.get('meetingConfigId'), ]) self.assertFalse( view.ws4pmSettings.checkAlreadySentToPloneMeeting( document, (self.request.get('meetingConfigId'), ))) # now it is consistent self.assertFalse(WS4PMCLIENT_ANNOTATION_KEY in annotations) self.assertTrue( len( ws4pmSettings._soap_searchItems( {'externalIdentifier': document.UID()})) == 0) # the item can be sent again and will be linked to a new created item self.assertTrue(view._doSendToPloneMeeting()) self.assertTrue( view.ws4pmSettings.checkAlreadySentToPloneMeeting( document, (self.request.get('meetingConfigId'), ))) self.assertTrue( len( ws4pmSettings._soap_searchItems( {'externalIdentifier': document.UID()})) == 1) # the item can also been send to another meetingConfig self.request.set('meetingConfigId', 'plonegov-assembly') view = document.restrictedTraverse(SEND_TO_PM_VIEW_NAME).form_instance view.proposingGroupId = 'developers' self.assertFalse( view.ws4pmSettings.checkAlreadySentToPloneMeeting( document, (self.request.get('meetingConfigId'), ))) self.request.form['form.widgets.category'] = [ u'deployment', ] self.assertTrue(view._doSendToPloneMeeting()) self.assertTrue( view.ws4pmSettings.checkAlreadySentToPloneMeeting( document, (self.request.get('meetingConfigId'), ))) self.assertTrue(annotations[WS4PMCLIENT_ANNOTATION_KEY] == [ 'plonemeeting-assembly', self.request.get('meetingConfigId'), ]) # if we remove the 2 items, a call to checkAlreadySentToPloneMeeting # without meetingConfigs will wipeout the annotations transaction.commit() itemUIDs = [ str(elt['UID']) for elt in ws4pmSettings._soap_searchItems( {'externalIdentifier': document.UID()}) ] item1 = self.portal.uid_catalog(UID=itemUIDs[0])[0].getObject() item2 = self.portal.uid_catalog(UID=itemUIDs[1])[0].getObject() item1.aq_inner.aq_parent.manage_delObjects(ids=[ item1.getId(), ]) item2.aq_inner.aq_parent.manage_delObjects(ids=[ item2.getId(), ]) transaction.commit() # annotations are still messed up self.assertTrue(annotations[WS4PMCLIENT_ANNOTATION_KEY] == [ 'plonemeeting-assembly', self.request.get('meetingConfigId'), ]) # wipe out annotations view.ws4pmSettings.checkAlreadySentToPloneMeeting(document) self.assertFalse(WS4PMCLIENT_ANNOTATION_KEY in annotations)
def test_generateItemTemplateView(self): """ Test the BrowserView that generates a given template of an item """ self.changeUser('admin') messages = IStatusMessage(self.request) document = createDocument(self.portal) DOCUMENT_ABSOLUTE_URL = document.absolute_url() # we must obviously be connected to PM... view = document.restrictedTraverse( '@@generate_document_from_plonemeeting') # nothing is generated, just redirected to the context self.assertFalse(view() != DOCUMENT_ABSOLUTE_URL) self.assertTrue(len(messages.show()) == 1) self.assertTrue(messages.show()[0].message == UNABLE_TO_CONNECT_ERROR) # _soap_connectToPloneMeeting is memoized... cleanMemoize(self.request) item = self._sendToPloneMeeting(document) # a statusmessage for having created the item successfully self.assertEqual(messages.show()[-1].message, CORRECTLY_SENT_TO_PM_INFO) view = document.restrictedTraverse( '@@generate_document_from_plonemeeting') # with no templateFormat defined, the mimetype can not be determined # an error statusmessage is displayed # last added statusmessage # nothing is generated, just redirected to the context self.assertFalse(view() != DOCUMENT_ABSOLUTE_URL) self.assertEqual(messages.show()[-1].message, UNABLE_TO_DETECT_MIMETYPE_ERROR) self.request.set('templateFormat', 'odt') # if no templateFilename, an error is displayed, nothing is generated view = document.restrictedTraverse( '@@generate_document_from_plonemeeting') # nothing is generated, just redirected to the context self.assertFalse(view() != DOCUMENT_ABSOLUTE_URL) self.assertEqual(messages.show()[-1].message, FILENAME_MANDATORY_ERROR) # if not valid itemUID defined, the item can not be found and so accessed self.request.set('templateFilename', 'filename') view = document.restrictedTraverse( '@@generate_document_from_plonemeeting') # nothing is generated, just redirected to the context self.assertFalse(view() != DOCUMENT_ABSOLUTE_URL) self.assertEqual( messages.show()[-1].message, u"An error occured while generating the document in " "PloneMeeting! The error message was : Server raised fault: 'You can not access this item!'" ) # now with a valid itemUID but no valid templateId self.request.set('itemUID', item.UID()) view = document.restrictedTraverse( '@@generate_document_from_plonemeeting') # nothing is generated, just redirected to the context self.assertFalse(view() != DOCUMENT_ABSOLUTE_URL) self.assertEqual( messages.show()[-1].message, u"An error occured while generating the document in " "PloneMeeting! The error message was : Server raised fault: 'You can not access this template!'" ) # now with all valid infos self.request.set('templateId', POD_TEMPLATE_ID_PATTERN.format('itemTemplate', 'odt')) view = document.restrictedTraverse( '@@generate_document_from_plonemeeting') res = view() # with have a real result, aka not redirected to the context, a file self.assertTrue(view() != DOCUMENT_ABSOLUTE_URL) self.assertTrue(len(res) > 10000) self.assertEquals( self.request.response.headers, { 'content-type': 'application/vnd.oasis.opendocument.text', 'location': '{0}/document'.format(self.portal.absolute_url()), 'content-disposition': 'inline;filename="filename.odt"' })