Setting up workflow notifications



This tutorial gives you an overview about the options available to set up notifications during the execution of workflows. It is written for ]po[ Developers.
Please note that notifications by email are not always the best option to notify a user about the assignment of a task or other incidences that require actions to be taken. 
Too many email notifications can annoy users and as a consequence mail messages send automatically send out buy the system will be perceived as spam and as a consequence often ignored by users. 
If users work frequently with the system, a email notification are usually obsolete since the "Workflow Home Inbox" always contains all tasks, the WF engine has assigned to user. 

Notification Types

]po[ supports the following notification types

a) Default notification through "Workflow Home Inbox"
b) Default Notification about task assignment
c) Individual Notification about task assignment using OpenACS Notifications
d) Simple Notifications 

a) Default Notification

Users receive notifications about workflow assignments through their "Workflow Inbox" on the home page:

Workflow Home Inbox


b) Default Notifications about task assignment

The notification callback will get called whenever a user is assigned to this transition. The notification callback is passed the default subject line and body, the recipient party, and the default sending party. It may modify any of these elements before posting the message, or may choose to ignore the request and return without posting at all. If no notification callback is provided, the party assigned to the task will be notified automatically by the workflow engine.

Very likely this automatic notification will not work on your ]po[ installation. Please check if the user to be notified has a notification registered for the "acs-workflow package".

Users notification settings can be managed at http://[YOUR_SERVER]/notifications/admin/


In case no such entry is found, you can execute the following tcl code to create a notification service.
(Please set user_id accordingly!)

set user_id 1234
set type_id [notification::type::get_type_id -short_name "workflow_assignee"]
set interval_id [notification::get_interval_id -name "instant"]
set delivery_method_id [notification::get_delivery_method_id -name "email"]
set wf_package_key [db_string get_data "select package_id from apm_packages where package_key = 'acs-workflow'" -default 0]

notification::request::new \
              -type_id $type_id \
              -user_id $user_id \
              -object_id $wf_package_key \
              -interval_id $interval_id \
              -delivery_method_id $delivery_method_id

c) Individual Notification about task assignment using OpenACS Notifications

For additional information on OpenACS notifications please see:

Create an OpenACS Notification Type 

