[Erp5-report] r27216 - in /erp5/trunk/products/ERP5: ./ Document/ PropertySheet/ Tool/ boot...

nobody at svn.erp5.org nobody at svn.erp5.org
Wed May 27 17:19:13 CEST 2009


Author: seb
Date: Wed May 27 17:19:12 2009
New Revision: 27216

URL: http://svn.erp5.org?rev=27216&view=rev
Log:
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

Added:
    erp5/trunk/products/ERP5/Document/Acknowledgement.py
    erp5/trunk/products/ERP5/PropertySheet/DocumentProxy.py   (with props)
    erp5/trunk/products/ERP5/Tool/AcknowledgementTool.py
    erp5/trunk/products/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/bulletin_board.xml
    erp5/trunk/products/ERP5/tests/testAcknowledgementTool.py
Modified:
    erp5/trunk/products/ERP5/Document/Document.py
    erp5/trunk/products/ERP5/Document/EmailDocument.py
    erp5/trunk/products/ERP5/Document/Event.py
    erp5/trunk/products/ERP5/ERP5Site.py
    erp5/trunk/products/ERP5/__init__.py
    erp5/trunk/products/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/template_erp5_xhtml_style.xml
    erp5/trunk/products/ERP5/bootstrap/erp5_xhtml_style/bt/revision

Added: erp5/trunk/products/ERP5/Document/Acknowledgement.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/Document/Acknowledgement.py?rev=27216&view=auto
==============================================================================
--- erp5/trunk/products/ERP5/Document/Acknowledgement.py (added)
+++ erp5/trunk/products/ERP5/Document/Acknowledgement.py [utf8] Wed May 27 17:19:12 2009
@@ -1,0 +1,80 @@
+##############################################################################
+#
+# Copyright (c) 2009 Nexedi SARL and Contributors. All Rights Reserved.
+#                    Ben Mayhew <maybewhen at gmx.net>
+#                    Sebastien Robin <seb at 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
+                   )
+
+

Modified: erp5/trunk/products/ERP5/Document/Document.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/Document/Document.py?rev=27216&r1=27215&r2=27216&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/Document/Document.py [utf8] (original)
+++ erp5/trunk/products/ERP5/Document/Document.py [utf8] Wed May 27 17:19:12 2009
@@ -375,6 +375,31 @@
       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 @@
         # 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
+    return base_url

Modified: erp5/trunk/products/ERP5/Document/EmailDocument.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/Document/EmailDocument.py?rev=27216&r1=27215&r2=27216&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/Document/EmailDocument.py [utf8] (original)
+++ erp5/trunk/products/ERP5/Document/EmailDocument.py [utf8] Wed May 27 17:19:12 2009
@@ -38,8 +38,9 @@
 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
 
@@ -61,6 +62,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):
   """

Modified: erp5/trunk/products/ERP5/Document/Event.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/Document/Event.py?rev=27216&r1=27215&r2=27216&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/Document/Event.py [utf8] (original)
+++ erp5/trunk/products/ERP5/Document/Event.py [utf8] Wed May 27 17:19:12 2009
@@ -32,7 +32,62 @@
 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.
 

Modified: erp5/trunk/products/ERP5/ERP5Site.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/ERP5Site.py?rev=27216&r1=27215&r2=27216&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/ERP5Site.py [utf8] (original)
+++ erp5/trunk/products/ERP5/ERP5Site.py [utf8] Wed May 27 17:19:12 2009
@@ -1338,6 +1338,8 @@
       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

Added: erp5/trunk/products/ERP5/PropertySheet/DocumentProxy.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/PropertySheet/DocumentProxy.py?rev=27216&view=auto
==============================================================================
--- erp5/trunk/products/ERP5/PropertySheet/DocumentProxy.py (added)
+++ erp5/trunk/products/ERP5/PropertySheet/DocumentProxy.py [utf8] Wed May 27 17:19:12 2009
@@ -1,0 +1,34 @@
+##############################################################################
+#
+# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
+#                    Sebastien Robin <seb at 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', )

Propchange: erp5/trunk/products/ERP5/PropertySheet/DocumentProxy.py
------------------------------------------------------------------------------
    svn:executable = *

Added: erp5/trunk/products/ERP5/Tool/AcknowledgementTool.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/Tool/AcknowledgementTool.py?rev=27216&view=auto
==============================================================================
--- erp5/trunk/products/ERP5/Tool/AcknowledgementTool.py (added)
+++ erp5/trunk/products/ERP5/Tool/AcknowledgementTool.py [utf8] Wed May 27 17:19:12 2009
@@ -1,0 +1,173 @@
+##############################################################################
+#
+# Copyright (c) 2009 Nexedi SARL and Contributors. All Rights Reserved.
+#                    Ben Mayhew <maybewhen at gmx.net>
+#                    Sebastien Robin <seb at 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)

Modified: erp5/trunk/products/ERP5/__init__.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/__init__.py?rev=27216&r1=27215&r2=27216&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/__init__.py [utf8] (original)
+++ erp5/trunk/products/ERP5/__init__.py [utf8] Wed May 27 17:19:12 2009
@@ -47,7 +47,8 @@
 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 @@
                  GadgetTool.GadgetTool,
                  ContributionRegistryTool.ContributionRegistryTool,
                  IntrospectionTool.IntrospectionTool,
+                 AcknowledgementTool.AcknowledgementTool,
                 )
 content_classes = ()
 content_constructors = ()

Added: erp5/trunk/products/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/bulletin_board.xml
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/bulletin_board.xml?rev=27216&view=auto
==============================================================================
--- erp5/trunk/products/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/bulletin_board.xml (added)
+++ erp5/trunk/products/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/bulletin_board.xml [utf8] Wed May 27 17:19:12 2009
@@ -1,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>

Modified: erp5/trunk/products/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/template_erp5_xhtml_style.xml
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/template_erp5_xhtml_style.xml?rev=27216&r1=27215&r2=27216&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/template_erp5_xhtml_style.xml [utf8] (original)
+++ erp5/trunk/products/ERP5/bootstrap/erp5_xhtml_style/SkinTemplateItem/portal_skins/erp5_xhtml_style/template_erp5_xhtml_style.xml [utf8] Wed May 27 17:19:12 2009
@@ -129,6 +129,9 @@
                   </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

Modified: erp5/trunk/products/ERP5/bootstrap/erp5_xhtml_style/bt/revision
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/bootstrap/erp5_xhtml_style/bt/revision?rev=27216&r1=27215&r2=27216&view=diff
==============================================================================
--- erp5/trunk/products/ERP5/bootstrap/erp5_xhtml_style/bt/revision [utf8] (original)
+++ erp5/trunk/products/ERP5/bootstrap/erp5_xhtml_style/bt/revision [utf8] Wed May 27 17:19:12 2009
@@ -1,1 +1,1 @@
-760
+761

Added: erp5/trunk/products/ERP5/tests/testAcknowledgementTool.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5/tests/testAcknowledgementTool.py?rev=27216&view=auto
==============================================================================
--- erp5/trunk/products/ERP5/tests/testAcknowledgementTool.py (added)
+++ erp5/trunk/products/ERP5/tests/testAcknowledgementTool.py [utf8] Wed May 27 17:19:12 2009
@@ -1,0 +1,118 @@
+##############################################################################
+# -*- coding: utf8 -*-
+# Copyright (c) 2007 Nexedi SA and Contributors. All Rights Reserved.
+#                    Sebastien Robin <seb at 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




More information about the Erp5-report mailing list