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.
]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
Users receive notifications about workflow assignments through their "Workflow Inbox" on the home page:
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
For additional information on OpenACS notifications please see:
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 '
declare
impl_id integer;
v_notif_type_id integer;
begin
-- the notification type impl
impl_id := acs_sc_impl__new (
''NotificationType'',
''ticket_notif_type'',
''jobs''
);
PERFORM acs_sc_impl_alias__new (
''NotificationType'',
''ticket_notif_type'',
''GetURL'',
''im_ticket::notification::get_url'',
''TCL''
);
PERFORM acs_sc_impl_alias__new (
''NotificationType'',
''ticket_notif_type'',
''ProcessReply'',
''im_ticket::notification::process_reply'',
''TCL''
);
PERFORM acs_sc_binding__new (
''NotificationType'',
''ticket_notif_type''
);
v_notif_type_id:= notification_type__new (
NULL,
impl_id,
''ticket_notif'',
''Ticket Changes'',
''Notifications of any change to a given ticket'',
now(),
NULL,
NULL,
NULL
);
-- 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();
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
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
-- 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 '
declare
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;
begin
-- 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';
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);
...
Users can manage their applications wide notifications at http:://[YOUR_SERVER]/notifications/manage
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:
The following placeholders are available:
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:
Additional customizations:
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
);
-- 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:
For customized output please set the localization strings in package intranet-timesheet2-workflow accordingly:
CREATE OR REPLACE FUNCTION im_customer-portal_notify_customer(integer, character varying, character varying)
RETURNS integer AS
$BODY$
declare
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;
begin
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;
end;$BODY$
LANGUAGE 'plpgsql' VOLATILE;
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:
Calle Aprestadora 19, 12o-2a
08902 Hospitalet de Llobregat (Barcelona)
Spain
Tel Europe: +34 609 953 751
Tel US: +1 415 200 2465
Mail: info@project-open.com