Have a look at ./intranet-helpdesk/sql/postgresql/intranet-helpdesk-notifications-create.sql to see how notifications are used
in ]project-open[:

-- /packages/intranet-helpdesk/sql/postgresql/intranet-helpdesk-notifications-create.sql
create function inline_0()
returns integer as '
        impl_id integer;
        v_notif_type_id  integer;
        -- the notification type impl
        impl_id := acs_sc_impl__new (

        PERFORM acs_sc_impl_alias__new (

        PERFORM acs_sc_impl_alias__new (

        PERFORM acs_sc_binding__new (

        v_notif_type_id:= notification_type__new (
                ''Ticket Changes'',
                ''Notifications of any change to a given ticket'',

        -- enable the various intervals and delivery methods
        insert into notification_types_intervals (type_id, interval_id)
        select v_notif_type_id, interval_id
        from notification_intervals where name in (''instant'',''hourly'',''daily'');

        insert into notification_types_del_methods (type_id, delivery_method_id)
        select v_notif_type_id, delivery_method_id
        from notification_delivery_methods where short_name in (''email'');

        return (0);
end;' language 'plpgsql';
select inline_0();
drop function inline_0();

Create a notification instance

Objects such as tickets, projects, etc. can have notifications assigned to them. The assignment usually happens with the creation of the object.
The code below shows how a notification of type "ticket_notif" is assigned to a newly created ticket:       

    notification::new \
        -type_id [notification::type::get_type_id -short_name ticket_notif] \
        -object_id $ticket_id \
        -response_id "" \
        -notif_subject $ticket_name \
        -notif_text $message

Subscribing a user to a notification

         set type_id [notification::type::get_type_id -short_name "inquiry_notif"]
         set interval_id [notification::get_interval_id -name "instant"]
         set delivery_method_id [notification::get_delivery_method_id -name "email"]

         notification::request::new \
              -type_id $type_id \
              -user_id $user_id \
              -object_id $project_id \
              -interval_id $interval_id \
              -delivery_method_id $delivery_method_id

Trigger a notification in a workflow task

Notification can be triggered by calling the workflow_case__notify_assignee function.

Example: /packages/intranet-timesheet2-workflow/sql/postgresql/intranet-timesheet2-workflow-workflow.sql
-- Unassigned callback that assigns the transition to the supervisor of the absence owner.
create or replace function im_timesheet_conf_object__assign_to_supervisor (integer,text)
returns integer as '
        p_task_id               alias for $1;
        p_custom_arg            alias for $2;

        v_case_id               integer;
        v_conf_id               integer;
        v_creation_user         integer;
        v_creation_ip           varchar;
        v_journal_id            integer;
        v_transition_key        varchar;
        v_transition_name       varchar;

        v_owner_id              integer;
        v_owner_name            varchar;
        v_supervisor_id         integer;
        v_supervisor_name       varchar;
        v_str                   text;
        row                     RECORD;
        -- Get information about the transition and the "environment"
        select  t.case_id, tr.transition_name, tr.transition_key, c.object_id, o.creation_user, o.creation_ip
        into    v_case_id, v_transition_name, v_transition_key, v_conf_id, v_creation_user, v_creation_ip
        from    wf_tasks t, wf_cases c, wf_transitions tr, acs_objects o
        where   t.task_id = p_task_id
                and t.case_id = c.case_id
                and o.object_id = t.case_id
                and t.workflow_key = tr.workflow_key
                and t.transition_key = tr.transition_key;

        select  v_creation_user, im_name_from_user_id(v_creation_user),
                e.supervisor_id, im_name_from_user_id(e.supervisor_id)
        into    v_owner_id, v_owner_name,
                v_supervisor_id, v_supervisor_name
        from    im_employees e
        where   e.employee_id = v_creation_user;

        RAISE NOTICE ''im_timesheet_conf_object__assign_to_supervisor: task_id=%, conf_id=%, owner_id=%, superv=%'',
                p_task_id, v_conf_id, v_owner_id, v_supervisor_id;

        IF v_supervisor_id is not null THEN
                v_journal_id := journal_entry__new(
                    null, v_case_id,
                    v_transition_key || '' assign_to_supervisor '' || v_supervisor_name,
                    v_transition_key || '' assign_to_supervisor '' || v_supervisor_name,
                    now(), v_creation_user, v_creation_ip,
                    ''Assigning to '' || v_supervisor_name || '', the supervisor of '' || v_owner_name || ''.''
                PERFORM workflow_case__add_task_assignment(p_task_id, v_supervisor_id, ''f'');
                PERFORM workflow_case__notify_assignee (p_task_id, v_supervisor_id, null, null, ''wf_im_timesheet_conf_object_review_notif'');
        END IF;
        return 0;
end;' language 'plpgsql';

Localize OpenACS notifications

For localized mail subjects, the system expects translation strings defined in the acs-workflow package for the workflow transition key and notification type:

        v_subject := 'Notification_Subject_' || v_transition_key || '_' || v_notification_type;
       	v_subject := acs_lang_lookup_message(v_locale, 'acs-workflow', v_subject);

Managing OpenACS notifications

Users can manage their applications wide notifications at http:://[YOUR_SERVER]/notifications/manage



d) Generic Notifications  

Using the OpenACS notifications can be in some cases an overhead. It is not always necessary, to give the user the possibility to subscribe/unsubscribe to a notifications or set the frequency of delivery. 
To simply send an email to an user to inform him about an assignment, use im_workflow__notification_simple as follows:


(Custom Argument contains the id of the user to be notified.)

For customized output please set the localization strings in package acs_workflow accordingly:

  • Notification_Subject_[TRANSITION_KEY]_simple_notif
  • Notification_Body_[TRANSITION_KEY]_simple_notif

The following placeholders are available:

  • %object_name%
  • %transition_name%
  • %workflow_url%

e) Specific Notifications for pre-configured Workflows

e1) Absence Workflow

For the ]po[ Absence WF the following two procedures are suitable:

For customized output please set the localization strings in package intranet-timesheet2 accordingly: 

  • Notification_Subject_Notify_Applicant_Absence_Approved
  • Notification_Body_Notify_Applicant_Absence_Approved
  • Notification_Subject_Notify_Applicant_Absence_Not_Approved
  • Notification_Body_Notify_Applicant_Absence_Not_Approved

Additional customizations:

  • Send HTML emails:
    Change 'acs_mail_nt__post_request' to:
      v_request_id := acs_mail_nt__post_request (
              v_party_from,                 -- party_from
              v_party_to,                   -- party_to
              'f',                          -- expand_group
              v_subject,                    -- subject
              v_body,                       -- message
              0,                            -- max_retries
              null,                         -- package_id
              'text/html'                   -- mime type
  • Add Supervisor comment to email: 
            -- Get message from journal
            select msg into v_msg from journal_entries where object_id = v_acs_object_id and action_pretty = 'Approve finish';
            IF v_msg IS NOT NULL THEN
                v_body := v_body || 'Note from Supervisor\n\n';
                v_body := v_body || v_msg || '\n\n';
            END IF;

e2) Timesheet Confirmation Workflow

The ]po[ Timesheet Confirmation WF provides:

  • im_ts_notify_applicant_approved
  • im_ts_notify_applicant_not_approved

For customized output please set the localization strings in package intranet-timesheet2-workflow accordingly: 

  • Notification_Subject_Notify_Applicant_TS_Approved
  • Notification_Body_Notify_Applicant_TS_Approved
  • Notification_Subject_Notify_Applicant_TS_Not_Approved
  • Notification_Body_Notify_Applicant_TS_Not_Approved 

f) Custom Notifications

If you require additional customization, have a look at procedure im_customer-portal_notify_customer, of package intranet-customer-portal

CREATE OR REPLACE FUNCTION im_customer-portal_notify_customer(integer, character varying, character varying)
  RETURNS integer AS
        p_case_id               alias for $1;
        p_transition_key        alias for $2;
	p_custom_arg            alias for $3;

        v_task_id               integer;        v_case_id               integer;
        v_creation_ip           varchar;        v_creation_user         integer;
        v_object_id             integer;        v_object_type           varchar;
        v_journal_id            integer;
        v_transition_key        varchar;        v_workflow_key          varchar;
        v_group_id              integer;        v_group_name            varchar;
        v_task_owner            integer;

        v_object_name           text;
        v_party_from            parties.party_id%TYPE;
        v_party_to              parties.party_id%TYPE;
        v_subject               text;
        v_body                  text;
        v_request_id            integer;

        v_locale                text;
        v_count                 integer;

        RAISE NOTICE 'Notify_assignee_project_approval: enter';

        -- Select out some frequently used variables of the environment
        select  c.object_id, c.workflow_key, co.creation_user, task_id, c.case_id, co.object_type, co.creation_ip
        into    v_object_id, v_workflow_key, v_creation_user, v_task_id, v_case_id, v_object_type, v_creation_ip
        from    wf_tasks t, wf_cases c, acs_objects co
        where   c.case_id = p_case_id
                and c.case_id = co.object_id
                and t.case_id = c.case_id
                and t.workflow_key = c.workflow_key
                and t.transition_key = p_transition_key;

        v_party_from := -1;

        -- Get locale of user
        select  language_preference into v_locale
        from    user_preferences
        where   user_id = v_creation_user;

        -- ------------------------------------------------------------
        -- Try with specific translation first
        v_subject := 'Notification_Subject_Notify_Customer_Quote_Created';
        v_subject := acs_lang_lookup_message(v_locale, 'intranet-customer-portal', v_subject);

        -- Fallback to generic (no transition key) translation
        IF substring(v_subject from 1 for 7) = 'MISSING' THEN
                v_subject := 'A quote has been created';
        END IF;

        -- Replace variables
        -- v_subject := replace(v_subject, '%object_name%', v_object_name);
        -- v_subject := replace(v_subject, '%transition_name%', v_transition_name);

        -- ------------------------------------------------------------
        -- Try with specific translation first
        v_body := 'Notification_Body_Notify_Customer_QuoteCreated';
        v_body := acs_lang_lookup_message(v_locale, 'intranet-customer-portal', v_body);

        -- Fallback to generic (no transition key) translation
        IF substring(v_body from 1 for 7) = 'MISSING' THEN
                v_body := 'Please check your RFQ box for a new quote';
        END IF;
        -- Replace variables
        -- v_body := replace(v_body, '%object_name%', v_object_name);
        -- v_body := replace(v_body, '%transition_name%', v_transition_name);

        RAISE NOTICE 'Notify_assignee_project_approval: Subject=%, Body=%', v_subject, v_body;

        v_request_id := acs_mail_nt__post_request (
		v_party_from,                 -- party_from
		v_creation_user,              -- party_to
		'f',                        -- expand_group
		v_subject,                    -- subject
		v_body,                       -- message
		0                             -- max_retries

        return 0;
ALTER FUNCTION im_customer-portal_notify_customer(integer, character varying, character varying) OWNER TO postgres;

Execute this stored procedure by setting your task actions accordingly:


Troubleshooting email notifications

For a comprehensive troubleshoot instructions please refer to this page
  Contact Us
  Project Open Business Solutions S.L.

Calle Aprestadora 19, 12o-2a

08902 Hospitalet de Llobregat (Barcelona)


 Tel Europe: +34 609 953 751
 Tel US: +1 415 200 2465