def test_restapi_add_item_with_annexes_children(self): """When creating an item, we may add annexes as __children__, we may add several annexes at once.""" cfg = self.meetingConfig self.changeUser("pmManager") endpoint_url = "{0}/@item".format(self.portal_url) json = { "config_id": cfg.getId(), "proposingGroup": self.developers.getId(), "title": "My item", "__children__": [ { "@type": "annex", "title": "My annex 1", "content_category": "item-annex", "file": { "data": "123456", "encoding": "ascii", "filename": "file.txt", }, }, { "@type": "annex", "title": "My annex 2", "content_category": "item-annex", "file": { "data": base64_pdf_data, "filename": "file.pdf" }, }, ], } response = self.api_session.post(endpoint_url, json=json) transaction.commit() # this tries to manage randomly failing test try: self.assertEqual(response.status_code, 201, response.content) except Exception: response = self.api_session.post(endpoint_url, json=json) transaction.commit() self.assertEqual(response.status_code, 201, response.content) pmFolder = self.getMeetingFolder() item = pmFolder.objectValues()[-1] annex1 = get_annexes(item)[0] annex2 = get_annexes(item)[1] self.assertEqual(annex1.file.filename, json["__children__"][0]["file"]["filename"]) self.assertEqual(annex1.file.size, 6) self.assertEqual(annex1.file.contentType, "text/plain") self.assertEqual(annex2.file.filename, json["__children__"][1]["file"]["filename"]) self.assertEqual(annex2.file.size, 6475) self.assertEqual(annex2.file.contentType, "application/pdf")
def test_ws_createItemWithAnnexNotRecognizedByLibmagicRequest(self): """ Test SOAP service behaviour when creating items with one annex that is not recognized by libmagic. The annex used here is a MS Word .doc file that fails to be recognized when using libmagic/file 5.09 """ self.changeUser('pmCreator1') req = self._prepareCreationData() data = { 'title': 'My crashing created annex', 'filename': 'file_crash_libmagic.doc', 'file': 'file_crash_libmagic.doc' } req._creationData._annexes = [self._prepareAnnexInfo(**data)] annex = req._creationData._annexes[0] # first make sure this file crash libmagic self.assertRaises(MagicException, magic.Magic(mime=True).from_buffer, annex._file) newItem, response = self._createItem(req) # the annex is nevertheless created and correctly recognized because it had a correct file extension annexes = get_annexes(newItem) self.failUnless(len(annexes) == 1) # the annex mimetype is correct annex = annexes[0] self.failUnless(annex.file.contentType == 'application/msword') # the annex metadata are ok self.failUnless( annex.Title() == 'My crashing created annex' and annex.getMeetingFileType().getId() == 'financial-analysis') # if libmagic crash and no valid filename provided, the annex is not created data = { 'title': 'My crashing NOT created annex', 'filename': 'file_crash_libmagic_without_extension', 'file': 'file_crash_libmagic.doc' } req._creationData._annexes = [self._prepareAnnexInfo(**data)] newItem2, response = self._createItem(req) self.failUnless(len(get_annexes(newItem2)) == 0) # a warning specifying that annex was not added because mimetype could # not reliabily be found is added in the response self.assertEqual(response._warnings, [ translate(MIMETYPE_NOT_FOUND_OF_ANNEX_WARNING, domain='imio.pm.ws', mapping={ 'annex_path': (data['filename']), 'item_path': newItem2.absolute_url_path() }, context=self.portal.REQUEST) ])
def test_restapi_add_item_with_annexes_and_check_encoding(self): """When creating an item, we may add annexes as __children__, if encoding not given, we assume it is 'base64'.""" cfg = self.meetingConfig self.changeUser("pmManager") endpoint_url = "{0}/@item".format(self.portal_url) json = { "config_id": cfg.getId(), "proposingGroup": self.developers.getId(), "title": "My item", "__children__": [ { "@type": "annex", "title": "My annex", "content_category": "item-annex", "file": { "data": base64_pdf_data, "filename": "file.pdf" }, }, ], } response = self.api_session.post(endpoint_url, json=json) transaction.begin() self.assertEqual(response.status_code, 201, response.content) pmFolder = self.getMeetingFolder() item = pmFolder.objectValues()[-1] annex = get_annexes(item)[0] self.assertEqual(annex.file.filename, json["__children__"][0]["file"]["filename"]) self.assertEqual(annex.file.size, 6475) self.assertEqual(annex.file.contentType, "application/pdf")
def showDecisionAnnexesSection(self): """Check if context contains decisionAnnexes or if there are some decisionAnnex annex types available in the configuration.""" if self.context.__class__.__name__ == 'MeetingItem' and \ (get_annexes(self.context, portal_types=['annexDecision']) or self._showAddAnnexDecision): return True return False
def test_restapi_add_a_meeting_with_annexes(self): """When creating a meeting, we may add annexes as __children__, we may add several annexes at once.""" cfg = self.meetingConfig self.changeUser("pmManager") endpoint_url = "{0}/@item".format(self.portal_url) json = { "config_id": cfg.getId(), "date": "2022-02-02 12:00", "__children__": [ { "@type": "annex", "title": "My annex 1", "file": { "data": "123456", "encoding": "ascii", "filename": "file.txt", }, }, { "@type": "annex", "title": "My annex 2", "file": { "data": base64_pdf_data, "filename": "file.pdf" }, }, ], } response = self.api_session.post(endpoint_url, json=json) transaction.begin() self.assertEqual(response.status_code, 201, response.content) pmFolder = self.getMeetingFolder() meeting = pmFolder.objectValues()[-1] annex1 = get_annexes(meeting)[0] annex2 = get_annexes(meeting)[1] self.assertEqual(annex1.file.filename, json["__children__"][0]["file"]["filename"]) self.assertEqual(annex1.file.size, 6) self.assertEqual(annex1.file.contentType, "text/plain") self.assertEqual(annex2.file.filename, json["__children__"][1]["file"]["filename"]) self.assertEqual(annex2.file.size, 6475) self.assertEqual(annex2.file.contentType, "application/pdf")
def test_ws_createItemWithOneAnnexRequest(self): """ Test SOAP service behaviour when creating items with one annex """ # by default no item exists self.changeUser('pmCreator1') req = self._prepareCreationData() data = { 'title': 'My annex 1', 'filename': 'smallTestFile.pdf', 'file': 'smallTestFile.pdf' } req._creationData._annexes = [self._prepareAnnexInfo(**data)] annex = req._creationData._annexes[0] # Serialize the request so it can be easily tested request = serializeRequest(req) # This is what the sent enveloppe should looks like expected = """POST /plone/createItemRequest HTTP/1.0 Authorization: Basic %s:%s Content-Length: 102 Content-Type: text/xml SOAPAction: / <SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" """ \ """xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ZSI="http://www.zolera.com/schemas/ZSI/" """ \ """xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">""" \ """<SOAP-ENV:Header></SOAP-ENV:Header><SOAP-ENV:Body xmlns:ns1="http://ws4pm.imio.be">""" \ """<ns1:createItemRequest><meetingConfigId>plonegov-assembly</meetingConfigId>""" \ """<proposingGroupId>developers</proposingGroupId><creationData xsi:type="ns1:CreationData">""" \ """<title>My new item title</title><category>development</category>""" \ """<description><p>Description</p></description>""" \ """<detailedDescription><p>Detailed description</p></detailedDescription>""" \ """<decision><p>Décision</p></decision>""" \ """<annexes xsi:type="ns1:AnnexInfo"><title>%s</title><annexTypeId>%s</annexTypeId><filename>%s</filename><file> %s</file></annexes></creationData><cleanHtml>true</cleanHtml></ns1:createItemRequest>""" \ """</SOAP-ENV:Body></SOAP-ENV:Envelope>""" % ('pmCreator1', 'meeting', annex._title, annex._annexTypeId, annex._filename, base64.encodestring(annex._file)) result = """POST /plone/createItemRequest HTTP/1.0 Authorization: Basic %s:%s Content-Length: 102 Content-Type: text/xml SOAPAction: / %s""" % ('pmCreator1', 'meeting', request) self.assertEqual(expected, result) newItem, response = self._createItem(req) # now check the created item have the annex annexes = get_annexes(newItem) # the annex is actually created self.failUnless(len(annexes) == 1) # the annex mimetype is correct annex = annexes[0] self.failUnless(annex.file.contentType == 'application/pdf') # the annex metadata are ok self.assertEqual(annex.Title(), 'My annex 1') self.assertEqual( annex.content_category, 'plonegov-assembly-annexes_types_-_item_annexes_-_financial-analysis' )
def print_all_annexes(self, portal_types=['annex'], filters={}, with_icon=False, with_filename=False): """ Printing Method use in templates : return all viewable annexes for item @param: filters is a dict of {"attribute" : value}. It excludes all non matching annex from the result. Example : {'confidential': True, 'publishable': False} possible keys are : 'confidential', 'to_print', 'to_sign' and 'publishable' (all are bool or None) """ res = [] annexes = get_annexes(self.context, portal_types=portal_types) mimetypes_registry = self.portal.mimetypes_registry if filters: effective_annexes = [] for annex in annexes: use_this_annex = True for attribute, value in filters.items(): if getattr(annex, attribute) != value: use_this_annex = False break if use_this_annex: effective_annexes.append(annex) annexes = effective_annexes for annex in annexes: url = annex.absolute_url() title = safe_unicode(cgi.escape(annex.Title())) file_type_icon = u'' if with_icon: mime_type = mimetypes_registry.lookup( annex.file.contentType)[0] file_type_icon = u' <img src="{0}/{1}"/>'.format( self.portal.absolute_url(), mime_type.icon_path) annex_type_icon = u'<img src="{0}/{1}"/>'.format( self.portal.absolute_url(), self.real_context.categorized_elements[ annex.UID()]['icon_url']) # sometimes filename may be None if annex.file.filename: extension = annex.file.filename.split(u'.')[-1] # escape just in case there is no file extension file_info = u' ({0})'.format( safe_unicode(cgi.escape(extension))) else: file_info = u' (???)' res.append(u'<p>{0} <a href="{1}">{2}</a>{3}{4}</p>'.format( annex_type_icon, url, title, file_type_icon, file_info)) if with_filename: file_name = safe_unicode(cgi.escape(annex.file.filename)) res.append(u'<p><i>{0}</i></p>'.format(file_name)) return u'\n'.join(res)
def printAllAnnexes(self, portal_types=['annex']): ''' Printing Method use in templates : return all viewable annexes for item ''' res = [] annexes = get_annexes(self.context, portal_types=portal_types) for annex in annexes: url = annex.absolute_url() title = annex.Title().replace('&', '&') res.append(u'<p><a href="{0}">{1}</a></p>'.format( url, safe_unicode(title))) return (u'\n'.join(res))
def test_pm_SendItemToOtherMCWithoutDefinedAnnexType(self): """When cloning an item to another meetingConfig or to the same meetingConfig, if we have annexes on the original item and destination meetingConfig (that could be same as original item or another) does not have annex types defined, it does not fail but annexes are not kept and a portal message is displayed.""" cfg = self.meetingConfig cfg2 = self.meetingConfig2 # first test when sending to another meetingConfig # remove every annexTypes from meetingConfig2 self.changeUser('admin') self._removeConfigObjectsFor(cfg2, folders=[ 'annexes_types/item_annexes', ]) self.assertTrue(not cfg2.annexes_types.item_annexes.objectValues()) # a portal message will be added, for now there is no message messages = IStatusMessage(self.request).show() self.assertTrue(not messages) # now create an item, add an annex and clone it to the other meetingConfig data = self._setupSendItemToOtherMC(with_annexes=True) originalItem = data['originalItem'] newItem = data['newItem'] # original item had annexes self.assertEqual( len(get_annexes(originalItem, portal_types=['annex'])), 2) self.assertEqual( len(get_annexes(originalItem, portal_types=['annexDecision'])), 2) # but new item is missing the normal annexes because # no annexType for normal annexes are defined in the cfg2 self.assertEqual(len(get_annexes(newItem, portal_types=['annex'])), 0) # XXX Seraing, decision's annexe are keep (but in their config, these annexes was send in simply annexes self.assertEqual( len(get_annexes(newItem, portal_types=['annexDecision'])), 2) # moreover a message was added messages = IStatusMessage(self.request).show() expectedMessage = translate( "annex_not_kept_because_no_available_annex_type_warning", mapping={'annexTitle': data['annex2'].Title()}, domain='PloneMeeting', context=self.request) self.assertEqual(messages[-2].message, expectedMessage) # now test when cloning locally, even if annexes types are not enabled # it works, this is the expected behavior, backward compatibility when an annex type # is no more enabled but no more able to create new annexes with this annex type self.changeUser('admin') for at in (cfg.annexes_types.item_annexes.objectValues() + cfg.annexes_types.item_decision_annexes.objectValues()): at.enabled = False # no available annex types, try to clone newItem now self.changeUser('pmManager') # clean status message so we check that a new one is added del IAnnotations(self.request)['statusmessages'] clonedItem = originalItem.clone(copyAnnexes=True) # annexes were kept self.assertEqual(len(get_annexes(clonedItem, portal_types=['annex'])), 2) # for Seraing, item had not annexes decisions self.assertEqual( len(get_annexes(clonedItem, portal_types=['annexDecision'])), 0)
def SearchableText(obj): """ Contained annex title and scan_id are indexed in the SearchableText. """ res = [] res.append(obj.SearchableText()) for annex in get_annexes(obj): res.append(annex.Title()) scan_id = getattr(annex, "scan_id", None) if scan_id: # we need utf-8, when edited thru the UI, scan_id is stored as unicode res.append(safe_encode(scan_id)) res = ' '.join(res) return res or _marker
def annexes_index(obj): """ Unique index with data relative to stored annexes : - to_print : - 'not_to_print' if contains annexes not to print; - 'to_print' otherwise; - confidential : - 'not_confidential' if contains not confidential annexes; - 'confidential' otherwise; - publishable : - 'not_publishable' if contains not publishable annexes; - 'publishable' otherwise; - to_sign/signed : - 'not_to_sign' if contains not to sign annexes; - 'to_sign' if contains to_sign but not signed annexes; - 'signed' if contains signed annexes. """ res = [] # use objectValues because with events order, an annex # could be added but still not registered in the categorized_elements dict for annex in get_annexes(obj): # to_print if annex.to_print: res.append('to_print') else: res.append('not_to_print') # confidential if annex.confidential: res.append('confidential') else: res.append('not_confidential') # publishable if annex.publishable: res.append('publishable') else: res.append('not_publishable') # to_sign/signed if annex.to_sign: if annex.signed: res.append('signed') else: res.append('to_sign') else: res.append('not_to_sign') # remove duplicates return list(set(res))
def test_restapi_annexes_endpoint_filters(self): """@annexes, it is possible to filter on existing boolean values to_print, publishable, ...""" cfg = self.meetingConfig config = cfg.annexes_types.item_annexes config.publishable_activated = True # create item with annexes self.changeUser("pmManager") item = self.create("MeetingItem") annex = self.addAnnex(item, publishable=True) self.addAnnex(item, publishable=False) transaction.commit() item_url = item.absolute_url() endpoint_url = "{0}/@annexes?publishable=true".format(item_url) response = self.api_session.get(endpoint_url) self.assertEqual(len(response.json()), 1) self.assertEqual(len(get_annexes(item)), 2) annex_infos = response.json()[0] self.assertEqual(annex_infos['UID'], annex.UID())
def test_restapi_add_item_with_annexes(self): """When creating an item, we may add annexes as __children__.""" cfg = self.meetingConfig self.changeUser("pmManager") endpoint_url = "{0}/@item".format(self.portal_url) json = { "config_id": cfg.getId(), "proposingGroup": self.developers.getId(), "title": "My item", "__children__": [ { "@type": "annex", "title": "My annex", "content_category": "item-annex", "file": { "data": "123456", "encoding": "ascii", "filename": "file.txt", }, }, ], } response = self.api_session.post(endpoint_url, json=json) transaction.begin() self.assertEqual(response.status_code, 201, response.content) pmFolder = self.getMeetingFolder() item = pmFolder.objectValues()[-1] self.assertEqual(item.Title(), json["title"]) annex = get_annexes(item)[0] self.assertEqual(annex.title, json["__children__"][0]["title"]) self.assertEqual( annex.content_category, calculate_category_id( cfg.annexes_types.item_annexes.get("item-annex")), ) self.assertEqual(annex.file.filename, json["__children__"][0]["file"]["filename"]) self.assertEqual(annex.file.size, 6) self.assertEqual(annex.file.contentType, "text/plain")
def doClose(self, stateChange): ''' ''' # Set the firstItemNumber self.context.update_first_item_number() # remove annex previews of every items if relevant if self.cfg.getRemoveAnnexesPreviewsOnMeetingClosure(): # add logging message to fingerpointing log for item in self.context.get_items(ordered=True): annexes = get_annexes(item) if annexes: for annex in annexes: self.tool._removeAnnexPreviewFor(item, annex) extras = 'item={0} number_of_annexes={1}'.format( repr(item), len(annexes)) fplog('remove_annex_previews', extras=extras) msg = translate( u"Preview of annexes were deleted upon meeting closure.", domain='PloneMeeting', context=self.context.REQUEST) api.portal.show_message(msg, request=self.context.REQUEST)
def onItemDuplicated(original, event): """After item's cloning, we removed decision annexe. """ newItem = event.newItem annexes = get_annexes(newItem) for annex in annexes: if getattr(annex, 'scan_id', None): unrestrictedRemoveGivenObject(annex) msg = translate( 'annex_not_kept_because_using_scan_id', mapping={'annexTitle': safe_unicode(annex.Title())}, domain='PloneMeeting', context=newItem.REQUEST) api.portal.show_message(msg, request=newItem.REQUEST, type='warning') # xxx Seraing clear some fields linked to meeting newRawDescri = _removeTypistNote(newItem.getRawDescription()) newItem.setDescription(newRawDescri) # Make sure we have 'text/html' for every Rich fields forceHTMLContentTypeForEmptyRichFields(newItem)
def test_pm_UpdateLocalRolesOn50ItemsWith0AnnexesAnd10Advices(self): '''Call.update_local_roles on items with 0 annexes and 10 advices.''' items = self._setupItemsForUpdateLocalRoles(add_advices=True, add_annexes=False) number_of_advices = 10 for item in items: self.assertEqual(len(item.adviceIndex), number_of_advices) # call update local roles 2 times uids = listify_uids([item.UID() for item in items]) number_of_annexes = 0 for item in items: self.assertEqual(len(get_annexes(item)), number_of_annexes) pm_logger.info( 'Call 1 to.update_local_roles on 50 items holding ' '{0} annexes and {1} auto asked advices.'.format(number_of_annexes, number_of_advices)) self._updateItemLocalRoles(uids) pm_logger.info( 'Call 2 to.update_local_roles on 50 items holding ' '{0} annexes and {1} auto asked advices.'.format(number_of_annexes, number_of_advices)) self._updateItemLocalRoles(uids)
def test_pm_DuplicatedItemDoesNotKeepDecisionAnnexes(self): """When an item is duplicated using the 'duplicate and keep link', decision annexes are not kept.""" self.changeUser('pmCreator1') item = self.create('MeetingItem') self.addAnnex(item) # xxx Namur, creator can't create "Annexe decision" self.changeUser('admin') self.addAnnex(item, relatedTo='item_decision') self.changeUser('pmCreator1') self.assertTrue(get_annexes(item, portal_types=['annex'])) self.assertTrue(get_annexes(item, portal_types=['annexDecision'])) # cloned and link not kept, decison annexes are removed clonedItem = item.clone() self.assertTrue(get_annexes(clonedItem, portal_types=['annex'])) self.assertFalse( get_annexes(clonedItem, portal_types=['annexDecision'])) # cloned but link kept, decison annexes are also removed clonedItemWithLink = item.clone(setCurrentAsPredecessor=True) self.assertTrue(get_annexes(clonedItemWithLink, portal_types=['annex'])) self.assertFalse( get_annexes(clonedItemWithLink, portal_types=['annexDecision']))
def test_restapi_add_annex_to_existing_element(self): """Use the @annex POST endpoint to create an annex.""" cfg = self.meetingConfig self._removeConfigObjectsFor(cfg) self.changeUser("pmManager") item = self.create('MeetingItem') item_uid = item.UID() date = None if not HAS_MEETING_DX: date = DateTime() meeting = self.create('Meeting', date=date) meeting_uid = meeting.UID() transaction.commit() # add annex to item json = { "title": "My annex", "content_category": "wrong-annex", "file": { "data": "123456", "encoding": "ascii", "filename": "file.txt" } } endpoint_url = "{0}/@annex/{1}".format(self.portal_url, item_uid) # wrong content_category response = self.api_session.post(endpoint_url, json=json) self.assertEqual(response.status_code, 400, response.content) self.assertEqual( response.json(), { u"message": ANNEX_CONTENT_CATEGORY_ERROR % "wrong-annex", u"type": u"BadRequest" }) # add annex to item correctly json["content_category"] = "item-annex" response = self.api_session.post(endpoint_url, json=json) transaction.begin() self.assertEqual(response.status_code, 201, response.content) # adding an annex without "content_category" will use default one # the default annex type is "financial-analysis" json.pop("content_category") response = self.api_session.post(endpoint_url, json=json) self.assertEqual(response.status_code, 201, response.content) self.assertEqual(response.json()["content_category"]["title"], u'Financial analysis') # add annexDecision to item correctly json["content_category"] = "decision-annex" json["decision_related"] = True response = self.api_session.post(endpoint_url, json=json) self.assertEqual(response.status_code, 201, response.content) # add annex to meeting # can not use parameter "decision_related" on a meeting json["content_category"] = "meeting-annex" endpoint_url = endpoint_url.replace(item_uid, meeting_uid) response = self.api_session.post(endpoint_url, json=json) self.assertEqual(response.status_code, 400, response.content) self.assertEqual( response.json(), { u"message": ANNEX_DECISION_RELATED_NOT_ITEM_ERROR, u"type": u"BadRequest" }) # add annex to meeting correctly json["decision_related"] = False response = self.api_session.post(endpoint_url, json=json) self.assertEqual(response.status_code, 201, response.content) transaction.begin() # annexes were added to item and meeting item_annexes = get_annexes(item, ["annex"]) self.assertEqual(len(item_annexes), 2) decision_annexes = get_annexes(item, ["annexDecision"]) self.assertEqual(len(decision_annexes), 1) meeting_annexes = get_annexes(meeting) self.assertEqual(len(meeting_annexes), 1)
def _testWholeDecisionProcessCouncil(self): """ This test covers the whole decision workflow. It begins with the creation of some items, and ends by closing a meeting. """ # meeting-config-college is tested in test_pm_WholeDecisionProcessCollege # we do the test for the council config self.meetingConfig = getattr(self.tool, self.cfg2_id) # pmCreator1 creates an item with 1 annex and proposes it self.changeUser('pmCreator1') item1 = self.create('MeetingItem', title='The first item') self.addAnnex(item1) # The creator can add a decision annex on created item self.addAnnex(item1, relatedTo='item_decision') self.do(item1, 'propose') # can add decision annex but not normal annex self.assertRaises(Unauthorized, self.addAnnex, item1) self.addAnnex(item1, relatedTo='item_decision') self.failIf(self.transitions(item1)) # He may trigger no more action # pmManager creates a meeting self.changeUser('pmManager') meeting = self.create('Meeting') # The meetingManager can add a decision annex self.addAnnex(item1, relatedTo='item_decision') # pmCreator2 creates and proposes an item self.changeUser('pmCreator2') item2 = self.create('MeetingItem', title='The second item', preferredMeeting=meeting.UID()) self.do(item2, 'propose') # pmReviewer1 validates item1 and adds an annex to it self.changeUser('pmReviewer1') # The reviewer can add a decision annex on proposed item self.addAnnex(item1, relatedTo='item_decision') self.do(item1, 'validate') # can not add decision annex or normal annex self.assertRaises(Unauthorized, self.addAnnex, item1) self.assertRaises(Unauthorized, self.addAnnex, item1, relatedTo='item_decision') # pmManager inserts item1 into the meeting and freezes it self.changeUser('pmManager') managerAnnex = self.addAnnex(item1) self.portal.restrictedTraverse('@@delete_givenuid')(managerAnnex.UID()) self.do(item1, 'present') self.changeUser('pmCreator1') # can not add decision annex or normal annex self.assertRaises(Unauthorized, self.addAnnex, item1) self.assertRaises(Unauthorized, self.addAnnex, item1, relatedTo='item_decision') self.changeUser('pmManager') self.do(meeting, 'freeze') # pmReviewer2 validates item2 self.changeUser('pmReviewer2') self.do(item2, 'validate') # pmManager inserts item2 into the meeting, as late item, and adds an # annex to it self.changeUser('pmManager') self.do(item2, 'present') self.addAnnex(item2) # So now I should have 1 normal item left and one late item in the meeting self.failIf(len(meeting.get_items()) != 2) self.failUnless(len(meeting.get_items(list_types=['late'])) == 1) self.changeUser('pmReviewer1') # can not add decision annex or normal annex self.assertRaises(Unauthorized, self.addAnnex, item1) self.assertRaises(Unauthorized, self.addAnnex, item1, relatedTo='item_decision') # pmManager adds a decision to item1 and publishes the meeting self.changeUser('pmManager') item1.setDecision(self.decisionText) self.do(meeting, 'publish') self.changeUser('pmReviewer2') # can not add decision annex or normal annex self.assertRaises(Unauthorized, self.addAnnex, item2) self.assertRaises(Unauthorized, self.addAnnex, item2, relatedTo='item_decision') self.changeUser('pmReviewer1') self.assertRaises(Unauthorized, self.addAnnex, item2) self.assertRaises(Unauthorized, self.addAnnex, item2, relatedTo='item_decision') # pmManager adds a decision for item2, decides and closes the meeting self.changeUser('pmManager') item2.setDecision(self.decisionText) self.do(meeting, 'decide') # check that a delayed item is duplicated self.assertEqual(len(item1.getBRefs('ItemPredecessor')), 0) self.do(item1, 'delay') # the duplicated item has item1 as predecessor duplicatedItem = item1.get_successors()[0] self.assertEqual(duplicatedItem.get_predecessor().UID(), item1.UID()) # when duplicated on delay, only normal annexes are kept, decision annexes are not self.assertEqual( len(get_annexes(duplicatedItem, portal_types=('annex', ))), 1) self.assertEqual( len(get_annexes(duplicatedItem, portal_types=('annexDecision', ))), 0) self.addAnnex(item2, relatedTo='item_decision') self.failIf(len(self.transitions(meeting)) != 2) # When a meeting is closed, items without a decision are automatically 'accepted' self.do(meeting, 'close') self.assertEqual(item2.query_state(), 'accepted') # An already decided item keep his given decision self.assertEqual(item1.query_state(), 'delayed') # XXX added tests regarding ticket #5887 # test back transitions self.changeUser('admin') self.do(meeting, 'backToDecided') self.changeUser('pmManager') self.do(meeting, 'backToPublished') # set an item back to published to test the 'freeze' meeting here under self.do(item1, 'backToItemPublished') self.do(meeting, 'backToFrozen') # this also test the 'doBackToCreated' action on the meeting self.do(meeting, 'backToCreated') self.do(meeting, 'freeze') self.do(meeting, 'publish') self.do(meeting, 'decide') self.do(meeting, 'close')
def test_ws_createItemWithSeveralAnnexesRequest(self): """ Test SOAP service behaviour when creating items with several annexes of different types """ # by default no item exists self.changeUser('pmCreator1') req = self._prepareCreationData() # add 4 extra annexes # no data give, some default values are used (smallTestFile.pdf) data1 = {'title': 'My annex 1', 'filename': 'smallTestFile.pdf', 'file': 'smallTestFile.pdf'} # other annexTypeId than the default one data2 = {'title': 'My annex 2', 'filename': 'arbitraryFilename.odt', 'file': 'mediumTestFile.odt', 'annexTypeId': 'budget-analysis'} # a wrong annexTypeId and a test with a large msword document data3 = {'title': 'My annex 3', 'filename': 'largeTestFile.doc', 'file': 'largeTestFile.doc', 'annexTypeId': 'wrong-annexTypeId'} # empty file data provided, at the end, the annex is not created but the item is correctly created data4 = {'title': 'My annex 4', 'filename': 'emptyTestFile.txt', 'file': 'emptyTestFile.txt', 'annexTypeId': 'budget-analysis'} # a file that will have several extensions found in mimetypes_registry # is not handled if no valid filename is provided data5 = {'title': 'My annex 5', 'filename': 'notValidFileNameNoExtension', 'file': 'octetStreamTestFile.bin', 'annexTypeId': 'budget-analysis'} # but if the filename is valid, then the annex is handled data6 = {'title': 'My annex 6', 'filename': 'validExtension.bin', 'file': 'octetStreamTestFile.bin', 'annexTypeId': 'budget-analysis'} req._creationData._annexes = [self._prepareAnnexInfo(**data1), self._prepareAnnexInfo(**data2), self._prepareAnnexInfo(**data3), self._prepareAnnexInfo(**data4), self._prepareAnnexInfo(**data5), self._prepareAnnexInfo(**data6)] # serialize the request so it can be easily tested request = serializeRequest(req) # build annexes part of the envelope annexesEnveloppePart = "" for annex in req._creationData._annexes: annexesEnveloppePart = annexesEnveloppePart + """<annexes xsi:type="ns1:AnnexInfo"><title>%s</title>""" \ """<annexTypeId>%s</annexTypeId><filename>%s</filename><file> %s</file></annexes>""" % (annex._title, annex._annexTypeId, annex._filename, base64.encodestring(annex._file)) # This is what the sent enveloppe should looks like expected = """POST /plone/createItemRequest HTTP/1.0 Authorization: Basic %s:%s Content-Length: 102 Content-Type: text/xml SOAPAction: / <SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" """ \ """xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ZSI="http://www.zolera.com/schemas/ZSI/" """ \ """xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">""" \ """<SOAP-ENV:Header></SOAP-ENV:Header><SOAP-ENV:Body xmlns:ns1="http://ws4pm.imio.be"><ns1:createItemRequest>""" \ """<meetingConfigId>plonegov-assembly</meetingConfigId><proposingGroupId>developers</proposingGroupId>""" \ """<creationData xsi:type="ns1:CreationData"><title>My new item title</title><category>development</category>""" \ """<description><p>Description</p></description>""" \ """<detailedDescription><p>Detailed description</p></detailedDescription>""" \ """<decision><p>Décision</p></decision>""" \ """%s</creationData><cleanHtml>true</cleanHtml></ns1:createItemRequest></SOAP-ENV:Body></SOAP-ENV:Envelope>""" \ % ('pmCreator1', 'meeting', annexesEnveloppePart) result = """POST /plone/createItemRequest HTTP/1.0 Authorization: Basic %s:%s Content-Length: 102 Content-Type: text/xml SOAPAction: / %s""" % ('pmCreator1', 'meeting', request) self.assertEqual(expected, result) newItem, response = self._createItem(req) annexes = get_annexes(newItem) # 4 annexes are actually created self.failUnless(len(annexes) == 4) # the annexes mimetype are corrects self.failUnless(annexes[0].file.contentType == 'application/pdf') self.failUnless(annexes[1].file.contentType == 'application/vnd.oasis.opendocument.text') self.failUnless(annexes[2].file.contentType == 'application/msword') self.failUnless(annexes[3].file.contentType == 'application/octet-stream') # the annexes metadata are ok self.failUnless( annexes[0].Title() == 'My annex 1' and annexes[0].content_category == 'plonegov-assembly-annexes_types_-_item_annexes_-_financial-analysis') self.failUnless( annexes[1].Title() == 'My annex 2' and annexes[1].content_category == 'plonegov-assembly-annexes_types_-_item_annexes_-_budget-analysis') # meetingFileType is back to default one when a wrong file type is given in the annexInfo self.failUnless( annexes[2].Title() == 'My annex 3' and annexes[2].content_category == 'plonegov-assembly-annexes_types_-_item_annexes_-_financial-analysis') self.failUnless( annexes[3].Title() == 'My annex 6' and annexes[3].content_category == 'plonegov-assembly-annexes_types_-_item_annexes_-_budget-analysis') # annexes filename are the ones defined in the 'filename', either it is generated self.failUnless(annexes[0].file.filename == u'smallTestFile.pdf') self.failUnless(annexes[1].file.filename == u'arbitraryFilename.odt') self.failUnless(annexes[2].file.filename == u'largeTestFile.doc') self.failUnless(annexes[3].file.filename == u'validExtension.bin') # now try to create an item with an annex that has no file # when file attribute is not provided, the annex is not created data = {'title': 'My annex 7', 'filename': 'validExtension.bin', 'annexTypeId': 'budget-analysis'} req._creationData._annexes = [self._prepareAnnexInfo(**data), ] self.assertEqual(req._creationData._annexes[0]._file, None) newItem, response = self._createItem(req) annexes = get_annexes(newItem) # no annexes have been added self.assertEqual(len(annexes), 0)
def test_pm_AnnexToPrintBehaviourWhenCloned(self): """When cloning an item with annexes, to the same or another MeetingConfig, the 'toPrint' field is kept depending on MeetingConfig.keepOriginalToPrintOfClonedItems. If it is True, the original value is kept, if it is False, it will use the MeetingConfig.annexToPrintDefault value.""" cfg = self.meetingConfig cfg2 = self.meetingConfig2 cfg2Id = cfg2.getId() cfg.setKeepOriginalToPrintOfClonedItems(False) cfg2.setKeepOriginalToPrintOfClonedItems(False) self.changeUser('pmManager') meeting = self.create('Meeting', date=DateTime('2016/02/02')) item = self.create('MeetingItem') annex = self.addAnnex(item) annex_config = get_config_root(annex) annex_group = get_group(annex_config, annex) self.assertFalse(annex_group.to_be_printed_activated) self.assertFalse(annex.to_print) annex.to_print = True self.assertTrue(annex.to_print) # decide the item so we may add decision annex item.setDecision(self.decisionText) self.presentItem(item) self.decideMeeting(meeting) self.do(item, 'accept') self.assertEquals(item.queryState(), 'accepted') annexDec = self.addAnnex(item, relatedTo='item_decision') annexDec_config = get_config_root(annexDec) annexDec_group = get_group(annexDec_config, annexDec) self.assertFalse(annexDec_group.to_be_printed_activated) self.assertFalse(annexDec.to_print) annexDec.to_print = True self.assertTrue(annexDec.to_print) # clone item locally, as keepOriginalToPrintOfClonedItems is False # default values defined in the config will be used self.assertFalse(cfg.getKeepOriginalToPrintOfClonedItems()) clonedItem = item.clone() annexes = get_annexes(clonedItem, portal_types=['annex']) if not annexes: pm_logger.info('No annexes found on duplicated item clonedItem') cloneItemAnnex = annexes and annexes[0] annexesDec = get_annexes(clonedItem, portal_types=['annexDecision']) if not annexesDec: pm_logger.info( 'No decision annexes found on duplicated item clonedItem') cloneItemAnnexDec = annexesDec and annexesDec[0] self.assertFalse(cloneItemAnnex and cloneItemAnnex.to_print) self.assertFalse(cloneItemAnnexDec and cloneItemAnnexDec.to_print) # enable keepOriginalToPrintOfClonedItems # some plugins remove annexes/decision annexes on duplication # so make sure we test if an annex is there... self.changeUser('siteadmin') cfg.setKeepOriginalToPrintOfClonedItems(True) self.changeUser('pmManager') clonedItem2 = item.clone() annexes = get_annexes(clonedItem2, portal_types=['annex']) if not annexes: pm_logger.info('No annexes found on duplicated item clonedItem2') cloneItem2Annex = annexes and annexes[0] annexesDec = get_annexes(clonedItem2, portal_types=['annexDecision']) if not annexesDec: pm_logger.info( 'No decision annexes found on duplicated item clonedItem2') cloneItem2AnnexDec = annexesDec and annexesDec[0] self.assertTrue(cloneItem2Annex and cloneItem2Annex.to_print or True) self.assertTrue(cloneItem2AnnexDec and cloneItem2AnnexDec.to_print or True) # clone item to another MC and test again # cfg2.keepOriginalToPrintOfClonedItems is True self.assertFalse(cfg2.getKeepOriginalToPrintOfClonedItems()) item.setOtherMeetingConfigsClonableTo((cfg2Id, )) clonedToCfg2 = item.cloneToOtherMeetingConfig(cfg2Id) annexes = get_annexes(clonedToCfg2, portal_types=['annex']) if not annexes: pm_logger.info('No annexes found on duplicated item clonedToCfg2') clonedToCfg2Annex = annexes and annexes[0] annexesDec = get_annexes(clonedToCfg2, portal_types=['annexDecision']) if not annexesDec: pm_logger.info( 'No decision annexes found on duplicated item clonedToCfg2') self.assertFalse(clonedToCfg2Annex and clonedToCfg2Annex.to_print) # enable keepOriginalToPrintOfClonedItems self.changeUser('siteadmin') cfg2.setKeepOriginalToPrintOfClonedItems(True) self.deleteAsManager(clonedToCfg2.UID()) # send to cfg2 again self.changeUser('pmManager') clonedToCfg2Again = item.cloneToOtherMeetingConfig(cfg2Id) annexes = get_annexes(clonedToCfg2Again, portal_types=['annex']) if not annexes: pm_logger.info( 'No annexes found on duplicated item clonedToCfg2Again') clonedToCfg2AgainAnnex = annexes and annexes[0] annexesDec = get_annexes(clonedToCfg2Again, portal_types=['annexDecision']) if not annexesDec: pm_logger.info( 'No decision annexes found on duplicated item clonedToCfg2Again' ) self.assertTrue( clonedToCfg2AgainAnnex and clonedToCfg2AgainAnnex.to_print or True)
def test_ws_getItemInfosWithAnnexesRequest(self): """ Test that getting an item with a given UID returns valuable informations and linked annexes """ cfg = self.meetingConfig self.changeUser('pmCreator1') self.failUnless( len(self.portal.portal_catalog(portal_type='MeetingItemPga')) == 0) # prepare data for a default item req = self._prepareCreationData() # add one annex data = { 'title': 'My annex 1', 'filename': 'smallTestFile.pdf', 'file': 'smallTestFile.pdf' } req._creationData._annexes = [self._prepareAnnexInfo(**data)] # create the item newItem, reponse = self._createItem(req) newItemUID = newItem.UID() # get informations about the item, by default 'showAnnexes' is False resp = self._getItemInfos(newItemUID) expected = """<ns1:getItemInfosResponse xmlns:ns1="http://ws4pm.imio.be" """ \ """xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" """ \ """xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" """ \ """xmlns:ZSI="http://www.zolera.com/schemas/ZSI/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" """ \ """xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <itemInfo xsi:type="ns1:ItemInfo"> <UID>{0}</UID> <id>{1}</id> <title>My new item title</title> <creator>pmCreator1</creator> <creation_date>{2}</creation_date> <modification_date>{3}</modification_date> <category>development</category> <description><p>Description</p></description> <detailedDescription><p>Detailed description</p></detailedDescription> <decision><p>Décision</p></decision> <preferredMeeting/> <preferred_meeting_date>1950-01-01T00:00:00.006Z</preferred_meeting_date> <review_state>itemcreated</review_state> <meeting_date>1950-01-01T00:00:00.006Z</meeting_date> <absolute_url>http://nohost/plone/Members/pmCreator1/mymeetings/plonegov-assembly/{4}</absolute_url> <externalIdentifier/> <extraInfos/> </itemInfo> </ns1:getItemInfosResponse> """.format(newItemUID, newItem.getId(), gDateTime.get_formatted_content(gDateTime(), localtime(newItem.created())), gDateTime.get_formatted_content(gDateTime(), localtime(newItem.modified())), newItem.getId()) # annexes are not shown by default self.assertEqual(expected, resp) # now with 'showAnnexes=True' financial_annex_type_id = calculate_category_id( cfg.annexes_types.item_annexes.get('financial-analysis')) item_annex_type_id = calculate_category_id( cfg.annexes_types.item_annexes.get('item-annex')) resp = self._getItemInfos(newItemUID, showAnnexes=True) expected = """<ns1:getItemInfosResponse xmlns:ns1="http://ws4pm.imio.be" """ \ """xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" """ \ """xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" """ \ """xmlns:ZSI="http://www.zolera.com/schemas/ZSI/" """ \ """xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <itemInfo xsi:type="ns1:ItemInfo"> <UID>{0}</UID> <id>{1}</id> <title>My new item title</title> <creator>pmCreator1</creator> <creation_date>{2}</creation_date> <modification_date>{3}</modification_date> <category>development</category> <description><p>Description</p></description> <detailedDescription><p>Detailed description</p></detailedDescription> <decision><p>Décision</p></decision> <preferredMeeting/> <preferred_meeting_date>1950-01-01T00:00:00.006Z</preferred_meeting_date> <review_state>itemcreated</review_state> <meeting_date>1950-01-01T00:00:00.006Z</meeting_date> <absolute_url>http://nohost/plone/Members/pmCreator1/mymeetings/plonegov-assembly/{4}</absolute_url> <externalIdentifier/> <extraInfos/> <annexes xsi:type="ns1:AnnexInfo"> <id>smalltestfile.pdf</id> <title>My annex 1</title> <annexTypeId>{5}</annexTypeId> <filename>smallTestFile.pdf</filename> <file> {6}</file> </annexes> </itemInfo> </ns1:getItemInfosResponse> """.format(newItemUID, newItem.getId(), gDateTime.get_formatted_content(gDateTime(), localtime(newItem.created())), gDateTime.get_formatted_content(gDateTime(), localtime(newItem.modified())), newItem.getId(), financial_annex_type_id, base64.encodestring(get_annexes(newItem)[0].file.data)) # one annex is shown self.assertEqual(expected, resp) # now check with several (2) annexes... afile = open( os.path.join(os.path.dirname(__file__), 'mediumTestFile.odt')) annex_file = afile.read() afile.close() api.content.create( title='My BeautifulTestFile title', type='annex', file=namedfile.NamedBlobFile( annex_file, filename=safe_unicode(u'myBeautifulTestFile.odt')), container=newItem, content_category=item_annex_type_id, to_print=False, confidential=False) resp = self._getItemInfos(newItemUID, showAnnexes=True) expected = """<ns1:getItemInfosResponse xmlns:ns1="http://ws4pm.imio.be" """ \ """xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" """ \ """xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" """ \ """xmlns:ZSI="http://www.zolera.com/schemas/ZSI/" """ \ """xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <itemInfo xsi:type="ns1:ItemInfo"> <UID>{0}</UID> <id>{1}</id> <title>My new item title</title> <creator>pmCreator1</creator> <creation_date>{2}</creation_date> <modification_date>{3}</modification_date> <category>development</category> <description><p>Description</p></description> <detailedDescription><p>Detailed description</p></detailedDescription> <decision><p>Décision</p></decision> <preferredMeeting/> <preferred_meeting_date>1950-01-01T00:00:00.006Z</preferred_meeting_date> <review_state>itemcreated</review_state> <meeting_date>1950-01-01T00:00:00.006Z</meeting_date> <absolute_url>http://nohost/plone/Members/pmCreator1/mymeetings/plonegov-assembly/{4}</absolute_url> <externalIdentifier/> <extraInfos/> <annexes xsi:type="ns1:AnnexInfo"> <id>{5}</id> <title>My annex 1</title> <annexTypeId>{6}</annexTypeId> <filename>smallTestFile.pdf</filename> <file> {7}</file> </annexes> <annexes xsi:type="ns1:AnnexInfo"> <id>{8}</id> <title>My BeautifulTestFile title</title> <annexTypeId>{9}</annexTypeId> <filename>myBeautifulTestFile.odt</filename> <file> {10}</file> </annexes> </itemInfo> </ns1:getItemInfosResponse> """.format(newItemUID, newItem.getId(), gDateTime.get_formatted_content(gDateTime(), localtime(newItem.created())), gDateTime.get_formatted_content(gDateTime(), localtime(newItem.modified())), newItem.getId(), get_annexes(newItem)[0].id, financial_annex_type_id, base64.encodestring(get_annexes(newItem)[0].file.data), get_annexes(newItem)[1].id, item_annex_type_id, base64.encodestring(get_annexes(newItem)[1].file.data)) # 2 annexes are shown self.assertEqual(expected, resp)