def _giveFinanceAdvice(self, item, adviser_group_id): """Given an p_item in state 'proposed_to_finance_waiting_advices', give the p_adviser_group_id advice on it.""" originalUserId = self.member.getId() self.changeUser('pmFinController') changeCompleteness = item.restrictedTraverse( '@@change-item-completeness') self.request.set('new_completeness_value', 'completeness_complete') self.request.form['form.submitted'] = True changeCompleteness() advice = createContentInContainer( item, 'meetingadvicefinances', **{ 'advice_group': adviser_group_id, 'advice_type': u'positive_finance', 'advice_comment': richtextval(u'<p>My comment finance</p>'), 'advice_observations': richtextval(u'<p>My observation finance</p>') }) self.do(advice, 'proposeToFinancialReviewer', comment='My financial controller comment') # as finance reviewer self.changeUser('pmFinReviewer') self.do(advice, 'proposeToFinancialManager', comment='My financial reviewer comment') # as finance manager self.changeUser('pmFinManager') self.do(advice, 'signFinancialAdvice', comment='My financial manager comment') self.changeUser(originalUserId) return advice
def test_AdviceTypeVocabulary(self): """'Products.PloneMeeting.content.advice.advice_type_vocabulary' was overrided to manage values of finance advice.""" item, finance_advice = self._setupCollegeItemWithFinanceAdvice() self.changeUser('pmManager') vocab = queryUtility( IVocabularyFactory, "Products.PloneMeeting.content.advice.advice_type_vocabulary") # ask 'vendors' advice on item item.setOptionalAdvisers((self.vendors_uid, )) item._update_after_edit() self.do(item, 'backToProposedToDirector') vendors_advice = createContentInContainer( item, 'meetingadvice', **{ 'advice_group': self.vendors_uid, 'advice_type': u'negative', 'advice_comment': richtextval(u'<p>My comment vendors</p>'), 'advice_observations': richtextval(u'<p>My observation vendors</p>') }) finance_keys = vocab(finance_advice).by_value.keys() finance_keys.sort() self.assertEquals(finance_keys, [ 'negative_finance', 'not_required_finance', 'positive_finance', 'positive_with_remarks_finance' ]) vendors_keys = vocab(vendors_advice).by_value.keys() vendors_keys.sort() self.assertEquals( vendors_keys, ['negative', 'nil', 'positive', 'positive_with_remarks'])
def _setupCollegeItemWithFinanceAdvice(self, ): """Setup, create a College item and give finance advice on it.""" self.changeUser('admin') # add finance groups self._createFinanceGroups() # configure customAdvisers for 'meeting-config-college' _configureCollegeCustomAdvisers(self.portal) # define relevant users for finance groups self._setupFinanceGroups() # first 'return' an item and test self.changeUser('pmManager') item = self.create('MeetingItem', title='An item with finance advice') # ask finance advice and give it financial_group_uids = self.tool.finance_group_uids() item.setFinanceAdvice(financial_group_uids[0]) item._update_after_edit() self.proposeItem(item) self.do(item, 'wait_advices_from_proposed_to_director') # make item completeness complete and add advice self.changeUser('pmFinController') changeCompleteness = item.restrictedTraverse( '@@change-item-completeness') self.request.set('new_completeness_value', 'completeness_complete') self.request.form['form.submitted'] = True changeCompleteness() advice = createContentInContainer( item, 'meetingadvicefinances', **{ 'advice_group': financial_group_uids[0], 'advice_type': u'positive_finance', 'advice_comment': richtextval(u'<p>My comment finance</p>'), 'advice_observations': richtextval(u'<p>My observation finance</p>') }) self.do(advice, 'proposeToFinancialReviewer', comment='My financial controller comment') # as finance reviewer self.changeUser('pmFinReviewer') self.do(advice, 'proposeToFinancialManager', comment='My financial reviewer comment') # as finance manager self.changeUser('pmFinManager') self.do(advice, 'signFinancialAdvice', comment='My financial manager comment') return item, advice
def insert_and_close_meeting(self, member_folder, csv_meeting): if not self._check_meeting_data(csv_meeting): return _id = "meetingimport.{external_id}".format( external_id=csv_meeting.external_id) meeting = self.object_already_exists(_id, csv_meeting.portal_type) if meeting and meeting[0]: message = "Skipping meeting {id} and it items because it already exists".format( id=_id) logger.info(message) self.errors["meeting"].append(message) return meeting_date = csv_meeting.date meetingid = member_folder.invokeFactory( type_name=csv_meeting.portal_type, id=_id, date=meeting_date) meeting = getattr(member_folder, meetingid) meeting.signatures = None if csv_meeting.assembly: meeting.assembly = richtextval(csv_meeting.assembly) meeting.date = meeting_date meeting.start_date = csv_meeting.started_on meeting.end_date = csv_meeting.ended_on meeting.creation_date = DateTime(csv_meeting.created_on) logger.info(u"Created {type} {id} {date}".format( type=csv_meeting.portal_type, id=_id, date=meeting.title)) if csv_meeting.annexes: self.add_all_annexes_to_object(csv_meeting.annexes, meeting, confidential=True) else: meeting.observations = u"<p><strong>Cette séance n'a aucune annexe</strong></p>" logger.info(u"Adding {items} items to {type} of {date}".format( items=len(csv_meeting.items), type=csv_meeting.portal_type, date=meeting.title)) self.portal.REQUEST["PUBLISHED"] = meeting for csv_item in csv_meeting.items: self.insert_and_present_item(member_folder, csv_item) if meeting.get_items(): meeting.portal_workflow.doActionFor(meeting, "freeze") meeting.portal_workflow.doActionFor(meeting, "decide") meeting.portal_workflow.doActionFor(meeting, "close") for item in meeting.get_items(): item.setModificationDate(meeting_date) item.reindexObject(idxs=["modified"]) meeting.setModificationDate(DateTime(meeting_date)) meeting.reindexObject(idxs=["modified"]) self.meeting_counter += 1 transaction.commit()
def migrate_to_text_x_html_safe(self): """Migrate richtext's outputMimeType to text/x-html-safe.""" for brain in self.catalog(object_provides='imio.project.core.content.project.IProject'): obj = brain.getObject() if obj.budget_comments: if obj.budget_comments.outputMimeType == 'text/html': obj.budget_comments = richtextval(obj.budget_comments.raw) obj.reindexObject() if obj.observation: if obj.observation.outputMimeType == 'text/html': obj.observation = richtextval(obj.observation.raw) obj.reindexObject() if obj.comments: if obj.comments.outputMimeType == 'text/html': obj.comments = richtextval(obj.comments.raw) obj.reindexObject()
def migrate_projects_richtextvalues(self): project_brains = self.catalog(object_provides=IProject.__identifier__) for project_brain in project_brains: for field_name in ['budget_comments', 'observation', 'comments']: project_obj = project_brain.getObject() field_value = getattr(project_obj, field_name) if field_value: if isinstance(field_value, RichTextValue): setattr(project_obj, field_name, richtextval(field_value.raw))
def test_restapi_search_meetings_fullobjects(self): """ """ endpoint_url = "{0}/@search?config_id={1}&type=meeting&fullobjects=True".format( self.portal_url, self.meetingConfig.getId()) # create 2 meetings self.changeUser("pmManager") pattern = '<p>Text with image <img src="{0}"/> and more text.</p>' meeting = self.create("Meeting", date=datetime(2019, 11, 18)) meeting2 = self.create("Meeting", date=datetime(2019, 11, 19)) if HAS_MEETING_DX: meeting2.assembly = RichTextValue( u'Mr Present, [[Mr Absent]], Mr Present2') img = self._add_image(meeting2) text = pattern.format(img.absolute_url()) meeting2.observations = richtextval(text) else: meeting2.setAssembly(u'Mr Present, [[Mr Absent]], Mr Present2') img = self._add_image(meeting2) text = pattern.format(img.absolute_url()) meeting2.setObservations(text) self.assertEqual(self.get_review_state(meeting), "created") self.closeMeeting(meeting2) self.assertEqual(self.get_review_state(meeting2), "closed") transaction.commit() # found response = self.api_session.get(endpoint_url) self.assertEqual(response.status_code, 200, response.content) self.assertEqual(response.json()[u"items_total"], 2) # may still use additional search parameters endpoint_url += "&review_state=closed" response = self.api_session.get(endpoint_url) self.assertEqual(response.status_code, 200, response.content) resp_json = response.json() self.assertEqual(resp_json[u"items_total"], 1) self.assertEqual(resp_json[u"items"][0][u"review_state"], u"closed") # includes every data as well as extra formatted values self.assertTrue("date" in resp_json["items"][0]) # AT/DX self.assertTrue("startDate" in resp_json["items"][0] or "start_date" in resp_json["items"][0]) self.assertTrue("notes" in resp_json["items"][0]) self.assertEqual( resp_json["items"][0]["formatted_assembly"], u'<p>Mr Present, <strike>Mr Absent</strike>, Mr Present2</p>') self.assertEqual(resp_json["items"][0]["observations"]["data"], pattern.format(IMG_BASE64_DATA)) transaction.abort()
def getAllMessages(self): """ Check if an address field is empty """ if IContactContent.providedBy(self.context): contacts = [self.context] elif IImioDmsOutgoingMail.providedBy(self.context): contacts = [] for rv in (self.context.recipients or []): if not rv.isBroken() and rv.to_path: contacts.append(self.context.restrictedTraverse( rv.to_path)) if not contacts: return [] errors = [] for contact in contacts: address = get_address(contact) empty_keys = [] for key in ( 'street', 'number', 'zip_code', 'city', ): if not address.get(key, ''): empty_keys.append( translate(key, domain='imio.dms.mail', context=self.request)) if empty_keys: errors.append((contact, empty_keys)) ret = [] for (contact, keys) in errors: msg = translate( u"This contact '${title}' has missing address fields: ${keys}", domain='imio.dms.mail', context=self.request, mapping={ 'title': object_link(contact, view='edit', attribute='get_full_title'), 'keys': ', '.join(keys) }) ret.append( PseudoMessage(msg_type='significant', text=richtextval(msg), hidden_uid=generate_uid(), can_hide=False)) return ret
def migrate_description(self): """ Migrate description field to description_rich """ for brain in self.catalog( object_provides='imio.project.core.content.project.IProject'): obj = brain.getObject() # old description contains something like : u'ligne1\r\nligne2\r\nligne3' if obj.description: new_val = [] for line in obj.description.split('\r\n'): new_val.append(line) obj.description_rich = richtextval( u'<p>{}</p>'.format(u'<br />'.join(new_val), outputMimeType='text/x-html-safe')) obj.description = u'' obj.reindexObject()
def test_pm_TransformAllRichTextFields(self): """Test that it does not alterate field content, especially links to internal content or image that uses resolveuid.""" # MeetingItem AT self.changeUser('pmCreator1') item = self.create('MeetingItem') # add image file_path = path.join(path.dirname(__file__), 'dot.gif') file_handler = open(file_path, 'r') data = file_handler.read() file_handler.close() img_id = item.invokeFactory('Image', id='dot.gif', title='Image', file=data) img = getattr(item, img_id) # link to image using resolveuid text = '<p>Internal image <img src="resolveuid/{0}" />.</p>'.format( img.UID()) item.setDescription(text) self.assertEqual(item.objectIds(), ['dot.gif']) transformAllRichTextFields(item) self.assertEqual(item.getRawDescription(), text) transformAllRichTextFields(item, onlyField="description") self.assertEqual(item.getRawDescription(), text) # Meeting DX self.changeUser('pmManager') meeting = self.create('Meeting') # add image img_id = meeting.invokeFactory('Image', id='dot.gif', title='Image', file=data) img = getattr(meeting, img_id) # link to image using resolveuid text = '<p>Internal image <img src="resolveuid/{0}" />.</p>'.format( img.UID()) meeting.observations = richtextval(text) self.assertEqual(meeting.objectIds(), ['dot.gif']) transformAllRichTextFields(meeting) self.assertEqual(meeting.observations.raw, text) transformAllRichTextFields(meeting, onlyField="observations") self.assertEqual(meeting.observations.raw, text)
def test_restapi_search_data_are_anonymized(self): """Data collected from PloneMeeting are anonymized.""" self._enableField("observations") self._enableField("observations", related_to="Meeting") # create 2 items text = '<p>Text shown<span class="pm-anonymize"> text hidden</span> and ' \ 'some <span class="highlight-red">highlighted text</span>.</p>' anonymized_text = '<p>Text shown<span class="pm-anonymize"></span> and ' \ 'some <span class="highlight-red">highlighted text</span>.</p>' self.changeUser("pmManager") item = self.create("MeetingItem") item.setObservations(text) item.setDecision(text) # in 4.1.x item observations was only readable until validated... self.validateItem(item) meeting = self.create("Meeting", date=datetime(2020, 6, 8, 8, 0)) if HAS_MEETING_DX: meeting.observations = richtextval(text) else: meeting.setObservations(text) transaction.commit() # query to get meeting and item description endpoint_url = "{0}/@search?fullobjects" \ "&include_all=false" \ "&extra_include=public_deliberation" \ "&metadata_fields=observations" \ "&UID={2}&UID={3}&sort_on=getId".format( self.portal_url, self.meetingConfig.getId(), item.UID(), meeting.UID()) response = self.api_session.get(endpoint_url) self.assertEqual(response.status_code, 200, response.content) resp_json = response.json() self.assertEqual(resp_json["items_total"], 2) # item # RichTextField self.assertEqual(resp_json["items"][0]["observations"]["data"], anonymized_text) # RichText treated by printXhtml self.assertEqual( resp_json["items"][0]["extra_include_deliberation"] ["public_deliberation"], anonymized_text) # meeting # RichTextField self.assertEqual(resp_json["items"][1]["observations"]["data"], anonymized_text)
def various_update(self): # replace front-page frontpage = getattr(self.portal, 'front-page') frontpage.title = _translate("front_page_title") frontpage.description = _translate("front_page_descr") frontpage.text = richtextval(_translate("front_page_text")) transitions(frontpage, ('retract', 'publish_internally')) frontpage.reindexObject() self.portal.templates.layout = 'dg-templates-listing' # plonegroup-organization pgo = get_own_organization() behaviour = ISelectableConstrainTypes(pgo) behaviour.setConstrainTypesMode(1) behaviour.setLocallyAllowedTypes([]) behaviour.setImmediatelyAddableTypes([]) ISelectableConstrainTypes(pgo['echevins']).setConstrainTypesMode(0) ISelectableConstrainTypes(pgo['services']).setConstrainTypesMode(0)
def various_update(self): # replace front-page frontpage = getattr(self.portal, 'front-page') frontpage.title = _translate("front_page_title") frontpage.description = _translate("front_page_descr") frontpage.text = richtextval(_translate("front_page_text")) transitions(frontpage, ('retract', 'publish_internally')) frontpage.reindexObject() self.portal.templates.layout = 'dg-templates-listing' self.portal.contacts.exclude_from_nav = True self.portal.contacts.reindexObject(['exclude_from_nav']) #Hiding folder contents self.portal.manage_permission('List folder contents', ('Manager', 'Site Administrator'), acquire=0) paob = self.portal.portal_actions.object_buttons for act in ('faceted.sync', 'faceted.disable', 'faceted.enable', 'faceted.search.disable', 'faceted.search.enable', 'faceted.actions.disable', 'faceted.actions.enable', 'ical_import_enable', 'ical_import_disable'): if act in paob: paob[act].visible = False
def test_call(self): omail1 = get_object(oid='reponse1', ptype='dmsoutgoingmail') omail1.send_modes = [u'email'] omail1.email_subject = u'Email subject' omail1.email_sender = u'*****@*****.**' omail1.email_recipient = u'*****@*****.**' omail1.email_body = richtextval(u'My email content.') view = omail1.unrestrictedTraverse('@@send_email') # Status before call self.assertEqual(api.content.get_state(omail1), 'created') self.assertIsNone(omail1.email_status) MockMailHost.secureSend = MockMailHost.send mail_host = get_mail_host() mail_host.reset() # view call view() self.assertIn('Subject: =?utf-8?q?Email_subject?=\n', mail_host.messages[0]) self.assertIn('My email content.', mail_host.messages[0]) self.assertEqual(api.content.get_state(omail1), 'sent') self.assertIsNotNone(omail1.email_status)
def various_update(self): # doc message for id in ('doc1-0', 'doc'): if id in self.portal['messages-config']: api.content.delete(self.portal['messages-config'][id]) if ('indispo' in self.portal['messages-config'] and api.content.get_state( self.portal['messages-config']['indispo']) == 'activated'): api.content.transition(self.portal['messages-config']['indispo'], 'deactivate') if 'doc' not in self.portal['messages-config']: add_message( 'doc', 'Documentation', u'<p>Vous pouvez consulter la ' u'<a href="https://docs.imio.be/imio-doc/ia.pst/" target="_blank">documentation en ligne de la ' u'dernière version</a>, ainsi que <a href="https://www.imio.be/nos-applications/ia-pst/' u'les-actus-de-pst/ia-pst-1-3" target="_blank">les nouveautés</a>.</p>', msg_type='significant', can_hide=True, req_roles=['Authenticated'], activate=True) # activate user external edit pref change_user_properties(self.portal, kw='ext_editor:True', dochange='1') # replace front-page frontpage = getattr(self.portal, 'front-page') frontpage.title = _tr("front_page_title") frontpage.description = _tr("front_page_descr") frontpage.text = richtextval(_tr("front_page_text")) transitions(frontpage, ('retract', 'publish_internally')) frontpage.reindexObject() # remove portlets on pst for brain in self.catalog( object_provides='imio.project.pst.interfaces.IImioPSTProject'): ann = IAnnotations(brain.getObject()) if 'plone.portlets.contextassignments' in ann: if 'plone.leftcolumn' in ann[ 'plone.portlets.contextassignments']: for name in ('portlet_dashboard', 'navigation', 'portlet_actions'): if name in ann['plone.portlets.contextassignments'][ 'plone.leftcolumn']: del ann['plone.portlets.contextassignments'][ 'plone.leftcolumn'][name] if not len(ann['plone.portlets.contextassignments'] ['plone.leftcolumn']): del ann['plone.portlets.contextassignments'][ 'plone.leftcolumn'] if not len(ann['plone.portlets.contextassignments']): del ann['plone.portlets.contextassignments'] # registry api.portal.set_registry_record( 'collective.contact.core.interfaces.IContactCoreParameters.' 'display_below_content_title_on_views', True) registry = getUtility(IRegistry) record = registry.records.get( 'imio.pm.wsclient.browser.settings.IWS4PMClientSettings.generated_actions' ) if record is not None: val = api.portal.get_registry_record( 'imio.pm.wsclient.browser.settings.IWS4PMClientSettings.' 'generated_actions') if val is not None: event.notify(RecordModifiedEvent(record, val, val)) # update dashboard criterias for brain in self.catalog( object_provides='imio.project.pst.interfaces.IImioPSTProject'): pst = brain.getObject() mapping = { 'strategicobjectives': 'strategicobjective', 'operationalobjectives': 'operationalobjective', 'pstactions': 'pstaction', 'tasks': 'task' } for col_folder_id, content_type in mapping.iteritems(): col_folder = pst[col_folder_id] reimport_faceted_config(col_folder, xml='{}.xml'.format(content_type), default_UID=col_folder['all'].UID()) # rename pst titles = { u'PST (2012-2018)': u'PST 2013-2018', u'PST (2018-2024)': u'PST 2019-2024', u'PST (2019-2024)': u'PST 2019-2024', u'PST': u'PST 2019-2024' } values = titles.values() for brain in self.catalog( object_provides='imio.project.pst.interfaces.IImioPSTProject'): pst = brain.getObject() for tit in titles: if pst.title == tit: pst.title = titles[tit] modified(pst) break else: if pst.title not in values: logger.warning("PST rename: not replaced '{}'".format( pst.title.encode('utf8'))) # update ckeditor to remove format and avoid html <h> tags cke_props = self.portal.portal_properties.ckeditor_properties custom = ( u"[\n['AjaxSave'],\n['Cut','Copy','Paste','PasteText','PasteFromWord','-'," u"'Scayt'],\n['Undo','Redo','-','RemoveFormat'],\n['Bold','Italic','Underline','Strike'],\n" u"['NumberedList','BulletedList','-','Outdent','Indent','Blockquote'],\n['JustifyLeft'," u"'JustifyCenter', 'JustifyRight','JustifyBlock'],\n['Table','SpecialChar','Link','Unlink'],\n'/'," u"\n['Styles'],\n['Maximize', 'ShowBlocks', 'Source']\n]") cke_props.toolbar_Custom = custom if cke_props.menuStyles.find(CKEDITOR_MENUSTYLES_CUSTOMIZED_MSG) == -1: enc = self.portal.portal_properties.site_properties.getProperty( 'default_charset') msg_highlight_red = _tr('ckeditor_style_highlight_in_red').encode( 'utf-8') msg_highlight_blue = _tr( 'ckeditor_style_highlight_in_blue').encode('utf-8') msg_highlight_green = _tr( 'ckeditor_style_highlight_in_green').encode('utf-8') msg_highlight_yellow = _tr( 'ckeditor_style_highlight_in_yellow').encode('utf-8') msg_x_small = _tr('ckeditor_style_x_small').encode('utf-8') msg_small = _tr('ckeditor_style_small').encode('utf-8') msg_large = _tr('ckeditor_style_large').encode('utf-8') msg_x_large = _tr('ckeditor_style_x_large').encode('utf-8') msg_indent = _tr('ckeditor_style_indent_first_line').encode( 'utf-8') msg_table_no_optimization = _tr( 'ckeditor_style_table_no_optimization').encode('utf-8') menuStyles = unicode( "[\n{0}\n{{ name : '{1}'\t\t, element : 'span', attributes : {{ 'class' : 'highlight-red' }} }},\n" "{{ name : '{2}'\t\t, element : 'span', attributes : {{ 'class' : 'highlight-blue' }} }},\n" "{{ name : '{3}'\t\t, element : 'span', attributes : {{ 'class' : 'highlight-green' }} }},\n" "{{ name : '{4}'\t\t, element : 'span', attributes : {{ 'class' : 'highlight-yellow' }} }},\n" "{{ name : '{5}'\t\t, element : 'p', attributes : {{ 'class' : 'xSmallText' }} }},\n" "{{ name : '{6}'\t\t, element : 'p', attributes : {{ 'class' : 'smallText' }} }},\n" "{{ name : '{7}'\t\t, element : 'p', attributes : {{ 'class' : 'largeText' }} }},\n" "{{ name : '{8}'\t\t, element : 'p', attributes : {{ 'class' : 'xLargeText' }} }},\n" "{{ name : '{9}'\t\t, element : 'table', styles : {{ 'table-layout' : 'fixed' }} }},\n" "{{ name : '{10}'\t\t, element : 'p', attributes : {{ 'style' : 'text-indent: 40px;' }} }},\n]\n" .format(CKEDITOR_MENUSTYLES_CUSTOMIZED_MSG, msg_highlight_red, msg_highlight_blue, msg_highlight_green, msg_highlight_yellow, msg_x_small, msg_small, msg_large, msg_x_large, msg_table_no_optimization, msg_indent), enc) cke_props.menuStyles = menuStyles
def test_FinancialManagerMayChangeAdviceDelayWhenAddableOrEditable(self): '''Check that a financial manager may still change advice asked to his financial group while the advice is still addable or editable.''' self.changeUser('admin') # add finance groups self._createFinanceGroups() # configure customAdvisers for 'meeting-config-college' _configureCollegeCustomAdvisers(self.portal) # define relevant users for finance groups self._setupFinanceGroups() # ask finance advice and ask advice (set item to 'proposed_to_finance_waiting_advices') # not need a finances advice self.changeUser('pmManager') item = self.create('MeetingItem', title='The first item') financial_group_uids = self.tool.finance_group_uids() item.setFinanceAdvice(financial_group_uids[0]) item._update_after_edit() self.assertTrue(financial_group_uids[0] in item.adviceIndex) self.proposeItem(item) self.do(item, 'wait_advices_from_proposed_to_director') item.setCompleteness('completeness_complete') item._update_after_edit() # ok, now advice can be given # a financial manager may change delays self.changeUser('pmFinManager') delayView = item.restrictedTraverse('@@advice-available-delays') # advice has been asked automatically financial_group_uids = self.tool.finance_group_uids() isAutomatic = not bool( item.adviceIndex[financial_group_uids[0]]['optional']) # advice is addable, delays may be changed self.assertTrue(delayView._mayEditDelays(isAutomatic=isAutomatic)) # add the advice, delay still changeable as advice is editable advice = createContentInContainer( item, 'meetingadvicefinances', **{ 'advice_group': financial_group_uids[0], 'advice_type': u'positive_finance', 'advice_comment': richtextval(u'My comment finance') }) self.assertTrue(delayView._mayEditDelays(isAutomatic=isAutomatic)) # other members of the finance group can not edit advice delay self.changeUser('pmFinController') self.assertTrue(not delayView._mayEditDelays(isAutomatic=isAutomatic)) # send to financial reviewer self.do(advice, 'proposeToFinancialReviewer') self.assertTrue(not delayView._mayEditDelays(isAutomatic=isAutomatic)) # delay editable by manager but not by others self.changeUser('pmFinManager') self.assertTrue(delayView._mayEditDelays(isAutomatic=isAutomatic)) self.changeUser('pmFinReviewer') self.assertTrue(not delayView._mayEditDelays(isAutomatic=isAutomatic)) # send to finance manager self.do(advice, 'proposeToFinancialManager') self.assertTrue(not delayView._mayEditDelays(isAutomatic=isAutomatic)) # delay editable by finance manager but not by others self.changeUser('pmFinManager') self.assertTrue(delayView._mayEditDelays(isAutomatic=isAutomatic)) # if manager sign the advice, so advice is no more editable # even the finance manager may no more edit advice delay self.do(advice, 'signFinancialAdvice') self.assertFalse( item.adviceIndex[financial_group_uids[0]]['advice_editable']) self.assertFalse(delayView._mayEditDelays(isAutomatic=isAutomatic))
def run(self): logger.info('Migrating to imio.dms.mail 1.0...') self.cleanRegistries() self.upgradeProfile('collective.dms.mailcontent:default') # We have to reapply type info before doing other subproducts migration self.runProfileSteps('imio.dms.mail', steps=['typeinfo']) # We have to update type schema because plone.dexterity doesn't detect schema_policy modification. BUG #44 for portal_type in ['dmsincomingmail', 'dmsoutgoingmail']: # i_e ok schemaName = dxutils.portalTypeToSchemaName(portal_type) schema = getattr(plone.dexterity.schema.generated, schemaName) fti = getUtility(IDexterityFTI, name=portal_type) model = fti.lookupModel() syncSchema(model.schema, schema, overwrite=True, sync_bases=True) notify(plone.dexterity.schema.SchemaInvalidatedEvent(portal_type)) self.upgradeProfile('collective.task:default') self.upgradeProfile('dexterity.localroles:default') self.upgradeProfile('dexterity.localrolesfield:default') self.upgradeProfile('collective.contact.plonegroup:default') self.runProfileSteps('imio.dms.mail', steps=[ 'actions', 'componentregistry', 'controlpanel', 'plone.app.registry', 'portlets', 'repositorytool', 'rolemap', 'sharing', 'workflow' ]) self.portal.portal_workflow.updateRoleMappings() self.runProfileSteps('collective.dms.mailcontent', steps=['controlpanel']) self.runProfileSteps('collective.contact.plonegroup', steps=['controlpanel']) self.reinstall([ 'collective.messagesviewlet:messages', 'collective.querynextprev:default', 'imio.dashboard:default', ]) # set jqueryui autocomplete to False. If not, contact autocomplete doesn't work self.registry[ 'collective.js.jqueryui.controlpanel.IJQueryUIPlugins.ui_autocomplete'] = False # delete old dmsmail portlet self.delete_portlet(self.portal, 'portlet_maindmsmail') # remove deprecated interfaces self.remove_contact_interfaces() # moved notes content to task_description catalog = api.portal.get_tool('portal_catalog') brains = catalog.searchResults(portal_type='dmsincomingmail') # i_e ok for brain in brains: obj = brain.getObject() if not base_hasattr(obj, 'notes') or not obj.notes: continue text = u'<p>%s</p>\r\n' % obj.notes.replace('\r\n', '<br />\r\n') obj.task_description = richtextval(text) delattr(obj, 'notes') # obj.reindexObject() # replace collections by Dashboard collections im_folder = self.portal['incoming-mail'] alsoProvides(im_folder, INextPrevNotNavigable) alsoProvides(im_folder, IIMDashboard) self.replaceCollections(im_folder) # apply contact faceted config reimport_faceted_config(self.portal['contacts'], 'contacts-faceted.xml') # add new indexes for dashboard addOrUpdateIndexes(self.portal, indexInfos={ 'mail_type': ('FieldIndex', {}), 'mail_date': ('DateIndex', {}), 'in_out_date': ('DateIndex', {}), }) # set dashboard on incoming mail configure_faceted_folder( im_folder, xml='default_dashboard_widgets.xml', default_UID=im_folder['mail-searches']['all_mails'].UID()) # set task local roles configuration configure_task_rolefields(self.portal) # update dexterity local roles configuration self.update_local_roles() # add task actionspanel config if not self.registry[ 'imio.actionspanel.browser.registry.IImioActionsPanelConfig.transitions']: self.registry['imio.actionspanel.browser.registry.IImioActionsPanelConfig.transitions'] = [] self.registry['imio.actionspanel.browser.registry.IImioActionsPanelConfig.transitions'] += \ ['task.back_in_created|', 'task.back_in_to_assign|', 'task.back_in_to_do|', 'task.back_in_progress|', 'task.back_in_realized|'] # activate ckeditor configure_ckeditor(self.portal, custom='ged') # Set markup allowed types adapter = MarkupControlPanelAdapter(self.portal) adapter.set_allowed_types(['text/html']) # update searchabletext self.update_dmsmainfile() self.upgradeAll() for prod in [ 'plone.formwidget.autocomplete', 'collective.documentviewer', 'plone.formwidget.masterselect', 'collective.contact.core', 'collective.contact.duplicated', 'collective.dms.basecontent', 'collective.dms.scanbehavior', 'collective.externaleditor', 'plone.app.collection', 'plone.app.intid', 'collective.contact.facetednav', 'plonetheme.imioapps', 'PasswordStrength', 'imio.dms.mail' ]: mark_last_version(self.portal, product=prod) self.portal.manage_permission( 'CMFEditions: Revert to previous versions', ('Manager', 'Site Administrator'), acquire=0) #self.refreshDatabase() self.finish()
def getAllMessages(self): ret = [] if self.context.portal_type == "operationalobjective": if self.context.planned_end_date: max_children_deadline = find_max_deadline_on_children( self.context, { "pstaction": "planned_end_date", "action_link": "planned_end_date", "pstsubaction": "planned_end_date", "subaction_link": "planned_end_date", "task": "due_date" }) if max_children_deadline: if max_children_deadline > self.context.planned_end_date: msg = _( u"The deadline of any one of children is greater than those of this element" ) ret.append( PseudoMessage( msg_type="significant", text=richtextval(msg), hidden_uid=generate_uid(), can_hide=False, )) else: msg = _( u"The deadline is not fill on this element, the system displays the largest of its " u"possible children") ret.append( PseudoMessage( msg_type="significant", text=richtextval(msg), hidden_uid=generate_uid(), can_hide=False, )) if self.context.portal_type == "pstaction": if self.context.planned_end_date: max_children_deadline = find_max_deadline_on_children( self.context, { "pstsubaction": "planned_end_date", "subaction_link": "planned_end_date", "task": "due_date" }) if max_children_deadline: if max_children_deadline > self.context.planned_end_date: msg = _( u"The deadline of any one of children is greater than those of this element" ) ret.append( PseudoMessage( msg_type="significant", text=richtextval(msg), hidden_uid=generate_uid(), can_hide=False, )) if is_smaller_deadline_on_parents( self.context, { "pstaction": "planned_end_date", "operationalobjective": "planned_end_date" }): msg = _( u"The deadline of this element is greater than one of its parents" ) ret.append( PseudoMessage( msg_type="significant", text=richtextval(msg), hidden_uid=generate_uid(), can_hide=False, )) else: msg = _( u"The deadline is not fill on this element, the system displays the largest of its " u"possible children") ret.append( PseudoMessage( msg_type="significant", text=richtextval(msg), hidden_uid=generate_uid(), can_hide=False, )) if self.context.portal_type == "pstsubaction": if self.context.planned_end_date: max_children_deadline = find_max_deadline_on_children( self.context, { "pstsubaction": "planned_end_date", "subaction_link": "planned_end_date", "task": "due_date" }) if max_children_deadline: if max_children_deadline > self.context.planned_end_date: msg = _( u"The deadline of any one of children is greater than those of this element" ) ret.append( PseudoMessage( msg_type="significant", text=richtextval(msg), hidden_uid=generate_uid(), can_hide=False, )) if is_smaller_deadline_on_parents( self.context, { "pstsubaction": "planned_end_date", "pstaction": "planned_end_date", "operationalobjective": "planned_end_date" }): msg = _( u"The deadline of this element is greater than one of its parents" ) ret.append( PseudoMessage( msg_type="significant", text=richtextval(msg), hidden_uid=generate_uid(), can_hide=False, )) else: msg = _( u"The deadline is not fill on this element, the system displays the largest of its " u"possible children") ret.append( PseudoMessage( msg_type="significant", text=richtextval(msg), hidden_uid=generate_uid(), can_hide=False, )) if self.context.portal_type == "task": if self.context.due_date: if is_smaller_deadline_on_parents( self.context, { "task": "due_date", "pstsubaction": "planned_end_date", "pstaction": "planned_end_date", "operationalobjective": "planned_end_date" }): msg = _( u"The deadline of this element is greater than one of its parents" ) ret.append( PseudoMessage( msg_type="significant", text=richtextval(msg), hidden_uid=generate_uid(), can_hide=False, )) else: msg = _(u"The deadline is not fill on this element") ret.append( PseudoMessage( msg_type="significant", text=richtextval(msg), hidden_uid=generate_uid(), can_hide=False, )) return ret
def _checkItemSentBackToServiceWhenEveryAdvicesGiven( self, item, askAdvicesTr, availableBackTr, returnState, askAdvicesTr2=None): """Helper method for 'test_subproduct_ItemSentBackToAskerWhenEveryAdvicesGiven'.""" # save current logged in user, the group asker user adviceAskerUserId = self.member.getId() # ask advices self.do(item, askAdvicesTr) # item can be sent back to returnState by creator even if every advices are not given self.assertTrue(availableBackTr in self.transitions(item)) self.assertFalse(_everyAdvicesAreGivenFor(item)) # now add advice as vendors and do not hide it, advice will be considered given self.changeUser('pmReviewer2') createContentInContainer( item, 'meetingadvice', **{ 'advice_group': self.vendors_uid, 'advice_type': u'positive', 'advice_hide_during_redaction': False, 'advice_comment': richtextval(u'My comment') }) # directly sent back to service self.assertEqual(item.query_state(), returnState) self.assertTrue(_everyAdvicesAreGivenFor(item)) # now add advice as vendors and hide it, advice is considered not given self.changeUser('siteadmin') item.restrictedTraverse('@@delete_givenuid')(item.meetingadvice.UID()) self.do(item, askAdvicesTr2 or askAdvicesTr) self.changeUser('pmReviewer2') advice = createContentInContainer( item, 'meetingadvice', **{ 'advice_group': self.vendors_uid, 'advice_type': u'positive', 'advice_hide_during_redaction': True, 'advice_comment': richtextval(u'My comment') }) # still waiting advices self.assertEqual(item.query_state(), '{0}_waiting_advices'.format(returnState)) self.assertFalse(_everyAdvicesAreGivenFor(item)) # if we just change 'advice_hide_during_redaction', advice is given and item's sent back advice.advice_hide_during_redaction = False notify(ObjectModifiedEvent(advice)) self.assertEqual(item.query_state(), returnState) self.assertTrue(_everyAdvicesAreGivenFor(item)) # now test with 'asked_again' self.changeUser(adviceAskerUserId) advice.restrictedTraverse('@@change-advice-asked-again')() self.assertEqual(advice.advice_type, 'asked_again') self.do(item, askAdvicesTr2 or askAdvicesTr) self.changeUser('pmReviewer2') notify(ObjectModifiedEvent(advice)) # still waiting advices self.assertEqual(item.query_state(), '{0}_waiting_advices'.format(returnState)) self.assertFalse(_everyAdvicesAreGivenFor(item)) # change advice_type, it will be sent back then advice.advice_type = u'positive' notify(ObjectModifiedEvent(advice)) self.assertEqual(item.query_state(), returnState) self.assertTrue(_everyAdvicesAreGivenFor(item))
def __call__(self): rendered = self.pt.pt_render(self.namespace) return richtextval(rendered)