products/CPSSubscriptions

changeset 1074:6c7bb40a72c0

merged stable branch in trunk
author Georges Racinet on purity.racinet.fr <georges@racinet.fr>
date Sat, 15 Oct 2011 23:31:01 +0200
parents 4061b0dd8488 4fe4f7bbd562
children f4e7e5ad2d08
files SubscriptionsTool.py
diffstat 17 files changed, 298 insertions(+), 34 deletions(-) [+]
line diff
     1.1 --- a/.hgtags
     1.2 +++ b/.hgtags
     1.3 @@ -74,3 +74,4 @@
     1.4  1bf47c7976772024322ecdd3acde077eb65a4acb 0.0.2
     1.5  d0062e5d9edd9bc6c842cd6c67bf56832a79ad4f 0.0.1
     1.6  41600f91aaa0cfb21f4469ed101f016bbd3d715f 1.3.2
     1.7 +0b34cc150e1b675349cd255771c49240ac96c97a 1.4.0-CPS-3.5
     2.1 --- a/CHANGES
     2.2 +++ b/CHANGES
     2.3 @@ -6,7 +6,7 @@
     2.4  -
     2.5  Bug fixes
     2.6  ~~~~~~~~~
     2.7 --
     2.8 +- #2049: detailed subscriptions summary
     2.9  New internal features
    2.10  ~~~~~~~~~~~~~~~~~~~~~
    2.11  - 
     3.1 --- a/HISTORY
     3.2 +++ b/HISTORY
     3.3 @@ -1,3 +1,20 @@
     3.4 +===========================================================
     3.5 +Package: CPSSubscriptions 1.4.0-CPS-3.5
     3.6 +===========================================================
     3.7 +First release built by: gracinet at: 2011-09-28T20:54:55
     3.8 +Requires
     3.9 +~~~~~~~~
    3.10 +-
    3.11 +New features
    3.12 +~~~~~~~~~~~~
    3.13 +- Creation of the stable CPS-3.5 branch
    3.14 +Bug fixes
    3.15 +~~~~~~~~~
    3.16 +- #2455: All subscriptions page is empty
    3.17 +New internal features
    3.18 +~~~~~~~~~~~~~~~~~~~~~
    3.19 +- 
    3.20 +
    3.21  ===========================================================
    3.22  Package: CPSSubscriptions 1.3.2
    3.23  ===========================================================
     4.1 --- a/SubscriptionsTool.py
     4.2 +++ b/SubscriptionsTool.py
     4.3 @@ -38,6 +38,8 @@
     4.4  from Globals import InitializeClass, DTMLFile
     4.5  from Acquisition import aq_parent, aq_inner, aq_base
     4.6  from AccessControl import ClassSecurityInfo
     4.7 +from AccessControl import Unauthorized
     4.8 +from ZTUtils import make_query
     4.9  
    4.10  from zope.interface import implements
    4.11  
    4.12 @@ -53,6 +55,7 @@
    4.13  from Products.CMFCore.permissions import ModifyPortalContent
    4.14  
    4.15  from permissions import ViewMySubscriptions
    4.16 +from permissions import ViewSubscriptions
    4.17  from permissions import CanSubscribe
    4.18  from permissions import ManageSubscriptions
    4.19  
    4.20 @@ -924,6 +927,9 @@
    4.21          else:
    4.22              container = aq_parent(aq_inner(object))
    4.23  
    4.24 +        if not _checkPermission(ViewSubscriptions, object):
    4.25 +            raise Unauthorized()
    4.26 +
    4.27          if event_type:
    4.28              subscriptions = self.getSubscriptionsFor(event_type, object, infos)
    4.29          else:
    4.30 @@ -934,18 +940,102 @@
    4.31  
    4.32          for subscription in subscriptions:
    4.33              if subscription.isInterestedInEvent(event_type, object, infos):
    4.34 -                for pt_recipient_rule in subscription.getRecipientsRules():
    4.35 -                    pt_recipients = pt_recipient_rule.getRecipients(event_type,
    4.36 -                                                                    object,
    4.37 -                                                                    infos)
    4.38 -                    if isinstance(pt_recipients, DictType):
    4.39 -                        for pt_recipient in pt_recipients.keys():
    4.40 -                            recipients[pt_recipient] = pt_recipients[
    4.41 -                                pt_recipient]
    4.42 +                for rule in subscription.getRecipientsRules():
    4.43 +                    rrecs = rule.getRecipients(event_type, object, infos)
    4.44 +                    if not isinstance(rrecs, dict):
    4.45 +                        logger.error("getRecipients of %r did not return a "
    4.46 +                                     "dict. Skipping.", rule)
    4.47 +                        continue
    4.48 +                    elif rrecs:
    4.49 +                        logger.debug("rule %r recipients=%r", rule, rrecs)
    4.50 +                    recipients.update(rrecs)
    4.51 +
    4.52 +        return recipients
    4.53 +
    4.54 +    security.declarePublic("getDetailedRecipientsFor")
    4.55 +    def getDetailedRecipientsFor(self, obj, infos={}):
    4.56 +        """Compute all the recipients of all events for given object
    4.57 +
    4.58 +        object can be a container as well
    4.59 +
    4.60 +        The returned structure is a dict with three keys:
    4.61 +          - members
    4.62 +          - groups
    4.63 +          - other
    4.64 +
    4.65 +        For easy iteration in page templates, values are lists of dicts,
    4.66 +        with these keys:
    4.67 +           + id : the recipient id, whose meaning depends on the category
    4.68 +           + events : list of events the recipient is being notified for
    4.69 +        """
    4.70 +
    4.71 +        if not _checkPermission(ViewSubscriptions, obj):
    4.72 +            raise Unauthorized()
    4.73 +
    4.74 +        members = {}
    4.75 +        groups = {}
    4.76 +        other = {}
    4.77 +        recipients = dict(members=members, groups=groups, other=other)
    4.78 +
    4.79 +        aclu = getToolByName(self, 'acl_users')
    4.80 +        utool = getToolByName(self, 'portal_url')
    4.81 +        dtool = getToolByName(self, 'portal_directories')
    4.82 +        base_url = utool.getBaseUrl()
    4.83 +
    4.84 +        def record(category, recipient, event):
    4.85 +            """Record that a recipient of category is notified for event_type.
    4.86 +
    4.87 +            Category is one of our three dicts
    4.88 +            """
    4.89 +            logger.debug("Recording %r for %r in %r",
    4.90 +                         recipient, event, category)
    4.91 +            rec_dict = category.get(recipient)
    4.92 +            if rec_dict is None:
    4.93 +                category[recipient] = rec_dict = dict(id=recipient, events=[])
    4.94 +
    4.95 +                if category is members:
    4.96 +                    attr = 'users_dir'
    4.97 +                elif category is groups:
    4.98 +                    attr = 'groups_dir'
    4.99 +                else:
   4.100 +                    attr = None
   4.101 +
   4.102 +                if attr:
   4.103 +                    dirname = getattr(aclu, attr)
   4.104 +                    dirobj = dtool[dirname]
   4.105 +                    try:
   4.106 +                        entry = dirobj.getEntry(recipient)
   4.107 +                    except Unauthorized:
   4.108 +                        pass
   4.109                      else:
   4.110 -                        logger.debug("ComputeRecipientsRules ERROR: "
   4.111 -                                     "You should provide a dictionnary %s"
   4.112 -                                     % pt_recipient_rule.absolute_url())
   4.113 +                        rec_dict['link'] = '%scpsdirectory_entry_view?%s' % (
   4.114 +                            base_url, make_query(dirname=dirname, id=recipient))
   4.115 +                        rec_dict['title'] = entry[dirobj.title_field]
   4.116 +
   4.117 +            rec_dict['events'].append(event)
   4.118 +
   4.119 +        expand_groups = not self.use_group_emails
   4.120 +
   4.121 +        for event in self.getEventsFromContext(context=obj):
   4.122 +            for subscription in self.getSubscriptionsFor(event, obj, infos):
   4.123 +                for rule in subscription.getRecipientsRules():
   4.124 +                    rrecs = rule.getRecipients(event, obj, infos,
   4.125 +                                               expand_groups=expand_groups)
   4.126 +
   4.127 +                    if expand_groups: # uniformity
   4.128 +                        rrecs = (rrecs, {})
   4.129 +                    elif not isinstance(rrecs, tuple) or len(rrecs) != 2:
   4.130 +                        logger.error("getRecipients of %r with expand_groups="
   4.131 +                                     "False should return two dicts, got %r "
   4.132 +                                     "instead (skipping).", rule, rrecs)
   4.133 +
   4.134 +                    for category, recs in zip((members, groups), rrecs):
   4.135 +                        for address, rid in recs.items():
   4.136 +                            if rid:
   4.137 +                                record(category, rid, event)
   4.138 +                            else:
   4.139 +                                record(other, address, event)
   4.140 +
   4.141          return recipients
   4.142  
   4.143      #############################################################
     5.1 --- a/VERSION
     5.2 +++ b/VERSION
     5.3 @@ -1,5 +1,5 @@
     5.4  #BUNDLEMAN PRODUCT CONFIGURATION FILE
     5.5  # do not edit this file
     5.6  PKG_NAME=CPSSubscriptions
     5.7 -PKG_VERSION=1.3.2
     5.8 +PKG_VERSION=1.4.0-CPS-3.5
     5.9  PKG_RELEASE=1
     6.1 --- a/i18n/all.pot
     6.2 +++ b/i18n/all.pot
     6.3 @@ -638,6 +638,21 @@
     6.4  msgid "mode_real_time"
     6.5  msgstr ""
     6.6  
     6.7 +msgid "heading_subscriptions_recipient"
     6.8 +msgstr ""
     6.9 +
    6.10 +msgid "heading_subscriptions_notif_types"
    6.11 +msgstr ""
    6.12 +
    6.13 +msgid "label_user_groups"
    6.14 +msgstr ""
    6.15 +
    6.16 +msgid "label_simple_users"
    6.17 +msgstr ""
    6.18 +
    6.19 +msgid "heading_subscriptions_other_recipients"
    6.20 +msgstr ""
    6.21 +
    6.22  # ## LABELS #######################################
    6.23  msgid "label_workspace_manager"
    6.24  msgstr ""
    6.25 @@ -760,6 +775,9 @@
    6.26  msgid "action_folder_notifications"
    6.27  msgstr ""
    6.28  
    6.29 +msgid "action_folder_notifications_recipients"
    6.30 +msgstr ""
    6.31 +
    6.32  msgid "action_my_subscriptions"
    6.33  msgstr ""
    6.34  
     7.1 --- a/i18n/custom.pot
     7.2 +++ b/i18n/custom.pot
     7.3 @@ -140,6 +140,9 @@
     7.4  msgid "action_folder_notifications"
     7.5  msgstr ""
     7.6  
     7.7 +msgid "action_folder_notifications_recipients"
     7.8 +msgstr ""
     7.9 +
    7.10  msgid "action_my_subscriptions"
    7.11  msgstr ""
    7.12  
     8.1 --- a/i18n/en.po
     8.2 +++ b/i18n/en.po
     8.3 @@ -10,14 +10,15 @@
     8.4  "PO-Revision-Date: 2005-10-30 18:11+0100\n"
     8.5  "Last-Translator: Julien Anguenot <ja@nuxeo.com>\n"
     8.6  "Language-Team:  <en@li.org>\n"
     8.7 +"Language: \n"
     8.8  "MIME-Version: 1.0\n"
     8.9 +"Content-Type: text/plain; charset=UTF-8\n"
    8.10  "Content-Transfer-Encoding: 8bit\n"
    8.11  "Plural-Forms: nplurals=1; plural=0;\n"
    8.12  "X-Generator: KBabel 1.10.2\n"
    8.13  "Language-Code: en\n"
    8.14  "Language-Name: English\n"
    8.15  "Domain: default\n"
    8.16 -"Content-Type: text/plain; charset=UTF-8\n"
    8.17  "Preferred-Encodings: utf-8\n"
    8.18  
    8.19  #.   <block>
    8.20 @@ -579,9 +580,26 @@
    8.21  #.   <label for="senderEmailAddress">Email address of sender:</label>
    8.22  #: from
    8.23  #: ../skins/cps_subscriptions/subscriptions_lib_display_subscriptions_table.pt
    8.24 +
    8.25  msgid "label_sender_email_address"
    8.26  msgstr "Sender email address"
    8.27  
    8.28 +msgid "heading_subscriptions_recipient"
    8.29 +msgstr "Recipient"
    8.30 +
    8.31 +msgid "heading_subscriptions_notif_types"
    8.32 +msgstr "Alert types"
    8.33 +
    8.34 +msgid "label_user_groups"
    8.35 +msgstr "User groups"
    8.36 +
    8.37 +msgid "label_simple_users"
    8.38 +msgstr "Individual users"
    8.39 +
    8.40 +msgid "heading_subscriptions_other_recipients"
    8.41 +msgstr "Other recipients"
    8.42 +
    8.43 +
    8.44  # ## LABELS #######################################
    8.45  #.   <span>
    8.46  #.                 Full Name
    8.47 @@ -782,6 +800,9 @@
    8.48  msgid "action_folder_notifications"
    8.49  msgstr "Manage notifications"
    8.50  
    8.51 +msgid "action_folder_notifications_recipients"
    8.52 +msgstr "Notified people"
    8.53 +
    8.54  msgid "action_my_subscriptions"
    8.55  msgstr "My subscriptions"
    8.56  
     9.1 --- a/i18n/fr.po
     9.2 +++ b/i18n/fr.po
     9.3 @@ -588,6 +588,21 @@
     9.4  msgid "label_sender_email_address"
     9.5  msgstr "Adresse électronique de l'émetteur"
     9.6  
     9.7 +msgid "heading_subscriptions_recipient"
     9.8 +msgstr "Destinataire"
     9.9 +
    9.10 +msgid "heading_subscriptions_notif_types"
    9.11 +msgstr "Types d'alertes"
    9.12 +
    9.13 +msgid "label_user_groups"
    9.14 +msgstr "Groupes d'utilisateurs"
    9.15 +
    9.16 +msgid "label_simple_users"
    9.17 +msgstr "Utilisateurs individuels"
    9.18 +
    9.19 +msgid "heading_subscriptions_other_recipients"
    9.20 +msgstr "Autres destinataires"
    9.21 +
    9.22  # ## LABELS #######################################
    9.23  #.   <span>
    9.24  #.                 Full Name
    9.25 @@ -788,6 +803,9 @@
    9.26  msgid "action_folder_notifications"
    9.27  msgstr "Gérer les alertes"
    9.28  
    9.29 +msgid "action_folder_notifications_recipients"
    9.30 +msgstr "Personnes alertées"
    9.31 +
    9.32  msgid "action_my_subscriptions"
    9.33  msgstr "Mes abonnements"
    9.34  
    10.1 --- a/permissions.py
    10.2 +++ b/permissions.py
    10.3 @@ -41,5 +41,10 @@
    10.4  ViewMySubscriptions = 'View My Subscriptions'
    10.5  setDefaultRoles( ViewMySubscriptions, ('Manager', 'Member'))
    10.6  
    10.7 +ViewSubscriptions = 'View Subscriptions'
    10.8 +# Giving to Member by default because users already need to get to traverse
    10.9 +# to object to call subscriptions synthethic pages
   10.10 +setDefaultRoles( ViewSubscriptions, ('Manager', 'Member'))
   10.11 +
   10.12  CanNotifyContent = 'Can Notify Content'
   10.13  setDefaultRoles( CanNotifyContent, ('Manager', 'Owner', 'Member'))
    11.1 --- a/profiles/default/actionicons.xml
    11.2 +++ b/profiles/default/actionicons.xml
    11.3 @@ -6,6 +6,9 @@
    11.4   <action-icon category="folder" action_id="folder_subscribe"
    11.5  	      title="Folder subscription" priority="320"
    11.6  	      icon_expr="actionicon_subscriptions.png"/>
    11.7 + <action-icon category="folder" action_id="folder_notifications_recipients"
    11.8 +	      title="Notification Recipients" priority="295"
    11.9 +	      icon_expr="actionicon_alerts_recipients.png"/>
   11.10   <action-icon category="folder" action_id="folder_notifications"
   11.11  	      title="Manage notifications" priority="300"
   11.12  	      icon_expr="actionicon_manage_alerts.png"/>
    12.1 --- a/profiles/default/actions.xml
    12.2 +++ b/profiles/default/actions.xml
    12.3 @@ -7,6 +7,14 @@
    12.4       url_expr="string:${object_url}/folder_notifications_form" visible="True">
    12.5     <permission>Manage Subscriptions</permission>
    12.6    </action>
    12.7 +  <action title="action_folder_notifications_recipients"
    12.8 +	  action_id="folder_notifications_recipients"
    12.9 +     category="folder"
   12.10 +     condition_expr="python:hasattr(object, 'portal_type') and object.portal_type in portal.portal_subscriptions.getContainerPortalTypes()"
   12.11 +     url_expr="string:${object_url}/folder_notifications_recipients"
   12.12 +     visible="True">
   12.13 +   <permission>View Subscriptions</permission>
   12.14 +  </action>
   12.15    <action title="action_my_subscriptions" action_id="my_subscriptions"
   12.16       category="user"
   12.17       condition_expr="python:not portal.portal_membership.isAnonymousUser()"
    13.1 --- a/skins/cps_subscriptions/folder_notifications_all_subscribers_view.pt
    13.2 +++ b/skins/cps_subscriptions/folder_notifications_all_subscribers_view.pt
    13.3 @@ -1,12 +1,10 @@
    13.4  <tal:block define="emptybody python:1">
    13.5 -<metal:block use-macro="here/main_template/macros/master">
    13.6 -  <metal:block fill-slot="header">
    13.7 + <metal:block use-macro="here/main_template/macros/master">
    13.8 +  <metal:block fill-slot="body">
    13.9      <h1 i18n:translate="heading_all_subscribers">
   13.10        All Subscribers
   13.11      </h1>
   13.12 +    <metal:block use-macro="here/subscriptions_lib_display_detailed_recipients/macros/display" />
   13.13    </metal:block>
   13.14 -  <metal:block fill-slot="main">
   13.15 -    <metal:block use-macro="here/subscriptions_lib_display_all_notifications_subscribers/macros/display_all_notifications_subscribers" />
   13.16 -  </metal:block>
   13.17 -</metal:block>
   13.18 + </metal:block>
   13.19  </tal:block>
   13.20 \ No newline at end of file
    14.1 new file mode 100644
    14.2 --- /dev/null
    14.3 +++ b/skins/cps_subscriptions/folder_notifications_recipients.pt
    14.4 @@ -0,0 +1,10 @@
    14.5 +<metal:block use-macro="here/main_template/macros/master">
    14.6 + <metal:block fill-slot="header">
    14.7 +  <h1 i18n:translate="heading_all_subscribers">
    14.8 +   All Subscribers
    14.9 +  </h1>
   14.10 + </metal:block>
   14.11 + <metal:block fill-slot="main">
   14.12 +  <metal:block use-macro="here/subscriptions_lib_display_detailed_recipients/macros/display" />
   14.13 + </metal:block>
   14.14 +</metal:block>
    15.1 --- a/skins/cps_subscriptions/subscriptions_lib_configure_subscription.pt
    15.2 +++ b/skins/cps_subscriptions/subscriptions_lib_configure_subscription.pt
    15.3 @@ -1,10 +1,3 @@
    15.4 -<!-- a subscriptions_lib macro -->
    15.5 -<!-- $Id$ -->
    15.6 -
    15.7 -<!--
    15.8 -&lt;!-- ###### Subscription Configuration --&gt;
    15.9 --->
   15.10 -
   15.11  <metal:block define-macro="configure_subscription">
   15.12    <tal:block define="subtool here/portal_subscriptions;
   15.13        event_id request/event_key|nothing;
    16.1 --- a/skins/cps_subscriptions/subscriptions_lib_display_all_notifications_subscribers.pt
    16.2 +++ b/skins/cps_subscriptions/subscriptions_lib_display_all_notifications_subscribers.pt
    16.3 @@ -1,10 +1,3 @@
    16.4 -<!-- a subscriptions_lib macro -->
    16.5 -<!-- $Id$ -->
    16.6 -
    16.7 -<!--
    16.8 -
    16.9 --->
   16.10 -
   16.11  <metal:block define-macro="display_all_notifications_subscribers">
   16.12    <tal:block define="recipients python:here.portal_subscriptions.getRecipientsFor(infos={'context':here})">
   16.13      <br />
    17.1 new file mode 100644
    17.2 --- /dev/null
    17.3 +++ b/skins/cps_subscriptions/subscriptions_lib_display_detailed_recipients.pt
    17.4 @@ -0,0 +1,86 @@
    17.5 +<metal:block define-macro="events_in_div">
    17.6 + <div tal:repeat="event item/events"
    17.7 +      i18n:translate="" tal:content="events/?event"/>
    17.8 +</metal:block>
    17.9 +
   17.10 +<metal:block define-macro="recipient">
   17.11 + <tal:block define="href item/link|nothing">
   17.12 +  <a tal:omit-tag="not:href" tal:attributes="href href"
   17.13 +     tal:content="item/title|item/id" />
   17.14 + </tal:block>
   17.15 +</metal:block>
   17.16 +
   17.17 +<metal:block define-macro="display"
   17.18 +             xmlns:metal="http://xml.zope.org/namespaces/metal"
   17.19 +             xmlns:tal="http://xml.zope.org/namespaces/tal">
   17.20 +
   17.21 +  <tal:block define="recipients python:here.portal_subscriptions.getDetailedRecipientsFor(here);
   17.22 +                     events python:here.portal_subscriptions.getEventsFromContext(context=here);
   17.23 +                     members recipients/members;
   17.24 +                     groups recipients/groups;
   17.25 +                     other recipients/other;">
   17.26 +
   17.27 +   <table class="subscriptionsSummary">
   17.28 +    <thead>
   17.29 +     <tr>
   17.30 +      <th i18n:translate="heading_subscriptions_recipient">Recipient</th>
   17.31 +      <th i18n:translate="heading_subscriptions_notif_types">
   17.32 +       Notification types
   17.33 +      </th>
   17.34 +     </tr>
   17.35 +    </thead>
   17.36 +    <tbody tal:condition="groups">
   17.37 +     <tr><th class="recipientCategory" colspan="0"
   17.38 +             i18n:translate="label_user_groups">Groups</th></tr>
   17.39 +     <tr tal:repeat="item python:groups.values()"
   17.40 +         tal:attributes="class python:test(repeat['item'].even(), 'even', 'odd')"
   17.41 +         >
   17.42 +      <td class="recipient">
   17.43 +       <metal:block use-macro="here/subscriptions_lib_display_detailed_recipients/macros/recipient"/>
   17.44 +      </td>
   17.45 +      <td class="recipientEvents">
   17.46 +       <metal:block use-macro="here/subscriptions_lib_display_detailed_recipients/macros/events_in_div"/>
   17.47 +      </td>
   17.48 +     </tr>
   17.49 +    </tbody>
   17.50 +
   17.51 +    <tbody tal:condition="members">
   17.52 +     <tr><th class="recipientCategory" colspan="0"
   17.53 +             i18n:translate="label_simple_users">
   17.54 +      Users
   17.55 +     </th></tr>
   17.56 +     <tr tal:repeat="item python:members.values()"
   17.57 +         tal:attributes="class python:test(repeat['item'].even(), 'even', 'odd')"
   17.58 +         >
   17.59 +      <td class="recipient">
   17.60 +        <metal:block use-macro="here/subscriptions_lib_display_detailed_recipients/macros/recipient"/>
   17.61 +      </td>
   17.62 +
   17.63 +      <td class="recipientEvents">
   17.64 +       <metal:block use-macro="here/subscriptions_lib_display_detailed_recipients/macros/events_in_div"/>
   17.65 +      </td>
   17.66 +     </tr>
   17.67 +    </tbody>
   17.68 +
   17.69 +    <tbody tal:condition="other">
   17.70 +     <tr><th  class="recipientCategory" colspan="0"
   17.71 +              i18n:translate="heading_subscriptions_other_recipients">
   17.72 +      Others
   17.73 +     </th>
   17.74 +     </tr>
   17.75 +     <tr tal:repeat="item python:members.items()"
   17.76 +         tal:attributes="class python:test(repeat['item'].even(), 'even', 'odd')"
   17.77 +         >
   17.78 +      <td class="recipient">
   17.79 +       <metal:block use-macro="here/subscriptions_lib_display_detailed_recipients/macros/recipient"/>
   17.80 +      </td>
   17.81 +      <td class="recipientEvents">
   17.82 +       <metal:block use-macro="here/subscriptions_lib_display_detailed_recipients/macros/events_in_div"/>
   17.83 +      </td>
   17.84 +     </tr>
   17.85 +    </tbody>
   17.86 +
   17.87 +   </table>
   17.88 +
   17.89 +  </tal:block>
   17.90 +</metal:block>