Event subscriptions and notifications

  $Id: subscriptions-draft.txt 20781 2004-02-16 16:17:13Z janguenot $

  Typical use cases

    1. Notify the creator of a document that it is published.

    2. Notify the submitter of a document that it is published.

    3. Notify the section reviewer that a document is submitted.

    4. Notify the forum reviewer that a post is pending approval.

    5. Notify all the intermediate workflow validators of a document
       that it has reached "approved" status.

    6. Notify all having "ResponsableCrise" roles that a subproblem has
       reached "resolved" status.

    7. Notify workspace members that a comment has been posted into a
       container forum.

    8. Notify the poster of a message that someone gave an answer.

    9. Notify workspace members that a document has been copy/pasted
       into the workspace.

    10. Notify workspace members that a new document version has been
        created (which means that a revision has been frozen). [Misill]

    11. When certain workflow transitions are done, it must be possible
        for the user to specify a flag "notify_local_only" which will
        instruct the user resolution mechanism to lookup only locally
        and not infer merged local roles. Another flag "notify_no_local"
        will instruct to not send any notification based on the placeful
        resolution. [Misill]

  Use case analysis

    The main use case is to "do something when something happens".

    When?

      - Workflow transition (create, edit, submit, publish, accept,
        copy/cut/paste, )

      - Forum related eventes (New post, Reply to post, New comment)

      - Other

    Do what?

      - Send emails

      - Launch some script

    Sending an email

      Notify whom?

	- Global list of users/groups

	- Local roles in the context of the event

	  - Direct local roles on the object

	    ex: the Owner role to notify the creator that his document
	    is published.

	    ex: the Reader role to notify local readers assigned to
	    the document. (?)

	  - Direct local roles on a parent of the object with specific
            characteristics

	    ex: the SectionManagers of the Forum above the current post.

	  - Merged local roles

	    ex: the SectionReviewers are all potential validators.

	  For publishing (and others), there are two contexts: source
	  and destination...

	- Local explicit list

	  This is what we have currently with "MailingList".

	- Configuration list in the parent of the object giving instructions

	  ex: notification of the poster of a message that someone
	  responded to him. A configuration object at the forum level
	  (or above) specifies that we lookup the Owner of the
	  Post/Thread above the new post and notify him.

	- Users having been implied by the workflow

	  This is important be able to notify actors of some workflow
	  transition. The workflow currently records actors in the
	  history, so it is possible to extract them.

	  Configuration examples:

	    - those that did the transitions "edit"

	    - those that did the LAST transition "edit"

	    - those that did the transitions "validate" or "publish"

	    - those that did any transition except "comment" and "view"

	  In some workflows, we accumulate a stack of people that will
	  be able to do some action later. We also want to be able to
	  notify those. XXX this depends on how we store them.

	  Note that there are two workflow history, the one local to
	  the proxy and the one global to the docid. Both may be
	  useful depending on the context:

	    - local: ex: people who have been acting on the proxy in a
              particular workspace

	    - global: ex: (?)

	Note that for publishing events, may want to check the source
	context (to notify people from the workspace) or the
	destination (to notify people from the section).

      Send what message?

	- message defined globally. i18n.

	- message defined locally.

	The message's content may have to be computed according to
	the context.

  Specification

    Concepts

      Subscriptions Tool

        portal_subcriptions is the central tool with the necessary
        methods to query the subscriptions and execute them.

        The subscriptions are looked up locally in a .cps_subscriptions
        folder in the object.

      Subscription

        A Subscription object holds the information about what kind of
        events are treated, the parameters needed to find the final
        recipients (recipients rules), and the notifications sent to the
        recipients.

        The recipients rules are stored as subobjects of the
        Subscription.

      RecipientsRule

        They describe how to get some recipients. A final recipient for
        a subscription is either a member or an explicit email. Explicit
        emails only make sense for email subscriptions.

        If configured so, members or anonymous users can add or remove
        themselves from the subscription list.

      Notification

        The something that is actually done. Usually it involves
        Recipients (sending email) but that's not mandatory (triggering
        an arbitrary script for instance).

    Scenario

      An event is sent. The event has a context object, and additional
      properties like the user flags specified in doActionFor for a
      workflow transition, or the workflow transition parameters like
      the source/destination container.

      The event is passed to the portal_subcriptions tool.

      The subscriptions tool does lookups from the context and up to
      find the subscriptions. It then sends the notification to all
      applicable subscriptions.

      Each subscription computes a set of recipients according to its
      recipient rules, and does its specific notification on the
      recipients.

    Implementation

      Subscriptions Tool

        This tool has id 'portal_subscriptions'.

        API

          - def notify_event(self, event_type, object, infos):
            """Standard event hook.

            Get the applicable subscriptions. Sends the event to the
            subscriptions.

            For workflow events, infos must contain the additional
            keyword arguments passed to the transition.
            """

          - def getSubscriptionsFor(self, event_type, object, infos):
            """Get the subscriptions applicable for this event.

            Some of the parameters may be None to get all subscriptions.
            """

        Also needed API

          - To what local subscription lists can the user subscribe?

      Subscription

        API

          - def isInterestedInEvent(self, event_type, object, infos):
            """Is the subscription interested in the given event."""

          - def sendEvent(self, event_type, object, infos):
            """Send an event to the subscription."""

          - def getRecipientsRules(self, ...):
            """Get the recipients rules objects...

            XXX matching what ?
            """

          - setters/getters for the properties.

	A Subscription instance has the following properties:

          Event filtering:

            - filter_event_types -- The event types on which to react.

              ex: workflow_create, workflow_in_publish

            - filter_object_types -- The types of the objects concerned
              by the subscription. The subscription is valid only if the
              context object's portal_type is in object_types.

          Recipient filtering:

           - recipient_emails_black_list -- The emails the subscription is
             blocking.

          Notifications:

            - notification_types -- What kinds of action are taken for
              the recipients:

              - 'tales': Call notification_expression.

              - 'email': Send an email to each recipient, according to
                notification_email_expression.

            - notification_expression -- A TALES expression called
              for each recipient. In the expression, the same namespace
              as in ComputedRecipient is available, with in addition:

              - email: The recipient's email.

              - member: The member if the recipient is a member, or
                None.

            - notification_email_expression -- A TALES expression
              returning the email headers and body as a string. No mail
              is sent if it returns a false value. In the expression,
              the same namespace as in action_expression is
              available.

      Recipients:

        There are different ways of getting recipients. The following
        classes subclass RecipientsRule and implement different
        strategies. Their properties are described below.

        API

          - def getRecipients(self, event_type, object, infos):
            """Get the recipients.

            Returns a mapping with 'members' and 'emails' as keys.
            """

        ComputedRecipientsRule

          - expression -- A TALES expression returning a mapping
            with recipients. In the expression, the following namespace
            is available:

            - portal: The portal object.

            - context: The context object (proxy) where the triggering
              event occured.

            - proxy: Alias for context.

            - doc: context.getContent().

            - container: The context's container.

            - ancestor: If 'ancestor_local' or 'ancestor_merged' was
              used for recipient_roles_origins, that object, else None.

            - event_type: The triggering event type.

            - triggering_user: The user who triggered the original
              action.

            - DateTime: A DateTime constructor.

        ExplicitRecipientsRule:

          - members -- The ids of the members subscribed manually.

          - members_allow_add -- Whether members are allowed to
            subscribe / unsubscribe manually to the Subscription (adding
            themselves to recipient_members).

    	  - groups -- The ids of the groups subscribed manually.

          - emails -- The emails subscribed manually.

          - emails_allow_add -- Whether anonymous visitors are allowed
            to subscribe / unsubscribe manually to the Subscription
            (adding themselves to recipient_emails).

          - emails_confirm -- Whether the emails have to be confirmed
            before being used. XXX this may be better as a global flag
            of portal_subcriptions instead..

          - emails_pending_add -- The emails subscribed manually but not
            yet confirmed.

          - emails_pending_delete -- The emails pending deletion from
            recipient_emails but not yet confirmed.

        RoleRecipientsRule:

          - roles -- The roles subscribed.

          - origins -- A sequence describing how roles are looked up. It
            can contain the following keys:

            - 'local': Direct local roles from the context object are
              used.

            - 'merged': All merged local roles of the context object are
              used.

            - 'ancestor_local': Direct local roles found on an ancestor
              object of type in ancestor_object_types. Only the closest
              matching ancestor object is used.

            - 'ancestor_merged': Idem but with merged local roles.

          - ancestor_object_types -- The portal types of the ancestor
            where a lookup of local roles is done if origins contains
            'ancestor_local' or 'ancestor_merged'.

        WorkflowImpliedRecipientsRule:

          - origins -- A sequence of keys used to determine which
            object's workflow is queried for recipients. It can contain
            the following keys:

            - 'context': The context object.

            - 'container': The container.

            - 'ancestor': Direct local roles found on an ancestor object
              of type in ancestor_object_types. Only the closest
              matching ancestor is used.

          - ancestor_object_types -- The portal types of the ancestor
            where workflow is queried for recipients.

          - history_lookup -- Determines what part of the history we
            lookup for the transition of type matching 'transitions':

            - 'last': The last transition of matching type is
              used.

            - 'all': All transitions of matching type are used.

          - transitions -- The name of the transitions examined for
            variables. '*' means all transitions. If the first element
            of the list is '-', then all types except the ones following
            it are used.

          - variables -- The workflow variables containing used
            recipients (string or list). (Will often be ['actor'].)

  Open problems

    - We need a "Mailing" object that contains the body of an email and
      that can be reused easily.

    - Where to place these mailing objects? Locally or globaly ?
      [JA:Globally (portal_subscriptions) and locally (Notification rules)

    - We need a way for local administrators to add a new subscription
      from a fixed predetermined list, without having able to
      re-parametrize them. This may mean that RecipientsRules have to be
      kept non-locally.

    - Notifications should be subclasses of NotificationRule and store
      also as subobjects, like RecipientsRule.
