From 51fcc20ce0c094eac36adce1174400dd71bc2b9f Mon Sep 17 00:00:00 2001 From: Sebastien Robin <seb@nexedi.com> Date: Wed, 27 May 2009 15:19:12 +0000 Subject: [PATCH] Add Acknowledgement System that allows to find event that need to be acknowledged by users with : - portal_acknowledgement : tool that provided methods to retrieve document to acknowledge and to acknowledge them - new document class : Acknowledgement - possibility to have proxy of Documents. Acknowledgements will only be a proxy to event, the content of the message will not be kept in the event, and we can access it from the Acknowledgement thanks to the proxy - the possibility to display site message on the top of the page with xhtml style - an unit test git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@27216 20353a03-c40f-0410-a6d1-a30d3c3de9de --- product/ERP5/Document/Acknowledgement.py | 80 ++++++++ product/ERP5/Document/Document.py | 27 ++- product/ERP5/Document/EmailDocument.py | 36 +++- product/ERP5/Document/Event.py | 57 +++++- product/ERP5/ERP5Site.py | 2 + product/ERP5/PropertySheet/DocumentProxy.py | 34 ++++ product/ERP5/Tool/AcknowledgementTool.py | 173 ++++++++++++++++++ product/ERP5/__init__.py | 4 +- .../erp5_xhtml_style/bulletin_board.xml | 79 ++++++++ .../template_erp5_xhtml_style.xml | 3 + .../bootstrap/erp5_xhtml_style/bt/revision | 2 +- product/ERP5/tests/testAcknowledgementTool.py | 118 ++++++++++++ 12 files changed, 610 insertions(+), 5 deletions(-) create mode 100644 product/ERP5/Document/Acknowledgement.py create mode 100755 product/ERP5/PropertySheet/DocumentProxy.py create mode 100644 product/ERP5/Tool/AcknowledgementTool.py create mode 100644 product/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/bulletin_board.xml create mode 100644 product/ERP5/tests/testAcknowledgementTool.py diff --git a/product/ERP5/Document/Acknowledgement.py b/product/ERP5/Document/Acknowledgement.py new file mode 100644 index 0000000000..6cb9729f53 --- /dev/null +++ b/product/ERP5/Document/Acknowledgement.py @@ -0,0 +1,80 @@ +############################################################################## +# +# Copyright (c) 2009 Nexedi SARL and Contributors. All Rights Reserved. +# Ben Mayhew <maybewhen@gmx.net> +# Sebastien Robin <seb@nexedi.com> +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsability of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# garantees and support are strongly adviced to contract a Free Software +# Service Company +# +# This program is Free Software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +############################################################################## +from AccessControl import ClassSecurityInfo +from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface + +from Products.ERP5.Document.EmailDocument import EmailDocumentProxyMixin +from Products.ERP5.Document.Event import Event + +class Acknowledgement(EmailDocumentProxyMixin, Event): + """ + goal : + + Acts as a proxy to the message in the case of + - private email + - message displayed to the user ? + + We need this proxy because the user might not have the right to access + to the original message, and we don't wish to duplicate the content of + the original message (which can use attachements). + + Use Case: + + - A Site Notification is created in order to notify to all people of a + company. Then every time an user will acknowledge the notification, + a new Acknowledgement is created. + """ + + meta_type = 'ERP5 Acknowledgement' + portal_type = 'Acknowledgement' + add_permission = Permissions.AddPortalContent + isPortalContent = 1 + isRADContent = 1 + isDelivery = 1 + + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.XMLObject + , PropertySheet.CategoryCore + , PropertySheet.Document + , PropertySheet.DublinCore + , PropertySheet.Snapshot + , PropertySheet.Task + , PropertySheet.Url + , PropertySheet.Arrow + , PropertySheet.Event + , PropertySheet.Delivery + , PropertySheet.DocumentProxy + ) + + diff --git a/product/ERP5/Document/Document.py b/product/ERP5/Document/Document.py index f3376720dc..ab8dc8bb20 100644 --- a/product/ERP5/Document/Document.py +++ b/product/ERP5/Document/Document.py @@ -375,6 +375,31 @@ class PermanentURLMixIn(ExtensibleTraversableMixIn): document = document.__of__(self) return document +class DocumentProxyMixin: + """ + Provides access to documents referenced by the follow_up field + """ + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + security.declareProtected(Permissions.AccessContentsInformation, + 'index_html' ) + def index_html(self, REQUEST, RESPONSE, format=None, **kw): + """ Only a proxy method """ + self.getProxiedDocument().index_html(REQUEST, RESPONSE, format, **kw) + + security.declareProtected(Permissions.AccessContentsInformation, + 'getProxiedDocument' ) + def getProxiedDocument(self): + """ + Try to retrieve the original document + """ + proxied_document = self.getDocumentProxyValue() + if proxied_document is None: + raise ValueError("Unable to find a proxied document") + return proxied_document + class UpdateMixIn: """ Provides an API to compute a date index based on the update @@ -1550,4 +1575,4 @@ class Document(PermanentURLMixIn, XMLObject, UrlMixIn, ConversionCacheMixin, Sna # Cut the trailing part in http://www.some.site/at/trailing.html # but not in http://www.some.site/at base_url = '/'.join(base_url_list[:-1]) - return base_url \ No newline at end of file + return base_url diff --git a/product/ERP5/Document/EmailDocument.py b/product/ERP5/Document/EmailDocument.py index 50588d960f..2a1426f4e3 100644 --- a/product/ERP5/Document/EmailDocument.py +++ b/product/ERP5/Document/EmailDocument.py @@ -38,8 +38,9 @@ from Products.CMFDefault.utils import isHTMLSafe from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface from Products.ERP5.Document.TextDocument import TextDocument from Products.ERP5.Document.File import File -from Products.ERP5.Document.Document import ConversionError +from Products.ERP5.Document.Document import ConversionError, DocumentProxyMixin from Products.ERP5.Tool.NotificationTool import buildEmailMessage +from MethodObject import Method from zLOG import LOG, INFO @@ -62,6 +63,39 @@ _MARKER = [] file_name_regexp = 'name="([^"]*)"' + +class EmailDocumentProxyMixin(DocumentProxyMixin): + """ + Provides access to documents referenced by the causality field + """ + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + +class ProxiedMethod(Method): + """ + Accessort that retrieve methods directly on the proxy + """ + + def __init__(self, proxied_method_id): + self.proxied_method_id = proxied_method_id + + def __call__(self, instance, *args, **kw): + proxied_document = instance.getProxiedDocument() + method = getattr(proxied_document, self.proxied_method_id) + return method(*args, **kw) + +# generate all proxy method on EmailDocumentProxyMixin +for method_id in ('getTextContent', 'getTextFormat', 'hasFile', + 'getContentInformation', 'getAttachmentData', + 'getAttachmentInformationList'): + EmailDocumentProxyMixin.security.declareProtected( + Permissions.AccessContentsInformation, + method_id) + setattr(EmailDocumentProxyMixin, method_id, + ProxiedMethod(method_id)) + class EmailDocument(File, TextDocument): """ EmailDocument is a File which stores its metadata in a form which diff --git a/product/ERP5/Document/Event.py b/product/ERP5/Document/Event.py index 1d1a2b421c..5ac30b3612 100644 --- a/product/ERP5/Document/Event.py +++ b/product/ERP5/Document/Event.py @@ -32,7 +32,62 @@ from Products.ERP5Type import Permissions, PropertySheet from Products.ERP5.Document.Movement import Movement from Products.ERP5.Document.EmailDocument import EmailDocument -class Event(EmailDocument, Movement): +class AcknowledgeableMixin: + """ + Mixin class for all documents that we can acknowledge + """ + # Declarative security + security = ClassSecurityInfo() + security.declareObjectProtected(Permissions.AccessContentsInformation) + + security.declareProtected(Permissions.AccessContentsInformation, 'acknowledge') + def acknowledge(self, **kw): + """ + Define what we want to do with acknowledgment. + + Possibilities : + - do nothing + - add an Acknowledge document every time someone read + an event corresponding to this ticket + - we could even think to move the workflow forward + when all event have been acknowledge + + Is the name buildAcknowledgement better ??? + """ + method = self._getTypeBasedMethod('acknowledge') + if method is not None: + return method(**kw) + return None + + def hasAcknowledgementActivity(self, user_name=None): + """ + We will check if there is some current activities running or not + """ + tag = "%s_%s" % (user_name, self.getRelativeUrl()) + result = False + # First look at activities, we check if an acknowledgement document + # is under reindexing + if self.portal_activities.countMessageWithTag(tag): + result = True + return result + + security.declareProtected(Permissions.AccessContentsInformation, 'isAcknowledged') + def isAcknowledged(self, user_name=None): + """ + Say if this ticket is already acknowledged or not by this user. + """ + result = self.hasAcknowledgementActivity(user_name=user_name) + if not result: + # Check in the catalog if we can find an acknowledgement + person_value = self.ERP5Site_getAuthenticatedMemberPersonValue( + user_name=user_name) + if len(self.portal_catalog(portal_type='Acknowledgement', + causality_relative_url=self.getRelativeUrl(), + destination_relative_url=person_value.getRelativeUrl())) > 0: + result = True + return result + +class Event(EmailDocument, Movement, AcknowledgeableMixin): """ Event is the base class for all events in ERP5. diff --git a/product/ERP5/ERP5Site.py b/product/ERP5/ERP5Site.py index cbc8f08e58..7ac091edb3 100644 --- a/product/ERP5/ERP5Site.py +++ b/product/ERP5/ERP5Site.py @@ -1338,6 +1338,8 @@ class ERP5Generator(PortalGenerator): addTool('ERP5 Test Tool', None) if not p.hasObject('portal_password'): addTool('ERP5 Password Tool', None) + if not p.hasObject('portal_acknowledgements'): + addTool('ERP5 Acknowledgement Tool', None) # Add ERP5Type Tool addTool = p.manage_addProduct['ERP5Type'].manage_addTool diff --git a/product/ERP5/PropertySheet/DocumentProxy.py b/product/ERP5/PropertySheet/DocumentProxy.py new file mode 100755 index 0000000000..cf49d3df83 --- /dev/null +++ b/product/ERP5/PropertySheet/DocumentProxy.py @@ -0,0 +1,34 @@ +############################################################################## +# +# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved. +# Sebastien Robin <seb@nexedi.com> +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsability of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# garantees and support are strongly adviced to contract a Free Software +# Service Company +# +# This program is Free Software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +############################################################################## + +class DocumentProxy: + """ + Document Proxy properties + """ + + _categories = ( 'document_proxy', ) diff --git a/product/ERP5/Tool/AcknowledgementTool.py b/product/ERP5/Tool/AcknowledgementTool.py new file mode 100644 index 0000000000..b00cc39025 --- /dev/null +++ b/product/ERP5/Tool/AcknowledgementTool.py @@ -0,0 +1,173 @@ +############################################################################## +# +# Copyright (c) 2009 Nexedi SARL and Contributors. All Rights Reserved. +# Ben Mayhew <maybewhen@gmx.net> +# Sebastien Robin <seb@nexedi.com> +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsability of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# garantees and support are strongly adviced to contract a Free Software +# Service Company +# +# This program is Free Software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +############################################################################## +from AccessControl import ClassSecurityInfo +from Globals import InitializeClass, DTMLFile +from Products.ERP5Type.Tool.BaseTool import BaseTool +from Products.ERP5Type import Permissions +from Products.ERP5 import _dtmldir +from Products.ERP5.Document.Acknowledgement import Acknowledgement +from zLOG import LOG +from DateTime import DateTime +from Products.ZSQLCatalog.SQLCatalog import Query, NegatedQuery + + +class AcknowledgementTool(BaseTool): + """ + Provide an entry point to track reception of events + + someone who can not view the ticket or the event + must be able to acknowledge reception of email + or of site message sent by CRM. + + This tools take into account that for some kind of document, + acknowledgements are not created in advance. For Site Message, + acknowledgements will be created every time the user confirm that he has + read the information. + + In the case of internal emails, acknowledgements are created in advance. + + Use Case: who read the emails I sent ? + Use Case: who said OK to Site Message ? + """ + id = 'portal_acknowledgements' + meta_type = 'ERP5 Acknowledgement Tool' + portal_type = 'Acknowledgement Tool' + allowed_types = ('ERP5 Acknowledgement',) + # Declarative Security + security = ClassSecurityInfo() + + + security.declarePublic('getUnreadAcknowledgementList') + def countUnread(self, *args, **kw): + """ + counts number of acknowledgements pending + """ + return len(self.getUnreadAcknowledgementList(*args, **kw)) + + security.declarePublic('getUnreadAcknowledgementList') + def getUnreadAcknowledgementList(self, portal_type=None, user_name=None, + url_list=None): + """ + returns acknowledgements pending + in the form of + - TempAcknowledgement (for Site Message) + - Acknowledgement (internal email) + """ + portal = self.getPortalObject() + return_list = [] + if url_list is None: + url_list = self.getUnreadDocumentUrlList(portal_type=portal_type, + user_name=user_name) + for url in url_list: + document = portal.restrictedTraverse(url) + if not document.isAcknowledged(user_name=user_name): + # If the document to acknowledge is a ticket, we should return + # a temp acknowledgement + if document.getPortalType() in portal.getPortalEventTypeList(): + module = portal.getDefaultModule('Acknowledgement') + temp_acknowledgement = module.newContent( + portal_type='Acknowledgement', + temp_object=1, + document_proxy=document.getRelativeUrl(), + causality=document.getRelativeUrl()) + return_list.append(temp_acknowledgement) + else: + # If not an event, this means that we have directly the document + # that we must acknowledge + return_list.append(document) + return return_list + + security.declarePublic('getUnreadDocumentUrlList') + def getUnreadDocumentUrlList(self, portal_type=None, user_name=None, **kw): + """ + returns document that needs to be acknowledged : + - Acknowledgement (internal email) + - Site Message + + This method will mainly be used by getUnreadAcknowledgementList. Also, + because url are used, the result will be easy to cache. + """ + document_list = [] + if user_name is not None: + portal = self.getPortalObject() + now = DateTime() + # First look at all event that define the current user as destination + all_document_list = [x for x in \ + self.portal_catalog(portal_type = portal_type, + simulation_state = self.getPortalTransitInventoryStateList(), + # start_date = {'query':now,'range':'max'}, + # stop_date = {'query':now,'range':'min'}, + default_destination_reference=user_name)] + # Now we can look directly at acknowledgement document not approved yet + # so not in a final state + final_state_list = self.getPortalCurrentInventoryStateList() + query = NegatedQuery(Query(simulation_state=final_state_list)) + all_document_list.extend([x for x in \ + self.portal_catalog(portal_type = portal_type, + query=query, + # start_date = {'query':now,'range':'max'}, + # stop_date = {'query':now,'range':'min'}, + destination_reference=user_name)]) + for document in all_document_list: + # We filter manually on dates until a good solution is found for + # searching by dates on the catalog + if (document.getStartDate() < now < (document.getStopDate()+1)): + acknowledged = document.isAcknowledged(user_name=user_name) + if not acknowledged: + document_list.append(document.getRelativeUrl()) + else: + raise ValueError('No user name given') + return document_list + + security.declareProtected(Permissions.AccessContentsInformation, + 'acknowledge') + def acknowledge(self, uid=None, path=None, user_name=None, **kw): + """ + Create an acknowledgement document for : + - a ticket + - an event + - an acknowledgement + + This methods needs to check if there is already ongoing ackowledgement + for the document of for this user. We will have to use activities with + tag and probably a serialization. + """ + document = None + if uid is not None: + document = self.portal_catalog.getObject(uid) + elif path is not None: + document = self.restrictedTraverse(path) + else: + raise ValueError("No path or uid given") + if document is None: + raise ValueError("Ticket does not exist or you don't have access to it") + return document.acknowledge(user_name=user_name, **kw) + + +InitializeClass(AcknowledgementTool) diff --git a/product/ERP5/__init__.py b/product/ERP5/__init__.py index 5cca0263e5..5499b6695e 100644 --- a/product/ERP5/__init__.py +++ b/product/ERP5/__init__.py @@ -47,7 +47,8 @@ product_path = package_home( globals() ) from Tool import CategoryTool, SimulationTool, RuleTool, IdTool, TemplateTool,\ TestTool, DomainTool, AlarmTool, OrderTool, DeliveryTool,\ TrashTool, ContributionTool, NotificationTool, PasswordTool,\ - GadgetTool, ContributionRegistryTool, IntrospectionTool + GadgetTool, ContributionRegistryTool, IntrospectionTool,\ + AcknowledgementTool import ERP5Site object_classes = ( ERP5Site.ERP5Site, ) @@ -68,6 +69,7 @@ portal_tools = ( CategoryTool.CategoryTool, GadgetTool.GadgetTool, ContributionRegistryTool.ContributionRegistryTool, IntrospectionTool.IntrospectionTool, + AcknowledgementTool.AcknowledgementTool, ) content_classes = () content_constructors = () diff --git a/product/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/bulletin_board.xml b/product/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/bulletin_board.xml new file mode 100644 index 0000000000..24b67a02b5 --- /dev/null +++ b/product/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/bulletin_board.xml @@ -0,0 +1,79 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <tuple> + <global name="ZopePageTemplate" module="Products.PageTemplates.ZopePageTemplate"/> + <tuple/> + </tuple> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>_bind_names</string> </key> + <value> + <object> + <klass> + <global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/> + </klass> + <tuple/> + <state> + <dictionary> + <item> + <key> <string>_asgns</string> </key> + <value> + <dictionary> + <item> + <key> <string>name_subpath</string> </key> + <value> <string>traverse_subpath</string> </value> + </item> + </dictionary> + </value> + </item> + </dictionary> + </state> + </object> + </value> + </item> + <item> + <key> <string>_text</string> </key> + <value> <string encoding="cdata"><![CDATA[ + +<tal:block xmlns:tal="http://xml.zope.org/namespaces/tal"\n + xmlns:metal="http://xml.zope.org/namespaces/metal"\n + xmlns:i18n="http://xml.zope.org/namespaces/i18n">\n + <tal:block metal:define-macro="master">\n + <tal:block tal:repeat="item here/AcknowledgementTool_getUserUnreadAcknowledgementList | nothing">\n + <div class="dialog_box">\n + <div class="list_dialog">\n + <tal:div content="structure item/text_content" />\n + </div>\n + <a tal:attributes="href item/acknowledge_url"><button>DISMISS</button></a>\n + </div>\n + </tal:block>\n + </tal:block>\n +</tal:block>\n + + +]]></string> </value> + </item> + <item> + <key> <string>content_type</string> </key> + <value> <string>text/html</string> </value> + </item> + <item> + <key> <string>expand</string> </key> + <value> <int>0</int> </value> + </item> + <item> + <key> <string>id</string> </key> + <value> <string>bulletin_board</string> </value> + </item> + <item> + <key> <string>title</string> </key> + <value> <string></string> </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/product/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/template_erp5_xhtml_style.xml b/product/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/template_erp5_xhtml_style.xml index 8c323e2878..3a9b291f1c 100644 --- a/product/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/template_erp5_xhtml_style.xml +++ b/product/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/template_erp5_xhtml_style.xml @@ -129,6 +129,9 @@ IDEAS:\n </tal:block>\n </div>\n <p class="clear"></p>\n + <div id="bulletin_board">\n + <div tal:content="structure here/bulletin_board"/>\n + </div>\n <div tal:content="request/portal_status_message | nothing" id="transition_message" />\n <div id="information_area" tal:condition="request/field_errors | nothing"\n i18n:translate="" i18n:domain="ui">\n diff --git a/product/ERP5/bootstrap/erp5_xhtml_style/bt/revision b/product/ERP5/bootstrap/erp5_xhtml_style/bt/revision index cbb5735d83..72c2a20c91 100644 --- a/product/ERP5/bootstrap/erp5_xhtml_style/bt/revision +++ b/product/ERP5/bootstrap/erp5_xhtml_style/bt/revision @@ -1 +1 @@ -760 \ No newline at end of file +761 diff --git a/product/ERP5/tests/testAcknowledgementTool.py b/product/ERP5/tests/testAcknowledgementTool.py new file mode 100644 index 0000000000..00d3383612 --- /dev/null +++ b/product/ERP5/tests/testAcknowledgementTool.py @@ -0,0 +1,118 @@ +############################################################################## +# -*- coding: utf8 -*- +# Copyright (c) 2007 Nexedi SA and Contributors. All Rights Reserved. +# Sebastien Robin <seb@nexedi.com> +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsability of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# garantees and support are strongly adviced to contract a Free Software +# Service Company +# +# This program is Free Software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +############################################################################## + +import unittest +from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase +import transaction +from DateTime import DateTime + +class TestAcknowledgementTool(ERP5TypeTestCase): + + def getTitle(self): + return "AcknowledgementTool" + + def getBusinessTemplateList(self): + return ('erp5_base', + 'erp5_crm',) + + def test_01_checkAcknowledgementToolWithOneEvent(self): + """ + Create an event of type site message, post it and check that the + acknowledgement tool is able to see it + """ + event_type = "Site Message" + portal = self.getPortalObject() + module = portal.getDefaultModule(event_type) + event = module.newContent(portal_type=event_type) + person_module = portal.getDefaultModule('Person') + person = person_module.newContent(portal_type='Person', title='Seb', + reference='seb') + now = DateTime() + event.edit(destination_value=person, + text_content="A Nice Message", + text_format="text/plain", + title="foo", + start_date = now-2, + stop_date = now+2) + portal.portal_workflow.doActionFor(event, 'start_action') + self.assertEqual(event.getSimulationState(), 'started') + transaction.commit() + self.tic() + + acknowledgement_tool_kw = {} + acknowledgement_tool_kw['user_name'] = 'seb' + acknowledgement_tool_kw['portal_type'] = event_type + document_url_list = portal.portal_acknowledgements\ + .getUnreadDocumentUrlList(**acknowledgement_tool_kw) + self.assertTrue(event.getRelativeUrl() in document_url_list) + + # function in order to retrieve many times the list of acknowledgements + def getEventAcknowlegementList(): + acknowledgement_list = portal.portal_acknowledgements\ + .getUnreadAcknowledgementList( + **acknowledgement_tool_kw) + event_acknowledgement_list = [x for x in acknowledgement_list + if x.getCausality() == event.getRelativeUrl()] + return event_acknowledgement_list + + # We should have unread acknowledgement + event_acknowledgement_list = getEventAcknowlegementList() + self.assertEqual(1, len(event_acknowledgement_list)) + + # Check that the content is retrieved on the original event + event_acknowledgement = event_acknowledgement_list[0] + self.assertEqual(event_acknowledgement.getTextContent(), "A Nice Message") + + # We know acknowledge the event + acknowledgement = portal.portal_acknowledgements.acknowledge( + path=event.getRelativeUrl(), + user_name='seb') + # Make sure that we have a new acknowledge document wich is a proxy of + # the event + self.assertEqual(acknowledgement.getPortalType(), 'Acknowledgement') + self.assertEqual(acknowledgement.getTextContent(), "A Nice Message") + transaction.commit() + + # We should not have any acknowledgements, we just commited previous + # transaction, this means that we look if the mechanism that looks at + # activity tags is working or not + event_acknowledgement_list = getEventAcknowlegementList() + # We should not have any acknowledgements, tic is finished + # the code should look directly for acnowledgement documents + self.tic() + event_acknowledgement_list = getEventAcknowlegementList() + self.assertEqual(0, len(event_acknowledgement_list)) + + # We should have one acknowledgement in the event module + + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TestAcknowledgementTool)) + return suite -- 2.30.9