DBA Data[Home] [Help]

PACKAGE BODY: APPS.WF_MAIL

Source


1 package body WF_MAIL as
2 /* $Header: wfmlrb.pls 120.31.12010000.4 2008/12/05 00:04:53 alepe ship $ */
3 
4 
5 --
6 -- Constants
7 --
8 
9 chr_indent     varchar2(8) := '        ';  -- 8 spaces (virtual tab)
10 chr_indentsize pls_integer := 8;  -- Number of char spaces in a tab
11 -- 3432204 The LOBLineBreak algorithm as been altered. The line wrapping
12 -- has been increased to allow for the horizontal line in text/plain
13 -- notifications
14 -- wf_linelen pls_integer := 72; -- Max line length for word wrap
15 wf_linelen pls_integer := 73; -- Max line length for word wrap
16 
17 FND_WFNTF_DETAILS NUMBER := 1014409;
18 
19 g_timezoneName varchar2(80) := '';
20 
21 g_direct_response boolean := FALSE;
22 g_send_accesskey boolean := TRUE;
23 g_autoclose_fyi boolean := TRUE;
24 g_template varchar2(30);  -- internal name for a message
25 g_fyi boolean := FALSE;
26 
27 -- CLOB Processing Variables
28 --g_text_message CLOB; -- LOB locator for text body message
29 --g_html_message CLOB; -- LOB locator for HTML body message
30 --g_attachment CLOB;   -- LOB locator for attachments
31 
32 
33 
34 -- Indexes for CLOB from the pool
35 g_LOBTable wf_temp_lob.wf_temp_lob_table_type;
36 
37 g_text_messageIdx pls_integer;
38 g_html_messageIdx pls_integer;
39 g_attachmentIdx   pls_integer;
40 
41 g_text_chunk   pls_integer;
42 g_html_chunk   pls_integer;
43 
44 -- More Info feature
45 g_moreinfo varchar2(3) := NULL;
46 g_to_role   varchar2(320);
47 
48 -- GLOBAL Package level varaibles that contain static data.
49 g_ntfHistory varchar2(30);
50 g_ntfActionHistory varchar2(50);
51 g_tab varchar2(1) := wf_core.tab;
52 g_moreInfoAPrompt varchar2(200);
53 g_moreInfoAnswer varchar2(200);
54 g_moreInfoQPrompt varchar2(200);
55 g_moreInfoSubject varchar2(200);
56 g_moreInfoSubmit varchar2(200);
57 g_moreInfoQuestion varchar2(200);
58 g_moreInfoFrom varchar2(200);
59 g_moreInfoRequested varchar2(200);
60 g_moreInfoRequestee varchar2(4000);
61 g_webAgent varchar2(200) := wf_core.translate('WF_WEB_AGENT');
62 g_wfmonId varchar2(60);
63 g_to varchar2(60);
64 g_from varchar2(60);
65 g_beginDate varchar2(100);
66 g_dueDate2 varchar2(60);
67 g_notificationId varchar2(60);
68 g_priority varchar2(60);
69 g_dueDate varchar2(60);
70 g_invalidRemarks varchar2(400);
71 g_forExample varchar2(200);
72 g_soOn varchar2(60);
73 g_none varchar2(200);
74 g_truncate varchar2(200);
75 g_noResult varchar2(200);
76 g_install varchar2(60) := wf_core.translate('WF_INSTALL');
77 g_ntfDocText varchar2(30) := wf_notification.doc_text;
78 g_ntfDocHtml varchar2(30) := wf_notification.doc_html;
79 g_Id varchar2(30);
80 g_isFwkNtf boolean;
81 g_sig_required varchar2(1);
82 g_fwk_flavor   varchar2(255);
83 g_email_flavor varchar2(255);
84 g_render       varchar2(255);
85 
86 -- response_quote VARCHAR2(1) := '"';
87 g_open_text_delimiter VARCHAR2(8) := '"';
88 g_close_text_delimiter VARCHAR2(8) := '"';
89 g_open_html_delimiter VARCHAR2(8) := '''';
90 g_close_html_delimiter VARCHAR2(8) := '''';
91 
92 
93 -- Generic mailer globals
94 g_Alert_Nodename varchar2(30) := 'ALR';
95 
96 --
97 -- HTML Table defaults
98 --
99 table_width  varchar2(8) := '100%';
100 table_border varchar2(2) := '0';
101 table_cellpadding varchar2(2) := '0';
102 table_cellspacing varchar2(2) := '0';
103 table_bgcolor varchar2(7) := 'white';
104 th_bgcolor varchar2(7) := '#ffffff';
105 th_fontcolor varchar2(7) := '#000000';
106 th_fontface varchar2(80) := 'Arial, Helvetica, Geneva, sans-serif';
107 th_fontsize varchar2(2) := '2';
108 td_bgcolor varchar2(7) := '#ffffff';
109 td_fontcolor varchar2(7) := 'black';
110 td_fontface varchar2(80) := 'Arial, Helvetica, Geneva, sans-serif';
111 td_fontsize varchar2(2) := '2';
112 
113 
114 -- UpdateStatus
115 --   Update mail status and close any notification with no response.
116 --   Handle error.
117 -- IN
118 --   notification id
119 --   status
120 --   error name (null if error is in WF_CORE)
121 procedure UpdateStatus(
122     nid        in number,
123     status     in varchar2,
124     error_name in varchar2)
125 is
126   l_autoclose VARCHAR(1);
127 begin
128    if wf_mail.autoclose_fyi then
129       l_autoclose := 'Y';
130    else
131       l_autoclose := 'N';
132    end if;
133    wf_mail.UpdateStatus2(nid => UpdateStatus.nid,
134                          status => UpdateStatus.status,
135                          autoclose => l_autoclose,
136                          error_name => UpdateStatus.error_name,
137                          external_error => null);
138 exception
139   when others then
140     wf_core.context('WF_MAIL', 'UpdateStatus', to_char(nid),
141                     UpdateStatus.status, UpdateStatus.error_name);
142     raise;
143 
144 end UpdateStatus;
145 
146 -- UpdateStatus2
147 --   Update mail status and close any notification with no response.
148 --   Handle error.
149 -- IN
150 --   nid notification id
151 --   status Status to set the notification
152 --   autoclose Flag to specify whether the notification should be closed
153 --             automitically
154 --   error name (null if error is in WF_CORE)
155 --   external_error Any error message that can not be reflected or captured
156 --                  through the wf_core.context facilty ie Java.
157 procedure UpdateStatus2(
158     nid        in number,
159     status     in varchar2,
160     autoclose  in varchar2,
161     error_name in varchar2,
162     external_error in varchar2)
163 is
164   l_mType VARCHAR2(8);
165   l_mName VARCHAR2(30);
166   l_currState VARCHAR2(8);
167   updateState boolean;
168   l_role varchar2(320);
169 
170   parameterList wf_parameter_list_t;
171 begin
172     select message_type, message_name, mail_status, recipient_role
173     into l_mType, l_mName, l_currState, l_role
174     from wf_notifications
175     where notification_id = nid;
176 
177     -- If the prevsious state was FAILED, then preserve this state.
178     -- A new status of null means that a null message was sent.
179     -- This does not cover the case for a pref deliberately set to
180     -- query or to summary (even from summary).
181     if l_currState = 'FAILED' and (status is null or status = '') then
182        updateState := false;
183     else
184        updateState := true;
185     end if;
186 
187     if updateState then
188        -- This notification had already locked by wfmail() in the mailer
189        update WF_NOTIFICATIONS
190        set    MAIL_STATUS = UpdateStatus2.status
191        where  NOTIFICATION_ID = nid;
192     end if;
193 
194 
195     if (UpdateStatus2.status = 'ERROR') then
196       WF_MAIL.HandleSendError(nid => UpdateStatus2.nid,
197                               status => UpdateStatus2.status,
198                               error_name => UpdateStatus2.error_name,
199                               external_error => UpdateStatus2.external_error);
200     elsif (UpdateStatus2.status = 'FAILED') then
201        -- Here we only raise an event and leave the message as is.
202        -- oracle.apps.wf.notification.send.failure
203        parameterlist := wf_parameter_list_t();
204 
205        wf_event.AddParameterToList('NOTIFICATION_ID',nid,parameterlist);
206        wf_event.AddParameterToList('ROLE',l_role,parameterlist);
207        wf_event.AddParameterToList('STATUS',status,parameterlist);
208        wf_event.AddParameterToList('ERROR_NAME',error_name,
209                                    parameterlist);
210        wf_event.AddParameterToList('EXTERNAL_ERROR',external_error,
211                                    parameterlist);
212        wf_event.addParameterToList('Q_CORRELATION_ID', l_mType || ':' || l_mName, parameterlist);
213 
214        --Raise the event
215        wf_event.Raise(p_event_name => 'oracle.apps.wf.notification.send.failure',
216                       p_event_key  => l_role,
217                       p_parameters => parameterlist);
218 
219     elsif UpdateStatus2.status = 'UNAVAIL' then
220        -- 4031628 The UNAVAIL mail_status has never been used, even
221        -- in the C mailer. It only happens when there is a match on
222        -- the pattern/actions. Here we can start to raise an event
223        -- that indicates that a recipient is not available to respond
224        -- to the notification.
225        parameterlist := wf_parameter_list_t();
226 
227        wf_event.AddParameterToList('NOTIFICATION_ID', UpdateStatus2.nid, parameterlist);
228        wf_event.AddParameterToList('STATUS', UpdateStatus2.status, parameterlist);
229 
230        -- Just for Future point of view.  (...receive.unavail event).
231        wf_event.addParameterToList('Q_CORRELATION_ID', l_mType || ':' || l_mName, parameterlist);
232 
233      -- Raise the event
234      -- As of now there is NO subscription of this event with SOURCE_TYPE LOCAL.
235      -- So this is just for future point of view ....
236      wf_event.Raise(p_event_name=>'oracle.apps.wf.notification.receive.unavail',
237                     p_event_key  => to_char(nid),
238                     p_parameters => parameterlist);
239 
240     elsif (UpdateStatus2.status = 'SENT') then
241       -- The default behaviour is to leave the notification open
242       -- unless there is a routing rule to tell otherwise.
243       -- This is contrary to the behaviour of previous releases
244       -- and will be re-Addressed a little later on.
245 
246       -- close this notification if there is no response
247       update WF_NOTIFICATIONS N
248       set    N.STATUS = 'CLOSED',
249              N.END_DATE = sysdate
250       where  N.NOTIFICATION_ID = nid
251       and not exists (select NULL
252                   from WF_MESSAGE_ATTRIBUTES MA
253                   where MA.MESSAGE_TYPE = N.MESSAGE_TYPE
254                   and   MA.MESSAGE_NAME = N.MESSAGE_NAME
255                   and   MA.SUBTYPE = 'RESPOND')
256       and (UpdateStatus2.autoclose = 'Y'
257          and not exists (select null
258                  from wf_routing_rules r
259                  where (r.message_type = n.message_type
260                    or r.message_type = '*')
261                    and (r.message_name = n.message_name
262                    or  r.message_name = '*')
263                    and r.action = 'FYIOPEN'
264                    and r.role = n.recipient_role
265                    and sysdate between nvl(begin_date, sysdate -1)
266                    and nvl(end_date, sysdate + 1)));
267     end if;
268 exception
269   when others then
270     wf_core.context('WF_MAIL', 'UpdateStatus2', to_char(nid),
271                     UpdateStatus2.status, UpdateStatus2.autoclose,
272                     UpdateStatus2.error_name);
273     raise;
274 
275 end UpdateStatus2;
276 
277 -- ResetFailed
278 --   Update mail status from FAILED to MAIL for open notifications.
279 -- IN
280 --   Queue number on which to process
281 procedure ResetFailed(p_queue varchar2)
282 is
283    l_nid number;
284    l_recipient WF_NOTIFICATIONS.RECIPIENT_ROLE%TYPE;
285    l_status varchar2 (8);
286    l_timeout boolean;
287    l_error_result varchar2(2000);
288 
289 begin
290 
291     -- Dequeue from the exception queue
292     -- and re-enqueue the message.
293     l_timeout := FALSE;
294     wf_xml.setFirstMessage('TRUE');
295     while not l_timeout loop
296        wf_xml.GetExceptionMessage(p_queue, l_nid, l_recipient, l_status,
297                                   l_timeout, l_error_result);
298        if ( not l_timeout ) then
299           update WF_NOTIFICATIONS N
300           set    N.MAIL_STATUS = 'MAIL'
301           where  N.NOTIFICATION_ID = l_nid;
302           -- wf_xml.EnqueueNotification(l_nid);
303        end if;
304    end loop;
305    commit;
306 
307 exception
308   when others then
309     wf_core.context('WF_MAIL', 'ResetFailed', p_queue);
310     raise;
311 
312 end ResetFailed;
313 
314 -- HandleSendError (PRIVATE)
315 --   Call any callback in error mode if error occurs in sending mail.
316 -- IN
317 --   notification id
318 --   mailer send status
319 --   error name (null if error is in WF_CORE)
320 --   external_error Any error message that can not be reflected or captured
321 --                  through the wf_core.context facilty ie Java.
322 procedure HandleSendError(
323     nid        in number,
324     status     in varchar2,
325     error_name in varchar2,
326     external_error in varchar2)
327 is
328     cb varchar2(240);
329     ctx varchar2(2000);
330     role varchar2(320);
331 
332     -- Dynamic sql stuff
333     sqlbuf varchar2(120);
334     tvalue varchar2(4000) := '';
335     nvalue number := '';
336     dvalue date := '';
337     l_dummy varchar2(1);
338 begin
339     -- Get the callback function.
340     select CALLBACK, CONTEXT, RECIPIENT_ROLE
341     into   cb, ctx, role
342     from   WF_NOTIFICATIONS
343     where  NOTIFICATION_ID = nid;
344 
345     -- If there is no callback, just clear any error and return.
346     if (cb is null) then
347         wf_core.clear;
348         return;
349     end if;
350 
351     -- Put supplied error message on stack, if any
352     if (error_name is not null) then
353         begin
354             wf_core.token('NID', to_char(nid));
355             wf_core.token('STATUS', status);
356             wf_core.token('ROLE', role);
357             if external_error is not null then
358                wf_core.token('EXTERNAL_ERROR', external_error);
359             end if;
360             wf_core.raise(error_name);
361         exception
362             when others then null;
363         end;
364     end if;
365 
366     -- Put default error message on stack, if none exists
367     if (wf_core.error_name is null) then
368         begin
369             wf_core.token('NID', to_char(nid));
370             if external_error is not null then
371                wf_core.token('EXTERNAL_ERROR', external_error);
372             end if;
373             wf_core.raise('WFMAIL_GENERIC');
374         exception
375             when others then null;
376         end;
377     end if;
378 
379     -- Call the CB in error mode
380     -- ### cb is from table
381     -- BINDVAR_SCAN_IGNORE
382     sqlbuf := 'begin '||cb||
383               '(:p1, :p2, :p3, :p4, :p5, :p6, :p7); end;';
384     execute immediate sqlbuf using
385       in 'ERROR',
386       in ctx,
387       in l_dummy,
388       in l_dummy,
389       in out tvalue,
390       in out nvalue,
391       in out dvalue;
392 
393     -- Clear the error from the stack.
394     wf_core.clear;
395 
396 exception
397     when others then
398         null;
399 end HandleSendError;
400 
401 -- Disable_Recipient_Ntf_pref
402 --    Updates the recipient of a notification to DISABLED where
403 --    there has been a failure to deliver to their email address.
404 --    This function is triggered by the oracle.apps.wf.notification.send.failure
405 --    event.
406 function Disable_Recipient_Ntf_Pref(p_subscription_guid in raw,
407                      p_event in out NOCOPY WF_EVENT_T) return varchar2
408 is
409 
410   role varchar2(320);
411   recipient varchar2(320);
412   pref varchar2(8);
413   email_address varchar2(320);
414   recipientAddress varchar2(320);
415   params wf_parameter_list_t;
416   errorName varchar2(320);
417   externError varchar2(4000);
418   nid number;
419 
420   delim pls_integer;
421 
422   errMessage varchar2(4000);
423   errStack varchar2(32000);
424 
425   tokens wf_mail_util.parserStack_t;
426   tk pls_integer;
427   dummy varchar2(2000);
428   dummyNumber number;
429 
430   orig_system varchar2(30);
431   orig_system_id number;
432 
433   paramList wf_parameter_list_t;
434 
435   invalidRoleList varchar2(32000);
436   reasonList varchar2(32000);
437   invalidRoleCount pls_integer := 0;
438   alertNid number;
439 
440   recipient_disabled boolean;
441   errorReport varchar2(32000);
442   current_maxthreshold number := null;
443 
444   cursor c_roles(parent in varchar2)
445   is
446   select name, email_address
447   from wf_roles r, wf_user_roles ur
448   where ur.role_name = parent
449     and r.name = ur.user_name;
450 
451 begin
452 
453    if (wf_log_pkg.level_procedure >= fnd_log.g_current_runtime_level) then
454       wf_log_pkg.string(WF_LOG_PKG.level_procedure,
455                       'wf.plsql.WF_MAIL.Disable_Recipient_Ntf_Pref',
456                       'BEGIN');
457    end if;
458    role := p_event.getEventKey();
459    params := p_event.getParameterList();
460    errorName := wf_event.getValueForParameter('ERROR_NAME', params);
461    externError := wf_event.getValueForParameter('EXTERNAL_ERROR', params);
462    nid := to_number(wf_event.getValueForParameter('NOTIFICATION_ID', params));
463 
464    if errorName = 'WFMLRSND_FAILED_UNDELIVERABLE' or
465       errorName = 'WFMLRSND_FAILED_DELIVERY' then
466 
467       -- Where the notification was at all undeliverable, then there
468       -- will be a comma sperated list of recipients. Each one of these
469       -- recipients will have to be disabled.
470       -- The format of the list is {{role}{role}...{role}}.
471 
472       -- FAILED_UNDELIVERABLE means that the message was not able to be
473       --                      dispatched
474       -- FAILED_DELIVERY means that it was dispatched but failed to be
475       --                 delivered at the receiving end. For this, there is
476       --                 only the nid for which it is associated.
477       if errorName = 'WFMLRSND_FAILED_DELIVERY' then
478          -- Make up the invalid role list from the recipient_role of the
479          -- undeliverable notification.
480          begin
481 
482             delim := instrb(externError, ':');
483 
484             if delim = 0 then
485                -- If the details are in the wrong format, don't
486                -- bother to do anything.
487                return 'SUCCESS';
488             end if;
489 
490             nid := to_number(substrb(externError, 1, delim -1));
491             recipientAddress := substrb(externError, delim+1);
492 
493             if (wf_log_pkg.level_statement >= fnd_log.g_current_runtime_level) then
494                 wf_log_pkg.string(WF_LOG_PKG.level_statement,
495                       'wf.plsql.WF_MAIL.Disable_Recipient_Ntf_Pref',
496                       'NID ['||to_char(nid)||
497                       '] RESPONDER ['||recipientAddress||']');
498             end if;
499 
500             select recipient_role
501                into recipient
502                from wf_notifications
503                where notification_id = nid;
504 
505          exception
506             when no_data_found then
507                -- If there is no notification, then don't do anything
508                return 'SUCCESS';
509             when others then
510                wf_core.context('WF_MAIL','Disable_Recipient_Ntf_Pref',
511                                errorName, externError, sqlerrm);
512                raise;
513          end;
514 
515           Wf_Directory.GetRoleInfoMail(role => recipient,
516                                        display_name => dummy,
517                                        email_address => email_address,
518                                        notification_preference => pref,
519                                        language => dummy,
520                                        territory => dummy,
521                                        orig_system => orig_system,
522                                        orig_system_id => orig_system_id,
523                                        installed_flag => dummy);
524 
525          if email_address is not null then
526             -- Expected case for normal recipients
527             externError := '{'||recipient||'}';
528          else
529             -- Case for recipients that are members of roles
530             externError := '';
531             for recip in c_roles(recipient) loop
532               if lower(recip.email_address) = lower(recipientAddress) then
533                  externError := externError||'{'||recip.name||'}';
534               end if;
535             end loop;
536          end if;
537       end if;
538 
539       tokens := wf_mail_util.strParser(externError, '{}');
540       paramList := wf_parameter_list_t(null);
541 
542       invalidRoleList := '';
543       reasonList := '';
544       invalidRoleCount := 0;
545       recipient_disabled := false;
546       errorReport := wf_core.translate('WFMLR_RECIPIENT_ROLE')||'   -   '||
547                      wf_core.translate('WFMLR_NOTIFICATION_PREFERENCE')||
548                      wf_core.newline||
549                      '=================================================='||
550                      wf_core.newline;
551       for tk in 1..tokens.COUNT loop
552          if tokens(tk) is not null and tokens(tk) <> ',' then
553 
554              if (wf_log_pkg.level_statement >= fnd_log.g_current_runtime_level) then
555                 wf_log_pkg.string(WF_LOG_PKG.level_statement,
556                       'wf.plsql.WF_MAIL.Disable_Recipient_Ntf_Pref',
557                       'TOKEN ['||tokens(tk)||']');
558              end if;
559              Wf_Directory.GetRoleInfoMail(role => tokens(tk),
560                                           display_name => dummy,
561                                           email_address => dummy,
562                                           notification_preference => pref,
563                                           language => dummy,
564                                           territory => dummy,
565                                           orig_system => orig_system,
566                                           orig_system_id => orig_system_id,
567                                           installed_flag => dummy);
568 
569              if (wf_log_pkg.level_statement >= fnd_log.g_current_runtime_level) then
570                 wf_log_pkg.string(WF_LOG_PKG.level_statement,
571                       'wf.plsql.WF_MAIL.Disable_Recipient_Ntf_Pref',
572                       'PREFERENCE ['||pref||']');
573              end if;
574 
575             if pref is not null and
576                pref not in ('DISABLED', 'QUERY', 'SUMMARY', 'SUMHTML') then
577 
578                 -- 4717488 Take note of the exsiting notification
579                 -- preference. This will go in the notification to the
580                 -- SYSADMIN to inform them that this recipient was disabled.
581                 -- For a group of recipient, only one notification should be
582                 -- sent.
583 
584                 errorReport := errorReport || tokens(tk)||'   -   '||pref||
585                                wf_core.newLine;
586                 recipient_disabled := true;
587 
588                if orig_system in ('FND_USR', 'PER') then
589                  FND_PREFERENCE.put(p_user_name => tokens(tk),
590 	                            p_module_name => 'WF',
591 	                            p_pref_name => 'MAILTYPE',
592 	                            p_pref_value => 'DISABLED');
593                else
594 
595                  paramList.DELETE;
596 
597                  wf_event.AddParameterToList('USER_NAME', tokens(tk),
598                                              paramList);
599                  wf_event.AddParameterToList('ORCLWORKFLOWNOTIFICATIONPREF',
600                                            'DISABLED', paramList);
601 
602                  wf_event.AddParameterToList('RAISEERRORS',
603                                            'TRUE', paramList);
604                  begin
605                     wf_local_synch.propagate_user(p_orig_system => orig_system,
606                                            p_orig_system_id => orig_system_id,
607                                            p_attributes => paramList);
608                  exception
609                    when others then
610                       -- Should the propagate fail, then save the
611                       -- role to be used to update the ErrorStack with
612                       -- the failed role and the reason.
613 
614                       wf_core.get_error(err_name => errorName,
615                                         err_message => errMessage,
616                                         err_stack => errStack);
617 
618                       invalidRoleList := invalidRoleList||'{'||tokens(tk)||
619                                          ', '||errMessage||'}';
620                       invalidRoleCount := invalidRoleCount + 1;
621                  end;
622 
623                end if;
624 
625             else
626                if (wf_log_pkg.level_statement >= fnd_log.g_current_runtime_level) then
627                   wf_log_pkg.string(WF_LOG_PKG.level_statement,
628                       'wf.plsql.WF_MAIL.Disable_Recipient_Ntf_Pref',
629                       'Unable to update notification preference for email ['||
630                       tokens(tk)||']. Check for duplicates');
631                end if;
632             end if;
633          end if;
634       end loop;
635 
636       -- 4717488 If there as a disable action, then send an error
637       -- message by calling the API. The error report
638       -- can only have 2000 characters! If larger, then it must be
639       -- truncated.
640       --
641       -- This will result in the WFMLRSND_FAILED_UNDELIVERABLE error
642       -- and the WFMLRSND_FAILED_DELIVERY error being raised.
643       --
644       -- The email for Notification NID was undeliverable. The \
645       -- following roles will be disabled DISABLE_REPORT
646       if (recipient_disabled) then
647          if (wf_log_pkg.level_statement >= fnd_log.g_current_runtime_level) then
648             wf_log_pkg.string(WF_LOG_PKG.level_statement,
649                               'wf.plsql.WF_MAIL.Disable_Recipient_Ntf_Pref',
650                               'One or more users have been disabled. Sending a notification to SYSADMIN');
651          end if;
652          if length(errorReport) > 2000 then
653             errorReport := substrb(errorReport, 1, 1900);
654             errorReport := errorReport||wf_core.translate('WFMLR_REPORT_TRUNC');
655          end if;
656 
657          -- Note, can not call HandleSendError here since that will result
658          -- in the notification activity going into error. That is too
659          -- drastic for this case. This becomes a FYI to the SYSADMIN
660          -- to inform them of the update to the user notification preference.
661 
662          -- Hardcoding the recipient as SYSADMIN. The WFERROR process
663          -- notifications all have the same, hardcoded value for their
664          -- recipients.
665 
666          -- Bug 6431003: Modifying the value of wf_event.phase_maxthreshold
667          -- so that the event gets deferred.
668 
669          -- Taking the backup of current wf_event.phase_maxthreshold value
670 
671          current_maxthreshold := wf_event.phase_maxthreshold;
672          wf_event.SetDispatchMode ('ASYNC');
673 
674          alertNid := WF_NOTIFICATION.send(role =>  'SYSADMIN',
675                                           msg_type => 'WFMAIL',
676                                           msg_name => 'USER_PREF_UPDATE_REPORT');
677 
678          -- Set the attributes for the report. The message won't be dispatched
679          -- until the commit is performed by the calling process.
680          WF_NOTIFICATION.setAttrText(nid => alertNid,
681                                      aname => 'NOTIFICATION_ID',
682                                      avalue => to_char(nid));
683          WF_NOTIFICATION.setAttrText(nid => alertNid,
684                                      aname => 'ROLE',
685                                      avalue => role);
686          WF_NOTIFICATION.setAttrText(nid => alertNid,
687                                      aname => 'UPDATED_USER_REPORT',
688                                      avalue =>  errorReport);
689 
690          -- Ensure that the subject is correctly populated.
691          WF_NOTIFICATION.Denormalize_Notification(alertNid);
692 
693         -- Resetting the wf_event.phase_maxthreshold value
694          wf_event.phase_maxthreshold := current_maxthreshold;
695 
696       end if;
697 
698    end if;
699 
700    if invalidRoleCount > 0 then
701       if (wf_log_pkg.level_procedure >= fnd_log.g_current_runtime_level) then
702          wf_log_pkg.string(WF_LOG_PKG.level_procedure,
703                          'wf.plsql.WF_MAIL.Disable_Recipient_Ntf_Pref',
704                          'END WARNING');
705       end if;
706 
707       p_event.setErrorMessage(wf_core.translate('WFMLR_ROLE_UPDATE_FAILURE'));
708       p_event.setErrorStack(substrb(invalidRoleList, 1, 4000));
709       return 'WARNING';
710    else
711       if (wf_log_pkg.level_procedure >= fnd_log.g_current_runtime_level) then
712          wf_log_pkg.string(WF_LOG_PKG.level_procedure,
713                          'wf.plsql.WF_MAIL.Disable_Recipient_Ntf_Pref',
714                          'END SUCCESS');
715       end if;
716       return 'SUCCESS';
717    end if;
718 
719 exception
720    when others then
721       wf_core.context('WF_MAIL','Disable_Recipient_Ntf_Pref',
722                       role, pref);
723       raise;
724 end Disable_Recipient_Ntf_Pref;
725 
726 -- GetLovList (PRIVATE)
727 --   Get Text Lov List
728 -- Inputs:
729 --      lk_type - lookup type, which lookup from table.
730 -- Output:
731 --      A list of valid lookup meanings, in the form
732 --              <tab> meaning
733 -- Example: LOOKUP_TYPE: YES_NO
734 -- Returns:
735 --      No
736 --      Yes
737 function GetLovList(
738         lk_type in varchar2)
739 return varchar2 is
740   cursor c is
741     select MEANING
742     from WF_LOOKUPS
743     where LOOKUP_TYPE = lk_type
744     order by MEANING;
745 
746   buffer varchar2(32000);
747 begin
748 
749   buffer := '';
750   --
751   -- Loop through selecting all lookups
752   --
753   for curs in c loop
754     -- Add lookup to end of buffer string.
755     buffer := buffer||chr_indent||curs.meaning||g_newLine;
756   end loop;
757 
758   return(buffer);
759 exception
760   when others then
761     wf_core.context('WF_MAIL', 'GetLovList', lk_type);
762     raise;
763 end GetLovList;
764 
765 
766 -- Bug# 2301881
767 -- FormatErrorMessage (PRIVATE)
768 --           Gets the error message for an Invalid
769 --           response and returns it formatted
770 -- IN
771 --   error message for name WFMLR_INVALID_LOOKUP
772 --
773 -- OUT
774 --   lookup type
775 --   remarks with expected values
776 
777 procedure FormatErrorMessage(lk_type in  varchar2,
778                              remarks out NOCOPY varchar2)
779 is
780     exp_values  varchar2(1000);
781     l_vstart    number;
782     l_vend      number;
783     l_value1    varchar2(100);
784     l_value2    varchar2(100);
785 begin
786 
787     remarks := g_invalidRemarks;
788 
789     -- get the values for the lookup type
790     exp_values := GetLovList(lk_type);
791 
792     -- get the first two values for the lookup type
793     l_vstart := instr(exp_values, g_newLine, 1);
794     l_vend := instr(exp_values, g_newLine, l_vstart+1);
795     l_value1 := trim(substr(exp_values, 1, l_vstart-1));
796     l_value2 := trim(substr(exp_values, l_vstart+1, l_vend-l_vstart-1));
797 
798     remarks := remarks || ' (' || g_forExample || ' "'
799                        || l_value1 || '", "' || l_value2
800                        || '", ' || g_soOn || ')';
801 exception
802     when others then
803        remarks := g_invalidRemarks;
804 end FormatErrorMessage;
805 
806 
807 -- HandleResponseError (PRIVATE) handle exception in response
808 --
809 --   Sets the MAIL_ERROR error message attribute, then sets the
810 --   notification status to INVALID.
811 --
812 -- IN
813 --   notification id
814 --   lookup type
815 --   value found
816 
817 procedure HandleResponseError(nid in number,
818                               lk_type in varchar2,
819                               lk_meaning in varchar2,
820                               error_result in out NOCOPY varchar2)
821 is
822     errname   varchar2(30);
823     errmsg    varchar2(2000);
824     errstack  varchar2(4000);
825     no_program_unit exception;
826     pragma exception_init(no_program_unit, -6508);
827 
828     -- Bug# 2301881 variables to handle new error message format
829     value_found varchar2(1000);
830     remarks     varchar2(1000);
831 
832     parameterlist  wf_parameter_list_t := wf_parameter_list_t();
833 
834     role varchar2(320);
835     group_id number;
836     mType varchar2(8);
837     mName varchar2(30);
838 
839 begin
840     if (wf_log_pkg.level_procedure >= fnd_log.g_current_runtime_level) then
841        wf_log_pkg.string(WF_LOG_PKG.level_procedure,
842                       'wf.plsql.WF_MAIL.HandleResponseError',
843                       'BEGIN');
844     end if;
845 
846     g_invalidRemarks :=  wf_core.translate('WFMLR_INVALID_REMARKS');
847     g_forExample := wf_core.translate('WFMLR_FOR_EXAMPLE');
848     g_soOn :=  wf_core.translate('WFMLR_SO_ON');
849     g_none :=  wf_core.translate('WFMLR_NONE');
850 
851     -- First look for a wf_core error.
852     wf_core.get_error(errname, errmsg, errstack);
853 
854     -- If no wf_core error look for a sql error.
855     if (errname is null) then
856         errmsg := sqlerrm;
857     end if;
858 
859     -- Bug# 2301881 Format the error message
860     if (lk_meaning is not null) then
861        value_found := lk_meaning;
862     else
863        value_found := g_none;
864     end if;
865     if (lk_type is not null) then
866        FormatErrorMessage(lk_type, remarks);
867     else
868        remarks := g_none;
869     end if;
870 
871 
872     error_result := errmsg;
873 
874     -- Set MAIL_ERROR_NAME attribute
875     begin
876         Wf_Notification.SetAttrText(nid, 'MAIL_ERROR_NAME', errname);
877     exception
878         when no_program_unit then
879             raise;
880         when others then
881             if (wf_core.error_name = 'WFNTF_ATTR') then
882                 Wf_Core.Clear;
883                 Wf_Notification.AddAttr(nid, 'MAIL_ERROR_NAME');
884                 Wf_Notification.SetAttrText(nid, 'MAIL_ERROR_NAME', errname);
885             end if;
886     end;
887 
888     -- Bug# 2301881 setting the values for message attributes
889     -- Set MAIL_ERROR_MESSAGE attribute
890     begin
891         Wf_Notification.SetAttrText(nid, 'MAIL_ERROR_MESSAGE', errmsg);
892     exception
893         when no_program_unit then
894             raise;
895         when others then
896             if (wf_core.error_name = 'WFNTF_ATTR') then
897                 Wf_Core.Clear;
898                 Wf_Notification.AddAttr(nid, 'MAIL_ERROR_MESSAGE');
899                 Wf_Notification.SetAttrText(nid, 'MAIL_ERROR_MESSAGE', errmsg);
900             end if;
901     end;
902 
903     -- Set MAIL_VALUE_FOUND attribute
904     begin
905         Wf_Notification.SetAttrText(nid, 'MAIL_VALUE_FOUND', value_found);
906     exception
907         when no_program_unit then
908             raise;
909         when others then
910             if (wf_core.error_name = 'WFNTF_ATTR') then
911                 Wf_Core.Clear;
912                 Wf_Notification.AddAttr(nid, 'MAIL_VALUE_FOUND');
913                 Wf_Notification.SetAttrText(nid, 'MAIL_VALUE_FOUND', value_found);
914             end if;
915     end;
916 
917     -- Set MAIL_EXP_VALUES attribute
918     begin
919         Wf_Notification.SetAttrText(nid, 'MAIL_EXP_VALUES', remarks);
920     exception
921         when no_program_unit then
922             raise;
923         when others then
924             if (wf_core.error_name = 'WFNTF_ATTR') then
925                 Wf_Core.Clear;
926                 Wf_Notification.AddAttr(nid, 'MAIL_EXP_VALUES');
927                 Wf_Notification.SetAttrText(nid, 'MAIL_EXP_VALUES', remarks);
928             end if;
929     end;
930 
931     -- End Bug# 2301881
932 
933     -- Set MAIL_ERROR_STACK attribute
934     if (errstack is null) then
935        errstack := ' ';
936     end if;
937 
938     begin
939         Wf_Notification.SetAttrText(nid, 'MAIL_ERROR_STACK', errstack);
940     exception
941         when no_program_unit then
942             raise;
943         when others then
944             if (wf_core.error_name = 'WFNTF_ATTR') then
945                 Wf_Core.Clear;
946                 Wf_Notification.AddAttr(nid, 'MAIL_ERROR_STACK');
947                 Wf_Notification.SetAttrText(nid, 'MAIL_ERROR_STACK', errstack);
948             end if;
949     end;
950 
951     -- Set the mail_status to INVALID (mailer will pick this up)
952     update WF_NOTIFICATIONS
953     set    MAIL_STATUS = 'INVALID'
954     where  NOTIFICATION_ID = nid;
955     -- wf_xml.enqueueNotification(nid);
956 
957     select recipient_role, group_id, message_type, message_name
958     into role, group_id, mType, mName
959     from wf_notifications
960     where notification_id = nid;
961 
962     wf_event.AddParameterToList('NOTIFICATION_ID', nid, parameterlist);
963     wf_event.AddParameterToList('ROLE', role, parameterlist);
964     wf_event.AddParameterToList('GROUP_ID', nvl(group_id, nid), parameterlist);
965     wf_event.addParameterToList('Q_CORRELATION_ID', mType || ':' || mName, parameterlist);
966 
967     if (wf_log_pkg.level_statement >= fnd_log.g_current_runtime_level) then
968        wf_log_pkg.string(WF_LOG_PKG.level_statement,
969                       'wf.plsql.WF_MAIL.HandleResponseError',
970                       'Raising the Send event');
971     end if;
972 
973   --Raise the event
974   wf_event.Raise(p_event_name => 'oracle.apps.wf.notification.send',
975                  p_event_key  => to_char(nid),
976                  p_parameters => parameterlist);
977 
978 exception
979     when others then
980         if (wf_log_pkg.level_exception >= fnd_log.g_current_runtime_level) then
981            wf_log_pkg.string(WF_LOG_PKG.level_exception,
982                           'wf.plsql.WF_MAIL.HandleResponseError',
983                           'Error Msg '|| sqlerrm);
984         end if;
985 
986         wf_core.context('WF_MAIL', 'HandleResponseError', nid);
987         raise;
988 end HandleResponseError;
989 
990 
991 -- WordWrap (PRIVATE)
992 --   Insert newlines to word wrap a line buffer.
993 -- Inputs:
994 --   text - text buffer
995 --   indent - number of tabs to indent each line by
996 -- Returns:
997 --   buffer contents with newlines and tabs embedded
998 function WordWrap(
999   text in varchar2,
1000   indent in number)
1001 return varchar2 is
1002   buf        varchar2(32000); -- Text buffer
1003   textlen    pls_integer;     -- Length of original 'text' argument
1004   newpos     pls_integer;     -- Current position in text
1005   space      pls_integer;     -- Position of space char to use for line break
1006   tabs       varchar2(80);    -- Tab string to prepend each line with
1007   maxlinelen pls_integer;     -- Max line length allowed
1008 begin
1009   -- Ignore empty string
1010   if (text is null) then
1011     return('');
1012   end if;
1013 
1014   buf := '';
1015   newpos := 1;
1016   textlen := length(text);
1017 
1018   -- Build the indentation string.  This is a string to pre-pend to every
1019   -- line, containing the requested number of tabs.
1020   -- (No, lpad/rpad won't work because initial string is null.)
1021   tabs := '';
1022   for i in 1 .. indent loop
1023     tabs := substr(tabs||chr_indent, 1, 80);
1024   end loop;
1025   -- Adjust line length to account for indentation
1026   maxlinelen := wf_linelen - (chr_indentsize * indent);
1027 
1028   loop
1029     -- Exit when all remaining text fits on one line.
1030     exit when (textlen - newpos <= maxlinelen);
1031 
1032     -- If next newline is before maxlinelen, then the line is already
1033     -- short enough.  Use the newline as the linebreak.
1034     space := instr(text, g_newLine, newpos, 1);
1035 
1036     if ((space = 0) or (space > (newpos + maxlinelen))) then
1037       -- Either no newlines, or next newline is beyond maxlinelen.
1038       -- Find the last space before maxlinelen.
1039       space := instr(text, ' ', -(textlen - newpos - maxlinelen), 1);
1040 
1041       if ((space = 0) or (space < newpos)) then
1042         -- No spaces on this line.
1043         -- Wrap at the next space or newline available.
1044         space := instr(replace(text, g_newLine, ' '),
1045                        ' ', newpos + maxlinelen, 1);
1046 
1047         if (space = 0) then
1048           -- No spaces or newlines left at all, so no more wrapping
1049           -- can be done.  Exit now and append any remaining text unaltered.
1050           exit;
1051         end if;
1052       end if;
1053     end if;
1054 
1055     -- Append the new line to the buffer followed by a newline,
1056     -- indented by requested number of tabs.
1057     buf := substrb(buf||tabs||rtrim(substr(text, newpos, space - newpos))||
1058                   g_newLine, 1, 32000);
1059 
1060     -- Start again after last space.
1061     newpos := space + 1;
1062   end loop;
1063 
1064   -- Append last partial line.
1065   buf := substrb(buf||tabs||rtrim(substr(text, newpos)), 1, 32000);
1066 
1067   return(buf);
1068 exception
1069   when others then
1070     wf_core.context('WF_MAIL', 'WordWrap', text, to_char(indent));
1071     raise;
1072 end WordWrap;
1073 
1074 
1075 -- GetLovMeaning (PRIVATE)
1076 --   Return the displayed meaning of a lookup
1077 -- Inputs:
1078 --   lk_type - lookup type
1079 --   lk_code - lookup code
1080 -- Returns:
1081 --   lookup meaning
1082 function GetLovMeaning(
1083   lk_type in varchar2,
1084   lk_code in varchar2)
1085 return varchar2 is
1086   buf varchar2(80);
1087 begin
1088   -- Allow null values
1089   if (lk_code is null) then
1090     return(null);
1091   end if;
1092 
1093   begin
1094     select MEANING
1095     into   buf
1096     from   WF_LOOKUPS
1097     where  LOOKUP_TYPE = lk_type and LOOKUP_CODE = lk_code;
1098   exception
1099     when no_data_found then
1100       wf_core.token('TYPE', lk_type);
1101       wf_core.token('CODE', lk_code);
1102       wf_core.raise('WFSQL_LOOKUP_CODE');
1103   end;
1104 
1105   return(buf);
1106 exception
1107   when others then
1108     wf_core.context('WF_MAIL', 'GetLovMeaning', lk_type, lk_code);
1109     raise;
1110 end GetLovMeaning;
1111 
1112 
1113 -- GetLovCode (PRIVATE) Return the hidden code of a lookup
1114 --
1115 -- IN
1116 --   lookup type
1117 --   lookup meaning
1118 -- RETURN
1119 --   lookup code
1120 function GetLovCode(
1121     lk_type    in varchar2,
1122     lk_meaning in varchar2)
1123 return varchar2 is
1124     buf varchar2(30);
1125 begin
1126     -- Allow null values
1127     if (lk_meaning is null) then
1128         wf_core.raise('WFMLR_INVALID_LOOKUP');
1129     end if;
1130 
1131     -- Exact match
1132     begin
1133         select LOOKUP_CODE
1134         into   buf
1135         from   WF_LOOKUPS
1136         where  LOOKUP_TYPE = lk_type
1137         and    MEANING = lk_meaning;
1138 
1139         return buf;
1140     exception
1141         when no_data_found then
1142             null;
1143     end;
1144 
1145     -- Case-insensitive match
1146     begin
1147         select LOOKUP_CODE
1148         into   buf
1149         from   WF_LOOKUPS
1150         where  LOOKUP_TYPE = lk_type
1151         and    upper(MEANING) = upper(lk_meaning);
1152     exception
1153         when no_data_found then
1154             wf_core.raise('WFMLR_INVALID_LOOKUP');
1155     end;
1156 
1157     return buf;
1158 exception
1159     when others then
1160         wf_core.context('WF_MAIL', 'GetLovCode', lk_type, lk_meaning);
1161         raise;
1162 end GetLovCode;
1163 
1164 
1165 -- GetLovListInternal (PRIVATE)
1166 --   Get Text Lov List (Internal Name)
1167 -- Inputs:
1168 --      lk_type - lookup type, which lookup from table.
1169 -- Output:
1170 --      A list of valid lookup meanings, in the form
1171 --              <tab> meaning
1172 -- Example: LOOKUP_TYPE: YES_NO
1173 -- Returns:
1174 --      No
1175 --      Yes
1176 function GetLovListInternal(
1177         lk_type in varchar2)
1178 return varchar2 is
1179   cursor c is
1180     select LOOKUP_CODE
1181     from WF_LOOKUPS
1182     where LOOKUP_TYPE = lk_type
1183     order by LOOKUP_CODE;
1184 
1185   buffer varchar2(32000);
1186 begin
1187 
1188   buffer := '';
1189   --
1190   -- Loop through selecting all lookups
1191   --
1192   for curs in c loop
1193     -- Add lookup to end of buffer string.
1194     buffer := buffer||curs.lookup_code||g_newLine;
1195   end loop;
1196 
1197   return(buffer);
1198 exception
1199   when others then
1200     wf_core.context('WF_MAIL', 'GetLovListInternal', lk_type);
1201     raise;
1202 end GetLovListInternal;
1203 
1204 -- GetDirectAnswer (PRIVATE)
1205 --   Get Answer for direct response
1206 -- Inputs:
1207 --      mail body.
1208 -- Output:
1209 --      Answer
1210 procedure GetDirectAnswer(body   in out NOCOPY varchar2,
1211                           one_answer out NOCOPY varchar2) is
1212   answer varchar2(4000);
1213   newline_pos pls_integer;
1214   close_doublequote_pos pls_integer;
1215   answer_syntax_error exception;
1216 begin
1217 
1218   -- Striping leading spaces and tabs
1219   while (substr(body, 1, 1) = ' ' or
1220          substr(body, 1, 1) = g_tab) loop
1221     body := substr(body, 2, length(body) - 1);
1222   end loop;
1223 
1224   if (substr(body, 1, 1) <> '"') then
1225     -- Answer not quoted in ""
1226     newline_pos := instr(body, g_newLine, 1);
1227     if (newline_pos = 0) then
1228          answer := substrb(body,1,4000);
1229     else
1230       if (newline_pos <> 1) then
1231         answer := substr(body, 1, newline_pos-1);
1232       else
1233         answer := null;
1234       end if;
1235       body := substr(body, newline_pos + 1, length(body) - newline_pos);
1236     end if;
1237     -- Striping trailing spaces, \r or \tab
1238     while ((substr(answer, length(answer), 1) = ' ') or
1239            (substr(answer, length(answer), 1) = g_newLine) or
1240            (substr(answer, length(answer), 1) = g_tab)) loop
1241       answer := substr(answer, 1, length(answer) - 1);
1242     end loop;
1243 
1244   else
1245     close_doublequote_pos := instr(body, '"', 2);
1246     if close_doublequote_pos > 4001 then
1247        answer := substr(body, 2, 3998);
1248     else
1249        answer := substr(body, 2, close_doublequote_pos -2);
1250     end if;
1251 
1252     if (substr(body, close_doublequote_pos + 1, 1) <> g_newLine) then
1253          raise answer_syntax_error;
1254     end if;
1255     body := substr(body, close_doublequote_pos + 2,
1256                                         length(body) - close_doublequote_pos - 1);
1257   end if;
1258 
1259   one_answer := answer;
1260 
1261 exception
1262   when answer_syntax_error then
1263     wf_core.context('WF_MAIL', 'GetDirectAnswer');
1264     raise;
1265   when others then
1266     wf_core.context('WF_MAIL', 'GetDirectAnswer');
1267     raise;
1268 end GetDirectAnswer;
1269 
1270 
1271 -- GetEmailResponse (PRIVATE) - Get Email Response Section
1272 -- IN
1273 --   notification id
1274 -- RETURN
1275 --   response template
1276 function GetEmailResponse(nid in number) return varchar2
1277 is
1278     cursor c1 is
1279     select WMA.DISPLAY_NAME, WMA.DESCRIPTION, WMA.TYPE, WMA.FORMAT,
1280            decode(WMA.TYPE,
1281              'VARCHAR2', decode(WMA.FORMAT,
1282                            '', WNA.TEXT_VALUE,
1283                            substr(WNA.TEXT_VALUE, 1, to_number(WMA.FORMAT))),
1284              'NUMBER', decode(WMA.FORMAT,
1285                          '', to_char(WNA.NUMBER_VALUE),
1286                          to_char(WNA.NUMBER_VALUE, WMA.FORMAT)),
1287              'DATE', decode(WMA.FORMAT,
1288                        '', to_char(WNA.DATE_VALUE),
1289                        to_char(WNA.DATE_VALUE, WMA.FORMAT)),
1290              'LOOKUP', WNA.TEXT_VALUE,
1291              WNA.TEXT_VALUE) VALUE
1292     from   WF_NOTIFICATION_ATTRIBUTES WNA,
1293            WF_NOTIFICATIONS WN,
1294            WF_MESSAGE_ATTRIBUTES_VL WMA
1295     where  WNA.NOTIFICATION_ID = nid
1296     and    WN.NOTIFICATION_ID = WNA.NOTIFICATION_ID
1297     and    WN.MESSAGE_TYPE = WMA.MESSAGE_TYPE
1298     and    WN.MESSAGE_NAME = WMA.MESSAGE_NAME
1299     and    WMA.NAME = WNA.NAME
1300     and    WMA.SUBTYPE = 'RESPOND'
1301     and    WMA.TYPE not in ('FORM', 'URL')
1302     order  by WMA.SEQUENCE;
1303 
1304     buffer varchar2(32000);
1305 begin
1306     -- for each response variable
1307     for rec in c1 loop
1308         -- Print description
1309         buffer := buffer||WordWrap(rec.description, 0);
1310         if (rec.description is not null) then
1311             buffer := buffer||g_newLine;
1312         end if;
1313 
1314         -- Print prompt
1315         buffer := buffer||'    '||rec.display_name||': '||
1316                   wf_mail.g_open_text_delimiter;
1317 
1318         -- Print field
1319         if (rec.type = 'LOOKUP') then
1320             -- LOOKUPs: show displayed meaning, list of choices
1321             buffer := buffer || GetLovMeaning(rec.format, rec.value) ||
1322                           wf_mail.g_close_text_delimiter || g_newLine ||
1323                           GetLovList(rec.format);
1324         else
1325             -- VARCHAR2, NUMBER, or DATE: use value directly.
1326             buffer := buffer || rec.value || wf_mail.g_close_text_delimiter ||
1327                       g_newLine;
1328         end if;
1329 
1330         buffer := buffer || g_newLine;
1331     end loop;
1332 
1333     return buffer;
1334 
1335 exception
1336     when others then
1337         wf_core.context('WF_MAIL', 'GetEmailResponse', to_char(nid));
1338         raise;
1339 end GetEmailResponse;
1340 
1341 
1342 -- GetEmailDirectResponse (PRIVATE) - Get Email Response Section
1343 -- IN
1344 --   notification id
1345 -- RETURN
1346 --   response template
1347 function GetEmailDirectResponse(nid in number) return varchar2
1348 is
1349     cursor c1 is
1350     select WMA.DISPLAY_NAME, WMA.DESCRIPTION, WMA.TYPE, WMA.FORMAT,
1351            decode(WMA.TYPE,
1352              'VARCHAR2', decode(WMA.FORMAT,
1353                            '', WNA.TEXT_VALUE,
1354                            substr(WNA.TEXT_VALUE, 1, to_number(WMA.FORMAT))),
1355              'NUMBER', decode(WMA.FORMAT,
1356                          '', to_char(WNA.NUMBER_VALUE),
1357                          to_char(WNA.NUMBER_VALUE, WMA.FORMAT)),
1358              'DATE', decode(WMA.FORMAT,
1359                        '', to_char(WNA.DATE_VALUE),
1360                        to_char(WNA.DATE_VALUE, WMA.FORMAT)),
1361              'LOOKUP', WNA.TEXT_VALUE,
1362              WNA.TEXT_VALUE) VALUE
1363     from   WF_NOTIFICATION_ATTRIBUTES WNA,
1364            WF_NOTIFICATIONS WN,
1365            WF_MESSAGE_ATTRIBUTES_VL WMA
1366     where  WNA.NOTIFICATION_ID = nid
1367     and    WN.NOTIFICATION_ID = WNA.NOTIFICATION_ID
1368     and    WN.MESSAGE_TYPE = WMA.MESSAGE_TYPE
1369     and    WN.MESSAGE_NAME = WMA.MESSAGE_NAME
1370     and    WMA.NAME = WNA.NAME
1371     and    WMA.SUBTYPE = 'RESPOND'
1372     and    WMA.TYPE not in ('FORM', 'URL')
1373     order  by WMA.SEQUENCE;
1374 
1375     buffer varchar2(32000) := '';
1376     seq pls_integer := 1;
1377 
1378 begin
1379 
1380     -- for each response variable
1381     for rec in c1 loop
1382         -- Construct response instruction:
1383         wf_core.token('DISPLAY_NAME', rec.display_name);
1384         wf_core.token('SEQ', seq);
1385         wf_core.token('DESCRIPTION', rec.description);
1386 
1387         -- Enter the <display_name> on line <#seq> <description>
1388         buffer := buffer||
1389                 wf_core.substitute('WFMLR', 'WFMLR_DIRECT_ENTER');
1390 
1391         if (rec.value is not null) then
1392           if (rec.type = 'LOOKUP') then
1393             wf_core.token('DEFAULT_VALUE',GetLovMeaning(rec.format,rec.value));
1394           else
1395             wf_core.token('DEFAULT_VALUE', rec.value);
1396           end if;
1397         end if;
1398         if (rec.format is not null) then
1399           wf_core.token('FORMAT', rec.format);
1400         elsif (rec.type = 'VARCHAR2') then
1401           wf_core.token('FORMAT', '2000');
1402         end if;
1403 
1404         buffer := buffer||g_newLine;
1405 
1406         -- Handle LOOKUP specially because of printing the LOV list
1407         if (rec.type = 'LOOKUP') then
1408             if (rec.value is not null) then
1409               buffer := buffer||
1410                 wf_core.substitute('WFMLR', 'WFMLR_DIRECT_LOOKUP_DEFAULT');
1411             else
1412               buffer := buffer||
1413                 wf_core.substitute('WFMLR', 'WFMLR_DIRECT_LOOKUP');
1414             end if;
1415             buffer := WordWrap(buffer, 0);
1416             buffer := buffer || g_newLine || GetLovList(rec.format);
1417         else
1418 
1419           -- Value must be ........
1420           if (rec.type = 'DATE') then
1421               -- DATE: show date format
1422               if (rec.format is not null) then
1423                 Buffer := buffer ||
1424                   wf_core.substitute('WFMLR', 'WFMLR_DIRECT_DATE_FORMAT');
1425               else
1426                 buffer := buffer ||
1427                   wf_core.substitute('WFMLR', 'WFMLR_DIRECT_DATE');
1428               end if;
1429           elsif (rec.type = 'NUMBER') then
1430               -- NUMBER: show number format
1431               if (rec.format is not null) then
1432                 buffer := buffer ||
1433                   wf_core.substitute('WFMLR','WFMLR_DIRECT_NUMBER_FORMAT');
1434               else
1435                 buffer := buffer||
1436                   wf_core.substitute('WFMLR', 'WFMLR_DIRECT_NUMBER');
1437               end if;
1438 
1439           elsif (rec.type = 'VARCHAR2') then
1440               -- VARCHAR2: show varchar2 format
1441               buffer := buffer||
1442                 wf_core.substitute('WFMLR', 'WFMLR_DIRECT_VARCHAR2_FORMAT');
1443           end if;
1444 
1445           if ((rec.value is not null) and (rec.type <> 'LOOKUP')) then
1446             wf_core.token('DEFAULT_VALUE', rec.value);
1447             buffer := buffer||g_newLine||
1448                wf_core.substitute('WFMLR', 'WFMLR_DIRECT_DEFAULT');
1449           end if;
1450 
1451           buffer := WordWrap(buffer, 0);
1452 
1453         end if;
1454 
1455         buffer := buffer || g_newLine || g_newLine;
1456         seq := seq + 1;
1457     end loop;
1458 
1459     return buffer;
1460 
1461 exception
1462     when others then
1463         wf_core.context('WF_MAIL', 'GetEmailDirectResponse', to_char(nid));
1464         raise;
1465 end GetEmailDirectResponse;
1466 
1467 
1468 -- UrlEncode (PRIVATE)
1469 -- Inputs:
1470 --      input string
1471 -- Output:
1472 --      encoded string
1473 function UrlEncode(in_string varchar2) return varchar2
1474 is
1475     encoded_string varchar2(32000);
1476 begin
1477 
1478     encoded_string := in_string;
1479 
1480     encoded_string := replace(encoded_string, '%', '%25' );
1481     encoded_string := replace(encoded_string, ' ', '%20' );
1482     encoded_string := replace(encoded_string, '!', '%21' );
1483     encoded_string := replace(encoded_string, '"', '%22' );
1484     encoded_string := replace(encoded_string, '#', '%23' );
1485     encoded_string := replace(encoded_string, '$', '%24' );
1486     encoded_string := replace(encoded_string, '&', '%26' );
1487     encoded_string := replace(encoded_string, '''', '%27' );
1488     encoded_string := replace(encoded_string, '(', '%28' );
1489     encoded_string := replace(encoded_string, ')', '%29' );
1490     encoded_string := replace(encoded_string, '*', '%2a' );
1491     encoded_string := replace(encoded_string, '+', '%2b' );
1492     encoded_string := replace(encoded_string, ',', '%2c' );
1493     encoded_string := replace(encoded_string, '-', '%2d' );
1494     encoded_string := replace(encoded_string, '.', '%2e' );
1495     encoded_string := replace(encoded_string, '/', '%2f' );
1496     encoded_string := replace(encoded_string, ';', '%3b' );
1497     encoded_string := replace(encoded_string, '<', '%3c' );
1498     encoded_string := replace(encoded_string, '=', '%3d' );
1499     encoded_string := replace(encoded_string, '>', '%3e' );
1500     encoded_string := replace(encoded_string, '?', '%3f' );
1501     encoded_string := replace(encoded_string, '@', '%40' );
1502     encoded_string := replace(encoded_string, '[', '%5b' );
1503     encoded_string := replace(encoded_string, '\', '%5c' );
1504     encoded_string := replace(encoded_string, ']', '%5d' );
1505     encoded_string := replace(encoded_string, '^', '%5e' );
1506     encoded_string := replace(encoded_string, '_', '%5f' );
1507     encoded_string := replace(encoded_string, '`', '%60' );
1508     encoded_string := replace(encoded_string, '{', '%7b' );
1509     encoded_string := replace(encoded_string, '|', '%7c' );
1510     encoded_string := replace(encoded_string, '}', '%7d' );
1511     encoded_string := replace(encoded_string, '~', '%7e' );
1512     encoded_string := replace(encoded_string, g_newLine,
1513                               '%0D%0A');
1514 
1515     return(encoded_string);
1516 
1517 end UrlEncode;
1518 
1519 -- GetMoreInfoLOV (PRIVATE) - bug 2282139
1520 --   Return a list of WF participants (PRIVATE)
1521 -- IN
1522 --   notification id
1523 --   current_role - role/user to whom the ntf is addresses
1524 -- RETURN list of roles whom have participated in the workflow
1525 
1526 function GetMoreInfoLOV(nid NUMBER, current_role in VARCHAR2)
1527 return varchar2
1528 is
1529    itemType   VARCHAR2(8);
1530    itemKey    VARCHAR2(200);
1531    context    VARCHAR2(2000);
1532    orig_sys   varchar2(30);
1533    orig_sysid number;
1534    pos        number;
1535    buffer     varchar2(32000);
1536 
1537    -- Cursor to find all users/roles associated with the notification
1538    -- other than the ones associated to recipient_role
1539    cursor c is
1540    select distinct role user_name from
1541    (select begin_date, role from
1542     (select wi.begin_date begin_date, owner_role role
1543      from   wf_items wi
1544      where  wi.item_type = itemType
1545      and    wi.item_key = itemKey
1546      and    owner_role is not null
1547      union all
1548      select ntf.begin_date begin_date, ntf.recipient_role role
1549      from (select notification_id
1550            from   wf_item_activity_statuses ias
1551            where  ias.item_type = itemType
1552            and    ias.item_key = itemKey
1553            union all
1554            select notification_id
1555            from   wf_item_activity_statuses_h ias
1556            where  ias.item_type = itemType
1557            and    ias.item_key = itemKey) intf, wf_notifications ntf
1558      where intf.notification_id = ntf.group_id)
1559     where role <> current_role
1560     and role not in (select wur.role_name
1561                      from wf_user_roles wur
1562                      where wur.user_name = current_role
1563                      and wur.user_orig_system = orig_sys
1564                      and wur.user_orig_system_id = orig_sysid)
1565    -- sacsharm bug 2887904 latest participant first
1566     order by begin_date desc);
1567 
1568 begin
1569    select  context
1570    into    context
1571    from    wf_notifications
1572    where   notification_id = nid;
1573 
1574    -- get item type and item key from the context
1575    pos := instr(context, ':', 1, 1);
1576    itemType := substr(context, 1, pos-1);
1577    pos := pos+1;
1578    itemKey := substr(context, pos, instr(context, ':', pos, 1) - pos);
1579    buffer := '';
1580 
1581    -- get role's orig sys and orig sys id from the role name
1582    pos := instr(current_role, ':', 1, 1);
1583    if (pos > 0) then
1584       orig_sys := substr(current_role, 1, pos-1);
1585       orig_sysid := substr(current_role, pos+1);
1586    else
1587       Wf_Directory.GetRoleOrigSysInfo(current_role, orig_sys, orig_sysid);
1588    end if;
1589 
1590    for curs in c loop
1591      -- sacsharm bug 2887904 if one particpant, no linefeed needed
1592      -- buffer := buffer || curs.user_name || g_newLine;
1593      if c%ROWCOUNT = 1 then
1594          buffer := curs.user_name;
1595 
1596          -- sacsharm bug 2887904 only latest particpant should be returned
1597          -- NOTE following exit should be removed later one issue of how to
1598          -- display participant roles in email is resolved. This is done
1599          -- so that currently this function only returns latest participant
1600          exit;
1601 
1602      elsif c%ROWCOUNT > 1 then
1603          buffer := buffer || g_newLine || curs.user_name;
1604      end if;
1605    end loop;
1606    return buffer;
1607 exception
1608    when others then
1609       WF_CORE.Context('WF_MAIL','GetMoreInfoLOV', to_char(nid));
1610       raise;
1611 end GetMoreInfoLOV;
1612 
1613 --
1614 -- GetMoreInfoMailTo (PRIVATE) - bug 2282139
1615 -- IN
1616 --   nid      - Notification id
1617 --   n_tag    - NID string
1618 --   reply_to - Reply to email id
1619 --   subject  - Email subject
1620 -- OUT
1621 --   mail to html tag for more info request or submission
1622 function GetMoreInfoMailTo(nid      in number,
1623                            n_tag    in varchar2,
1624                            reply_to in varchar2,
1625                            subject  in varchar2) return varchar2
1626 is
1627   buffer       varchar2(32000);
1628   body         varchar2(32000);
1629   encoded_tag  varchar2(240);
1630   question     varchar2(4000);
1631   l_requestee  varchar2(32000);
1632 
1633   cursor c_questions is
1634   select user_comment
1635   from   wf_comments
1636   where  notification_id = nid
1637   and    action = 'QUESTION'
1638   order by comment_date desc ;
1639 begin
1640   -- this gives mailto tag for the OPEN_MOREINFO template to submit
1641   -- requested information
1642   if (g_moreinfo = 'SUB') then
1643      -- Encode any special characters
1644      encoded_tag := UrlEncode(n_tag||'[4]');
1645      -- sacsharm, too much space
1646      -- body := body || g_newLine;
1647      body := body || g_moreInfoQPrompt;
1648 
1649      open c_questions;
1650      fetch c_questions into question;
1651      close c_questions;
1652 
1653      -- provide response delimiters based on the content of the question
1654      if (instr(question, '''', 1) > 0 and instr(question, '"', 1) > 0 and
1655         (instr(question, '[', 1) > 0 or instr(question, ']', 1) > 0)) then
1656         -- all delimiters are used in the question, escape one and enclose
1657         -- within the escaped one
1658         question := replace(question, '''', '\\''');
1659         body := body || ': '''||question||'''';
1660      elsif (instr(question, '''', 1) > 0 and instr(question, '"', 1) > 0) then
1661         body := body || ': ['||question||']';
1662      elsif (instr(question, '''', 1) > 0) then
1663         body := body || ': "'||question||'"';
1664      else
1665         body := body || ' :'''||question||'''';
1666      end if;
1667 
1668      body := body || g_newLine;
1669      body := body || g_moreInfoAPrompt;
1670 
1671      -- ankung (removing <)
1672      -- body := body || ': ''<';
1673      body := body || ': '||wf_mail.g_open_html_delimiter;
1674 
1675      body := body || g_moreInfoAnswer;
1676 
1677      -- ankung (removing >)
1678      -- body := body || '>''';
1679      body := body || wf_mail.g_close_html_delimiter;
1680 
1681      body := body || g_newLine;
1682      body := UrlEncode(body);
1683      buffer := buffer ||'&'||'nbsp;'||'&'||'nbsp;'||
1684                '<A class="OraLink" HREF="mailto:'||reply_to||'?subject=%20'||
1685                UrlEncode(g_moreInfoSubject)||':%20'||
1686                UrlEncode(subject)||'&'||'body=%20'||body||
1687                '%0D%0A%0D%0A'||encoded_tag||'">'||
1688                '<FONT size=+1><B>'||g_moreInfoSubmit||
1689                '</FONT></B>'||'</A>';
1690 
1691   -- this gives the additional link along with the response links to
1692   -- request for more information from a user/role.
1693   elsif (g_moreinfo = 'REQ') then
1694     -- Encode any special characters
1695      encoded_tag := UrlEncode(n_tag||'[3]');
1696 
1697      -- ankung (placing role between the '')
1698      -- body := body || g_moreInfoFrom ||': ''''';
1699      -- body := body || g_newLine;
1700      body := body || g_moreInfoFrom;
1701      body := body || ': '||wf_mail.g_open_html_delimiter;
1702 
1703      l_requestee := GetMoreInfoLOV(nid, g_to_role);
1704      if (l_requestee is null) then
1705        l_requestee := g_moreInfoRequestee;
1706      end if;
1707      body := body || wf_notification.SubstituteSpecialChars(l_requestee);
1708      -- body := body || GetMoreInfoLOV(nid, g_to_role);
1709 
1710      -- ankung (continuation of above)
1711      body := body || wf_mail.g_close_html_delimiter;
1712 
1713      -- sacsharm commented out, too much space
1714      -- body := body || g_newLine;
1715      body := body || g_newLine;
1716      body := body || g_moreInfoQPrompt;
1717 
1718      -- ankung (removing <)
1719      -- body := body || ': ''<';
1720      body := body || ': '||wf_mail.g_open_html_delimiter;
1721 
1722      body := body || g_moreInfoQuestion;
1723 
1724      -- ankung (removing >)
1725      -- body := body || '>''';
1726      body := body || wf_mail.g_close_html_delimiter;
1727 
1728      -- sacsharm commented out, too much space
1729      -- body := body || g_newLine;
1730      body := body || g_newLine;
1731      body := UrlEncode(body);
1732      buffer := buffer ||'&'||'nbsp;'||'&'||'nbsp;'||
1733                '<A class="OraLink" HREF="mailto:'||reply_to||'?subject=%20'||
1734                UrlEncode(g_moreInfoRequested)||':%20'||
1735                UrlEncode(subject)||'&'||'body=%20'||body||
1736                '%0D%0A%0D%0A'||encoded_tag||'">'||
1737                '<FONT size=+1><B>'||g_moreInfoSubject||
1738                '</FONT></B>'||'</A>';
1739   end if;
1740   return buffer;
1741 exception
1742   when others then
1743     wf_core.context('Wf_Mail', 'GetMoreInfoMailTo', to_char(nid));
1744     raise;
1745 end GetMoreInfoMailTo;
1746 
1747 
1748 -- GetMailToBody (PRIVATE) - Construct the mailto body part.
1749 -- IN
1750 --   notification id
1751 --   One of the Action(Result) attribute answer
1752 -- RETURN
1753 --   mailto html tag with the subject and body
1754 function GetMailToBody(nid in number,
1755                        result_answer in varchar2) return varchar2
1756 is
1757     cursor c1 is
1758     select WMA.NAME, WMA.DISPLAY_NAME, WMA.DESCRIPTION, WMA.TYPE, WMA.FORMAT,
1759            decode(WMA.TYPE,
1760              'VARCHAR2', decode(WMA.FORMAT,
1761                            '', WNA.TEXT_VALUE,
1762                            substr(WNA.TEXT_VALUE, 1, to_number(WMA.FORMAT))),
1763              'NUMBER', decode(WMA.FORMAT,
1764                          '', to_char(WNA.NUMBER_VALUE),
1765                          to_char(WNA.NUMBER_VALUE, WMA.FORMAT)),
1766              'DATE', decode(WMA.FORMAT,
1767                        '', to_char(WNA.DATE_VALUE),
1768                        to_char(WNA.DATE_VALUE, WMA.FORMAT)),
1769              'LOOKUP', WNA.TEXT_VALUE,
1770              WNA.TEXT_VALUE) VALUE
1771     from   WF_NOTIFICATION_ATTRIBUTES WNA,
1772            WF_NOTIFICATIONS WN,
1773            WF_MESSAGE_ATTRIBUTES_VL WMA
1774     where  WNA.NOTIFICATION_ID = nid
1775     and    WN.NOTIFICATION_ID = WNA.NOTIFICATION_ID
1776     and    WN.MESSAGE_TYPE = WMA.MESSAGE_TYPE
1777     and    WN.MESSAGE_NAME = WMA.MESSAGE_NAME
1778     and    WMA.NAME = WNA.NAME
1779     and    WMA.SUBTYPE = 'RESPOND'
1780     and    WMA.TYPE not in ('FORM', 'URL')
1781     order  by decode(WMA.NAME, 'RESULT', -100, WMA.SEQUENCE);
1782 
1783     buffer varchar2(32000);
1784 begin
1785 
1786     -- for each response variable
1787     for rec in c1 loop
1788         -- Print description
1789         if ((rec.name <> 'RESULT') or (result_answer = 'Respond')) then
1790             if (rec.description is not null) then
1791                 buffer := buffer||WordWrap(rec.description, 0);
1792                 buffer := buffer||g_newLine;
1793             end if;
1794         end if;
1795 
1796         -- Print prompt
1797         buffer := buffer||rec.display_name||': '||wf_mail.g_open_html_delimiter;
1798 
1799         -- Preseed the answer so that recipient does not have to type in manually.
1800         if ((rec.name = 'RESULT') and (result_answer <> 'Respond')) then
1801             rec.value := result_answer;
1802         end if;
1803 
1804         -- Print field
1805         if (rec.type = 'LOOKUP') then
1806             -- LOOKUPs: show displayed meaning, list of choices
1807             buffer := buffer || GetLovMeaning(rec.format, rec.value) ||
1808                       wf_mail.g_close_html_delimiter|| g_newLine;
1809             if (rec.name <> 'RESULT' )  then
1810                 buffer := buffer || GetLovList(rec.format);
1811             end if;
1812         else
1813             -- VARCHAR2, NUMBER, or DATE: use value directly.
1814             buffer := buffer || rec.value ||wf_mail.g_close_html_delimiter||
1815                       g_newLine;
1816         end if;
1817 
1818         buffer := buffer || g_newLine;
1819     end loop;
1820 
1821     buffer := UrlEncode(buffer);
1822 
1823     return buffer;
1824 
1825 exception
1826     when others then
1827         wf_core.context('WF_MAIL', 'GetMailToBody', to_char(nid));
1828         raise;
1829 end GetMailToBody;
1830 
1831 -- GetMailToBody (PRIVATE) - Construct the mailto body part.
1832 -- IN
1833 --   notification id
1834 --   One of the Action(Result) attribute answer
1835 -- RETURN
1836 --   mailto html tag with the subject and body
1837 procedure GetMailToBody(nid in number,
1838                        result_answer in varchar2,
1839                        doc in out NOCOPY CLOB)
1840 is
1841     cursor c1 is
1842     select WMA.NAME, WMA.DISPLAY_NAME, WMA.DESCRIPTION, WMA.TYPE, WMA.FORMAT,
1843            decode(WMA.TYPE,
1844              'VARCHAR2', decode(WMA.FORMAT,
1845                            '', WNA.TEXT_VALUE,
1846                            substr(WNA.TEXT_VALUE, 1, to_number(WMA.FORMAT))),
1847              'NUMBER', decode(WMA.FORMAT,
1848                          '', to_char(WNA.NUMBER_VALUE),
1849                          to_char(WNA.NUMBER_VALUE, WMA.FORMAT)),
1850              'DATE', decode(WMA.FORMAT,
1851                        '', to_char(WNA.DATE_VALUE),
1852                        to_char(WNA.DATE_VALUE, WMA.FORMAT)),
1853              'LOOKUP', WNA.TEXT_VALUE,
1854              WNA.TEXT_VALUE) VALUE
1855     from   WF_NOTIFICATION_ATTRIBUTES WNA,
1856            WF_NOTIFICATIONS WN,
1857            WF_MESSAGE_ATTRIBUTES_VL WMA
1858     where  WNA.NOTIFICATION_ID = nid
1859     and    WN.NOTIFICATION_ID = WNA.NOTIFICATION_ID
1860     and    WN.MESSAGE_TYPE = WMA.MESSAGE_TYPE
1861     and    WN.MESSAGE_NAME = WMA.MESSAGE_NAME
1862     and    WMA.NAME = WNA.NAME
1863     and    WMA.SUBTYPE = 'RESPOND'
1864     and    WMA.TYPE not in ('FORM', 'URL')
1865     order  by decode(WMA.NAME, 'RESULT', -100, WMA.SEQUENCE);
1866 
1867     str_buffer varchar2(32000);
1868     --buffer CLOB;
1869     -- bufferIdx pls_integer;
1870 
1871 begin
1872     -- bufferIdx := wf_temp_lob.getLob(g_LOBTable);
1873 
1874     -- for each response variable
1875     for rec in c1 loop
1876         -- Print description
1877         if ((rec.name <> 'RESULT') or (result_answer = 'Respond')) then
1878             if (rec.description is not null) then
1879                 str_buffer := str_buffer||WordWrap(rec.description, 0);
1880                 str_buffer := str_buffer||g_newLine;
1881             end if;
1882         end if;
1883 
1884         -- Print prompt
1885         str_buffer := str_buffer||rec.display_name||': '||
1886                       wf_mail.g_open_html_delimiter;
1887 
1888        -- Preseed the answer so that recipient does not have
1889        -- to type in manually.
1890 
1891        if ((rec.name = 'RESULT') and (result_answer <> 'Respond')) then
1892            rec.value := result_answer;
1893        end if;
1894 
1895         -- Print field
1896         if (rec.type = 'LOOKUP') then
1897             -- LOOKUPs: show displayed meaning, list of choices
1898             str_buffer := str_buffer || GetLovMeaning(rec.format, rec.value) ||
1899                           wf_mail.g_close_html_delimiter|| g_newLine;
1900             if (rec.name <> 'RESULT' )  then
1901                 str_buffer := str_buffer || GetLovList(rec.format);
1902             end if;
1903         else
1904             -- VARCHAR2, NUMBER, or DATE: use value directly.
1905             str_buffer := str_buffer || rec.value ||
1906                           wf_mail.g_close_html_delimiter||g_newLine;
1907         end if;
1908 
1909         str_buffer := str_buffer || g_newLine;
1910 
1911         if(length(str_buffer) > 24000) then
1912            str_buffer := UrlEncode(str_buffer);
1913            DBMS_LOB.writeAppend(lob_loc => doc,
1914                                 amount => length(str_buffer),
1915                                 buffer => str_buffer);
1916            str_buffer := '';
1917         end if;
1918 
1919         -- DBMS_LOB.writeAppend(g_LOBTable(bufferIdx).temp_lob,
1920         --                      length(str_buffer),
1921         --                      str_buffer);
1922         -- str_buffer := '';
1923 
1924     end loop;
1925     if(length(str_buffer) > 0) then
1926        str_buffer := UrlEncode(str_buffer);
1927        DBMS_LOB.writeAppend(lob_loc => doc,
1928                             amount => length(str_buffer),
1929                             buffer => str_buffer);
1930        str_buffer := '';
1931     end if;
1932     -- DBMS_LOB.Append(doc, g_LOBTable(bufferIdx).temp_lob);
1933     -- wf_temp_lob.releaseLob(g_LOBTable, bufferIdx);
1934 
1935 exception
1936     when others then
1937         -- wf_temp_lob.releaseLob(g_LOBTable, bufferIdx);
1938         wf_core.context('WF_MAIL', 'GetMailToBody', to_char(nid));
1939         raise;
1940 end GetMailToBody;
1941 
1942 -- GetMailTo - Construct MailTo Section (PRIVATE)
1943 -- IN
1944 --   notification id
1945 --   notification tag
1946 --   notification reply to
1947 --   notification subject
1948 -- RETURN
1949 --   mailto html tag with the subject and body
1950 function GetMailTo(nid in number,
1951                    n_tag in varchar2,
1952                    reply_to in varchar2,
1953                    subject in varchar2) return varchar2
1954 is
1955     -- SQL Statement for fetching URL RESPONSE attributes.
1956     cursor c1 is
1957     select WMA.NAME, WMA.DISPLAY_NAME, WNA.TEXT_VALUE, WMA.DESCRIPTION
1958     from   WF_NOTIFICATION_ATTRIBUTES WNA,
1959            WF_NOTIFICATIONS WN,
1960            WF_MESSAGE_ATTRIBUTES_VL WMA
1961     where  WNA.NOTIFICATION_ID = nid
1962     and    WN.NOTIFICATION_ID = WNA.NOTIFICATION_ID
1963     and    WN.MESSAGE_TYPE = WMA.MESSAGE_TYPE
1964     and    WN.MESSAGE_NAME = WMA.MESSAGE_NAME
1965     and    WMA.NAME = WNA.NAME
1966     and    WMA.SUBTYPE = 'RESPOND'
1967     and    WMA.TYPE = 'URL'
1968     order  by WMA.SEQUENCE;
1969 
1970     lov varchar2(64);
1971     lov_list varchar2(240);
1972     buffer varchar2(32000);
1973     auto_answer varchar2(64);
1974     newline_pos number;
1975     disp_name varchar2(80);
1976     attr_type varchar2(8);
1977     attr_format varchar2(240);
1978     attr_value varchar2(32000);
1979     attr_desc varchar2(240);
1980     encoded_tag varchar2(240);
1981 
1982 begin
1983 
1984     -- Clear buffer
1985     buffer := '';
1986 
1987     -- URL RESPONSE attributes overrides the normal RESULT attributes.
1988     -- So, my goal here is to check for this case.
1989     -- URL RESPONSE attributes is going to appear as a anchor and don't have
1990     -- to construct the MAILTO html tag stuff that we do for the normal
1991     -- RESULT attribute.
1992 
1993     -- NOTE: Please do know that I don't want to destablize the existing code
1994     -- for the normal RESULT attribute MAILTO handleing so that I am coding
1995     -- these two cases seperately.
1996     -- for each response variable
1997     for rec in c1 loop
1998         buffer := buffer||'<P>';
1999         if (rec.description is not null) then
2000             buffer := buffer||rec.description||'<P>';
2001         end if;
2002         buffer := buffer|| '<A class="OraLink" HREF="'||wf_notification.geturltext(rec.text_value, nid)||'" target="_top">';
2003         buffer := buffer||'<FONT size=+1> <B>'||rec.display_name;
2004         buffer := buffer||'</B></FONT>'||'</A>';
2005         buffer := buffer||g_newLine;
2006     end loop;
2007 
2008     if (buffer is not null) then
2009         return(buffer);
2010     end if;
2011 
2012     --
2013     -- Normal RESULT attribute handling
2014     --
2015     begin
2016     select WMA.DISPLAY_NAME, WMA.TYPE, WMA.FORMAT,
2017            decode(WMA.TYPE,
2018              'VARCHAR2', decode(WMA.FORMAT,
2019                            '', WNA.TEXT_VALUE,
2020                            substr(WNA.TEXT_VALUE, 1, to_number(WMA.FORMAT))),
2021              'NUMBER', decode(WMA.FORMAT,
2022                          '', to_char(WNA.NUMBER_VALUE),
2023                          to_char(WNA.NUMBER_VALUE, WMA.FORMAT)),
2024              'DATE', decode(WMA.FORMAT,
2025                        '', to_char(WNA.DATE_VALUE),
2026                        to_char(WNA.DATE_VALUE, WMA.FORMAT)),
2027              'LOOKUP', WNA.TEXT_VALUE,
2028              WNA.TEXT_VALUE), WMA.DESCRIPTION
2029     into   disp_name, attr_type, attr_format, attr_value, attr_desc
2030     from   WF_NOTIFICATION_ATTRIBUTES WNA,
2031            WF_NOTIFICATIONS WN,
2032            WF_MESSAGE_ATTRIBUTES_VL WMA
2033     where  WNA.NOTIFICATION_ID = nid
2034     and    WN.NOTIFICATION_ID = WNA.NOTIFICATION_ID
2035     and    WN.MESSAGE_TYPE = WMA.MESSAGE_TYPE
2036     and    WN.MESSAGE_NAME = WMA.MESSAGE_NAME
2037     and    WMA.NAME = WNA.NAME
2038     and    WMA.SUBTYPE = 'RESPOND'
2039     and    WMA.NAME = 'RESULT'
2040     and    WMA.TYPE not in ('FORM', 'URL');
2041 
2042     -- We can only construct answer button or mailto link if is lookup
2043     if (attr_type <> 'LOOKUP') then
2044         auto_answer := 'Respond';
2045     else
2046         -- If is LOOKUP RESULT attribute, we need to show the description here.
2047         if (attr_desc is not null) then
2048             buffer := buffer||'<P>'||attr_desc;
2049         end if;
2050         auto_answer :=  attr_format;
2051     end if;
2052 
2053     exception
2054     when no_data_found then
2055         auto_answer := 'Respond';
2056     end;
2057 
2058     -- Encode any special characters
2059     encoded_tag := UrlEncode(n_tag);
2060 
2061     -- Construct mailto syntax
2062     buffer := buffer||'<P>'||disp_name||': <A class="OraLink" HREF="mailto:'||reply_to||
2063               '?subject=%20'||
2064               UrlEncode(subject)||'&'||'body=%20';
2065 
2066     if (auto_answer = 'Respond') then
2067       buffer := buffer||GetMailToBody(nid, auto_answer)||
2068                 '%0D%0A%0D%0A'||encoded_tag||'">'||'<FONT size=+1><B>'||
2069                  g_noResult||'</B></FONT>'||'</A>';
2070     else
2071         lov_list := GetLovListInternal(auto_answer);
2072         lov_list := substr(lov_list, 1, length(lov_list)-1);
2073 
2074         while (lov_list is not null) loop
2075             newline_pos := instr(lov_list, g_newLine);
2076 
2077             if (newline_pos = 0) then
2078                 lov := lov_list;
2079             else
2080                 lov := substr(lov_list, 1, newline_pos - 1);
2081             end if;
2082 
2083             buffer := buffer||GetMailToBody(nid, lov);
2084             buffer :=buffer||'%0D%0A%0D%0A'||encoded_tag||'">'||
2085                      '<FONT size=+1><B>'||GetLovMeaning(attr_format, lov)||
2086                      '</FONT></B>'||'</A>';
2087             if (newline_pos = 0) then
2088                 lov_list := null;
2089                 if (g_moreinfo = 'REQ') then
2090                    buffer := buffer || GetMoreInfoMailTo(nid, n_tag,
2091                                             reply_to, subject);
2092                 end if;
2093             else
2094                 lov_list := substr(lov_list, newline_pos+1,
2095                                    length(lov_list) - newline_pos);
2096                 buffer := buffer||g_newLine;
2097                 buffer := buffer||'&'||'nbsp;'||'&'||'nbsp;'||
2098                           '<A class="OraLink" HREF="mailto:'||reply_to||
2099                           '?subject=%20'||UrlEncode(subject)||'&'||'body=%20';
2100             end if;
2101         end loop;
2102 
2103     end if;
2104 
2105     return(buffer);
2106 
2107 exception
2108     when others then
2109       wf_core.context('WF_MAIL', 'GetMailTo', nid);
2110       raise;
2111 
2112 end GetMailTo;
2113 
2114 
2115 -- Substitute - replaces standard tokens in mail text
2116 function Substitute(
2117     txt           in  varchar2,
2118     n_nid         in  number,
2119     n_code        in  varchar2,
2120     n_status      in  varchar2,
2121     n_to_role     in  varchar2,
2122     r_dname       in  varchar2,
2123     r_email       in  varchar2,
2124     n_start_date  in  date,
2125     n_due_date    in  date,
2126     n_end_date    in  date,
2127     n_from_user   in  varchar2,
2128     n_priority    in  number,
2129     n_comment     in  varchar2,
2130     m_subject     in  varchar2,
2131     m_header      in  varchar2,
2132     m_body        in  varchar2,
2133     err_name      in  varchar2,
2134     err_message   in  varchar2,
2135     err_invalid   in  varchar2,
2136     err_expected  in varchar2,
2137     n_timezone    in  varchar2) return varchar2
2138 as
2139     stxt          varchar2(32000);
2140     n_due_date_text varchar2(64);
2141     n_start_date_text varchar2(64);
2142     n_end_date_text varchar2(64);
2143     n_priority_text varchar2(240);
2144 
2145 begin
2146     -- BLAF recommends displaying date with the TIME element
2147 
2148     -- n_due_date_text := to_char(n_due_date, Wf_Notification.g_nls_date_mask);
2149     -- n_start_date_text := to_char(n_start_date, Wf_Notification.g_nls_date_mask);
2150     -- <sstomar>>: 7578922
2151     n_start_date_text := wf_notification_util.GetCalendarDate(n_nid, n_start_date, null, true);
2152     n_due_date_text := wf_notification_util.GetCalendarDate(n_nid, n_due_date, null, true);
2153 
2154     n_end_date_text := wf_notification_util.GetCalendarDate(n_nid, n_end_date, null, true);
2155 
2156     if (n_priority > 66) then
2157       --Bug 2774891 fix - sacsharm
2158       --n_priority_text := wf_core.substitute('WFTKN', 'HIGH');
2159       n_priority_text := wf_core.substitute('WFTKN', 'LOW');
2160     elsif (n_priority > 33) then
2161       n_priority_text := wf_core.substitute('WFTKN', 'NORMAL');
2162     else
2163       --Bug 2774891 fix - sacsharm
2164       --n_priority_text := wf_core.substitute('WFTKN', 'LOW');
2165       n_priority_text := wf_core.substitute('WFTKN', 'HIGH');
2166     end if;
2167 
2168     stxt := substrb(txt, 1, 32000);
2169     stxt := substrb(replace(stxt, '&'||'NOTIFICATION_ID', to_char(n_nid)), 1,
2170                             32000);
2171     stxt := substrb(replace(stxt, '&'||'NOTIFICATION', n_code), 1, 32000);
2172     stxt := substrb(replace(stxt, '&'||'STATUS', n_status), 1, 32000);
2173     stxt := substrb(replace(stxt, '&'||'TO_DNAME', r_dname), 1, 32000);
2174     stxt := substrb(replace(stxt, '&'||'TO_EMAIL', r_email), 1, 32000);
2175     stxt := substrb(replace(stxt, '&'||'TO', n_to_role), 1, 32000);
2176     stxt := substrb(replace(stxt, '&'||'PRIORITY', n_priority_text), 1, 32000);
2177     stxt := substrb(replace(stxt, '&'||'START_DATE', n_start_date_text), 1, 32000);
2178     stxt := substrb(replace(stxt, '&'||'DUE_DATE', n_due_date_text), 1, 32000);
2179 
2180    -- stxt := substrb(replace(stxt, '&'||'END_DATE', n_end_date), 1, 32000);
2181 
2182     stxt := substrb(replace(stxt, '&'||'END_DATE', n_end_date_text), 1, 32000);
2183 
2184 
2185     -- Bug 2094159 substituting sender in email notification for From label
2186     stxt := substrb(replace(stxt, '&'||'SENDER', n_from_user), 1, 32000);
2187     stxt := substrb(replace(stxt, '&'||'COMMENT', n_comment), 1, 32000);
2188     stxt := substrb(replace(stxt, '&'||'TIMEZONE', n_timezone), 1, 32000);
2189     stxt := substrb(replace(stxt, '&'||'SUBJECT', m_subject), 1, 32000);
2190     stxt := substrb(replace(stxt, '&'||'HEADER', m_header), 1, 32000);
2191     stxt := substrb(replace(stxt, '&'||'BODY', m_body), 1, 32000);
2192     stxt := substrb(replace(stxt, '&'||'MAIL_ERROR_NAME', err_name), 1, 32000);
2193     stxt := substrb(replace(stxt, '&'||'MAIL_ERROR_MESSAGE', err_message),
2194         1, 32000);
2195     stxt := substrb(replace(stxt, '&'||'MAIL_VALUE_FOUND', err_invalid), 1, 32000);
2196     stxt := substrb(replace(stxt, '&'||'MAIL_EXP_VALUES', err_expected), 1, 32000);
2197 
2198     return stxt;
2199 end;
2200 
2201 -- GetWarning - get warning messages
2202 --
2203 -- IN
2204 --   Template
2205 --   unsolicited from
2206 --   unsolicited subject
2207 --   unsolicited body
2208 -- OUT
2209 --   message subject
2210 --   message body (text)
2211 --   message body (html)
2212 procedure GetWarning(
2213     template  in  varchar2,
2214     ufrom     in  varchar2,
2215     usubject  in  varchar2,
2216     ubody     in varchar2,
2217     subject   out NOCOPY varchar2,
2218     text_body_text out NOCOPY varchar2,
2219     html_body_text out NOCOPY varchar2)
2220 as
2221     t_subject     varchar2(240);
2222     t_text_body   varchar2(32000);
2223     t_html_body   varchar2(32000);
2224     l_pos integer;
2225     l_templ_val varchar2(1000);
2226     itemType varchar2(8);
2227     messageName varchar2(30);
2228 
2229 begin
2230 
2231     l_pos := instrb(template, ':', 1);
2232     if l_pos > 0 then
2233        itemType := substrb(template, 1, l_pos-1);
2234        messageName := substrb(template, l_pos+1);
2235     else
2236        itemType := 'WFMAIL';
2237        messageName := 'WARNING';
2238     end if;
2239 
2240     -- Get template 'WARNING'
2241     begin
2242       select SUBJECT, BODY, HTML_BODY
2243       into   t_subject, t_text_body, t_html_body
2244       from   WF_MESSAGES_VL
2245       where  NAME = messageName
2246       and    TYPE = itemType;
2247     exception
2248       when no_data_found then
2249         wf_core.token('NAME', messageName);
2250         wf_core.token('TYPE', itemType);
2251         wf_core.raise('WFNTF_MESSAGE');
2252     end;
2253 
2254     -- Substitute USER_NAME with role display name
2255     t_text_body := substrb(replace(t_text_body, '&'||'UFROM', ufrom), 1, 32000);
2256     t_text_body := substrb(replace(t_text_body, '&'||'USUBJECT',
2257                              nvl(usubject, ' ')), 1, 32000);
2258     t_text_body := substrb(replace(t_text_body, '&'||'UBODY',
2259                              nvl(ubody, ' ')), 1, 32000);
2260 
2261     -- Substitute USER_NAME with role display name
2262     t_html_body := substrb(replace(t_html_body, '&'||'UFROM', ufrom), 1, 32000);
2263     t_html_body := substrb(replace(t_html_body, '&'||'USUBJECT',
2264                              nvl(usubject, ' ')), 1, 32000);
2265     t_html_body := substrb(replace(t_html_body, '&'||'UBODY',
2266                              nvl(ubody, ' ')), 1, 32000);
2267 
2268     subject   := t_subject;
2269     text_body_text := t_text_body;
2270     html_body_text := t_html_body;
2271 
2272 exception
2273   when others then
2274     wf_core.context('WF_MAIL', 'GetWarning', ufrom, usubject, ubody);
2275     raise;
2276 end GetWarning;
2277 
2278 
2279 
2280 -- GetWarning - get warning messages
2281 --
2282 -- IN
2283 --   unsolicited from
2284 --   unsolicited subject
2285 --   unsolicited body
2286 -- OUT
2287 --   message subject
2288 --   message body (text)
2289 --   message body (html)
2290 procedure GetWarning(
2291     ufrom     in  varchar2,
2292     usubject  in  varchar2,
2293     ubody     in varchar2,
2294     subject   out NOCOPY varchar2,
2295     text_body_text out NOCOPY varchar2,
2296     html_body_text out NOCOPY varchar2)
2297 as
2298 begin
2299    wf_mail.GetWarning('WFMAIL:WARNING', ufrom, usubject, ubody, subject,
2300                       text_body_text, html_body_text);
2301 end GetWarning;
2302 
2303 -- GetTemplateName - Get the template type and name based on the
2304 --                   status of the notification and whether, or not,
2305 --                   the name has been overridden in the configuration
2306 --                   parameters or on the message definition itself.
2307 --
2308 -- IN
2309 --    Notification ID
2310 --    Notification status
2311 --    Notification Mail status
2312 -- OUT
2313 --    Item type for template
2314 --    Message name for template
2315 procedure getTemplateName(nid in number, n_status in varchar2,
2316                           n_mstatus in varchar2, t_type out NOCOPY varchar2,
2317                           t_name out NOCOPY varchar2)
2318 is
2319 
2320    colPos number;
2321    altTempl varchar2(100);
2322    fyi pls_integer;
2323    mType varchar2(8);
2324    mName varchar2(30);
2325    validTemplate pls_integer;
2326    inAttr varchar2(1);
2327 
2328 begin
2329     t_type := 'WFMAIL'; -- Set the default type;
2330     wf_mail.Set_FYI_Flag(FALSE);
2331 
2332     -- Get template name
2333     if (n_status = 'OPEN') then
2334         t_name := 'OPEN_'||n_mstatus;
2335     else
2336         t_name := n_status;
2337     end if;
2338 
2339     if (g_moreinfo = 'SUB' and n_mstatus in ('MAIL','INVALID')) then
2340        if (n_mstatus = 'MAIL') then
2341           t_name := 'OPEN_MORE_INFO';
2342        elsif(n_mstatus = 'INVALID') then
2343           t_name := 'OPEN_INVALID_MORE_INFO';
2344        end if;
2345     end if;
2346 
2347     -- Check if this is FYI type of message
2348     if (t_name = 'OPEN_MAIL') then
2349       begin
2350         select 1 into fyi
2351         from dual
2352         where not exists (select NULL
2353                   from WF_MESSAGE_ATTRIBUTES MA,
2354                        WF_NOTIFICATIONS N
2355                   where N.NOTIFICATION_ID = nid
2356                   and   MA.MESSAGE_TYPE = N.MESSAGE_TYPE
2357                   and   MA.MESSAGE_NAME = N.MESSAGE_NAME
2358                   and   MA.SUBTYPE = 'RESPOND');
2359         -- Set the template name to FYI
2360         t_name :=  t_name||'_FYI';
2361         wf_mail.Set_FYI_Flag(TRUE);
2362 
2363         exception
2364           when NO_DATA_FOUND then
2365              -- If a different mail template name is specified, it took
2366              -- precedence over direct response.
2367              if (g_template is not null) then
2368                 t_name := g_template;
2369 
2370              -- This is a response required notification.
2371              -- Qualify if DIRECT_RESPONSE=Y
2372              elsif wf_mail.direct_response then
2373                 t_name := t_name || '_DIRECT';
2374              end if;
2375         end;
2376     end if;
2377 
2378     select message_type, message_name
2379     into mType, mName
2380     from wf_notifications
2381     where notification_id = nid;
2382 
2383     -- Now that the template name has been derrived, see
2384     -- if the default value has been overridden.
2385     altTempl := WF_MAILER_PARAMETER.getValueForCorr(nid, mType || ':'|| mName, t_name, inAttr);
2386     colPos := instrb(altTempl, ':', 1);
2387     if colPos > 0 then
2388        t_type := substrb(altTempl, 1, colPos -1);
2389        t_name := substrb(altTempl, colPos + 1, length(altTempl)-colPos);
2390        -- 3438107 Validate the template name incase the value from the
2391        -- message attribute if used contains a typo
2392        begin
2393           select 1
2394           into validTemplate
2395           from WF_MESSAGES_VL
2396           where  NAME = t_name
2397             and TYPE = t_type;
2398        exception
2399           when NO_DATA_FOUND then
2400              wf_core.context('WF_MAIL','getTemplateName',
2401                              'nid => '||to_char(nid),
2402                              'n_status => '||n_status,
2403                              'n_mstatus => '||n_mStatus,
2404                              't_type => '||t_type,
2405                              't_name => '||t_name);
2406              wf_core.token('TYPE', t_type);
2407              wf_core.token('NAME', t_name);
2408              wf_core.raise('WFMLR_NOTEMPLATE');
2409        end;
2410     end if;
2411 
2412 exception
2413    when others then
2414       WF_CORE.Context('WF_MAIL','getTemplateName',to_char(nid), n_status,
2415                       n_mstatus, t_type, t_name);
2416       raise;
2417 end getTemplateName;
2418 
2419 -- ProcessSignaturePolicy (PRIVATE) Bug 2375920
2420 --   Processes mail message based on the signature policy requirement
2421 --   for the notification if the notification requires a response
2422 -- IN
2423 --   notification id
2424 --   signature policy for the notification
2425 --   notification status
2426 --   notification mail mstatus
2427 --   access key
2428 --   node
2429 -- OUT
2430 --   template name
2431 --   template type
2432 --   NID string
2433 procedure ProcessSignaturePolicy(
2434      nid               in  number,
2435      n_sig_policy      in  varchar2,
2436      n_status          in  varchar2,
2437      n_mstatus         in  varchar2,
2438      n_key             in  varchar2,
2439      node              in  varchar2,
2440      t_type            out NOCOPY varchar2,
2441      t_name            out NOCOPY varchar2,
2442      n_nid_str         out NOCOPY varchar2)
2443 as
2444   l_sec_policy   varchar2(100);
2445 begin
2446    -- signature policy is DEFAULT or NULL means no password is required
2447    -- to respond to the notification
2448    t_type := 'WFMAIL';
2449 
2450    -- If the content is secure, just dont send anything pertaining to the notification
2451    -- other than the nid.
2452    Wf_Mail.ProcessSecurityPolicy(nid, l_sec_policy, t_name);
2453    if (t_name is not null) then
2454       n_nid_str := 'NID '||to_char(nid);
2455       return;
2456    end if;
2457 
2458    Wf_Notification.GetSignatureRequired(n_sig_policy, nid, g_sig_required,
2459                                         g_fwk_flavor, g_email_flavor, g_render);
2460 
2461    -- No signature required for this notification
2462    if (g_sig_required = 'N') then
2463       getTemplateName(nid, n_status, n_mstatus, t_type, t_name);
2464 
2465       if (g_moreinfo = 'SUB' and n_mstatus in ('MAIL','INVALID')) then
2466          n_nid_str := 'NID['||to_char(nid)||'/'||n_key||'@'||node||']'||'[4]';
2467          return;
2468       end if;
2469 
2470 
2471       -- construct the NID string for the notification
2472       if (wf_mail.direct_response) then
2473          n_nid_str := 'NID['||to_char(nid)||'/'||n_key||'@'||node||']'||'[2]';
2474       else
2475          n_nid_str := 'NID['||to_char(nid)||'/'||n_key||'@'||node||']';
2476       end if;
2477    elsif (g_sig_required = 'Y') then
2478       -- OPEN_SIGN is for warning that email response will not be processed
2479       -- for notifications requiring password signature
2480       if (n_status = 'OPEN') then
2481          if (n_mstatus = 'INVALID') then
2482             t_name := 'OPEN_SIGN';
2483          else
2484             -- Template to inform the user that the ntf requires a signature
2485             -- ** not sure if signing through email will be supported in future **
2486             t_name := 'OPEN_MAIL_SIGNATURE';
2487          end if;
2488      else
2489          t_name := n_status;
2490      end if;
2491      n_nid_str := 'NID ' || to_char(nid);
2492    else
2493      wf_core.token('NID', to_char(nid));
2494      wf_core.token('POLICY', n_sig_policy);
2495      wf_core.raise('WFMLR_INVALID_SIG_POLICY');
2496    end if;
2497 
2498 exception
2499   when others then
2500     wf_core.context('WF_MAIL', 'ProcessSignaturePolicy', to_char(nid));
2501     raise;
2502 end ProcessSignaturePolicy;
2503 
2504 -- Returns TRUE if the language is Bi directional
2505 -- Provided as a function to centralise the management of determining
2506 -- the direction for the text.
2507 function isBiDi(lang in varchar2) return boolean
2508 is
2509 begin
2510    if upper(lang) in ('ARABIC','HEBREW') then
2511        return true;
2512    else
2513        return false;
2514    end if;
2515 end isBiDi;
2516 
2517 -- Gets the table of header attributes to be
2518 -- displayed before the body
2519 -- Introduced with BUG 2659681
2520 -- IN
2521 -- nid - Notification ID
2522 -- notification_pref The document type to be displayed
2523 procedure GetHeaderTable(document_id in varchar2,
2524                          display_type in varchar2,
2525                          document in out NOCOPY varchar2,
2526                          document_type in out NOCOPY varchar2)
2527 is
2528    cursor headers(nid in Number) is
2529    select WMA.NAME
2530    from WF_MESSAGE_ATTRIBUTES_VL WMA,
2531         WF_NOTIFICATION_ATTRIBUTES WNA,
2532         WF_NOTIFICATIONS WN
2533    where WNA.NOTIFICATION_ID = nid
2534      and WN.NOTIFICATION_ID = WNA.NOTIFICATION_ID
2535      and WN.MESSAGE_TYPE = WMA.MESSAGE_TYPE
2536      and WN.MESSAGE_NAME = WMA.MESSAGE_NAME
2537      and WMA.NAME = WNA.NAME
2538      and WMA.NAME like '#HDR%'
2539      and (WNA.TEXT_VALUE is not null OR
2540           WNA.NUMBER_VALUE is not null OR
2541           WNA.DATE_VALUE is not null)
2542    order by WMA.SEQUENCE;
2543 
2544    nid number;
2545    attrList varchar2(4000);
2546    cells wf_notification.tdType;
2547    j pls_integer;
2548    pos pls_integer;
2549    language varchar2(30);
2550    headerTable varchar2(32000);
2551    l_dir varchar2(2);
2552    l_dirAttr varchar2(10);
2553 
2554    l_due_date  date;
2555    l_from_user varchar2(360);
2556 begin
2557 
2558    pos := instrb(document_id, ':',1);
2559    language := substrb(document_id, 1, pos-1);
2560    nid := to_number(substrb(document_id, pos+1));
2561    if isBiDi(language) then
2562       l_dir := 'R';
2563       l_dirAttr := 'dir="RTL"';
2564    else
2565       l_dir := 'L';
2566       l_dirAttr := NULL;
2567    end if;
2568 
2569    begin
2570      SELECT due_date, from_user
2571      INTO   l_due_date, l_from_user
2572      FROM   wf_notifications
2573      WHERE  notification_id = nid;
2574    exception
2575      when no_data_found then
2576         wf_core.token('NID', to_char(nid));
2577         wf_core.raise('WFNTF_NID');
2578    end;
2579 
2580    -- Cache the values of the product notification headers.
2581    attrList := '';
2582    for header in headers(nid) loop
2583       attrList := attrList || header.name || ',';
2584    end loop;
2585    if length(attrList) > 0 then
2586       attrList := substrb(attrList,1, length(attrList)-1);
2587    end if;
2588 
2589    document := '';
2590    if display_type = g_ntfDocText then
2591       if length(attrList) > 0 then
2592          document := wf_notification.wf_msg_attr(nid, attrList, display_type);
2593       end if;
2594    elsif display_type = g_ntfDocHtml then
2595       document := '<TABLE width=100% valign="top" cellpadding="0" cellspacing="0" border="0" '||l_dirAttr||'>'||g_newLine;
2596       document := document || '<TR valign="top"><TD width=30%>';
2597       j := 1;
2598       if (l_from_user is not null) then
2599          cells(j) :=  'E:'||g_from;
2600          j := j + 1;
2601          cells(j) := 'S12:';
2602          j := j + 1;
2603          cells(j) := 'S:'||'&'||'SENDER';
2604          j := j + 1;
2605       end if;
2606 
2607       cells(j) :=  'E:'||g_to;
2608       j := j + 1;
2609       cells(j) := 'S12:';
2610       j := j + 1;
2611       -- sacsharm bug 2897428 fix
2612       -- cells(j) := 'S:'||'&'||'TO';
2613       cells(j) := 'S:'||'&'||'TO_DNAME';
2614       j := j + 1;
2615 
2616       cells(j) :=  'E:'||g_beginDate;
2617       j := j + 1;
2618       cells(j) := 'S12:';
2619       j := j + 1;
2620       cells(j) := 'S:'||'&'||'START_DATE';
2621       j := j + 1;
2622 
2623       if (l_due_date is not null) then
2624          cells(j) :=  'E:'||g_dueDate2;
2625          j := j + 1;
2626          cells(j) := 'S12:';
2627          j := j + 1;
2628          cells(j) := 'S:'||'&'||'DUE_DATE';
2629          j := j + 1;
2630       end if;
2631 
2632       cells(j) :=  'E:'||g_Id;
2633       j := j + 1;
2634       cells(j) := 'S12:';
2635       j := j + 1;
2636       cells(j) := 'S:'||'&'||'NOTIFICATION_ID';
2637       wf_notification.NTF_Table(cells => cells, col => 3, type => 'N'||l_dir,
2638                                 rs => headerTable);
2639 
2640      document := document || headerTable || g_newLine;
2641      document := document || '</TD><TD>' || g_newLine;
2642       if length(attrList) > 0 then
2643          wf_notification.set_ntf_table_type('N');
2644          wf_notification.set_ntf_table_direction(l_dir);
2645          document := document||wf_notification.wf_msg_attr(nid, attrList, display_type);
2646          wf_notification.set_ntf_table_type('V');
2647       end if;
2648       document := document || '</TD></TR></TABLE>'||g_newLine;
2649    else
2650       document := '';
2651    end if;
2652    document_type := display_type;
2653 
2654 exception
2655    when others then
2656       wf_core.context('WF_MAIL','GetHeaderTable',document_id, display_type);
2657       raise;
2658 end GetHeaderTable;
2659 
2660 
2661 -- GetMessage - get email message data
2662 --
2663 -- IN
2664 --   notification id
2665 --   mailer node name
2666 --   web agent path
2667 -- OUT
2668 --   message subject
2669 --   message body (text)
2670 --   message body (html)
2671 -- NOTE
2672 -- This API was used by the C mailer which is now obsolete. This
2673 -- procedure is now considered deprecated.
2674 procedure GetMessage(
2675     nid       in  number,
2676     node      in  varchar2,
2677     agent     in  varchar2,
2678     replyto   in  varchar2,
2679     subject   out NOCOPY varchar2,
2680     text_body out NOCOPY varchar2,
2681     html_body out NOCOPY varchar2,
2682     body_atth out NOCOPY varchar2,
2683     error_result in out NOCOPY varchar2)
2684 as
2685     n_status      varchar2(8);
2686     n_mstatus     varchar2(8);
2687     n_key         varchar2(80);
2688     n_to_role     varchar2(320);
2689     n_from_user   varchar2(320);  -- Bug 2094159
2690     n_due_date    date;
2691     n_start_date  date;
2692     n_end_date    date;
2693     n_priority    number;
2694     n_comment     varchar2(4000);
2695     n_subject     varchar2(2000);
2696     n_response    varchar2(32000);
2697     n_text_body   varchar2(32000);
2698     n_html_body   varchar2(32000);
2699     n_direct      varchar2(3);
2700     n_click_here  varchar2(4000);
2701     n_disp_click  varchar2(240);
2702     r_dname       varchar2(360);
2703     r_email       varchar2(2000);
2704     r_language    varchar2(4000);
2705     r_territory   varchar2(4000);
2706     r_ntf_pref    varchar2(240);
2707     t_type        varchar2(100);
2708     t_name        varchar2(100);
2709     t_subject     varchar2(240);
2710     t_text_body   varchar2(4000);
2711     t_html_body   varchar2(4000);
2712     t_headerText  varchar2(32000);
2713     n_headerText  varchar2(32000);
2714     n_timezone    varchar2(230);
2715     t_headerHTML  varchar2(32000);
2716     n_headerHTML  varchar2(32000);
2717     m_html        varchar2(32000);
2718     err_name      varchar2(30);
2719     err_message   varchar2(2000);
2720     err_stack     varchar2(4000);
2721     fyi           pls_integer;
2722     body_start    pls_integer;
2723     body_end      pls_integer;
2724     tag_pos       pls_integer;
2725     dir_pos       pls_integer;
2726     start_cnt     pls_integer;
2727     end_cnt       pls_integer;
2728     crpos         pls_integer;
2729     str_length    pls_integer;
2730     lnsize        pls_integer;
2731     temp          varchar2(32000);
2732     line          varchar2(32000);
2733     buffer        varchar2(32000);
2734     no_program_unit exception;
2735     pragma exception_init(no_program_unit, -6508);
2736     dummy         varchar2(4000);
2737     -- Bug# 2301881 variables to handle invalid response error message
2738     err_invalid   varchar2(1000);
2739     err_expected  varchar2(1000);
2740     -- Bug 2395898 variable to check if response attr exists
2741     n_response_exists varchar2(1);
2742     -- Bug 2375920 variables to process message based on signature
2743     n_sig_policy     varchar2(100);
2744     n_nid_str        varchar2(200);
2745     -- bug 2282139 more info feature
2746     n_more_info_role varchar2(320);
2747     n_mailto         varchar2(10000);
2748     n_html_history   varchar2(32000);
2749     n_text_history   varchar2(32000);
2750     n_last_ques      varchar2(4000);
2751     n_dir            varchar2(16);
2752 
2753 begin
2754     -- Get notification information
2755     -- Bug 2094159 get from_user from wf_notifications
2756     begin
2757       select STATUS, MAIL_STATUS, ACCESS_KEY,
2758              RECIPIENT_ROLE, PRIORITY, USER_COMMENT,
2759              BEGIN_DATE, END_DATE, DUE_DATE, FROM_USER,
2760              MORE_INFO_ROLE
2761       into   n_status, n_mstatus, n_key,
2762              n_to_role, n_priority, n_comment,
2763              n_start_date, n_end_date, n_due_date, n_from_user,
2764              n_more_info_role
2765       from   WF_NOTIFICATIONS
2766       where  NOTIFICATION_ID = nid;
2767     exception
2768       when no_data_found then
2769         wf_core.token('NID', to_char(nid));
2770         wf_core.raise('WFNTF_NID');
2771     end;
2772 
2773     -- More information processing - bug 2282139
2774     g_moreinfo := NULL;
2775 
2776     if (wf_mail.test_flag = TRUE) then
2777        n_mstatus := 'MAIL';
2778        if (n_status not in ('OPEN','CANCELED','CLOSED')) then
2779           n_status := 'OPEN';
2780        end if;
2781     end if;
2782 
2783     -- g_to_role global variable is to identify to whom the email is addressed
2784     -- when contructing the More Info MAILTO, so that the role name is not displayed
2785     -- among the participants
2786     -- Timezone will not be supported in this version of the GetMessage
2787     -- API.
2788     n_timezone := '';
2789 
2790     if (wf_notification.HideMoreInfo(nid) = 'N') then
2791        if(n_more_info_role is not null) then
2792           n_to_role := n_more_info_role;
2793           g_to_role := n_more_info_role;
2794           g_moreinfo := 'SUB';
2795        else
2796           g_to_role := n_to_role;
2797           g_moreinfo := 'REQ';
2798        end if;
2799     end if;
2800 
2801     -- Get Recipient information
2802     Wf_Directory.GetRoleInfo(n_to_role, r_dname, r_email, r_ntf_pref,
2803                              r_language, r_territory);
2804     r_ntf_pref := nvl(r_ntf_pref, 'QUERY');
2805 
2806     wf_notification.GetComments(nid, g_ntfDocText,
2807                                 n_text_history, n_last_ques);
2808     wf_notification.GetComments(nid, g_ntfDocHtml,
2809                                 n_html_history, n_last_ques);
2810     if n_text_history is not null or n_text_history <> '' then
2811        if isBiDi(r_language) then
2812           n_text_history := n_text_history||' '|| g_ntfHistory;
2813        else
2814           n_text_history := g_ntfHistory||' '|| n_text_history;
2815        end if;
2816     end if;
2817     if n_html_history is not null or n_html_history <> '' then
2818        if isBiDi(r_language) then
2819           n_dir := 'dir="rtl" ';
2820        else
2821           n_dir := '';
2822        end if;
2823 
2824        n_html_history := '<table '||n_dir||
2825                          'bgcolor="'||table_bgcolor||'" width="'||
2826                          table_width||'" cellpadding="'||
2827                          table_cellpadding||'" cellspacing="'||
2828                          table_cellspacing||'" >'||
2829                          '<tr valign="top"><td width="10%">'||
2830                          '<font face="'||
2831                          td_fontface||'" size="'||td_fontsize||'" >'||
2832                          g_ntfHistory||
2833                          '</font></td><td>'|| n_html_history ||
2834                          '</td></tr></table>'||g_newLine;
2835     end if;
2836 
2837     -- Bug 2375920 get signature policy for the notification
2838     Wf_Mail.GetSignaturePolicy(nid, n_sig_policy);
2839 
2840     n_subject := WF_NOTIFICATION.GetSubject(nid, 'text/plain');
2841 
2842     -- We will always fetch plain text version of the message because
2843     -- Because for sendmail MAILATTH case, we need to send out html message
2844     -- body as attachment and then the plain text message as the body.
2845     -- For MAPI MAILATTH and MAILHTML cases, same thing.
2846     if isBiDi(r_language) then
2847        WF_NOTIFICATION.Set_NTF_Table_Direction('R');
2848     else
2849        WF_NOTIFICATION.Set_NTF_Table_Direction('L');
2850     end if;
2851 
2852     n_text_body := WF_NOTIFICATION.GetBody(nid, g_ntfDocText);
2853 
2854     GetHeaderTable(r_language||':'||to_char(nid), g_ntfDocText,
2855                    t_headerText, dummy);
2856 
2857     if r_ntf_pref in ('MAILHTML', 'MAILATTH', 'MAILHTML2') then
2858         n_html_body := WF_NOTIFICATION.GetBody(nid, g_ntfDocHtml);
2859         GetHeaderTable(r_language||':'||to_char(nid),
2860                         g_ntfDocHtml, t_headerHTML, dummy);
2861 
2862         -- Extracts content between <BODY> and </BODY> if there is body tag
2863         -- This is to deal with people import the whole html file to the
2864         -- html message body through the builder.
2865         -- The logic here is that if we don't see <BODY> and </BODY>, then
2866         -- this is already just a html <BODY> portion. Otherwise, extract
2867         -- the content in between <BODY> and </BODY>.
2868         body_start := 0;
2869         body_start := instr(upper(n_html_body), '<BODY>');
2870         if (body_start <> 0) then
2871             body_start := body_start + length('<BODY>');
2872             body_end := instr(upper(n_html_body), '</BODY>');
2873             if (body_end = 0) then
2874                 body_end := length(n_html_body);
2875             else
2876                 body_end := body_end - 1;
2877             end if;
2878             n_html_body := substr(n_html_body, body_start, body_end);
2879         end if;
2880         --
2881         -- For every 900 character, we insert a newline just in case
2882         -- Because this whole message body may go out to the Unix SMTP gateway
2883         -- which does not like a line longer than 1000 characters.
2884         -- We do it 900 here just for safty.
2885         -- 2001/03/23 Changed algorithm to start at 900 point and then
2886         -- move to the nearest whitespace.
2887         --
2888         lnsize := 900;
2889         start_cnt := 1;
2890         end_cnt := lnsize;
2891         temp := '';
2892         str_length := length(n_html_body);
2893         while start_cnt < str_length loop
2894            -- use the existing newlines as a natural break
2895            crpos := instr(n_html_body, g_newLine, start_cnt+1, 1) -
2896                         start_cnt;
2897            if crpos > 0 and crpos < end_cnt then
2898               end_cnt := crpos;
2899            else
2900 
2901               -- Move forward to the next white space.
2902               while (start_cnt + end_cnt < str_length) and
2903                     substr(n_html_body, start_cnt + end_cnt, 1) not in
2904                     (' ', g_newLine, g_tab)
2905                     and end_cnt < 999 loop
2906                  end_cnt := end_cnt + 1;
2907               end loop;
2908 
2909               -- We need to understand the full conditions underwhich
2910               -- the previous loop exited. All characters must be preserved
2911               -- and the line, no matter what can not exceed 900 characters.
2912 
2913               if end_cnt >= (999) then
2914                 end_cnt := lnsize;
2915                 while (start_cnt + end_cnt > start_cnt) and
2916                     substr(n_html_body, start_cnt + end_cnt, 1) not in
2917                           (' ', g_newLine, g_tab)
2918                     and end_cnt > 0 loop
2919                    end_cnt := end_cnt - 1;
2920                 end loop;
2921                 -- If we can not locate a white space, then use the default
2922                 if end_cnt <= 0 then
2923                    end_cnt := lnsize;
2924                 end if;
2925               end if;
2926            end if;
2927 
2928            -- Ensure the last characters are not lost.
2929            if start_cnt + end_cnt >= str_length then
2930               line := substr(n_html_body, start_cnt);
2931            else
2932               line := substr(n_html_body, start_cnt, end_cnt);
2933            end if;
2934 
2935            temp := temp || line;
2936 
2937            -- If there is a newline at this point,
2938            -- then do not bother with another.
2939            if substr(n_html_body, start_cnt + end_cnt, 1) <>
2940                      g_newLIne then
2941               temp := temp||g_newLine;
2942            end if;
2943 
2944            -- We do not want to start the new line with the space.
2945            if substr(n_html_body, start_cnt + end_cnt, 1) = ' ' then
2946               start_cnt := start_cnt + end_cnt + 1;
2947            else
2948               start_cnt := start_cnt + end_cnt;
2949            end if;
2950            end_cnt := lnsize;
2951         end loop;
2952 
2953         n_html_body := temp;
2954 
2955     end if;
2956 
2957     -- Bug 2375920 Process the email message based on the signature policy
2958     ProcessSignaturePolicy(nid, n_sig_policy, n_status, n_mstatus,
2959                            n_key, node, t_type, t_name, n_nid_str);
2960 
2961     -- Get template
2962     begin
2963       select SUBJECT, BODY, HTML_BODY
2964       into   t_subject, t_text_body, t_html_body
2965       from   WF_MESSAGES_VL
2966       where  NAME = t_name and TYPE = t_type;
2967     exception
2968       when no_data_found then
2969         wf_core.token('NAME', t_name);
2970         wf_core.token('TYPE', t_type);
2971         wf_core.raise('WFNTF_MESSAGE');
2972     end;
2973 
2974     -- Get Click here Response display value
2975     begin
2976       select DESCRIPTION
2977         into n_disp_click
2978         from WF_MESSAGE_ATTRIBUTES_TL
2979        where MESSAGE_TYPE = t_type
2980          and MESSAGE_NAME = t_name
2981          and NAME = 'CLICK_HERE_RESPONSE'
2982          and LANGUAGE = userenv('LANG');
2983     exception
2984       when NO_DATA_FOUND then
2985         -- ignore if this attribute does not exist
2986         null;
2987     end;
2988 
2989     -- Retrieve errror attributes for INVALID message
2990     if (t_name = 'OPEN_INVALID') then
2991       begin
2992         err_name := Wf_Notification.GetAttrText(nid, 'MAIL_ERROR_NAME');
2993         err_message := Wf_Notification.GetAttrText(nid, 'MAIL_ERROR_MESSAGE');
2994         err_invalid := Wf_Notification.GetAttrText(nid, 'MAIL_VALUE_FOUND');
2995         err_expected := Wf_Notification.GetAttrText(nid, 'MAIL_EXP_VALUES');
2996       exception
2997         when others then null;
2998       end;
2999     end if;
3000 
3001     -- If there is no html template available, use the plain text one.
3002     if (t_html_body is null) then
3003       t_html_body := replace(t_text_body, g_newLine,
3004                              '<BR>'||g_newLine);
3005       -- Ensure the direction of the text is correct for the language
3006       if isBiDi(r_language) then
3007          t_html_body := '<HTML DIR="RTL"><BODY>'||t_html_body;
3008       else
3009          t_html_body := '<HTML><BODY>'||t_html_body;
3010       end if;
3011     else
3012       -- Ensure that the direction of the text is correctly specified.
3013       if isBiDi(r_language) then
3014          tag_pos := instrb(upper(t_html_body), '<HTML', 1);
3015          if tag_pos > 0 then
3016            dir_pos := instrb(upper(t_html_body), ' DIR="', 1);
3017            if dir_pos = 0 then
3018               buffer := substrb(t_html_body, 1, 5);
3019               buffer := buffer||' DIR="RTL" '||substrb(t_html_body, tag_pos+5);
3020               t_html_body := buffer;
3021            end if;
3022          end if;
3023       end if;
3024     end if;
3025 
3026     -- Substitute
3027     if wf_mail.direct_response then
3028        n_direct := '[2]';
3029     else
3030        n_direct := NULL;
3031     end if;
3032 
3033     -- Bug# 2301881 replacing err_stack with err_invalid and err_expected
3034     -- to make the WARNING message to the responder more user-friendly
3035     n_subject := Substitute(t_subject, nid, n_nid_str,
3036                             n_status, n_to_role, r_dname, r_email,
3037                             n_start_date, n_due_date, n_end_date, n_from_user,
3038                             n_priority, n_comment, n_subject, dummy,
3039                             dummy, err_name, err_message, err_invalid,
3040                             err_expected, n_timezone);
3041     n_headerText := Substitute(t_headerText, nid, n_nid_str,
3042                             n_status, n_to_role, r_dname, r_email,
3043                             n_start_date, n_due_date, n_end_date, n_from_user,
3044                             n_priority, n_comment, n_subject, dummy,
3045                             dummy, err_name, err_message, err_invalid,
3046                             err_expected, n_timezone);
3047     n_headerHTML := Substitute(t_headerText, nid, n_nid_str,
3048                             n_status, n_to_role, r_dname, r_email,
3049                             n_start_date, n_due_date, n_end_date, n_from_user,
3050                             n_priority, n_comment, n_subject, dummy,
3051                             dummy, err_name, err_message, err_invalid,
3052                             err_expected, n_timezone);
3053     n_text_body    := Substitute(t_text_body, nid, n_nid_str,
3054                             n_status, n_to_role, r_dname, r_email,
3055                             n_start_date, n_due_date, n_end_date, n_from_user,
3056                             n_priority, n_comment, n_subject, n_headerText,
3057                             n_text_body, err_name, err_message, err_invalid,
3058                             err_expected, n_timezone);
3059     n_html_body    := Substitute(t_html_body, nid, n_nid_str,
3060                             n_status, n_to_role, r_dname, r_email,
3061                             n_start_date, n_due_date, n_end_date, n_from_user,
3062                             n_priority, n_comment,
3063                             UrlEncode(n_subject), n_headerHTML, n_html_body,
3064                             err_name, err_message, err_invalid,
3065                             err_expected, n_timezone);
3066 
3067     -- Wrap the body into nice pretty lines.
3068     if (r_ntf_pref in ('MAILTEXT', 'MAILATTH')) then
3069         n_text_body := WordWrap(n_text_body, 0);
3070     end if;
3071 
3072     if (g_moreinfo = 'SUB') then
3073        n_response := g_moreInfoAPrompt || ': "<' ||
3074                      g_moreInfoAnswer ||'>"';
3075        n_response := n_response || g_newLine;
3076     else
3077        if wf_mail.direct_response then
3078           n_response :=  GetEmailDirectResponse(nid);
3079        else
3080           n_response :=  GetEmailResponse(nid);
3081        end if;
3082     end if;
3083 
3084     -- make sure total length will not exceed 32K
3085     -- if it does truncate the body, leaving room for truncation string
3086     if (length(n_text_body) + length(n_response)) > 32000 then
3087       n_text_body := substr(n_text_body, 1, 31900  - length(n_response))
3088                 ||g_newLine|| g_truncate;
3089     end if;
3090 
3091 
3092     -- Add email response section
3093     if instr(n_text_body,'&'||'RESPONSE')> 0 then
3094        n_text_body := substrb(replace(n_text_body, '&'||'RESPONSE', n_response),
3095                      1, 32000);
3096     else
3097       --  Fix for bug 2395898 - do not append the response when no token
3098       --  Check to see if the response is included in the template
3099           begin
3100               select 'Y'
3101               into n_response_exists
3102               from WF_MESSAGES_VL
3103               where NAME = t_name and TYPE = 'WFMAIL'
3104               and instr(body,'&'||'RESPONSE')<>0;
3105            exception
3106               when no_data_found then
3107                  n_response_exists := 'N';
3108            end;
3109 
3110        -- we must have truncated the token, so just append the response
3111         if (n_response_exists = 'Y') then
3112               n_text_body := n_text_body||n_response;
3113         end if;
3114       end if;
3115 
3116     -- repeat for html body
3117     if (length(n_html_body) + length(n_response)) > 32000 then
3118       n_html_body := substr(n_html_body, 1, 31900  - length(n_response))
3119                 ||g_newLine||
3120                 g_truncate;
3121     end if;
3122 
3123     -- Add email response section
3124     if instr(n_html_body,'&'||'RESPONSE')> 0 then
3125        n_html_body := substrb(replace(n_html_body, '&'||'RESPONSE', n_response),
3126                      1, 32000);
3127     end if;
3128 
3129     -- More Information processing - adding history of questions and answers
3130     -- to outbound notifications
3131     if instr(n_html_body,'&'||'QUESTION')> 0 then
3132        n_html_body := substrb(replace(n_html_body, '&'||'QUESTION', n_last_ques),
3133                      1, 32000);
3134     end if;
3135     if instr(n_text_body,'&'||'QUESTION')> 0 then
3136        n_text_body := substrb(replace(n_text_body, '&'||'QUESTION', n_last_ques),
3137                      1, 32000);
3138     end if;
3139     if instr(n_html_body,'&'||'HISTORY')> 0 then
3140        n_html_body := substrb(replace(n_html_body, '&'||'HISTORY', n_html_history),
3141                      1, 32000);
3142     end if;
3143     if instr(n_text_body,'&'||'HISTORY')> 0 then
3144        n_text_body := substrb(replace(n_text_body, '&'||'HISTORY', n_text_history),
3145                      1, 32000);
3146     end if;
3147 
3148     -- Add mailto section
3149     -- when template used is OPEN_MOREINFO, providing a template to submit
3150     -- more information
3151     if (g_moreinfo = 'SUB') then
3152        n_mailto := GetMoreInfoMailTo(nid, 'NID['||to_char(nid)||'/'||n_key
3153                                     ||'@'||node||']',replyto, n_subject);
3154     else
3155        n_mailto := GetMailTo(nid, 'NID['||to_char(nid)||'/'||n_key||'@'||node||']',
3156                              replyto, n_subject);
3157     end if;
3158     n_html_body := substrb(replace(n_html_body, '&'||'MAILTO', n_mailto), 1, 32000);
3159 
3160     -- Add click_here_response section
3161     n_click_here := '<A class="OraLink" HREF="';
3162     if (agent is null) then
3163        if wf_mail.send_accesskey then
3164           n_click_here := n_click_here||g_webAgent
3165                           ||'/WFA_HTML.DetailLink?nid='||to_char(nid)
3166                           ||'&'||'nkey='||n_key
3167                           ||'&'||'agent='||g_webAgent;
3168        else
3169           n_click_here := n_click_here||g_webAgent
3170                           ||'/'||wfa_sec.DirectLogin(nid);
3171        end if;
3172     else
3173        if wf_mail.send_accesskey then
3174           n_click_here := n_click_here||agent
3175                           ||'/WFA_HTML.DetailLink?nid='||to_char(nid)
3176                           ||'&'||'nkey='||n_key
3177                           ||'&'||'agent='||agent;
3178        else
3179           n_click_here := n_click_here||agent
3180                           ||'/'||wfa_sec.DirectLogin(nid);
3181        end if;
3182     end if;
3183     n_click_here := n_click_here||'">'||n_disp_click||'</A>';
3184 
3185     n_html_body := substrb(replace(n_html_body, '&'||'CLICK_HERE_RESPONSE',
3186                             n_click_here), 1, 32000);
3187 
3188     -- Get HTML attachment
3189     if (agent is null) then
3190        m_html := substrb(WFA_HTML.Detail2(nid, n_key, g_webAgent), 1, 32000);
3191     else
3192          m_html := substrb(WFA_HTML.Detail2(nid, n_key, agent), 1, 32000);
3193     end if;
3194 
3195      if isBiDi(r_language) then
3196         tag_pos := instrb(upper(m_html), '<HTML', 1);
3197         if tag_pos > 0 then
3198           dir_pos := instrb(upper(m_html), ' DIR="', 1);
3199           if dir_pos = 0 then
3200              buffer := substrb(m_html, 1, 5);
3201              buffer := buffer||' DIR="RTL" '||substrb(m_html, tag_pos+5);
3202              m_html := buffer;
3203           end if;
3204         end if;
3205      end if;
3206 
3207      -- Close of the HTML Body only where this is none
3208      if instr(n_html_body, '</BODY>') = 0 then
3209         n_html_body := n_html_body || '</BODY></HTML>';
3210      end if;
3211 
3212 
3213     subject   := n_subject;
3214     text_body := n_text_body;
3215     html_body := n_html_body;
3216     -- this is for the little attachment to the detail frame
3217     body_atth := m_html;
3218 
3219 exception
3220   when no_program_unit then
3221     wf_core.context('WF_MAIL', 'GetMessage', to_char(nid), node);
3222     raise;
3223   when others then
3224     -- First look for a wf_core error.
3225     wf_core.get_error(err_name, err_message, err_stack);
3226 
3227     -- If no wf_core error look for a sql error.
3228     if (err_name is null) then
3229         err_message := sqlerrm;
3230     end if;
3231 
3232     error_result := err_message||g_newLine||err_stack;
3233     wf_core.context('WF_MAIL', 'GetMessage', to_char(nid), node, error_result);
3234 
3235 end GetMessage;
3236 
3237 -- LOBReplace
3238 -- To replace the given token in message with the token value
3239 -- IN
3240 -- message The CLOB message containing the tokens
3241 -- token   The varchar token, complete with '&' prefix
3242 -- tokenValue The varchar value to substitute for the token
3243 -- append The boolean flag to say if the token was NOT found, then
3244 --        append the tokenValue regardless
3245 --
3246 procedure LOBReplace(message IN OUT NOCOPY CLOB,
3247                      token IN VARCHAR2,
3248                      tokenValue IN VARCHAR2,
3249                      append IN BOOLEAN)
3250 is
3251   -- temp CLOB;
3252   tempIdx pls_integer;
3253   pos NUMBER;
3254   msgLen NUMBER;
3255   amount  NUMBER;
3256 
3257   continue boolean;
3258   offset number;
3259   sourcePos number;
3260   targetPos number;
3261   tokenLen pls_integer;
3262 
3263   nextChar varchar2(10);
3264   validToken boolean;
3265   xSet varchar2(100) := 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'||
3266                         '0123456789_';
3267   tokenFound boolean;
3268 
3269 begin
3270 
3271    -- DBMS_LOB.CreateTemporary(temp, TRUE, DBMS_LOB.CALL);
3272    tempIdx := -1;
3273 
3274    offset := 1;
3275    sourcePos := 1;
3276    targetPos := 1;
3277    continue := TRUE;
3278    msgLen := DBMS_LOB.GetLength(message);
3279    tokenLen := length(token);
3280    tokenFound := FALSE;
3281 
3282    while continue
3283    loop
3284 
3285       pos := DBMS_LOB.Instr(message, token, offset, 1);
3286       if (pos <> 0) then
3287          if (pos + tokenLen <= msgLen) then
3288             nextChar := upper(dbms_lob.substr(message, 1, pos + tokenLen));
3289             if (instr(xSet, nextChar,1,1) > 0) then
3290                validToken := false;
3291             else
3292                validToken := true;
3293             end if;
3294          else
3295             validToken := true;
3296          end if;
3297       else
3298          validToken := false;
3299          continue := FALSE;
3300       end if;
3301 
3302       if continue then
3303          if validToken then
3304             -- Only request a LOB if it is necessary and only if one
3305             -- has not already been requested.
3306             if tempIdx = -1 then
3307                tempIdx := wf_temp_lob.getLob(g_LOBTable);
3308             end if;
3309             amount := pos - sourcePos;
3310             DBMS_LOB.Copy(dest_lob => g_LOBTable(tempIdx).temp_lob,
3311                           src_lob => message,
3312                           amount => amount,
3313                           dest_offset => targetPos,
3314                           src_offset => sourcePos);
3315             if (tokenValue <> '' or tokenValue is not null) then
3316                DBMS_LOB.WriteAppend(g_LOBTable(tempIdx).temp_lob,
3317                                     length(tokenValue), tokenValue);
3318             end if;
3319             sourcePos := pos + tokenLen;
3320             targetPos := DBMS_LOB.GetLength(g_LOBTable(tempIdx).temp_lob) + 1;
3321             tokenFound := TRUE;
3322          end if;
3323          offset := pos + tokenLen;
3324       else
3325          amount := msgLen - sourcePos +1;
3326          if (amount > 0)then
3327             if tempIdx = -1 then
3328                tempIdx := wf_temp_lob.getLob(g_LOBTable);
3329             end if;
3330             DBMS_LOB.Copy(g_LOBTable(tempIdx).temp_lob, message, amount,
3331                           targetPos, sourcePos);
3332          end if;
3333 
3334          if (append and tokenFound = FALSE and msgLen > 0) then
3335             if (tokenValue <> '' or tokenValue is not null) then
3336                if tempIdx = -1 then
3337                   tempIdx := wf_temp_lob.getLob(g_LOBTable);
3338                end if;
3339                DBMS_LOB.WriteAppend(g_LOBTable(tempIdx).temp_lob,
3340                                     length(tokenValue), tokenValue);
3341             end if;
3342          end if;
3343       end if; -- CONTINUE
3344 
3345    end loop;
3346 
3347    if tokenFound = TRUE then
3348       msgLen := DBMS_LOB.GetLength(g_LOBTable(tempIdx).temp_lob);
3349       DBMS_LOB.Trim(message, 0);
3350       DBMS_LOB.Copy(message, g_LOBTable(tempIdx).temp_lob, msgLen);
3351    end if;
3352 
3353    -- << sstomar bug 6511028 >> Release allocated TEMP LOb.
3354    if tempIdx <> -1 then
3355       wf_temp_lob.releaseLob(g_LOBTable, tempIdx);
3356    end if;
3357 
3358 exception
3359    when others then
3360       wf_temp_lob.releaseLob(g_LOBTable, tempIdx);
3361       WF_CORE.Context('WF_MAIL','LOBReplace',token,
3362                       tokenValue);
3363       raise;
3364 
3365 end LOBReplace;
3366 
3367 -- LOBReplace
3368 -- To replace the given token in message with the token value
3369 -- IN
3370 -- message The CLOB message containing the tokens
3371 -- token   The varchar token, complete with '&' prefix
3372 -- tokenValue The CLOB value to substitute for the token
3373 -- append The boolean flag to say if the token was NOT found, then
3374 --        append the tokenValue regardless
3375 --
3376 procedure LOBReplace(message IN OUT NOCOPY CLOB,
3377                      token IN VARCHAR2,
3378                      tokenValue IN OUT NOCOPY CLOB,
3379                      append IN BOOLEAN)
3380 is
3381   -- temp CLOB;
3382   tempIdx pls_integer;
3383   pos NUMBER;
3384   msgLen NUMBER;
3385   amount NUMBER;
3386   tokenValueLen number;
3387 
3388   continue boolean;
3389   offset number;
3390   sourcePos number;
3391   targetPos number;
3392   tokenLen pls_integer;
3393 
3394   nextChar varchar2(10);
3395   validToken boolean;
3396   xSet varchar2(100) := 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'||
3397                         '0123456789_';
3398   tokenFound boolean;
3399 
3400 begin
3401    -- DBMS_LOB.CreateTemporary(temp, FALSE, DBMS_LOB.CALL);
3402    tempIdx := wf_temp_lob.getLob(g_LOBTable);
3403 
3404    offset := 1;
3405    sourcePos := 1;
3406    targetPos := 1;
3407    continue := TRUE;
3408    msgLen := DBMS_LOB.GetLength(message);
3409    tokenLen := length(token);
3410    tokenFound := FALSE;
3411 
3412    while continue
3413    loop
3414       pos := DBMS_LOB.Instr(message, token, offset, 1);
3415 
3416       if pos <> 0 then
3417          if (pos + tokenLen <= msgLen) then
3418             nextChar := upper(dbms_lob.substr(message, 1, pos + tokenLen));
3419             if (instr(xSet, nextChar,1,1) > 0) then
3420                validToken := false;
3421             else
3422                validToken := true;
3423             end if;
3424          else
3425             validToken := true;
3426          end if;
3427       else
3428          validToken := false;
3429          continue := FALSE;
3430       end if;
3431 
3432       if continue then
3433          if validToken then
3434             amount := pos - sourcePos;
3435             DBMS_LOB.Copy(g_LOBTable(tempIdx).temp_lob, message, amount,
3436                           targetPos, sourcePos);
3437             tokenValueLen := DBMS_LOB.GetLength(tokenValue);
3438             if (tokenValueLen > 0) then
3439                DBMS_LOB.Append(g_LOBTable(tempIdx).temp_lob, tokenValue);
3440             end if;
3441             sourcePos := pos + tokenLen;
3442             targetPos := DBMS_LOB.GetLength(g_LOBTable(tempIdx).temp_lob) + 1;
3443             tokenFound := TRUE;
3444          end if;
3445          offset := pos + tokenLen;
3446       else
3447          amount := msgLen - sourcePos + 1;
3448          if (amount > 0) then
3449             DBMS_LOB.Copy(g_LOBTable(tempIdx).temp_lob, message, amount,
3450                           targetPos, sourcePos);
3451          end if;
3452 
3453          if (append and tokenFound = FALSE and msgLen > 0) then
3454             tokenValueLen := DBMS_LOB.GetLength(tokenValue);
3455             if tokenValueLen <> 0 then
3456                DBMS_LOB.Append(g_LOBTable(tempIdx).temp_lob, tokenValue);
3457             end if;
3458          end if;
3459       end if;
3460    end loop;
3461 
3462    if tokenFound then
3463       msgLen := DBMS_LOB.GetLength(g_LOBTable(tempIdx).temp_lob);
3464       DBMS_LOB.Trim(message, 0);
3465       DBMS_LOB.Copy(message, g_LOBTable(tempIdx).temp_lob, msgLen);
3466    end if;
3467    wf_temp_lob.releaseLob(g_LOBTable, tempIdx);
3468 
3469 exception
3470    when others then
3471       wf_temp_lob.releaseLob(g_LOBTable, tempIdx);
3472       WF_CORE.Context('WF_MAIL','LOBReplace',token, 'LOB');
3473       raise;
3474 
3475 end LOBReplace;
3476 
3477 -- LOBSubstitute
3478 --   Template contains a max length of 4000 only. We require only a varchar2.
3479 -- IN
3480 -- template IN OUT NOCOPY VARCHAR2,
3481 -- n_nid IN NUMBER,
3482 -- n_code IN VARCHAR2,
3483 -- n_status IN VARCHAR2,
3484 -- n_to_role IN VARCHAR2,
3485 -- r_dName IN VARCHAR2,
3486 -- r_email IN VARCHAR2,
3487 -- n_start_Date IN DATE,
3488 -- n_due_Date IN DATE,
3489 -- n_end_Date IN DATE,
3490 -- n_from_user IN VARCHAR2,
3491 -- n_priority IN VARCHAR2,
3492 -- n_comment IN VARCHAR2,
3493 -- m_subject IN VARCHAR2,
3494 -- m_body IN OUT NOCOPY CLOB,
3495 -- err_Name IN OUT NOCOPY VARCHAR2,
3496 -- err_Message IN OUT NOCOPY VARCHAR2,
3497 -- err_Stack IN OUT NOCOPY VARCHAR2,
3498 -- n_timezone IN VARCHAR2)
3499 procedure LOBSubstitute(template IN OUT NOCOPY VARCHAR2,
3500                         n_nid IN NUMBER,
3501                         n_code IN VARCHAR2,
3502                         n_status IN VARCHAR2,
3503                         n_to_role IN VARCHAR2,
3504                         r_dName IN VARCHAR2,
3505                         r_email IN VARCHAR2,
3506                         n_start_Date IN DATE,
3507                         n_due_Date IN DATE,
3508                         n_end_Date IN DATE,
3509                         n_from_user IN VARCHAR2,
3510                         n_priority IN VARCHAR2,
3511                         n_comment IN VARCHAR2,
3512                         m_subject IN VARCHAR2,
3513                         m_header IN VARCHAR2,
3514                         m_body IN OUT NOCOPY CLOB,
3515                         err_Name IN OUT NOCOPY VARCHAR2,
3516                         err_Message IN OUT NOCOPY VARCHAR2,
3517                         err_Invalid IN OUT NOCOPY VARCHAR2,
3518                         err_Expected IN OUT NOCOPY VARCHAR2,
3519                         n_timezone IN VARCHAR2)
3520 is
3521     -- temp          CLOB;
3522     tempIdx       pls_integer;
3523     tempPos       NUMBER := 0;
3524     pos           NUMBER := 0;
3525     amper         NUMBER := 0;
3526 
3527     msgLen        NUMBER := 0;
3528     tknSize       NUMBER := 0;
3529 
3530     tokenName     VARCHAR2(60);
3531     tokenMatch    BOOLEAN;
3532 
3533     n_due_date_text varchar2(64);
3534     n_start_date_text varchar2(64);
3535     n_end_date_text varchar2(64);
3536 
3537     n_priority_text varchar2(240);
3538     n_nidStr varchar2(50);
3539 
3540     eot NUMBER;
3541 
3542 begin
3543 
3544     -- BLAF requriement to display the date with TIME elelment
3545     -- n_due_date_text := to_char(n_due_date, Wf_Notification.g_nls_date_mask);
3546     -- n_start_date_text := to_char(n_start_date, Wf_Notification.g_nls_date_mask);
3547 
3548     --<< sstomar>: NLS changes, bug 7578922
3549     n_due_date_text    :=  wf_notification_util.GetCalendarDate(n_nid, n_due_date, null, true);
3550     n_start_date_text  := wf_notification_util.GetCalendarDate(n_nid, n_start_date, null, true);
3551     n_end_date_text    := wf_notification_util.GetCalendarDate(n_nid, n_end_date, null, true);
3552 
3553     if (n_priority > 66) then
3554       --Bug 2774891 fix - sacsharm
3555       --n_priority_text := wf_core.substitute('WFTKN', 'HIGH');
3556       n_priority_text := wf_core.substitute('WFTKN', 'LOW');
3557     elsif (n_priority > 33) then
3558       n_priority_text := wf_core.substitute('WFTKN', 'NORMAL');
3559     else
3560       --Bug 2774891 fix - sacsharm
3561       --n_priority_text := wf_core.substitute('WFTKN', 'LOW');
3562       n_priority_text := wf_core.substitute('WFTKN', 'HIGH');
3563     end if;
3564 
3565     -- DBMS_LOB.CreateTemporary(temp, TRUE, DBMS_LOB.SESSION);
3566     -- DBMS_LOB.Open(temp, DBMS_LOB.LOB_READWRITE);
3567     tempIdx := wf_temp_lob.getLob(g_LOBTable);
3568 
3569     pos := 1;
3570     tempPos := 1;
3571     msgLen := length(template);
3572     while pos < msgLen loop
3573        -- Locate each instance of an ampersand and assume it is
3574        -- a token reference.
3575        amper := instr(template, '&', pos, 1);
3576 
3577        if amper = 0 then
3578           -- No ampers left. so write the rest of the CLOB
3579           if pos < msgLen then
3580              -- DBMS_LOB.Copy(temp, template, (msgLen - pos)+1, tempPos, pos);
3581              DBMS_LOB.Write(g_LOBTable(tempIdx).temp_lob, (msgLen - pos)+1, tempPos,
3582                                substr(template, pos, (msgLen - pos)+1));
3583           end if;
3584           EXIT;
3585        end if;
3586 
3587        -- Now we have the position of the amper, workout what
3588        -- token it is, if any.
3589 
3590        -- write from the last pos to the new token.
3591        if amper > pos then
3592           -- DBMS_LOB.Copy(temp, template, (amper - pos), tempPos, pos);
3593           DBMS_LOB.Write(g_LOBTable(tempIdx).temp_lob, (amper - pos), tempPos,
3594                              substr(template, pos, (amper - pos)));
3595           tempPos := tempPos + ((amper - pos));
3596        end if;
3597        pos := amper + 1;
3598 
3599        eot := amper;
3600        while ((eot <= msgLen) and
3601           (substr(template, eot, 1) not in (' ', g_newLine, g_tab))) loop
3602           eot := eot +1;
3603        end loop;
3604 
3605        tknSize := (eot - amper) - 1;
3606        tokenName := substr(template, amper+1, tknSize);
3607 
3608        tokenMatch := FALSE;
3609        if instr(tokenName,'NOTIFICATION_ID',1,1)=1 then
3610           n_nidStr := to_char(n_nid);
3611           if n_nidStr is not null then
3612              -- DBMS_LOB.WriteAppend(temp, length(n_nidStr), n_nidStr);
3613              DBMS_LOB.WriteAppend(g_LOBTable(tempIdx).temp_lob, length(n_nidStr),
3614                                   n_nidStr);
3615              tempPos := tempPos + length(n_nidStr);
3616           end if;
3617           pos := amper + length('NOTIFICATION_ID') + 1;
3618           tokenMatch := TRUE;
3619        end if;
3620        if instr(tokenName,'NOTIFICATION',1,1)=1 then
3621           if n_code is not null then
3622              -- DBMS_LOB.WriteAppend(temp, length(n_code), n_code);
3623              DBMS_LOB.WriteAppend(g_LOBTable(tempIdx).temp_lob, length(n_code),
3624                                   n_code);
3625              tempPos := tempPos + length(n_code);
3626           end if;
3627           pos := amper + length('NOTIFICATION') + 1;
3628           tokenMatch := TRUE;
3629        end if;
3630        if instr(tokenName,'STATUS',1,1)=1 then
3631           if n_status is not null then
3632              -- DBMS_LOB.WriteAppend(temp, length(n_status), n_status);
3633              DBMS_LOB.WriteAppend(g_LOBTable(tempIdx).temp_lob, length(n_status), n_status);
3634              tempPos := tempPos + length(n_status);
3635           end if;
3636           pos := amper + length('STATUS') + 1;
3637           tokenMatch := TRUE;
3638        end if;
3639        if instr(tokenName, 'TO_DNAME', 1, 1)=1 then
3640           if r_dname is not null then
3641              -- DBMS_LOB.WriteAppend(temp, length(r_dname), r_dname);
3642              DBMS_LOB.WriteAppend(g_LOBTable(tempIdx).temp_lob, length(r_dname), r_dname);
3643              tempPos := tempPos + length(r_dname);
3644           end if;
3645           pos := amper + length('TO_DNAME') + 1;
3646           tokenMatch := TRUE;
3647        end if;
3648        if instr(tokenName, 'TO_EMAIL', 1, 1)=1 then
3649           if r_email is not null then
3650              -- DBMS_LOB.WriteAppend(temp, length(r_email), r_email);
3651              DBMS_LOB.WriteAppend(g_LOBTable(tempIdx).temp_lob, length(r_email), r_email);
3652              tempPos := tempPos + length(r_email);
3653           end if;
3654           pos := amper + length('TO_EMAIL') + 1;
3655           tokenMatch := TRUE;
3656        end if;
3657        if instr(tokenName,'TO',1,1)=1 then
3658           if n_to_role is not null then
3659              -- DBMS_LOB.WriteAppend(temp, length(n_to_role), n_to_role);
3660              DBMS_LOB.WriteAppend(g_LOBTable(tempIdx).temp_lob, length(n_to_role), n_to_role);
3661              tempPos := tempPos + length(n_to_role);
3662           end if;
3663           pos := amper + length('TO') + 1;
3664           tokenMatch := TRUE;
3665        end if;
3666        if instr(tokenName, 'PRIORITY', 1, 1)=1 then
3667           if n_priority_text is not null then
3668              -- DBMS_LOB.WriteAppend(temp, length(n_priority_text), n_priority_text);
3669              DBMS_LOB.WriteAppend(g_LOBTable(tempIdx).temp_lob, length(n_priority_text),
3670                                   n_priority_text);
3671              tempPos := tempPos + length(n_priority_text);
3672           end if;
3673           pos := amper + length('PRIORITY') + 1;
3674           tokenMatch := TRUE;
3675        end if;
3676        if instr(tokenName, 'START_DATE', 1, 1)=1 then
3677           if n_start_date is not null then
3678              -- DBMS_LOB.WriteAppend(temp, length(n_start_date), n_start_date);
3679              DBMS_LOB.WriteAppend(g_LOBTable(tempIdx).temp_lob,
3680                                   length(n_start_date_text), n_start_date_text);
3681              tempPos := tempPos + length(n_start_date_text);
3682           end if;
3683           pos := amper + length('START_DATE') + 1;
3684           tokenMatch := TRUE;
3685        end if;
3686        if instr(tokenName, 'DUE_DATE', 1, 1)=1 then
3687           if n_due_date is not null then
3688              -- DBMS_LOB.WriteAppend(temp, length(n_due_date_text), n_due_date_text);
3689              DBMS_LOB.WriteAppend(g_LOBTable(tempIdx).temp_lob, length(n_due_date_text),
3690                                   n_due_date_text);
3691              tempPos := tempPos + length(n_due_date_text);
3692           end if;
3693           pos := amper + length('DUE_DATE') + 1;
3694           tokenMatch := TRUE;
3695        end if;
3696        if instr(tokenName, 'END_DATE', 1, 1)=1 then
3697           if n_end_date is not null then
3698              -- DBMS_LOB.WriteAppend(temp, length(n_end_date), n_end_date);
3699              -- DBMS_LOB.WriteAppend(g_LOBTable(tempIdx).temp_lob, length(n_end_date),
3700              --                      n_end_date);
3701              DBMS_LOB.WriteAppend(g_LOBTable(tempIdx).temp_lob, length(n_end_date_text),
3702                                   n_end_date_text);
3703 
3704              tempPos := tempPos + length(n_end_date_text);
3705           end if;
3706           pos := amper + length('END_DATE') + 1;
3707           tokenMatch := TRUE;
3708        end if;
3709        if instr(tokenName, 'SENDER', 1, 1)=1 then
3710           if n_from_user is not null then
3711              -- DBMS_LOB.WriteAppend(temp, length(n_from_user), n_from_user);
3712              DBMS_LOB.WriteAppend(g_LOBTable(tempIdx).temp_lob, length(n_from_user),
3713                                   n_from_user);
3714              tempPos := tempPos + length(n_from_user);
3715           end if;
3716           pos := amper + length('SENDER') + 1;
3717           tokenMatch := TRUE;
3718        end if;
3719        if instr(tokenName, 'COMMENT', 1, 1)=1 then
3720          if n_comment is not null then
3721             -- DBMS_LOB.WriteAppend(temp, length(n_comment), n_comment);
3722             DBMS_LOB.WriteAppend(g_LOBTable(tempIdx).temp_lob,
3723                                 length(n_comment), n_comment);
3724             tempPos := tempPos + length(n_comment);
3725          end if;
3726          pos := amper + length('COMMENT') + 1;
3727          tokenMatch := TRUE;
3728        end if;
3729        if instr(tokenName, 'TIMEZONE', 1, 1)=1 then
3730           if n_timezone is not null then
3731              DBMS_LOB.WriteAppend(g_LOBTable(tempIdx).temp_lob,
3732                                   length(n_timezone), n_timezone);
3733              tempPos := tempPos + length(n_timezone);
3734           end if;
3735           pos := amper + length('TIMEZONE') + 1;
3736           tokenMatch := TRUE;
3737        end if;
3738        if instr(tokenName, 'SUBJECT', 1, 1)=1 then
3739           if m_subject is not null then
3740              -- DBMS_LOB.WriteAppend(temp, length(m_subject), m_subject);
3741              DBMS_LOB.WriteAppend(g_LOBTable(tempIdx).temp_lob, length(m_subject), m_subject);
3742              tempPos := tempPos + length(m_subject);
3743           end if;
3744           pos := amper + length('SUBJECT') + 1;
3745           tokenMatch := TRUE;
3746        end if;
3747        if instr(tokenName, 'HEADER', 1, 1)=1 then
3748           if m_header is not null then
3749              --DBMS_LOB.WriteAppend(temp, length(m_header), m_header);
3750              DBMS_LOB.WriteAppend(g_LOBTable(tempIdx).temp_lob, length(m_header), m_header);
3751              tempPos := tempPos + length(m_header);
3752           end if;
3753           pos := amper + length('HEADER') + 1;
3754           tokenMatch := TRUE;
3755        end if;
3756        if instr(tokenName, 'BODY', 1, 1)=1 then
3757           if (m_body is not null and dbms_lob.getLength(m_body) > 0) or not g_isFwkNtf then
3758              DBMS_LOB.Append(g_LOBTable(tempIdx).temp_lob, m_body);
3759              tempPos := tempPos + DBMS_LOB.GetLength(m_body);
3760              pos := amper + length('BODY') + 1;
3761              tokenMatch := TRUE;
3762           else
3763              tokenMatch := FALSE;
3764           end if;
3765        end if;
3766        if instr(tokenName, 'MAIL_ERROR_NAME', 1, 1)=1 then
3767           if err_name is not null then
3768              -- DBMS_LOB.WriteAppend(temp, length(err_name), err_name);
3769              DBMS_LOB.WriteAppend(g_LOBTable(tempIdx).temp_lob, length(err_name), err_name);
3770              tempPos := tempPos + length(err_name);
3771           end if;
3772           pos := amper + length('MAIL_ERROR_NAME') + 1;
3773           tokenMatch := TRUE;
3774        end if;
3775        if instr(tokenName, 'MAIL_ERROR_MESSAGE', 1, 1)=1 then
3776           if err_message is not null then
3777              -- DBMS_LOB.WriteAppend(temp, length(err_message), err_message);
3778              DBMS_LOB.WriteAppend(g_LOBTable(tempIdx).temp_lob, length(err_message),
3779                                   err_message);
3780              tempPos := tempPos + length(err_message);
3781           end if;
3782           pos := amper + length('MAIL_ERROR_MESSAGE') + 1;
3783           tokenMatch := TRUE;
3784        end if;
3785 
3786        -- Bug# 2301881 Replacing error stack with invalid value
3787        --              found and the original response values
3788        --              to go with the WARNING message
3789 
3790        if instr(tokenName, 'MAIL_VALUE_FOUND', 1, 1)=1 then
3791           if err_invalid is not null then
3792              -- DBMS_LOB.WriteAppend(temp, length(err_invalid), err_invalid);
3793              DBMS_LOB.WriteAppend(g_LOBTable(tempIdx).temp_lob, length(err_invalid),
3794                                   err_invalid);
3795              tempPos := tempPos + length(err_invalid);
3796           end if;
3797           pos := amper + length('MAIL_VALUE_FOUND') + 1;
3798           tokenMatch := TRUE;
3799        end if;
3800 
3801        if instr(tokenName, 'MAIL_EXP_VALUES', 1, 1)=1 then
3802           if err_expected is not null then
3803              -- DBMS_LOB.WriteAppend(temp, length(err_expected), err_expected);
3804              DBMS_LOB.WriteAppend(g_LOBTable(tempIdx).temp_lob, length(err_expected),
3805                                   err_expected);
3806              tempPos := tempPos + length(err_expected);
3807           end if;
3808           pos := amper + length('MAIL_EXP_VALUES') + 1;
3809           tokenMatch := TRUE;
3810        end if;
3811 
3812        if not tokenMatch and amper > 0 then
3813           -- DBMS_LOB.WriteAppend(temp, 1, '&');
3814           DBMS_LOB.WriteAppend(g_LOBTable(tempIdx).temp_lob, 1, '&');
3815           tempPos := tempPos + 1;
3816        end if;
3817 
3818     end loop;
3819 
3820     -- msgLen := DBMS_LOB.GetLength(temp);
3821     msgLen := DBMS_LOB.GetLength(g_LOBTable(tempIdx).temp_lob);
3822     if msgLen > 0 then
3823        DBMS_LOB.Trim(m_body, 0);
3824        -- DBMS_LOB.Copy(m_body, temp, msgLen);
3825        DBMS_LOB.Copy(m_body, g_LOBTable(tempIdx).temp_lob, msgLen);
3826     end if;
3827 
3828     -- DBMS_LOB.Close(temp);
3829     -- DBMS_LOB.FreeTemporary(temp);
3830     wf_temp_lob.releaseLob(g_LOBTable, tempIdx);
3831 
3832 exception
3833    when others then
3834       wf_temp_lob.releaseLob(g_LOBTable, tempIdx);
3835       WF_CORE.Context('WF_MAIL','LOBSubstitute',tokenName,
3836          to_char(msgLen)||':'||to_char(pos)||':'||to_char(tempPos));
3837       raise;
3838 end LOBSubstitute;
3839 
3840 -- OBSOLETELOBLineBreak - To add a new line every 900 characters
3841 -- or the nearst white space
3842 -- IN
3843 --    message
3844 --    line size default 900
3845 procedure OBSOLETELOBLineBreak(message IN OUT NOCOPY CLOB,
3846                        lineSize in INTEGER,
3847                        maxLineSize INTEGER)
3848 is
3849    lnsize NUMBER;
3850    start_cnt NUMBER;
3851    end_cnt NUMBER;
3852    crpos NUMBER;
3853    tempOffset NUMBER;
3854    str_length NUMBER;
3855 
3856    -- temp CLOB;
3857    -- line CLOB;
3858    tempIdx  pls_integer;
3859    lineIdx  pls_integer;
3860 
3861 begin
3862 
3863    --
3864    -- For every 900 character, we insert a newline just in case
3865    -- Because this whole message body may go out to the Unix SMTP gateway
3866    -- which does not like a line longer than 1000 characters.
3867    -- We do it 900 here just for safty.
3868    -- 2001/03/23 Changed algorithm to start at 900 point and then
3869    -- move to the nearest whitespace.
3870    --
3871    lnsize := lineSize;
3872    start_cnt := 1;
3873    end_cnt := lnsize;
3874    str_length := DBMS_LOB.GetLength(message);
3875    if str_length = 0 then
3876       return;
3877    end if;
3878 
3879    -- DBMS_LOB.CreateTemporary(temp, TRUE, DBMS_LOB.CALL);
3880    -- DBMS_LOB.CreateTemporary(line, TRUE, DBMS_LOB.CALL);
3881    -- DBMS_LOB.Open(temp, DBMS_LOB.LOB_READWRITE);
3882    -- DBMS_LOB.Open(line, DBMS_LOB.LOB_READWRITE);
3883    tempIdx := wf_temp_lob.getLob(g_LOBTable);
3884    lineIdx := wf_temp_lob.getLob(g_LOBTable);
3885 
3886    while start_cnt < str_length loop
3887       -- use the existing newlines as a natural break
3888       crpos := DBMS_LOB.instr(message, g_newLine, start_cnt+1, 1) - start_cnt;
3889 
3890 
3891       if crpos > 0 and crpos < end_cnt then
3892          -- If there was a line bread and it is positioned less than the
3893          -- maximum allowed, then use that instead.
3894          end_cnt := crpos;
3895       else
3896          if crpos < 0 and (str_length - start_cnt) < end_cnt then
3897             end_cnt := str_length - start_cnt;
3898          end if;
3899 
3900          -- Move forward to the next white space.
3901          while (start_cnt + end_cnt < str_length) and
3902                DBMS_LOB.substr(message, 1, start_cnt + end_cnt)
3903                          not in (' ', g_newLine, g_tab)
3904                and end_cnt < maxLineSize loop
3905             end_cnt := end_cnt + 1;
3906          end loop;
3907 
3908          -- We need to understand the full conditions underwhich
3909          -- the previous loop exited. All characters must be preserved
3910          -- and the line, no matter what can not exceed 900 characters.
3911 
3912          if end_cnt >= (maxLineSize) then
3913            end_cnt := lnsize;
3914            while (start_cnt + end_cnt > start_cnt) and
3915                DBMS_LOB.substr(message, 1, start_cnt + end_cnt)
3916                          not in (' ', g_newLine, g_tab)
3917                and end_cnt > 0 loop
3918               end_cnt := end_cnt - 1;
3919            end loop;
3920            -- If we can not locate a white space, then use the default
3921            if end_cnt <= 0 then
3922               end_cnt := lnsize;
3923            end if;
3924          end if;
3925       end if;
3926 
3927       -- Ensure the last characters are not lost.
3928       -- DBMS_LOB.Trim(line, 0);
3929       DBMS_LOB.Trim(g_LOBTable(lineIdx).temp_lob, 0);
3930       tempOffset := dbms_lob.getLength(g_LOBTable(tempIdx).temp_lob) +1;
3931       if start_cnt + end_cnt >= str_length then
3932          -- line := DBMS_LOB.substr(message, start_cnt);
3933          -- DBMS_LOB.Copy(line, message,
3934          --              DBMS_LOB.GetLength(message) - start_cnt +1 , 1,
3935          --              start_cnt);
3936          -- DBMS_LOB.Copy(g_LOBTable(lineIdx).temp_lob, message,
3937          --               DBMS_LOB.GetLength(message) - start_cnt +1 , 1,
3938          --               start_cnt);
3939          DBMS_LOB.Copy(dest_lob => g_LOBTable(tempIdx).temp_lob,
3940                        src_lob => message,
3941                        amount => DBMS_LOB.GetLength(message) - start_cnt +1 ,
3942                        dest_offset => tempOffset,
3943                        src_offset => start_cnt);
3944 
3945       else
3946          -- line := DBMS_LOB.substr(message, start_cnt, end_cnt);
3947          -- DBMS_LOB.Copy(line, message, end_cnt, 1, start_cnt);
3948          -- DBMS_LOB.Copy(g_LOBTable(lineIdx).temp_lob, message, end_cnt, 1,
3949          --               start_cnt);
3950          DBMS_LOB.Copy(dest_lob => g_LOBTable(tempIdx).temp_lob,
3951                        src_lob => message,
3952                        amount => end_cnt,
3953                        dest_offset => tempOffset,
3954                        src_offset => start_cnt);
3955       end if;
3956 
3957       -- temp := temp || line;
3958       -- DBMS_LOB.Append(temp, line);
3959       -- DBMS_LOB.Append(g_LOBTable(tempIdx).temp_lob,
3960       --                 g_LOBTable(lineIdx).temp_lob);
3961 
3962       -- If there is a newline at this point,
3963       -- then do not bother with another.
3964       if DBMS_LOB.substr(message, 1, start_cnt + end_cnt) <>
3965                 g_newLine then
3966          -- DBMS_LOB.WriteAppend(temp, 1, g_newLine);
3967          DBMS_LOB.WriteAppend(g_LOBTable(tempIdx).temp_lob, 1, g_newLine);
3968       end if;
3969 
3970       -- We do not want to start the new line with the space.
3971       if DBMS_LOB.substr(message, 1, start_cnt + end_cnt) = ' ' then
3972          start_cnt := start_cnt + end_cnt + 1;
3973       else
3974          start_cnt := start_cnt + end_cnt;
3975       end if;
3976       end_cnt := lnsize;
3977    end loop;
3978    DBMS_LOB.Trim(message, 0);
3979    -- DBMS_LOB.Copy(message, temp, DBMS_LOB.GetLength(temp), 1, 1);
3980    DBMS_LOB.Copy(message, g_LOBTable(tempIdx).temp_lob,
3981                  DBMS_LOB.GetLength(g_LOBTable(tempIdx).temp_lob), 1, 1);
3982 
3983 
3984    -- if DBMS_LOB.IsOpen(temp)=1 then
3985    --   DBMS_LOB.Close(temp);
3986    --   DBMS_LOB.FreeTemporary(temp);
3987    -- end if;
3988    -- if DBMS_LOB.IsOpen(line)=1 then
3989    --   DBMS_LOB.Close(line);
3990    --   DBMS_LOB.FreeTemporary(line);
3991    -- end if;
3992    wf_temp_lob.releaseLob(g_LOBTable, lineIdx);
3993    wf_temp_lob.releaseLob(g_LOBTable, tempIdx);
3994 
3995 end OBSOLETELOBLineBreak;
3996 
3997 -- OBSOLETE2LOBLineBreak
3998 -- To wrap the text at textWidth but no line can exceed maLineSize
3999 -- NOTE:
4000 -- In order to be performant, the textWidth is given
4001 -- as a guide. If not natural break appears, then the nearest
4002 -- whitespace after textWidth will be used.
4003 procedure OBSOLETE2LOBLineBreak(message IN OUT NOCOPY CLOB,
4004                        textWidth in INTEGER,
4005                        maxLineSize INTEGER)
4006 is
4007 
4008    lnsize NUMBER;
4009    bfr_start NUMBER;
4010    chk_start NUMBER;
4011    line_size NUMBER;
4012    crpos NUMBER;
4013    wsLoc NUMBER;
4014    doc_length NUMBER;
4015    tempOffset NUMBER;
4016    insertNewLine boolean;
4017    whitespace boolean;
4018 
4019    tempIdx  pls_integer;
4020    lineBuffer varchar2(32000);
4021 
4022 begin
4023 
4024    lnsize := textWidth;
4025    bfr_start := 1;
4026    chk_start := 1;
4027    line_size := lnsize;
4028    doc_length := DBMS_LOB.GetLength(message);
4029    if doc_length = 0 or doc_length < line_size then
4030       return;
4031    end if;
4032 
4033    tempIdx := wf_temp_lob.getLob(g_LOBTable);
4034 
4035    -- While the block start counter is less than the length of the
4036    -- document, then start scanning to make sure that the document
4037    -- conforms to the requirements of the parameters.
4038    while chk_start < doc_length loop
4039 
4040       wsLoc := -1;
4041       insertNewLine := false;
4042 
4043       -- use the existing newlines as a natural break. If found
4044       -- crpos will be the relative position from chk_start.
4045       crpos := DBMS_LOB.instr(lob_loc => message,
4046                               pattern => g_newLine,
4047                               offset => chk_start,
4048                               nth => 1) - chk_start;
4049 
4050       -- If the position of crpos makes the respective line less than
4051       -- the required linesize, then use that. Otherwise, go looking for
4052       -- some whitespace to insert a new line.
4053 
4054       if crpos > -1 and crpos < line_size then
4055          line_size := crpos +1;
4056          insertNewLine := false;
4057       else
4058          -- Move forward to the next white space.
4059 
4060          wsLoc := DBMS_LOB.instr(lob_loc => message,
4061                                  pattern => ' ',
4062                                  offset => (chk_start + line_size),
4063                                  nth => 1) - chk_start;
4064 
4065          crpos := DBMS_LOB.instr(lob_loc => message,
4066                                  pattern => g_newline,
4067                                  offset => (chk_start + line_size),
4068                                  nth => 1) - chk_start;
4069 
4070          if(crpos > -1 and crpos < wsLoc and crpos < maxLineSize) then
4071             line_size := crpos +1;
4072             insertNewLine := false;
4073          elsif (wsLoc > -1 and wsLoc < maxLineSize) then
4074             line_size := wsLoc;
4075             insertNewLine := true;
4076             whitespace := true;
4077          else
4078 
4079             wsLoc := DBMS_LOB.instr(lob_loc => message,
4080                                     pattern => g_newLine,
4081                                     offset => chk_start + line_size,
4082                                     nth => 1) - chk_start;
4083 
4084             if (wsLoc > -1 and wsLoc < maxLineSize) then
4085                line_size := wsLoc;
4086                wsLoc := 0;
4087                insertNewLine := false;
4088                whitespace := true;
4089             else
4090 
4091                wsLoc := DBMS_LOB.instr(lob_loc => message,
4092                                        pattern => g_tab,
4093                                        offset => chk_start + line_size,
4094                                        nth => 1) - chk_start;
4095 
4096                if (wsLoc > -1 and wsLoc < maxLineSize) then
4097                   line_size := wsLoc;
4098                   insertNewLine := true;
4099                   whitespace := true;
4100                else
4101                   line_size := maxLineSize;
4102                   insertNewLine := true;
4103                   whitespace := true;
4104                end if;
4105             end if;
4106          end if;
4107       end if;
4108 
4109       -- Ensure the last characters are not lost.
4110       if chk_start + line_size >= doc_length then
4111          -- copy from the chk_start to the end of the document.
4112          tempOffset := dbms_lob.getLength(g_LOBTable(tempIdx).temp_lob) +1;
4113 
4114          DBMS_LOB.Copy(dest_lob => g_LOBTable(tempIdx).temp_lob,
4115                        src_lob => message,
4116                        amount => doc_length - bfr_start +1 ,
4117                        dest_offset => tempOffset,
4118                        src_offset => bfr_start);
4119       elsif insertNewLine then
4120          -- Copy partial and make note of the current position
4121          -- This is to minimise the number of calls to dbms_lob.copy.
4122          tempOffset := dbms_lob.getLength(g_LOBTable(tempIdx).temp_lob) +1;
4123          DBMS_LOB.Copy(dest_lob => g_LOBTable(tempIdx).temp_lob,
4124                        src_lob => message,
4125                        amount => (chk_start - bfr_start) + line_size,
4126                        dest_offset => tempOffset,
4127                        src_offset => bfr_start);
4128          DBMS_LOB.WriteAppend(g_LOBTable(tempIdx).temp_lob, 1, g_newLine);
4129          bfr_start := chk_start + line_size +1;
4130       end if;
4131 
4132       if wsLoc > 0 then
4133          chk_start := chk_start + line_size + 1;
4134       else
4135          chk_start := chk_start + line_size;
4136       end if;
4137       line_size := lnsize;
4138    end loop;
4139 
4140    DBMS_LOB.Trim(message, 0);
4141    DBMS_LOB.Copy(message, g_LOBTable(tempIdx).temp_lob,
4142                  DBMS_LOB.GetLength(g_LOBTable(tempIdx).temp_lob), 1, 1);
4143 
4144    DBMS_LOB.Trim(g_LOBTable(tempIdx).temp_lob, 0);
4145    wf_temp_lob.releaseLob(g_LOBTable, tempIdx);
4146 
4147 end OBSOLETE2LOBLineBreak;
4148 
4149 
4150 -- LOBLineBreak
4151 -- To wrap the text at textWidth but no line can exceed maLineSize
4152 -- NOTE:
4153 -- In order to be performant, the textWidth is given
4154 -- as a guide. If not natural break appears, then the nearest
4155 -- whitespace after textWidth will be used.
4156 -- Re-written for bug 4510044
4157 procedure LOBLineBreak(message IN OUT NOCOPY CLOB,
4158                        textWidth in INTEGER,
4159                        maxLineSize INTEGER)
4160 is
4161 
4162    l_amount number := 0;
4163    l_offset number := 1;
4164    l_bufferSize number := 0;
4165    l_forcedBreak boolean := false;
4166    l_strBuffer varchar2(32000);
4167 
4168    l_linesize number := textWidth;
4169 
4170    l_lastCR number;
4171    l_lastWS number;
4172 
4173    l_tempIdx pls_integer;
4174 
4175 begin
4176 
4177    l_tempIdx := wf_temp_lob.getLob(g_LOBTable);
4178 
4179    l_amount := dbms_lob.getLength(message);
4180    l_offset := 1;
4181    l_bufferSize := l_lineSize;
4182    l_forcedBreak := false;
4183 
4184    if(l_amount > 0) then
4185       loop
4186          l_lastCR := 0;
4187          l_forcedBreak := false;
4188          if(l_amount > l_lineSize) then
4189             dbms_lob.read(lob_loc => message, amount => l_lineSize,
4190                           offset => l_offSet, buffer => l_strBuffer);
4191 
4192             l_lastCR := instr(l_strBuffer, wf_core.newline, -1);
4193             l_lastWS := instr(l_strBuffer, ' ', -1);
4194 
4195             if(l_lastCR > 1) then
4196                l_bufferSize := l_lastCR;
4197             elsif(l_lastWS > 1) then
4198                l_bufferSize := l_lastWS;
4199                l_forcedBreak := true;
4200             else
4201                l_bufferSize := l_lineSize;
4202             end if;
4203 
4204             l_amount := l_amount - l_bufferSize;
4205             l_offset := l_offset + l_bufferSize;
4206 
4207             if (l_forcedBreak) then
4208                l_strBuffer := substr(l_strBuffer, 1, l_bufferSize - 1)||
4209                                      wf_core.newline;
4210                -- Recalculate the buffer size incase newline is not the
4211                -- same size as the space that was removed
4212                l_bufferSize := length(l_strBuffer);
4213             end if;
4214 
4215             DBMS_LOB.writeAppend(lob_loc => g_LOBTable(l_tempIdx).temp_lob,
4216                                  amount => l_bufferSize,
4217                                  buffer => l_strBuffer);
4218 
4219             l_strBuffer := '';
4220          else
4221 
4222             l_amount := (dbms_lob.getLength(message) - l_offset) + 1;
4223             dbms_lob.read(lob_loc => message, amount => l_amount,
4224                           offset => l_offset, buffer => l_strBuffer);
4225 
4226             dbms_lob.writeAppend(lob_loc => g_LOBTable(l_tempIdx).temp_lob,
4227                                  amount => l_amount,
4228                                  buffer => l_strBuffer);
4229 
4230             exit;
4231          end if;
4232       end loop;
4233       DBMS_LOB.Trim(message, 0);
4234       DBMS_LOB.Copy(message, g_LOBTable(l_tempIdx).temp_lob,
4235                     DBMS_LOB.GetLength(g_LOBTable(l_tempIdx).temp_lob), 1, 1);
4236 
4237       DBMS_LOB.Trim(g_LOBTable(l_tempIdx).temp_lob, 0);
4238       wf_temp_lob.releaseLob(g_LOBTable, l_tempIdx);
4239    end if;
4240 
4241 -- exception
4242    -- when others then
4243    -- wf_core.context('WF_MAIL', 'LOBLineBreak',  to_char(textWidth));
4244    -- raise;
4245 end LOBLineBreak;
4246 
4247 
4248 -- GetMailTo - Construct MailTo Section (PRIVATE)
4249 -- IN
4250 --   notification id
4251 --   notification tag
4252 --   notification reply to
4253 --   notification subject
4254 -- RETURN
4255 --   mailto html tag with the subject and body
4256 procedure GetMailTo(nid in number,
4257                    n_tag in varchar2,
4258                    reply_to in varchar2,
4259                    subject in varchar2,
4260                    doc in out nocopy CLOB)
4261 is
4262     -- SQL Statement for fetching URL RESPONSE attributes.
4263     cursor c1 is
4264     select WMA.NAME, WMA.DISPLAY_NAME, WNA.TEXT_VALUE, WMA.DESCRIPTION
4265     from   WF_NOTIFICATION_ATTRIBUTES WNA,
4266            WF_NOTIFICATIONS WN,
4267            WF_MESSAGE_ATTRIBUTES_VL WMA
4268     where  WNA.NOTIFICATION_ID = nid
4269     and    WN.NOTIFICATION_ID = WNA.NOTIFICATION_ID
4270     and    WN.MESSAGE_TYPE = WMA.MESSAGE_TYPE
4271     and    WN.MESSAGE_NAME = WMA.MESSAGE_NAME
4272     and    WMA.NAME = WNA.NAME
4273     and    WMA.SUBTYPE = 'RESPOND'
4274     and    WMA.TYPE = 'URL'
4275     order  by WMA.SEQUENCE;
4276 
4277     lov varchar2(64);
4278     lov_list varchar2(240);
4279     str_buffer varchar2(32000);
4280     -- buffer CLOB;
4281     bufferIdx  pls_integer;
4282     auto_answer varchar2(64);
4283     newline_pos number;
4284     disp_name varchar2(80);
4285     attr_type varchar2(8);
4286     attr_format varchar2(240);
4287     attr_value varchar2(32000);
4288     attr_desc varchar2(240);
4289     encoded_tag varchar2(240);
4290 
4291 begin
4292 
4293     -- Clear buffer
4294     str_buffer := '';
4295     -- DBMS_LOB.createTemporary(buffer, FALSE, dbms_lob.call);
4296     bufferIdx := wf_temp_lob.getLob(g_LOBTable);
4297 
4298     -- URL RESPONSE attributes overrides the normal RESULT attributes.
4299     -- So, my goal here is to check for this case.
4300     -- URL RESPONSE attributes is going to appear as a anchor and don't have
4301     -- to construct the MAILTO html tag stuff that we do for the normal
4302     -- RESULT attribute.
4303 
4304     -- NOTE: Please do know that I don't want to destablize the existing code
4305     -- for the normal RESULT attribute MAILTO handling so that I am coding
4306     -- these two cases seperately.
4307     -- for each response variable
4308     for rec in c1 loop
4309         str_buffer := str_buffer||'<P>';
4310         if (rec.description is not null) then
4311             str_buffer := str_buffer||rec.description||'<P>';
4312         end if;
4313         str_buffer := str_buffer|| '<A class="OraLink" HREF="'||
4314                       wf_notification.geturltext(rec.text_value, nid)||
4315                       '" target="_top">';
4316         str_buffer := str_buffer||'<FONT size=+1> <B>'||rec.display_name;
4317         str_buffer := str_buffer||'</B></FONT>'||'</A>';
4318         str_buffer := str_buffer||g_newLine;
4319         -- DBMS_LOB.writeAppend(buffer, length(str_buffer), str_buffer);
4320         DBMS_LOB.writeAppend(g_LOBTable(bufferIdx).temp_lob, length(str_buffer), str_buffer);
4321         str_buffer := '';
4322     end loop;
4323 
4324     -- if (dbms_lob.GetLength(buffer) > 0) then
4325     if (dbms_lob.GetLength(g_LOBTable(bufferIdx).temp_lob) > 0) then
4326         -- LOBReplace(doc, '&'||'MAILTO', buffer, FALSE);
4327         LOBReplace(doc, '&'||'MAILTO', g_LOBTable(bufferIdx).temp_lob, FALSE);
4328         RETURN;
4329     end if;
4330 
4331     --
4332     -- Normal RESULT attribute handling
4333     --
4334     begin
4335     select WMA.DISPLAY_NAME, WMA.TYPE, WMA.FORMAT,
4336            decode(WMA.TYPE,
4337              'VARCHAR2', decode(WMA.FORMAT,
4338                            '', WNA.TEXT_VALUE,
4339                            substr(WNA.TEXT_VALUE, 1, to_number(WMA.FORMAT))),
4340 
4341              'NUMBER', decode(WMA.FORMAT,
4342                          '', to_char(WNA.NUMBER_VALUE),
4343                          to_char(WNA.NUMBER_VALUE, WMA.FORMAT)),
4344              'DATE', decode(WMA.FORMAT,
4345                        '', to_char(WNA.DATE_VALUE),
4346                        to_char(WNA.DATE_VALUE, WMA.FORMAT)),
4347              'LOOKUP', WNA.TEXT_VALUE,
4348              WNA.TEXT_VALUE), WMA.DESCRIPTION
4349     into   disp_name, attr_type, attr_format, attr_value, attr_desc
4350     from   WF_NOTIFICATION_ATTRIBUTES WNA,
4351            WF_NOTIFICATIONS WN,
4352            WF_MESSAGE_ATTRIBUTES_VL WMA
4353     where  WNA.NOTIFICATION_ID = nid
4354     and    WN.NOTIFICATION_ID = WNA.NOTIFICATION_ID
4355     and    WN.MESSAGE_TYPE = WMA.MESSAGE_TYPE
4356     and    WN.MESSAGE_NAME = WMA.MESSAGE_NAME
4357     and    WMA.NAME = WNA.NAME
4358     and    WMA.SUBTYPE = 'RESPOND'
4359     and    WMA.NAME = 'RESULT'
4360     and    WMA.TYPE not in ('FORM', 'URL');
4361 
4362     -- We can only construct answer button or mailto link if is lookup
4363     if (attr_type <> 'LOOKUP') then
4364         auto_answer := 'Respond';
4365     else
4366         -- If is LOOKUP RESULT attribute, we need to show the description here.
4367         if (attr_desc is not null) then
4368             str_buffer := str_buffer||'<P>'||attr_desc;
4369         end if;
4370         auto_answer :=  attr_format;
4371     end if;
4372 
4373     exception
4374     when no_data_found then
4375         auto_answer := 'Respond';
4376     end;
4377 
4378     -- Encode any special characters
4379     encoded_tag := UrlEncode(n_tag);
4380 
4381     -- Construct mailto syntax
4382     str_buffer := str_buffer||'<P>'||disp_name||': <A class="OraLink" HREF="mailto:'||reply_to||
4383               '?subject=%20'||
4384               UrlEncode(subject)||'&'||'body=%20';
4385     -- DBMS_LOB.WriteAppend(buffer, length(str_buffer), str_buffer);
4386     DBMS_LOB.WriteAppend(g_LOBTable(bufferIdx).temp_lob, length(str_buffer), str_buffer);
4387     str_buffer := '';
4388     if (auto_answer = 'Respond') then
4389       -- GetMailToBody(nid, auto_answer, buffer);
4390       GetMailToBody(nid, auto_answer, g_LOBTable(bufferIdx).temp_lob);
4391       str_buffer := '%0D%0A%0D%0A'||encoded_tag||'">'||'<FONT size=+1><B>'||
4392                  g_noResult||'</B></FONT>'||'</A>';
4393        -- DBMS_LOB.WriteAppend(buffer, length(str_buffer), str_buffer);
4394        DBMS_LOB.WriteAppend(g_LOBTable(bufferIdx).temp_lob, length(str_buffer), str_buffer);
4395        str_buffer := '';
4396     else
4397         lov_list := GetLovListInternal(auto_answer);
4398         lov_list := substr(lov_list, 1, length(lov_list)-1);
4399 
4400         while (lov_list is not null) loop
4401             newline_pos := instr(lov_list, g_newLine);
4402 
4403             if (newline_pos = 0) then
4404                 lov := lov_list;
4405             else
4406                 lov := substr(lov_list, 1, newline_pos - 1);
4407             end if;
4408 
4409             -- GetMailToBody(nid, lov, buffer);
4410             GetMailToBody(nid, lov, g_LOBTable(bufferIdx).temp_lob);
4411             str_buffer :=str_buffer||'%0D%0A%0D%0A'||encoded_tag||'">'||
4412                      '<FONT size=+1><B>'||GetLovMeaning(attr_format, lov)||
4413                      '</FONT></B>'||'</A>';
4414             if (newline_pos = 0) then
4415                 lov_list := null;
4416                 if (g_moreinfo = 'REQ') then
4417                    str_buffer := str_buffer || GetMoreInfoMailTo(nid, n_tag, reply_to, subject);
4418                 end if;
4419             else
4420                 lov_list := substr(lov_list, newline_pos+1,
4421                                    length(lov_list) - newline_pos);
4422                 str_buffer := str_buffer||g_newLine;
4423                 str_buffer := str_buffer||'&'||'nbsp;'||'&'||'nbsp;'||
4424                           '<A class="OraLink" HREF="mailto:'||reply_to||
4425                           '?subject=%20'||UrlEncode(subject)||'&'||'body=%20';
4426             end if;
4427             -- DBMS_LOB.WriteAppend(buffer, length(str_buffer), str_buffer);
4428             DBMS_LOB.WriteAppend(g_LOBTable(bufferIdx).temp_lob, length(str_buffer), str_buffer);
4429             str_buffer := '';
4430         end loop;
4431 
4432     end if;
4433 
4434     -- LOBReplace(doc, '&'||'MAILTO', buffer, FALSE);
4435     LOBReplace(doc, '&'||'MAILTO', g_LOBTable(bufferIdx).temp_lob, FALSE);
4436     wf_temp_lob.releaseLob(g_LOBTable, bufferIdx);
4437 
4438 exception
4439     when others then
4440       wf_temp_lob.releaseLob(g_LOBTable, bufferIdx);
4441       wf_core.context('WF_MAIL', 'GetMailTo', nid);
4442       raise;
4443 
4444 end GetMailTo;
4445 
4446 --
4447 -- Validate_JSP_Agent
4448 --  Function to parse the given URL to check if it is a valid JSP agent.
4449 --  If the agent is from the old mailer config with /pls/wf/ or from
4450 --  APPS_FRAMEWORK_AGENT profile option without /OA_HTML/ or /pls/wf/ this
4451 --  function will return in format http://hostname.domain:port/OA_HTML/
4452 --  (The logic is based on fnd_run_function.get_jsp_agent)
4453 --
4454 function Validate_JSP_Agent(p_web_agent in varchar2)
4455 return varchar2
4456 is
4457    l_web_agent varchar2(2000);
4458    l_pos1      pls_integer;
4459    l_pos2      pls_integer;
4460 begin
4461    l_web_agent := trim(p_web_agent);
4462 
4463    if (l_web_agent is not null) then
4464      -- Add a trailing slash if not available
4465      if (substr(l_web_agent, -1, 1) <> '/') then
4466        l_web_agent := l_web_agent||'/';
4467      end if;
4468 
4469      -- http://
4470      l_pos1 := instrb(l_web_agent, '//', 1) + 2;
4471      -- http://hostname.domain:port/
4472      l_pos2 := instrb(l_web_agent, '/', l_pos1);
4473 
4474      l_web_agent := substrb(l_web_agent, 1, l_pos2)||'OA_HTML/';
4475 
4476      if (wf_log_pkg.level_statement >= fnd_log.g_current_runtime_level) then
4477         wf_log_pkg.string(wf_log_pkg.level_statement,
4478                        'wf.plsql.wf_mail.Validate_JSP_Agent',
4479                        'Validated JSP Agent -> '||l_web_agent);
4480      end if;
4481    end if;
4482 
4483    return l_web_agent;
4484 
4485 exception
4486    when others then
4487      if (wf_log_pkg.level_error >= fnd_log.g_current_runtime_level) then
4488        wf_log_pkg.string(wf_log_pkg.level_error,
4489                         'wf.plsql.wf_mail.Validate_JSP_Agent',
4490                         'Error validating JSP Agent -> '||sqlerrm);
4491      end if;
4492      return p_web_agent;
4493 end Validate_JSP_Agent;
4494 
4495 -- Returns the Applications Framework function URL
4496 function Get_Ntf_Function_URL(nid              in number,
4497                               n_key            in varchar2,
4498                               n_sig_policy     in varchar2,
4499                               n_override_agent in varchar2)
4500 return varchar2
4501 is
4502    url varchar2(4000);
4503    validateAccess varchar2(1);
4504    params varchar2(240);
4505    functionName varchar2(240);
4506    functionId number;
4507 
4508    sig_required varchar2(1);
4509    fwk_flavor varchar2(255);
4510    email_flavor varchar2(255);
4511    render varchar2(255);
4512 begin
4513 
4514       validateAccess := FND_PROFILE.value('WF_VALIDATE_NTF_ACCESS');
4515 
4516       Wf_Notification.GetSignatureRequired(p_sig_policy => n_sig_policy,
4517                                            p_nid => nid,
4518                                            p_sig_required => sig_required,
4519                                            p_fwk_sig_flavor => fwk_flavor,
4520                                            p_email_sig_flavor => email_flavor,
4521                                            p_render_hint => render);
4522 
4523       if sig_required = 'Y' then
4524          -- TODO set the correct name for hte PSIG function.
4525          functionName := 'FND_WFNTF_DETAILS';
4526       else
4527          functionName := 'FND_WFNTF_DETAILS';
4528       end if;
4529 
4530       functionId := fnd_function.get_function_id (functionName);
4531 
4532       -- The default set of parameters for all functions.
4533       params := 'wfMailer=Y&'||'NtfId='||to_char(nid);
4534 
4535       if validateAccess = 'Y' then
4536          -- Add on the access key only when requried.
4537          params := params||'&'||'wfnkey='||n_key;
4538       end if;
4539 
4540       -- Moreinfo will be globally set from the calling procedure
4541       if g_moreinfo = 'SUB' then
4542          params := params||'&'||'wfMoreinfo=Y';
4543       else  -- moreinfo REQ or NULL
4544          params := params||'&'||'wfMoreinfo=N';
4545       end if;
4546 
4547       if (wf_log_pkg.level_statement >= fnd_log.g_current_runtime_level) then
4548          wf_log_pkg.string(WF_LOG_PKG.level_statement,
4549                            'wf.plsql.WF_MAIL.Get_Ntf_Function_url',
4550                            'params: {'||params||'} override agent: {'
4551                            ||n_override_agent||'}');
4552       end if;
4553 
4554       url := fnd_run_function.get_run_function_url(
4555                     p_function_id => functionId,
4556                     p_resp_appl_id => -1,
4557                     p_resp_id => -1,
4558                     p_security_group_id => null,
4559                     p_parameters => params,
4560                     p_override_agent => Validate_JSP_Agent(n_override_agent));
4561 
4562       return url;
4563 
4564 end get_Ntf_Function_url;
4565 
4566 -- PRIVATE function to return the CLICK_HERE_TO_RESPOND value
4567 function getClickHereResponse(nid in number, n_key in varchar2,
4568                               agent in varchar2, n_disp_click in varchar2,
4569                               n_sig_policy in varchar2)
4570          return varchar2
4571 is
4572 
4573    n_click_here varchar2(4000);
4574    l_function_id number;
4575    params varchar2(240);
4576    url varchar2(4000);
4577    validateAccess varchar2(1);
4578 
4579 begin
4580    n_click_here := '<A class="OraLink" HREF="';
4581 
4582    if g_install = 'EMBEDDED' then
4583       url := get_Ntf_Function_URL(nid => nid,
4584                                   n_key => n_key,
4585                                   n_sig_policy => n_sig_policy,
4586                                   n_override_agent => agent);
4587 
4588       n_click_here := n_click_here || url;
4589 
4590    else
4591       if (agent is null) then
4592          if wf_mail.send_accesskey then
4593             n_click_here := n_click_here||g_webAgent
4594                           ||'/WFA_HTML.DetailLink?nid='||to_char(nid)
4595                           ||'&'||'nkey='||n_key
4596                           ||'&'||'agent='||g_webAgent;
4597          else
4598             n_click_here := n_click_here||g_webAgent
4599                              ||'/'|| wfa_sec.DirectLogin(nid);
4600          end if;
4601       else
4602          if wf_mail.send_accesskey then
4603             n_click_here := n_click_here||agent
4604                              ||'/WFA_HTML.DetailLink?nid='||to_char(nid)
4605                              ||'&'||'nkey='||n_key
4606                              ||'&'||'agent='||agent;
4607          else
4608             n_click_here := n_click_here||agent
4609                              ||'/'|| wfa_sec.DirectLogin(nid);
4610          end if;
4611       end if;
4612    end if;
4613 
4614    n_click_here := n_click_here||'">'||n_disp_click||'</A>';
4615 
4616    if (wf_log_pkg.level_statement >= fnd_log.g_current_runtime_level) then
4617       wf_log_pkg.string(WF_LOG_PKG.level_statement,'wf.plsql.WF_MAIL.newLOBTag',
4618                           'URL: {'||n_click_here||'}');
4619    end if;
4620 
4621 
4622    return n_click_here;
4623 
4624 end getClickHereResponse;
4625 
4626 --
4627 -- setContext (PRIVATE)
4628 --   Set the context by executing the selector function
4629 -- IN
4630 --   nid - Notification id
4631 --
4632 procedure setContext(nid NUMBER)
4633 is
4634    context VARCHAR2(2000);        /* Bug 2312742 */
4635    callback VARCHAR2(100);
4636    tvalue varchar2(4000);
4637    nvalue number;
4638    dvalue date;
4639 
4640    sqlbuf varchar2(4000);
4641    l_dummy varchar2(1);
4642 begin
4643 
4644    SELECT context, callback
4645       into context, callback
4646    FROM wf_notifications
4647    where notification_id = nid;
4648 
4649    wf_engine.preserved_context := FALSE;
4650    if (callback is not null) then
4651       -- ### callback is from table
4652       -- BINDVAR_SCAN_IGNORE
4653       sqlbuf := 'begin '||callback||
4654                 '(:p1, :p2, :p3, :p4, :p5, :p6, :p7); end;';
4655       execute immediate sqlbuf using
4656          in 'TESTCTX',
4657          in context,
4658          in l_dummy,
4659          in l_dummy,
4660          in out tvalue,
4661          in out nvalue,
4662          in out dvalue;
4663 
4664       if (tvalue in ('FALSE', 'NOTSET')) then
4665          execute immediate sqlbuf using
4666             in 'SETCTX',
4667             in context,
4668             in l_dummy,
4669             in l_dummy,
4670             in out tvalue,
4671             in out nvalue,
4672             in out dvalue;
4673       end if;
4674    end if;
4675 
4676 exception
4677    when others then
4678       WF_CORE.Context('WF_MAIL','SetContext',to_char(nid));
4679       raise;
4680 end setContext;
4681 
4682 -- Get_Last_Question (Private)
4683 -- Retrieves the last More Information question
4684 -- If no question exists, then it is blank.
4685 -- IN
4686 --   p_nid - Notification ID
4687 -- OUT
4688 --   p_last_ques - Returned last question
4689 procedure Get_Last_Question(p_nid in number,
4690                             p_last_ques out nocopy varchar2)
4691 is
4692 
4693   CURSOR c_ques IS
4694   SELECT user_comment
4695   FROM   wf_comments
4696   WHERE  notification_id = p_nid
4697   AND    action = 'QUESTION'
4698   ORDER BY comment_date desc;
4699 
4700 begin
4701 
4702   -- Fetch the last question asked
4703   open c_ques;
4704   fetch c_ques into p_last_ques;
4705   if (c_ques%notfound) then
4706     p_last_ques := '';
4707   end if;
4708   close c_ques;
4709 
4710 end Get_Last_Question;
4711 
4712 
4713 -- Get_Action_History (Private)
4714 --   Scans the notifications message definition and determines if Action History
4715 --   should be displayed
4716 -- IN
4717 --   p_nid  - Notification Id
4718 --   p_lang - User Language
4719 -- OUT
4720 --   p_html_history - Action History in HTML
4721 --   p_text_history - Action History in Text
4722 --   p_last_ques    - Last Question Asked if More Infor Requested
4723 procedure Get_Action_History(p_nid          in  number,
4724                              p_lang         in  varchar2,
4725                              p_html_history out nocopy varchar2,
4726                              p_text_history out nocopy varchar2,
4727                              p_last_ques    out nocopy varchar2)
4728 is
4729   l_text_body    varchar2(4000);
4730   l_html_body    varchar2(4000);
4731   l_comm_cnt     pls_integer;
4732   l_text_history varchar2(32000);
4733   l_html_history varchar2(32000);
4734   l_get_html     boolean;
4735   l_get_text     boolean;
4736 
4737   CURSOR c_comm IS
4738   SELECT count(1)
4739   FROM   wf_comments
4740   WHERE  action_type in ('REASSIGN', 'QA')
4741   AND    notification_id = p_nid;
4742 
4743 begin
4744 
4745   -- Fetch the last question asked
4746   Get_Last_Question(Get_Action_History.p_nid,
4747                     Get_Action_History.p_last_ques);
4748 
4749   l_get_html := false;
4750   l_get_text := false;
4751 
4752   SELECT wm.body, wm.html_body
4753   INTO   l_text_body, l_html_body
4754   FROM   wf_notifications n, wf_messages_vl wm
4755   WHERE  n.notification_id = p_nid
4756   AND    n.message_name = wm.name
4757   AND    n.message_type = wm.type;
4758 
4759   -- FYI notification
4760   if (wf_mail.Get_FYI_Flag) then
4761     open c_comm;
4762     fetch c_comm into l_comm_cnt;
4763     if (c_comm%notfound) then
4764       l_comm_cnt := 0;
4765     end if;
4766 
4767     -- If HISTORY macro is defined, Action History would appear anyways. If the FYI notification
4768     -- was reassigned at least once, include the Action History if even macro is not defined
4769     if (l_comm_cnt > 0) then
4770       if (instrb(l_text_body, 'WF_NOTIFICATION(HISTORY)') = 0) then
4771          l_get_text := true;
4772       end if;
4773       if (instrb(l_html_body, 'WF_NOTIFICATION(HISTORY)') = 0) then
4774          l_get_html := true;
4775       end if;
4776     end if;
4777     close c_comm;
4778 
4779    -- Response Required notification
4780    else
4781      -- If HISTORY macro is defined, Action History would appear anyways. Otherwise
4782      -- display it in the e-mail
4783      if (instrb(l_text_body, 'WF_NOTIFICATION(HISTORY)') = 0) then
4784         l_get_text := true;
4785      end if;
4786      if (instrb(l_html_body, 'WF_NOTIFICATION(HISTORY)') = 0) then
4787         l_get_html := true;
4788      end if;
4789    end if;
4790 
4791    -- Call the GetComments2 procedure to get the Action History
4792    if (l_get_text) then
4793       Wf_Notification.GetComments2(p_nid => Get_Action_History.p_nid,
4794                                    p_display_type => wf_notification.doc_text,
4795                                    p_hide_reassign => 'N',
4796                                    p_hide_requestinfo => 'N',
4797                                    p_action_history => l_text_history);
4798    end if;
4799    if (l_get_html)then
4800       Wf_Notification.GetComments2(p_nid => Get_Action_History.p_nid,
4801                                    p_display_type => wf_notification.doc_html,
4802                                    p_hide_reassign => 'N',
4803                                    p_hide_requestinfo => 'N',
4804                                    p_action_history => l_html_history);
4805    end if;
4806 
4807    p_text_history := l_text_history;
4808    p_html_history := l_html_history;
4809 
4810 exception
4811   when others then
4812     if (wf_log_pkg.level_exception >= fnd_log.g_current_runtime_level) then
4813        wf_log_pkg.string(WF_LOG_PKG.level_exception,
4814                          'wf.plsql.wf_mail.Get_Action_History',
4815                          'Get_Action_History failed. Error: ' || sqlerrm);
4816     end if;
4817     p_text_history := '';
4818     p_html_history := '';
4819 end Get_Action_History;
4820 
4821 -- GetLOBMessage3 - get email message data as a LOB
4822 --
4823 -- IN
4824 --   notification id
4825 --   mailer node name
4826 --   web agent path
4827 --   reply to
4828 --   recipient role
4829 --   lanague
4830 --   territory
4831 --   notification preference
4832 --   dsiplay name
4833 --   Render BODY token flag
4834 -- OUT
4835 --   message subject
4836 --   message body (text)
4837 --   message body (html)
4838 procedure GetLOBMessage3(
4839     nid       in  number,
4840     node      in  varchar2,
4841     agent     in  varchar2,
4842     replyto   in  varchar2,
4843     recipient in varchar2,
4844     language  in varchar2,
4845     territory in varchar2,
4846     ntf_pref  in varchar2,
4847     email     in varchar2,
4848     dname     in varchar2,
4849     renderBody in varchar2,
4850     subject   out NOCOPY varchar2,
4851     body_atth out NOCOPY varchar2,
4852     error_result in out NOCOPY varchar2,
4853     bodyToken in out NOCOPY varchar2 )
4854 as
4855     n_status      varchar2(8);
4856     n_mstatus     varchar2(8);
4857     n_key         varchar2(80);
4858     n_to_role     varchar2(320);
4859     n_from_user   varchar2(320); -- Bug# 2094159
4860     n_due_date    date;
4861     n_start_date  date;
4862     n_end_date    date;
4863     n_priority    number;
4864     n_comment     varchar2(4000);
4865     n_subject     varchar2(2000);
4866     n_response    varchar2(32000);
4867     n_direct      varchar2(3);
4868     n_click_here  varchar2(4000);
4869     n_disp_click  varchar2(240);
4870     n_text_timezone varchar2(240);
4871     n_html_timezone varchar2(240);
4872     r_dname       varchar2(360);
4873     r_email       varchar2(2000);
4874     r_ntf_pref    varchar2(240);
4875     r_language    varchar2(4000);
4876     r_territory   varchar2(4000);
4877     t_type        varchar2(100);
4878     t_name        varchar2(100);
4879     t_subject     varchar2(240);
4880     t_text_body   varchar2(4000);
4881     t_html_body   varchar2(4000);
4882     t_hdrRequired boolean;
4883     m_html        varchar2(32000);
4884     mailTo        VARCHAR2(32000);
4885     t_headerText   varchar2(32000);
4886     n_headerText   varchar2(32000);
4887     t_headerHTML   varchar2(32000);
4888     n_headerHTML   varchar2(32000);
4889     err_name      varchar2(30);
4890     err_message   varchar2(2000);
4891     err_stack     varchar2(4000);
4892     str_dummy     varchar2(4000) := NULL;
4893     fyi           pls_integer;
4894     body_start    pls_integer;
4895     body_end      pls_integer;
4896     tmp_start     pls_integer;
4897     tmp_end       pls_integer;
4898     tag_pos       pls_integer;
4899     dir_pos       pls_integer;
4900     start_cnt     pls_integer;
4901     end_cnt       pls_integer;
4902     str_length    pls_integer;
4903     end_of_message boolean;
4904     buffer        VARCHAR2(32767);
4905 
4906     no_program_unit exception;
4907     pragma exception_init(no_program_unit, -6508);
4908     dummy         varchar2(4000);
4909     -- Bug# 2301881 variables to handle invalid response error message
4910     err_invalid   varchar2(2000);
4911     err_expected  varchar2(2000);
4912     n_sig_policy     varchar2(100);
4913     n_nid_str        varchar2(200);
4914     -- More Info feature bug 2282139
4915     n_more_info_role varchar2(320);
4916     n_mailto         varchar2(10000);
4917     n_text_history   varchar2(32000);
4918     n_html_history   varchar2(32000);
4919     n_last_ques      varchar2(4000);
4920     n_dir            varchar2(16);
4921 
4922     step             varchar2(200);
4923     textForHtml      boolean;
4924 
4925     htmlBodyPos pls_integer;
4926     textBodyPos pls_integer;
4927 
4928     n_start_date2 varchar2(64);
4929 
4930 begin
4931 
4932    if (wf_log_pkg.level_procedure >= fnd_log.g_current_runtime_level) then
4933          wf_log_pkg.string(WF_LOG_PKG.level_procedure,
4934                           'wf.plsql.WF_MAIL.GetLOBMessage3', 'BEGIN');
4935    end if;
4936 
4937    -- 3532615 Moved these from being initialized at the session level
4938    -- back to the notification level.
4939    g_ntfHistory := wf_core.translate('WFNTF_HISTORY');
4940    g_ntfActionHistory := wf_core.translate('WFNTF_ACTION_HISTORY');
4941    g_moreInfoAPrompt := wf_core.translate('WFNTF_MOREINFO_APROMPT');
4942    g_moreInfoAnswer := wf_core.translate('WFNTF_MOREINFO_ANSWER');
4943    g_moreInfoQPrompt := wf_core.translate('WFNTF_MOREINFO_QPROMPT');
4944    g_moreInfoSubject := wf_core.translate('WFNTF_MOREINFO');
4945    g_moreInfoSubmit := wf_core.translate('WFNTF_MOREINFO_SUBMIT');
4946    g_moreInfoQuestion := wf_core.translate('WFNTF_MOREINFO_QUESTION');
4947    g_moreInfoFrom := wf_core.translate('WFNTF_MOREINFO_FROM');
4948    g_moreInfoRequested := wf_core.translate('WFNTF_MOREINFO_REQUESTED');
4949    g_moreInfoRequestee := wf_core.translate('WFNTF_MOREINFO_REQUESTEE');
4950    g_wfmonId := wf_core.translate('WFMON_ID');
4951    g_to := wf_core.translate('TO');
4952    g_from := wf_core.translate('FROM');
4953 
4954    g_beginDate := wf_core.translate('BEGIN_DATE');
4955    g_dueDate2 := wf_core.translate('DUE_DATE');
4956    g_notificationId := wf_core.translate('NOTIFICATION_ID');
4957    g_priority := wf_core.translate('PRIORITY');
4958 
4959    g_dueDate := wf_core.translate('WFMON_DUE_DATE');
4960 
4961    g_invalidRemarks :=  wf_core.translate('WFMLR_INVALID_REMARKS');
4962    g_forExample := wf_core.translate('WFMLR_FOR_EXAMPLE');
4963    g_soOn :=  wf_core.translate('WFMLR_SO_ON');
4964    g_none :=  wf_core.translate('WFMLR_NONE');
4965    g_truncate :=  wf_core.translate('WFNTF_TRUNCATE');
4966    g_noResult :=  wf_core.translate('WFNTF_NO_RESULT');
4967    g_Id := wf_core.translate('ID');
4968 
4969     step := 'Getting notification information';
4970     -- Get notification information
4971     begin
4972       select STATUS, MAIL_STATUS, ACCESS_KEY,
4973              PRIORITY, USER_COMMENT,
4974              BEGIN_DATE, END_DATE, DUE_DATE, FROM_USER,
4975              MORE_INFO_ROLE
4976       into   n_status, n_mstatus, n_key,
4977              n_priority, n_comment,
4978              n_start_date, n_end_date, n_due_date, n_from_user,
4979              n_more_info_role
4980       from   WF_NOTIFICATIONS
4981       where  NOTIFICATION_ID = nid;
4982     exception
4983       when no_data_found then
4984         wf_core.token('NID', to_char(nid));
4985         wf_core.raise('WFNTF_NID');
4986     end;
4987 
4988 
4989     n_to_role := recipient;
4990     r_language := language;
4991     r_territory := territory;
4992     r_ntf_pref := ntf_pref;
4993     r_email := email;
4994     r_dname := dname;
4995 
4996     if (wf_mail.test_flag = TRUE) then
4997         n_mstatus := 'MAIL';
4998         if (n_status not in ('OPEN','CANCELED','CLOSED')) then
4999            n_status := 'OPEN';
5000         end if;
5001      end if;
5002 
5003     -- More information processing
5004     g_moreinfo := NULL;
5005     if (wf_notification.HideMoreInfo(nid) = 'N') then
5006        if(n_more_info_role is not null) then
5007           n_to_role := n_more_info_role;
5008           g_to_role := n_more_info_role;
5009           -- Flags that template for More Info submission needs to be used
5010           g_moreinfo := 'SUB';
5011        else
5012           -- Flags that template for More Info request needs to be used
5013           g_to_role := n_to_role;
5014           g_moreinfo := 'REQ';
5015        end if;
5016     end if;
5017 
5018     r_ntf_pref := nvl(r_ntf_pref, 'QUERY');
5019 
5020     step := 'Getting Signature policy';
5021     -- Bug 2375920 get signature policy for the notification
5022     Wf_Mail.GetSignaturePolicy(nid, n_sig_policy);
5023 
5024     -- Bug 2375920 Process the email message based on the signature policy
5025     ProcessSignaturePolicy(nid, n_sig_policy, n_status, n_mstatus,
5026                            n_key, node, t_type, t_name, n_nid_str);
5027 
5028     if isBiDi(r_language) then
5029        WF_NOTIFICATION.Set_NTF_Table_Direction('R');
5030     else
5031        WF_NOTIFICATION.Set_NTF_Table_Direction('L');
5032     end if;
5033 
5034     -- PLSQL Action History for the notification will not be explicitly processed by the
5035     -- mailer now. The notification sub-system would handle this as part of GetFullBody.
5036 
5037     -- if (renderBody = 'Y') then
5038     --   step := 'Getting action history';
5039     --   Get_Action_History(nid, r_language, n_html_history, n_text_history,
5040     --                      n_last_ques);
5041     -- else
5042     Get_Last_Question(nid, n_last_ques);
5043     n_html_history := '';
5044     n_text_history := '';
5045     -- end if;
5046 
5047     step := 'Getting template';
5048     -- Get template
5049     begin
5050       select SUBJECT, BODY, HTML_BODY
5051       into   t_subject, t_text_body, t_html_body
5052       from   WF_MESSAGES_VL
5053       where  NAME = t_name and TYPE = t_type;
5054     exception
5055       when no_data_found then
5056         wf_core.token('NAME', t_name);
5057         wf_core.token('TYPE', t_type);
5058         wf_core.raise('WFNTF_MESSAGE');
5059     end;
5060 
5061     g_isFwkNtf := true;
5062     if (t_name in ('VIEW_FROMUI', 'VIEW_FROMUI_FYI')) then
5063        t_hdrRequired := TRUE;
5064     elsif renderBody = 'Y' then
5065        t_hdrRequired := TRUE;
5066        g_isFwkNtf := false;
5067     else
5068        t_hdrRequired := FALSE;
5069        g_isFwkNtf := true;
5070     end if;
5071 
5072     step := 'Getting timezone details';
5073     n_text_timezone := wf_mail_util.getTimezone(g_ntfDocText);
5074     n_html_timezone := wf_mail_util.getTimezone(g_ntfDocHtml);
5075 
5076     step := 'Getting subject';
5077     n_subject := WF_NOTIFICATION.GetSubject(nid, 'text/plain');
5078 
5079     -- We will always fetch plain text version of the message because
5080     -- Because for sendmail MAILATTH case, we need to send out html message
5081     -- body as attachment and then the plain text message as the body.
5082     -- For MAPI MAILATTH and MAILHTML cases, same thing.
5083 
5084     -- we will request a temp LOB from the pool only if necessary
5085     -- we donot need the text message if the ntf pref is MAILHTML
5086     -- this will reduce the time spent in unecessarily processing
5087     -- the text LOB Message
5088     t_headerText := '';
5089     t_headerHTML := '';
5090     step := 'Getting body';
5091     if (r_ntf_pref in ('MAILTEXT', 'MAILATTH')) then
5092        -- Below Allocated TEMP LOB here is released within WF_XML.getBodyPart
5093        -- ( which is called by caller of getLOBMessage3 API)
5094        -- based on the Ntf type (text/plain or text/html) by calling wf_mail.CloseLOB
5095        g_text_messageIdx := wf_temp_lob.getLob(g_LOBTable);
5096 
5097        if (renderbody = 'Y') then
5098           end_of_message := FALSE;
5099           step := 'Getting text/plain body';
5100           begin
5101              while not (end_of_message) loop
5102                 WF_NOTIFICATION.GetFullBody(nid, buffer, end_of_message,
5103                                             g_ntfDocText);
5104                 if buffer is not null and length(buffer) > 0 then
5105                    DBMS_LOB.WriteAppend(g_LOBTable(g_text_messageIdx).temp_lob,
5106                                         length(buffer), buffer);
5107                 end if;
5108              end loop;
5109           exception
5110              when others then
5111 
5112                 wf_core.context('WF_MAIL','GetLOBMessage3',
5113                                 'nid => '||to_char(nid),
5114                                 'r_ntf_pref => '||r_ntf_pref);
5115                 wf_core.token('ERROR',sqlerrm);
5116                 wf_core.raise('WFMLR_NTFERR');
5117           end;
5118           -- Get the table of header attributes. Use a PL/SQL document
5119           -- API format.
5120        end if; -- RENDERBODY
5121        if (t_hdrRequired = TRUE) then
5122           GetHeaderTable(r_language||':'||to_char(nid),
5123                          g_ntfDocText, t_headerText, str_dummy);
5124        end if;
5125     end if;
5126 
5127     if (r_ntf_pref in ('MAILHTML', 'MAILATTH', 'MAILHTM2')) then
5128        -- Below Allocated TEMP LOB IS released within  WF_XML.getBodyPart
5129        -- based on the Ntf type (text/plain or text/html) by calling wf_mail.CloseLOB
5130        g_html_messageIdx := wf_temp_lob.getLob(g_LOBTable);
5131 
5132        if (renderbody = 'Y') then
5133           end_of_message := FALSE;
5134           step := 'Getting text/html body';
5135           begin
5136              while not (end_of_message) loop
5137                 WF_NOTIFICATION.GetFullBody(nid, buffer, end_of_message,
5138                                             g_ntfDocHtml);
5139                 if buffer is not null and length(buffer) > 0 then
5140                    DBMS_LOB.WriteAppend(g_LOBTable(g_html_messageIdx).temp_lob,
5141                                         length(buffer), buffer);
5142                 end if;
5143              end loop;
5144            exception
5145               when others then
5146                  wf_core.context('WF_MAIL','GetLOBMessage3',
5147                                  'nid => '||to_char(nid),
5148                                  'r_ntf_pref => '||r_ntf_pref);
5149                  wf_core.token('ERROR',sqlerrm);
5150                  wf_core.raise('WFMLR_NTFERR');
5151            end;
5152 
5153        end if; -- RENDERBODY
5154        if (t_hdrRequired = TRUE) then
5155           GetHeaderTable(r_language||':'||to_char(nid),
5156                          g_ntfDocHtml, t_headerHTML, str_dummy);
5157 
5158 
5159        end if;
5160     end if;
5161 
5162 
5163     step := 'Getting click-here section';
5164     -- Get Click here Response display value
5165     begin
5166       select DESCRIPTION
5167         into n_disp_click
5168         from WF_MESSAGE_ATTRIBUTES_TL
5169        where MESSAGE_TYPE = t_type
5170          and MESSAGE_NAME = t_name
5171          and NAME = 'CLICK_HERE_RESPONSE'
5172          and LANGUAGE = userenv('LANG');
5173     exception
5174       when NO_DATA_FOUND then
5175         -- ignore if this attribute does not exist
5176         null;
5177     end;
5178 
5179     step := 'Getting error information for invalid response';
5180     -- Retrieve error attributes for INVALID message
5181     -- Bug# 2301881 Replacing err_stack with err_invalid and err_expected
5182     --              to make the WARNING message to the responder more
5183     --              user-friendly
5184     if (t_name in ('OPEN_INVALID', 'OPEN_INVALID_MORE_INFO')) then
5185       begin
5186         err_name := Wf_Notification.GetAttrText(nid, 'MAIL_ERROR_NAME');
5187         err_message := Wf_Notification.GetAttrText(nid, 'MAIL_ERROR_MESSAGE');
5188         err_invalid := Wf_Notification.GetAttrText(nid, 'MAIL_VALUE_FOUND');
5189         err_expected := Wf_Notification.GetAttrText(nid, 'MAIL_EXP_VALUES');
5190       exception
5191         when others then null;
5192       end;
5193     end if;
5194 
5195     -- If there is no html template available, use the plain text one.
5196     step := 'Setting content to the template';
5197     if (t_html_body is null) then
5198       t_html_body := replace(t_text_body, g_newLine,
5199                              '<BR>'||g_newLine);
5200       -- Ensure the direction of the text is correct for the language
5201       textForHtml := true;
5202       if isBiDi(r_language) then
5203          t_html_body := '<HTML DIR="RTL"><BODY>'||t_html_body;
5204       else
5205          t_html_body := '<HTML><BODY>'||t_html_body;
5206       end if;
5207     else
5208       -- Ensure that the direction of the text is correctly specified.
5209       if isBiDi(r_language) then
5210          tag_pos := instrb(upper(t_html_body), '<HTML', 1);
5211          if tag_pos > 0 then
5212            dir_pos := instrb(upper(t_html_body), ' DIR="', 1);
5213            if dir_pos = 0 then
5214               buffer := substrb(t_html_body, 1, tag_pos+4);
5215               buffer := buffer||' DIR="RTL" '||substrb(t_html_body, tag_pos+5);
5216               t_html_body := buffer;
5217            end if;
5218          end if;
5219       end if;
5220     end if;
5221 
5222     -- DBMS_LOB.Write(template_html, length(t_html_body), 1, t_html_body);
5223 
5224     if wf_mail.direct_response then
5225        n_direct := '[2]';
5226     else
5227        n_direct := NULL;
5228     end if;
5229 
5230     -- More info feature
5231     if (g_moreinfo = 'SUB') then
5232        n_response := g_moreInfoAPrompt || ': '||wf_mail.g_open_text_delimiter||
5233                      '<' ||
5234                      g_moreInfoAnswer ||'>'||wf_mail.g_close_text_delimiter;
5235        n_response := n_response || g_newLine;
5236     else
5237        if wf_mail.direct_response then
5238           n_response :=  GetEmailDirectResponse(nid);
5239        else
5240           n_response :=  GetEmailResponse(nid);
5241        end if;
5242     end if;
5243 
5244     -- Substitute
5245 
5246     htmlBodyPos := instrb(t_html_body, '&'||'BODY',1, 1);
5247     textBodyPos := instrb(t_text_body, '&'||'BODY',1, 1);
5248 
5249     if (htmlBodyPos > 0 or textBodyPos > 0) then
5250        bodyToken := 'Y';
5251     else
5252        bodyToken := 'N';
5253     end if;
5254 
5255     step := 'Performing token substitution';
5256     n_subject := Substitute(t_subject, nid, n_nid_str,
5257                             n_status, n_to_role, r_dname, r_email,
5258                             n_start_date, n_due_date, n_end_date, n_from_user,
5259                             n_priority, n_comment, n_subject, str_dummy,
5260                             str_dummy, err_name, err_message, err_invalid,
5261                             err_expected, n_text_timezone);
5262 
5263     if (r_ntf_pref in ('MAILTEXT', 'MAILATTH')) then
5264 
5265        n_headerText := Substitute(t_headerText, nid, n_nid_str,
5266                             n_status, n_to_role, r_dname, r_email,
5267                             n_start_date, n_due_date, n_end_date, n_from_user,
5268                             n_priority, n_comment, n_subject, str_dummy,
5269                             str_dummy, err_name, err_message, err_invalid,
5270                             err_expected, n_text_timezone);
5271        LOBSubstitute(t_text_body, nid, n_nid_str,
5272                             n_status, n_to_role, r_dname, r_email,
5273                             n_start_date, n_due_date, n_end_date, n_from_user,
5274                             n_priority, n_comment, n_subject, n_headerText,
5275                             g_LOBTable(g_text_messageIdx).temp_lob,
5276                             err_name, err_message, err_invalid,
5277                             err_expected, n_text_timezone);
5278 
5279        -- Wrap the body into nice pretty lines.
5280        LOBLineBreak(g_LOBTable(g_text_messageIdx).temp_lob, wf_linelen, 999);
5281 
5282        -- Add email response section
5283        LOBReplace(g_LOBTable(g_text_messageIdx).temp_lob,
5284                   '&'||'RESPONSE', n_response, FALSE);
5285 
5286        -- More Information Processing - Bug 2282139
5287        LOBReplace(g_LOBTable(g_text_messageIdx).temp_lob,
5288                              '&'||'HISTORY', n_text_history, FALSE);
5289        LOBReplace(g_LOBTable(g_text_messageIdx).temp_lob,
5290                   '&'||'QUESTION', n_last_ques, FALSE);
5291     end if;
5292 
5293     if (r_ntf_pref in ('MAILHTML', 'MAILATTH', 'MAILHTM2')) then
5294 
5295        n_headerHTML := Substitute(t_headerHTML, nid, n_nid_str,
5296                             n_status, n_to_role, r_dname, r_email,
5297                             n_start_date, n_due_date, n_end_date, n_from_user,
5298                             n_priority, n_comment, n_subject, str_dummy,
5299                             str_dummy, err_name, err_message, err_invalid,
5300                             err_expected, n_html_timezone);
5301 
5302 
5303        LOBSubstitute(t_html_body, nid, n_nid_str,
5304                             n_status, n_to_role, r_dname, r_email,
5305                             n_start_date, n_due_date, n_end_date,
5306                             n_from_user, n_priority, n_comment,
5307                             UrlEncode(n_subject), n_headerHTML,
5308                             g_LOBTable(g_html_messageIdx).temp_lob,
5309                             err_name, err_message, err_invalid,
5310                             err_expected, n_html_timezone);
5311 
5312        -- Add the local stylesheet references.
5313        LOBReplace(g_LOBTable(g_html_messageIdx).temp_lob,
5314                              '&'||'TEMPLATE_STYLE', g_template_style, FALSE);
5315 
5316        -- Add email response section
5317        LOBReplace(g_LOBTable(g_html_messageIdx).temp_lob,
5318                              '&'||'RESPONSE', n_response, FALSE);
5319 
5320        -- More Information processing
5321        LOBReplace(g_LOBTable(g_html_messageIdx).temp_lob,
5322                              '&'||'HISTORY', n_html_history, FALSE);
5323        LOBReplace(g_LOBTable(g_html_messageIdx).temp_lob,
5324                              '&'||'QUESTION', n_last_ques, FALSE);
5325 
5326        -- Add mailto section
5327        if (g_moreinfo = 'SUB') then
5328           n_mailto := GetMoreInfoMailTo(nid, 'NID['||to_char(nid)||'/'
5329                                  ||n_key||'@'||node||']', replyTo, n_subject);
5330           LOBReplace(g_LOBTable(g_html_messageIdx).temp_lob,
5331                                 '&'||'MAILTO', n_mailto, FALSE);
5332        else
5333           GetMailTo(nid, 'NID['||to_char(nid)||'/'||n_key||'@'||
5334                     node||']', replyTo, n_subject,
5335                     g_LOBTable(g_html_messageIdx).temp_lob);
5336        end if;
5337 
5338        -- If GUEST access is enabled and signature is required for response,
5339        -- no click here reponse link is provided to discourage signing under
5340        -- GUEST login
5341        if (g_sig_required = 'Y' and wf_mail.Send_AccessKey) then
5342          n_click_here := '';
5343        else
5344          n_click_here := getClickHereResponse(nid => nid, n_key => n_key,
5345                                               agent => agent,
5346                                               n_disp_click => n_disp_click,
5347                                               n_sig_policy => n_sig_policy);
5348        end if;
5349 
5350        LOBReplace(g_LOBTable(g_html_messageIdx).temp_lob,
5351                    '&'||'CLICK_HERE_RESPONSE', n_click_here, FALSE);
5352 
5353         -- Close of the HTML Body only where this is none
5354         -- This is only done for those tempaltes that the html/body tag
5355         -- was added. Otherwise there is a clash where there is nested
5356         -- HTML. Since this is rendered in a LOB, there should be no
5357         -- cause for the exising end /body tag should drop off the end
5358         -- like what would happen in the varchar2 version.
5359         if textForHtml then
5360            DBMS_LOB.WriteAppend(g_LOBTable(g_html_messageIdx).temp_lob,
5361                                 length('</BODY></HTML>'), '</BODY></HTML>');
5362         end if;
5363 
5364     end if;
5365 
5366     step := 'Getting HTML attachment';
5367      -- Get HTML attachment
5368      m_html := substrb(WFA_HTML.Detail2(nid, n_key, agent), 1, 32000);
5369      if isBiDi(r_language) then
5370         tag_pos := instrb(upper(m_html), '<HTML', 1);
5371         if tag_pos > 0 then
5372           dir_pos := instrb(upper(m_html), ' DIR="', 1);
5373           if dir_pos = 0 then
5374              buffer := substrb(m_html, 1, 5);
5375              buffer := buffer||' DIR="RTL" '||substrb(m_html, tag_pos+5);
5376              m_html := buffer;
5377           end if;
5378         end if;
5379      end if;
5380 
5381     subject   := n_subject;
5382     -- text_body := n_text_body;
5383     -- html_body := n_html_body;
5384     -- this is for the little attachment to the detail frame
5385     body_atth := m_html;
5386 
5387     -- if (DBMS_LOB.IsOpen(template_text)=1) then
5388     --   DBMS_LOB.Close(template_text);
5389     --   DBMS_LOB.FreeTemporary(template_text);
5390     -- end if;
5391     -- if (DBMS_LOB.IsOpen(template_html)=1) then
5392     --    DBMS_LOB.Close(template_html);
5393     --    DBMS_LOB.FreeTemporary(template_html);
5394     -- end if;
5395 
5396     if (wf_log_pkg.level_procedure >= fnd_log.g_current_runtime_level) then
5397          wf_log_pkg.string(WF_LOG_PKG.level_procedure,
5398                           'wf.plsql.WF_MAIL.GetLOBMessage3', 'END');
5399    end if;
5400 
5401 exception
5402   when no_program_unit then
5403     wf_core.context('WF_MAIL', 'GetLOBMessage3', to_char(nid), node,
5404                     'step -> '||step);
5405 
5406      if(g_html_messageIdx IS NOT NULL AND g_html_messageIdx > 0 ) then
5407        wf_temp_lob.releaseLob(g_LOBTable, g_html_messageIdx);
5408      end if;
5409 
5410      if(g_text_messageIdx IS NOT NULL AND g_text_messageIdx > 0 ) then
5411        wf_temp_lob.releaseLob(g_LOBTable, g_text_messageIdx);
5412      end if;
5413 
5414     raise;
5415   when others then
5416     -- First look for a wf_core error.
5417     wf_core.get_error(err_name, err_message, err_stack);
5418     -- If no wf_core error look for a sql error.
5419     if (err_name is null) then
5420         err_message := sqlerrm;
5421     end if;
5422 
5423     if(g_html_messageIdx is not null and g_html_messageIdx > 0 ) then
5424        wf_temp_lob.releaseLob(g_LOBTable, g_html_messageIdx);
5425     end if;
5426 
5427     if(g_text_messageIdx is not null and g_text_messageIdx > 0 ) then
5428        wf_temp_lob.releaseLob(g_LOBTable, g_text_messageIdx);
5429     end if;
5430 
5431     error_result := err_message||g_newLine||err_stack;
5432     wf_core.context('WF_MAIL', 'GetLOBMessage3', to_char(nid), node,
5433                     error_result, 'Step -> '||step);
5434 end GetLOBMessage3;
5435 
5436 
5437 -- GetLOBMessage2 - get email message data as a LOB
5438 --
5439 -- IN
5440 --   notification id
5441 --   mailer node name
5442 --   web agent path
5443 --   reply to
5444 --   recipient role
5445 --   lanague
5446 --   territory
5447 --   notification preference
5448 --   dsiplay name
5449 -- OUT
5450 --   message subject
5451 --   message body (text)
5452 --   message body (html)
5453 procedure GetLOBMessage2(
5454     nid       in  number,
5455     node      in  varchar2,
5456     agent     in  varchar2,
5457     replyto   in  varchar2,
5458     recipient in varchar2,
5459     language  in varchar2,
5460     territory in varchar2,
5461     ntf_pref  in varchar2,
5462     email     in varchar2,
5463     dname     in varchar2,
5464     subject   out NOCOPY varchar2,
5465     body_atth out NOCOPY varchar2,
5466     error_result in out NOCOPY varchar2)
5467 as
5468 
5469     err_name      varchar2(30);
5470     err_message   varchar2(2000);
5471     err_stack     varchar2(4000);
5472 
5473     bodyToken varchar2(1);
5474 
5475     no_program_unit exception;
5476     pragma exception_init(no_program_unit, -6508);
5477 
5478 begin
5479 
5480    wf_mail.getLobMessage3(nid, node, agent, replyto, recipient, language,
5481                           territory, ntf_pref, email, dname, 'Y',
5482                           subject, body_atth, error_result, bodyToken);
5483 
5484 exception
5485   when no_program_unit then
5486     wf_core.context('WF_MAIL', 'GetLOBMessage2', to_char(nid), node);
5487     raise;
5488   when others then
5489     -- First look for a wf_core error.
5490     wf_core.get_error(err_name, err_message, err_stack);
5491 
5492     -- If no wf_core error look for a sql error.
5493     if (err_name is null) then
5494         err_message := sqlerrm;
5495     end if;
5496 
5497     error_result := err_message||g_newLine||err_stack;
5498     wf_core.context('WF_MAIL', 'GetLOBMessage2', to_char(nid), node,
5499                     error_result);
5500 
5501 end getLOBMessage2;
5502 
5503 
5504 -- GetLOBMessage - get email message data as a LOB
5505 --
5506 -- IN
5507 --   notification id
5508 --   mailer node name
5509 --   web agent path
5510 -- OUT
5511 --   message subject
5512 --   message body (text)
5513 --   message body (html)
5514 procedure GetLOBMessage(
5515     nid       in  number,
5516     node      in  varchar2,
5517     agent     in  varchar2,
5518     replyto   in  varchar2,
5519     subject   out NOCOPY varchar2,
5520     body_atth out NOCOPY varchar2,
5521     error_result in out NOCOPY varchar2)
5522 as
5523     n_to_role     varchar2(320);
5524     r_dname       varchar2(360);
5525     r_email       varchar2(2000);
5526     r_ntf_pref    varchar2(240);
5527     r_language    varchar2(30);
5528     r_territory   varchar2(30);
5529     r_orig_system varchar2(30);
5530     r_orig_system_id number;
5531     r_installed   varchar2(1);
5532     err_name      varchar2(30);
5533     err_message   varchar2(2000);
5534     err_stack     varchar2(4000);
5535 
5536     no_program_unit exception;
5537     pragma exception_init(no_program_unit, -6508);
5538 
5539 begin
5540     -- Get notification information
5541     begin
5542       select RECIPIENT_ROLE
5543       into   n_to_role
5544       from   WF_NOTIFICATIONS
5545       where  NOTIFICATION_ID = nid;
5546     exception
5547       when no_data_found then
5548         wf_core.token('NID', to_char(nid));
5549         wf_core.raise('WFNTF_NID');
5550     end;
5551 
5552     -- Get Recipient information
5553     Wf_Directory.GetRoleInfoMail(n_to_role, r_dname, r_email, r_ntf_pref,
5554                                  r_language, r_territory, r_orig_system,
5555                                  r_orig_system_id, r_installed);
5556 
5557    if r_installed = 'N' then
5558       r_language := 'AMERICAN';
5559       r_territory := 'AMERICA';
5560    end if;
5561 
5562    wf_mail.getLobMessage2(nid, node, agent, replyto, n_to_role, r_language,
5563                           r_territory, r_ntf_pref, r_email, r_dname,
5564                           subject, body_atth, error_result);
5565 
5566 exception
5567   when no_program_unit then
5568     wf_core.context('WF_MAIL', 'GetLOBMessage', to_char(nid), node);
5569     raise;
5570   when others then
5571     -- First look for a wf_core error.
5572     wf_core.get_error(err_name, err_message, err_stack);
5573 
5574     -- If no wf_core error look for a sql error.
5575     if (err_name is null) then
5576         err_message := sqlerrm;
5577     end if;
5578 
5579     error_result := err_message||g_newLine||err_stack;
5580     wf_core.context('WF_MAIL', 'GetLOBMessage', to_char(nid), node,
5581                     error_result);
5582 end GetLOBMessage;
5583 
5584 -- GetSummary - get summary messages for one role
5585 --              ( with LOB support )
5586 -- IN
5587 --   role name
5588 --   display role name
5589 --   mailer node name
5590 -- OUT
5591 --   message subject
5592 --   message body (text)
5593 --   message body (html)
5594 procedure GetSummary(
5595     role      in  varchar2,
5596     dname     in  varchar2,
5597     node      in  varchar2,
5598     subject   out NOCOPY varchar2,
5599     body_text out NOCOPY varchar2)
5600 as
5601     lob varchar2(1);
5602 begin
5603     GetSummary(role, dname, node, subject, body_text,lob);
5604     if lob = 'Y' then
5605        wf_core.context('WF_MAIL', 'GetSummary', role, node);
5606        wf_core.raise('WFMLR_SUMMARY_TOOBIG');
5607     end if;
5608 exception
5609   when others then
5610     wf_core.context('WF_MAIL', 'GetSummary', role, node);
5611     raise;
5612 end GetSummary;
5613 
5614 -- GetSummary2 - get summary messages for one role
5615 -- Support the render flag for Applications Framework.
5616 -- If set, no body will be rendered as it will be
5617 -- deferred to the middle tier.
5618 -- IN
5619 --   role name
5620 --   display role name
5621 --   mailer node name
5622 --   content type
5623 -- OUT
5624 --   message subject
5625 --   message body (text)
5626 --   message body (html)
5627 --   lob (Y or N)
5628 procedure GetSummary2(
5629     role      in  varchar2,
5630     dname     in  varchar2,
5631     node      in  varchar2,
5632     renderBody in varchar2,
5633     contType  in  varchar2,
5634     subject   out NOCOPY varchar2,
5635     body_text out NOCOPY varchar2,
5636     lob       out NOCOPY varchar2)
5637 as
5638     n_key         varchar2(80);
5639     n_subject     varchar2(240);
5640     n_summ        varchar2(32000);
5641     n_buf         varchar2(32000);
5642     templateName  varchar2(30);
5643     altTempl      varchar2(40);
5644     templateType  varchar2(8) := 'WFMAIL';
5645     t_subject     varchar2(240);
5646     t_body        varchar2(32000);
5647     t_timezone    varchar2(240);
5648     t_html_body   varchar2(32000);
5649     n_timezone    varchar2(240);
5650     nid           pls_integer;
5651     to_name       varchar2(320);
5652     colon         pls_integer;
5653     rorig_system  varchar2(30);
5654     rorig_system_id number;
5655     priority_text varchar2(240);
5656 
5657     ntf_pref      varchar2(240);
5658     dummyStr      varchar2(2000);
5659     r_language    varchar2(30);
5660     r_displayName varchar2(360);
5661     tag_pos       pls_integer;
5662     dir_pos       pls_integer;
5663     buffer        varchar2(32000);
5664 
5665 
5666     lob_init      boolean := FALSE;
5667     -- temp_text     CLOB;
5668     temp_textIdx  pls_integer;
5669 
5670     -- Bug 1753464 included sort order for the query
5671     -- Bug 2439529 Altered query to use UNION instead of OR.
5672     cursor c1 is
5673       select NOTIFICATION_ID, RECIPIENT_ROLE, ACCESS_KEY, PRIORITY, DUE_DATE
5674       from WF_NOTIFICATIONS
5675       where STATUS = 'OPEN'
5676         and RECIPIENT_ROLE IN
5677              (select role from dual
5678               union
5679               select UR.ROLE_NAME
5680               from WF_USER_ROLES UR
5681               where UR.USER_ORIG_SYSTEM = rorig_system
5682                 and UR.USER_ORIG_SYSTEM_ID = rorig_system_id
5683                 and UR.USER_NAME = role)
5684       order by PRIORITY desc, DUE_DATE asc, NOTIFICATION_ID asc ;
5685 
5686 begin
5687 
5688     if (wf_log_pkg.level_event >= fnd_log.g_current_runtime_level) then
5689         wf_log_pkg.string(wf_log_pkg.level_event,
5690                           'wf.plsql.WF_MAIL.GetSummary2',
5691                            'BEGIN');
5692     end if;
5693 
5694     g_wfmonId := wf_core.translate('WFMON_ID');
5695     g_to := wf_core.translate('TO');
5696     g_priority := wf_core.translate('PRIORITY');
5697     g_dueDate := wf_core.translate('WFMON_DUE_DATE');
5698 
5699     -- Bug# 2358498 - flag to indicate if the sumamry is LOB
5700     lob := 'N';
5701 
5702     if (renderBody = 'Y') then
5703        templateName := 'SUMMARY';
5704     else
5705        templateName := 'SUMHTML';
5706     end if;
5707 
5708     -- We have the basic template name. Now check to see if has been
5709     -- redirected using a mailer configuration parameter of the same name.
5710     altTempl := WF_MAILER_PARAMETER.GetValueForCorr('WFMAIL', templateName);
5711     colon := instrb(altTempl, ':', 1);
5712     if colon > 0 then
5713        templateType := substrb(altTempl, 1, colon -1);
5714        templateName := substrb(altTempl, colon + 1, length(altTempl)-colon);
5715     end if;
5716 
5717     -- Get notification information
5718 
5719     -- Get template 'SUMMARY'
5720     begin
5721       select SUBJECT, BODY, HTML_BODY
5722       into   t_subject, t_body, t_html_body
5723       from   WF_MESSAGES_VL
5724       where  NAME = templateName
5725       and    TYPE = templateType;
5726     exception
5727       when no_data_found then
5728         wf_core.token('NAME', templateName);
5729         wf_core.token('TYPE', templateType);
5730         wf_core.raise('WFNTF_MESSAGE');
5731     end;
5732 
5733     -- Retrieve role orig_system ids for index access
5734     -- <<sstomar>> : It is OK to use OLD API here,
5735     ---              instead of Wf_Directory.GetRoleInfoMail2
5736     Wf_Directory.GetRoleInfoMail(role, r_displayName, dummyStr, ntf_pref,
5737                                  r_language, dummyStr, rorig_system,
5738                                  rorig_system_id, dummyStr);
5739 
5740     t_subject := substrb(replace(t_subject, '&'||'USER_NAME',
5741                          r_displayName), 1, 240);
5742 
5743     if (rorig_system is null or rorig_system = '') then
5744       wf_core.token('ROLE', role);
5745       wf_core.raise('WFNTF_ROLE');
5746     end if;
5747 
5748     if contType = g_ntfDocHtml then
5749        if (t_html_body is null) then
5750          t_html_body := replace(t_body, g_newLine,
5751                                 '<BR>'||g_newLine);
5752          -- Ensure the direction of the text is correct for the language
5753          if isBiDi(r_language) then
5754             t_html_body := '<HTML DIR="RTL"><BODY>'||t_html_body;
5755          else
5756             t_html_body := '<HTML><BODY>'||t_html_body;
5757          end if;
5758        else
5759          -- Ensure that the direction of the text is correctly specified.
5760          if isBiDi(r_language) then
5761             tag_pos := instrb(upper(t_html_body), '<HTML', 1);
5762             if tag_pos > 0 then
5763               dir_pos := instrb(upper(t_html_body), ' DIR="', 1);
5764               if dir_pos = 0 then
5765                  buffer := substrb(t_html_body, tag_pos, 5);
5766                  buffer := buffer||' DIR="RTL" '||
5767                            substrb(t_html_body, tag_pos+5);
5768                  t_html_body := buffer;
5769               end if;
5770             end if;
5771          end if;
5772        end if;
5773     end if;
5774 
5775 
5776     -- Substitute USER_NAME with role display name
5777     t_timezone := wf_mail_util.getTimezone(contType);
5778 
5779     if contType = g_ntfDocText then
5780        t_body := substrb(replace(t_body, '&'||'USER_NAME', dname), 1, 32000);
5781        t_body := substrb(replace(t_body, '&'||'TIMEZONE', t_timezone), 1, 32000);
5782     else
5783        t_html_body := substrb(replace(t_html_body, '&'||'TEMPLATE_STYLE',
5784                               g_template_style), 1, 32000);
5785        t_html_body := substrb(replace(t_html_body, '&'||'USER_NAME', dname),
5786                               1, 32000);
5787        t_html_body := substrb(replace(t_html_body, '&'||'TIMEZONE',
5788                               t_timezone), 1, 32000);
5789     end if;
5790 
5791     if (renderBody = 'Y') then
5792        -- Prepare summary header
5793        if contType = g_ntfDocText then
5794           n_summ := g_newLine;
5795           n_summ := n_summ||rpad(g_wfmonId, 7)||' ';
5796           n_summ := n_summ||rpad(g_to, 42)||' ';
5797           n_summ := n_summ||rpad(g_priority, 12)||' ';
5798           n_summ := n_summ||rpad(g_dueDate, 12)||
5799                             g_newLine;
5800           n_summ := n_summ||'------- ------------------------------------------ ';
5801           n_summ := n_summ||'------------ ------------'||g_newLine;
5802 
5803           n_buf  := '';
5804        end if;
5805 
5806 
5807        for rec in c1 loop
5808 
5809          nid := rec.notification_id;
5810          n_key := rec.access_key;
5811          to_name := rec.recipient_role;
5812 
5813          -- Get and token subsitute subject
5814          n_subject := WF_NOTIFICATION.GetSubject(nid, 'text/plain');
5815 
5816          if contType = g_ntfDocText then
5817             n_buf := lpad(to_char(nid), 7, ' ')||' '||
5818                      rpad(substr(dname, 1, 42), 42, ' ')||' ';
5819             if (rec.priority > 66) then
5820               --Bug 2774891 fix - sacsharm
5821               --priority_text := wf_core.substitute('WFTKN', 'HIGH');
5822               priority_text := wf_core.substitute('WFTKN', 'LOW');
5823             elsif (rec.priority > 33) then
5824               priority_text := wf_core.substitute('WFTKN', 'NORMAL');
5825             else
5826               --Bug 2774891 fix - sacsharm
5827               --priority_text := wf_core.substitute('WFTKN', 'LOW');
5828               priority_text := wf_core.substitute('WFTKN', 'HIGH');
5829             end if;
5830 
5831             n_buf := n_buf||lpad(priority_text, 12, ' ');
5832             n_buf := n_buf||' '||to_char(rec.due_date)||g_newLine;
5833             n_buf := n_buf||WordWrap(n_subject, 1);
5834 
5835             n_summ := n_summ||n_buf||g_newLine||g_newLine;
5836          end if;
5837 
5838          -- Bug 2358498 write content to LOB if there is a possibility
5839          -- that the size might go beyond 32K
5840          if length(n_summ) > 30000 then
5841            lob := 'Y';
5842 
5843            if NOT lob_init then
5844              lob_init := TRUE;
5845              -- DBMS_LOB.CreateTemporary(g_text_message, TRUE, DBMS_LOB.SESSION);
5846              -- DBMS_LOB.Open(g_text_message, DBMS_LOB.LOB_READWRITE);
5847 
5848              -- This g_text_messageIdx Locator will be returned back to pool within caller
5849              -- GenerateSummaryDoc -> getBodyPart
5850              g_text_messageIdx := wf_temp_lob.getLob(g_LOBTable);
5851              if contType = g_ntfDocText then
5852                 DBMS_LOB.WriteAppend(g_LOBTable(g_text_messageIdx).temp_lob,
5853                                      length(t_body), t_body);
5854              else
5855                 DBMS_LOB.WriteAppend(g_LOBTable(g_text_messageIdx).temp_lob,
5856                                      length(t_html_body), t_html_body);
5857              end if;
5858 
5859              -- DBMS_LOB.CreateTemporary(temp_text, FALSE, DBMS_LOB.CALL);
5860              temp_textIdx := wf_temp_lob.getLob(g_LOBTable);
5861            end if;
5862            DBMS_LOB.WriteAppend(g_LOBTable(temp_textIdx).temp_lob,
5863                                 length(n_summ), n_summ);
5864            n_summ := '';
5865          end if;
5866        end loop;
5867        if lob = 'Y' then
5868           t_body := '';
5869           t_html_body := '';
5870           DBMS_LOB.WriteAppend(g_LOBTable(temp_textIdx).temp_lob,
5871                                length(n_summ), n_summ);
5872           LOBReplace(g_LOBTable(g_text_messageIdx).temp_lob, '&'||'SUMMARY',
5873                      g_LOBTable(temp_textIdx).temp_lob, FALSE);
5874 
5875            -- Release temp_textIdx locator too. << bug 6511028 >>
5876            wf_temp_lob.releaseLob(g_LOBTable, temp_textIdx);
5877 
5878        else
5879           t_body := replace(t_body, '&'||'SUMMARY', n_summ);
5880        end if;
5881     end if; -- RENDERBODY
5882 
5883     subject   := t_subject;
5884     if contType = g_ntfDocText then
5885        body_text := t_body;
5886     else
5887        body_text := t_html_body;
5888     end if;
5889 
5890     if (wf_log_pkg.level_event >= fnd_log.g_current_runtime_level) then
5891         wf_log_pkg.string(wf_log_pkg.level_event,
5892                           'wf.plsql.WF_MAIL.GetSummary2',
5893                            'END');
5894     end if;
5895 
5896 
5897 exception
5898   when others then
5899      -- Release temp_textIdx and g_text_messageIdx locators in case of any exception .
5900      if(lob = 'Y' ) then
5901        if (temp_textIdx is not null and temp_textIdx > 0 ) then
5902         wf_temp_lob.releaseLob(g_LOBTable, temp_textIdx);
5903        end if;
5904 
5905        if ( g_text_messageIdx is not null and g_text_messageIdx > 0 ) then
5906           wf_temp_lob.releaseLob(g_LOBTable, g_text_messageIdx);
5907        end if;
5908 
5909      end if;
5910 
5911 
5912     wf_core.context('WF_MAIL', 'GetSummary2', role, node, contType);
5913     raise;
5914 end GetSummary2;
5915 
5916 -- GetSummary - get summary messages for one role
5917 --              ( with LOB support )
5918 -- IN
5919 --   role name
5920 --   display role name
5921 --   mailer node name
5922 -- OUT
5923 --   message subject
5924 --   message body (text)
5925 --   message body (html)
5926 --   lob (Y or N)
5927 procedure GetSummary(
5928     role      in  varchar2,
5929     dname     in  varchar2,
5930     node      in  varchar2,
5931     subject   out NOCOPY varchar2,
5932     body_text out NOCOPY varchar2,
5933     lob       out NOCOPY varchar2)
5934 as
5935 
5936 begin
5937    GetSummary2(role, dname, node, 'Y', 'text/plain', subject, body_text, lob);
5938 exception
5939   when others then
5940     wf_core.context('WF_MAIL', 'GetSummary', role, node);
5941     raise;
5942 end GetSummary;
5943 
5944 
5945 -- initFetchLOB
5946 --
5947 -- IN
5948 -- Document type (TEXT or HTML)
5949 --
5950 procedure InitFetchLOB(doc_type VARCHAR2,
5951                        doc_length OUT NOCOPY NUMBER)
5952 is
5953 begin
5954    if doc_type = g_ntfDocHtml then
5955       g_html_chunk := 0;
5956       -- doc_length := DBMS_LOB.GetLength(g_html_message);
5957       doc_length := DBMS_LOB.GetLength(g_LOBTable(g_html_messageIdx).temp_lob);
5958    else
5959       -- Always assume that the caller wants the TEXT
5960       g_text_chunk := 0;
5961       -- doc_length := DBMS_LOB.GetLength(g_text_message);
5962       doc_length := DBMS_LOB.GetLength(g_LOBTable(g_text_messageIdx).temp_lob);
5963    end if;
5964 end InitFetchLOB;
5965 
5966 
5967 -- FetchLOBContent
5968 --
5969 -- IN
5970 -- type of document to fetch TEXT/HTML
5971 -- End of LOB marker
5972 -- OUT
5973 -- 32K chunk of the LOB
5974 --
5975 -- Use the API in the following manner
5976 -- WF_MAIL.InitFetchLob(g_ntfDocText)
5977 -- while not clob_end loop
5978 --    WF_MAIL.FetchLobContent(cBuf, g_ntfDocText, clob_end);
5979 --    ...
5980 -- end loop;
5981 --
5982 procedure FetchLOBContent(buffer OUT NOCOPY VARCHAR2,
5983                           doc_type IN VARCHAR2,
5984                           end_of_clob IN OUT NOCOPY NUMBER)
5985 is
5986    pos NUMBER;
5987    buffer_length pls_integer := 16000;
5988 begin
5989    if doc_type = g_ntfDocHtml then
5990       pos := (buffer_length * nvl(g_html_chunk,0))+1;
5991       -- DBMS_LOB.Read(g_html_message, buffer_length, pos, buffer);
5992       DBMS_LOB.Read(g_LOBTable(g_html_messageIdx).temp_lob, buffer_length, pos, buffer);
5993       if pos+buffer_length > DBMS_LOB.GetLength(g_LOBTable(g_html_messageIdx).temp_lob) then
5994          end_of_clob := 1;
5995          g_html_chunk := 0;
5996       else
5997          g_html_chunk := g_html_chunk + 1;
5998       end if;
5999    else
6000       -- Always assume that the caller wants the TEXT
6001       pos := (buffer_length * nvl(g_text_chunk,0))+1;
6002       -- DBMS_LOB.Read(g_text_message, buffer_length, pos, buffer);
6003       DBMS_LOB.Read(g_LOBTable(g_text_messageIdx).temp_lob, buffer_length, pos, buffer);
6004       if pos+buffer_length > DBMS_LOB.GetLength(g_LOBTable(g_text_messageIdx).temp_lob) then
6005          end_of_clob := 1;
6006          g_text_chunk := 0;
6007       else
6008          g_text_chunk := g_text_chunk + 1;
6009       end if;
6010    end if;
6011 exception
6012   when others then
6013       WF_CORE.Context('WF_MAIL','FetchLOBContent',doc_type,
6014                       to_char(pos)||':'||buffer);
6015       raise;
6016 end FetchLOBContent;
6017 
6018 -- CloseLOB - Close the message LOBs ready for use again later
6019 --
6020 procedure CloseLOB(doc_type in VARCHAR2)
6021 is
6022 begin
6023    if doc_type = g_ntfDocHtml then
6024       -- DBMS_LOB.close(g_html_message);
6025       -- DBMS_LOB.FreeTemporary(g_html_message);
6026       wf_temp_lob.releaseLob(g_LOBTable, g_html_messageIdx);
6027    else
6028       -- DBMS_LOB.close(g_text_message);
6029       -- DBMS_LOB.FreeTemporary(g_text_message);
6030       wf_temp_lob.releaseLob(g_LOBTable, g_text_messageIdx);
6031    end if;
6032 exception
6033    when others then
6034       WF_CORE.Context('WF_MAIL','CloseLOB', doc_type);
6035       raise;
6036 end;
6037 
6038 -- CloseLOB - Close the message LOBs ready for use again later
6039 --
6040 procedure CloseLOB
6041 is
6042 begin
6043    WF_MAIL.CloseLOB(g_ntfDocText);
6044    WF_MAIL.CloseLOB(g_ntfDocHtml);
6045 exception
6046    when others then
6047       WF_CORE.Context('WF_MAIL','CloseLOB');
6048       raise;
6049 end;
6050 
6051 
6052 -- FetchUrlContent - Fetched the content from the global buffer which
6053 --                   populated by GetUrlContent().
6054 --
6055 -- IN
6056 --   piece_count - the index to the url_content_array.
6057 -- OUT
6058 --   piece_value - the data stored in the global content_array table.
6059 function FetchUrlContent(piece_count in number,
6060                          error_result in out NOCOPY varchar2) return varchar2 as
6061 begin
6062     return(wf_mail.content_array(piece_count));
6063 
6064 exception
6065   when NO_DATA_FOUND then
6066     return('NO_DATA_FOUND');
6067 
6068   when others then
6069     error_result := sqlerrm;
6070     wf_core.context('WF_MAIL', 'FetchUrlContent', piece_count);
6071 
6072 end FetchUrlContent;
6073 
6074 
6075 -- GetUrlContent - get URL content
6076 --
6077 -- IN
6078 --   url address id
6079 -- OUT
6080 --   piece_count
6081 --   error result
6082 procedure GetUrlContent(
6083     url          in  varchar2,
6084     piece_count  out NOCOPY number,
6085     error_result in out NOCOPY varchar2)
6086 
6087 as
6088     no_program_unit exception;
6089     pragma exception_init(no_program_unit, -6508);
6090 
6091     url_pieces utl_http.html_pieces;
6092     err_name      varchar2(30);
6093     err_message   varchar2(2000);
6094     err_stack     varchar2(4000);
6095     content_array url_content_array;
6096 
6097 begin
6098 
6099     url_pieces := utl_http.request_pieces(url);
6100 
6101     for l_rec_num in 1..url_pieces.count loop
6102         wf_mail.content_array(l_rec_num) := url_pieces(l_rec_num);
6103         piece_count := l_rec_num;
6104     end loop;
6105 
6106 exception
6107 
6108   when utl_http.init_failed then
6109     error_result := 'UTL_HTTP.INIT_FAILED';
6110     error_result := error_result || sqlerrm;
6111     wf_core.context('WF_MAIL', 'GetUrlContent', url);
6112 
6113   when utl_http.request_failed then
6114     error_result := 'UTL_HTTP.REQUEST_FAILED';
6115     error_result := error_result || sqlerrm;
6116     wf_core.context('WF_MAIL', 'GetUrlContent', url);
6117 
6118   when no_program_unit then
6119     wf_core.context('WF_MAIL', 'GetUrlContent', url);
6120     raise;
6121   when others then
6122     error_result := sqlerrm;
6123     wf_core.context('WF_MAIL', 'GetUrlContent', url);
6124 
6125 end GetUrlContent;
6126 
6127 -- GetDocContent - get Document content
6128 --
6129 -- IN
6130 --   notification id
6131 --   document attribute name
6132 --   display type
6133 -- OUT
6134 --   document content
6135 --   error result
6136 procedure GetDocContent(
6137     nid          in  number,
6138     docattrname  in  varchar2,
6139     disptype     in  varchar2,
6140     doccontent   out NOCOPY varchar2,
6141     error_result in out NOCOPY varchar2)
6142 
6143 as
6144     no_program_unit exception;
6145     pragma exception_init(no_program_unit, -6508);
6146 
6147     err_name      varchar2(30);
6148     err_message   varchar2(2000);
6149     err_stack     varchar2(4000);
6150 begin
6151 
6152     doccontent := Wf_Notification.GetAttrDoc(nid, docattrname, disptype);
6153 
6154 exception
6155   when no_program_unit then
6156     wf_core.context('WF_MAIL', 'GetDocContent', docattrname);
6157     raise;
6158   when others then
6159     -- First look for a wf_core error.
6160     wf_core.get_error(err_name, err_message, err_stack);
6161 
6162     -- If no wf_core error look for a sql error.
6163     if (err_name is null) then
6164         err_message := sqlerrm;
6165     end if;
6166 
6167     error_result := err_message;
6168     wf_core.context('WF_MAIL', 'GetDocContent', docattrname);
6169 
6170 end GetDocContent;
6171 
6172 -- GetLOBDocContent - get Document content
6173 --   Returns the document type of the PLSQLCLOB document
6174 --
6175 -- IN
6176 --   notification id
6177 --   document attribute name
6178 --   display type
6179 -- OUT
6180 --   document content
6181 --   error result
6182 procedure GetLOBDocContent(
6183     nid          in  number,
6184     docattrname  in  varchar2,
6185     disptype     in  varchar2,
6186     error_result in out NOCOPY varchar2)
6187 as
6188   doctype varchar2(500);
6189   no_program_unit exception;
6190   pragma exception_init(no_program_unit, -6508);
6191   err_name      varchar2(30);
6192   err_message   varchar2(2000);
6193   err_stack     varchar2(4000);
6194 begin
6195 
6196   Wf_Mail.GetLOBDocContent(nid, docattrname, disptype, doctype, error_result);
6197 
6198 exception
6199   when no_program_unit then
6200     wf_core.context('WF_MAIL', 'oldGetLOBDocContent', docattrname);
6201     raise;
6202   when others then
6203     -- First look for a wf_core error.
6204     wf_core.get_error(err_name, err_message, err_stack);
6205 
6206     -- If no wf_core error look for a sql error.
6207     if (err_name is null) then
6208         err_message := sqlerrm;
6209     end if;
6210 
6211     error_result := err_message;
6212     wf_core.context('WF_MAIL', 'oldGetDocLOBContent', docattrname);
6213 end GetLOBDocContent;
6214 
6215 -- GetLOBDocContent - get Document content
6216 --
6217 -- IN
6218 --   notification id
6219 --   document attribute name
6220 --   display type
6221 -- OUT
6222 --   document type
6223 --   document content
6224 --   error result
6225 procedure GetLOBDocContent(
6226     nid          in  number,
6227     docattrname  in  varchar2,
6228     disptype     in  varchar2,
6229     doctype      out NOCOPY varchar2,
6230     error_result in  out NOCOPY varchar2)
6231 
6232 as
6233     no_program_unit exception;
6234     pragma exception_init(no_program_unit, -6508);
6235 
6236     doc varchar(32000) := '';
6237 
6238     aname         varchar2(30);
6239     err_name      varchar2(30);
6240     err_message   varchar2(2000);
6241     err_stack     varchar2(4000);
6242 begin
6243     -- There is a difference betwen PLSQL: and PLSQLCLOB: documents
6244     -- First go for the PLSQL: if that returns the name of the
6245     -- attribute, then try the PLSQLCLOB:
6246     doc := WF_NOTIFICATION.GetAttrDoc(nid, docattrname, disptype);
6247     if disptype = g_ntfDocHtml then
6248        -- DBMS_LOB.CreateTemporary(g_html_message, true, dbms_lob.SESSION);
6249        -- DBMS_LOB.Open(g_html_message, DBMS_LOB.LOB_READWRITE);
6250        g_html_messageIdx := wf_temp_lob.getLob(g_LOBTable);
6251        if doc = '&'||docattrname then
6252           Wf_Notification.GetAttrCLOB(nid, docattrname, disptype,
6253                                       g_LOBTable(g_html_messageIdx).temp_lob, doctype, aname);
6254        else
6255           DBMS_LOB.Write(g_LOBTable(g_html_messageIdx).temp_lob, length(doc), 1, doc);
6256        end if;
6257     else
6258        -- DBMS_LOB.CreateTemporary(g_text_message, true, dbms_lob.SESSION);
6259        -- DBMS_LOB.Open(g_text_message, DBMS_LOB.LOB_READWRITE);
6260        g_text_messageIdx := wf_temp_lob.getLob(g_LOBTable);
6261        if doc = '&'||docattrname then
6262           Wf_Notification.GetAttrCLOB(nid, docattrname, disptype,
6263                                       g_LOBTable(g_text_messageIdx).temp_lob, doctype,  aname);
6264        else
6265           DBMS_LOB.Write(g_LOBTable(g_text_messageIdx).temp_lob, length(doc), 1, doc);
6266        end if;
6267     end if;
6268 
6269 
6270 exception
6271   when no_program_unit then
6272     wf_core.context('WF_MAIL', 'GetLOBDocContent', docattrname);
6273     raise;
6274   when others then
6275     -- First look for a wf_core error.
6276     wf_core.get_error(err_name, err_message, err_stack);
6277 
6278     -- If no wf_core error look for a sql error.
6279     if (err_name is null) then
6280         err_message := sqlerrm;
6281     end if;
6282 
6283     error_result := err_message;
6284     wf_core.context('WF_MAIL', 'GetDocLOBContent', docattrname);
6285 
6286 end GetLOBDocContent;
6287 
6288 -- RemoveSpace (PRIVATE)
6289 --   Removes white spaces between response prompt and colon, from colon
6290 --   to the quote.
6291 -- IN
6292 --   body       - Email response body
6293 --   resp_attrs - Response attribtue info for the current notification
6294 -- OUT
6295 --   body - Email body with the white spaces removed wherever required
6296 
6297 function RemoveSpace(body       in varchar2,
6298                      resp_attrs in resp_attrs_t)
6299 return varchar2
6300 is
6301   colonPos pls_integer;
6302   quotePos pls_integer;
6303   prompPos  pls_integer;
6304   prompt    varchar2(80);
6305   tmpStr   varchar2(32000);
6306   tmpBody  varchar2(32000);
6307 
6308 begin
6309 
6310   tmpBody := body;
6311 
6312   -- remove spaces from the response prompt till the following colon
6313   for i in 1..resp_attrs.COUNT loop
6314      prompt := resp_attrs(i).attr_prompt;
6315      prompPos := instrb(tmpBody, prompt, 1);
6316      while (prompPos > 0) loop
6317         prompPos := prompPos + length(prompt) - 1;
6318         colonPos := instrb(tmpBody, ':', prompPos);
6319         tmpStr := substrb(tmpBody, prompPos + 1, (colonPos - prompPos) - 1);
6320         tmpStr := replace(tmpStr, g_tab);
6321         tmpStr := replace(tmpStr, g_newLine);
6322         if (ltrim(rtrim(tmpStr)) is NULL and colonPos > 0) then
6323            tmpBody := substrb(tmpBody, 1, prompPos) || substrb(tmpBody, colonPos);
6324         end if;
6325         prompPos := instrb(tmpBody, prompt, prompPos);
6326      end loop;
6327   end loop;
6328 
6329   -- first occurence of a colon
6330   colonPos := instrb(tmpBody, ':', 1);
6331   -- loop until there is a colon
6332   while colonPos > 0 loop
6333     -- first occurence of a double quote after the colon
6334     quotePos := instrb(tmpBody, '"', colonPos);
6335     if (quotePos > 0) then
6336        -- examine the string between the colon the following double quote
6337        -- and replace tab and newline
6338        tmpStr := substrb(tmpBody, colonPos+1, (quotePos-colonPos)-1);
6339        tmpStr := replace(tmpStr, g_tab);
6340        tmpStr := replace(tmpStr, g_newLine);
6341        -- if trim results in NULL, the string need not exist within the email
6342        if (ltrim(rtrim(tmpStr)) IS NULL) then
6343           tmpBody := substrb(tmpBody, 1, colonPos) || substrb(tmpBody, quotePos);
6344        end if;
6345     end if;
6346 
6347     -- first occurence of a single quote after the colon
6348     quotePos := instrb(tmpBody, '''', colonPos);
6349     if (quotePos > 0) then
6350        -- examine the string between the colon the following single quote
6351        -- and replace tab and newline
6352        tmpStr := substrb(tmpBody, colonPos+1, (quotePos-colonPos)-1);
6353        tmpStr := replace(tmpStr, g_tab);
6354        tmpStr := replace(tmpStr, g_newLine);
6355        -- if trim results in NULL, the string need not exist within the email
6356        if (ltrim(rtrim(tmpStr)) IS NULL) then
6357           tmpBody := substrb(tmpBody, 1, colonPos) || substrb(tmpBody, quotePos);
6358        end if;
6359     end if;
6360     colonPos := instrb(tmpBody, ':', colonPos+1);
6361   end loop;
6362   return tmpBody;
6363 exception
6364   when others then
6365     wf_core.context('WF_MAIL', 'RemoveSpace');
6366     raise;
6367 end RemoveSpace;
6368 
6369 -- PutMessage
6370 --   Reply processor.  Read body of a notification reply, set any
6371 --   response attributes, and complete response.
6372 --   Used by the notification mail response processor.
6373 -- IN
6374 --   notification id
6375 --   mailer node name
6376 --   response body text
6377 --   email 'from' address
6378 procedure PutMessage(
6379     nid       in  number,
6380     node      in  varchar2,
6381     resp_body in  varchar2,
6382     from_addr in  varchar2,
6383     error_result in out NOCOPY varchar2)
6384 as
6385     TYPE stack_t IS TABLE OF
6386       varchar2(32000) INDEX BY BINARY_INTEGER;
6387 
6388     stack stack_t;
6389     contentStack stack_t;
6390     resp_attrs resp_attrs_t;
6391 
6392     value   varchar2(4000);
6393     prompt  varchar2(2000);
6394     buffer  varchar2(32000);
6395     tmpbuf  varchar2(32000);
6396     token   varchar2(32000);
6397     loc     pls_integer;
6398     stk     pls_integer;
6399     i       pls_integer;
6400     j       pls_integer;
6401     k       pls_integer;
6402     l       pls_integer;
6403     prompPos pls_integer;
6404     nextPos  pls_integer := 0;
6405     tmpPos   pls_integer;
6406     dleft   number;
6407     dright  number;
6408     sleft   number;
6409     sright  number;
6410     left    number;
6411     right   number;
6412     msg_name varchar2(30);
6413     msg_type varchar2(8);
6414     stat     varchar2(8);
6415     response boolean := FALSE;
6416     lk_type  varchar2(100);
6417     lk_meaning varchar2(100);
6418     n_sig_policy varchar2(100);
6419 
6420     -- Select msg response attrs.
6421     -- Order-by is to insure longest prompts are processed first to prevent
6422     -- problems where one prompt is a substring of another prompt.
6423     cursor c1 is
6424       select NAME, DISPLAY_NAME, TYPE, FORMAT
6425       from   WF_MESSAGE_ATTRIBUTES_VL
6426       where  MESSAGE_NAME = msg_name
6427       and    MESSAGE_TYPE = msg_type
6428       and    SUBTYPE = 'RESPOND'
6429       and    TYPE not in ('FORM', 'URL')
6430       order by length(DISPLAY_NAME) desc;
6431 
6432     no_program_unit exception;
6433     pragma exception_init(no_program_unit, -6508);
6434 
6435 begin
6436     -- Get notification message and status
6437     begin
6438       select MESSAGE_NAME, MESSAGE_TYPE, STATUS
6439       into   msg_name, msg_type, stat
6440       from   WF_NOTIFICATIONS
6441       where  NOTIFICATION_ID = nid;
6442     exception
6443       when no_data_found then
6444         wf_core.token('NID', to_char(nid));
6445         wf_core.raise('WFNTF_NID');
6446     end;
6447     i := 1;
6448     -- collect all the response attributes and their details
6449     -- for the notification
6450     for rec in c1 loop
6451       resp_attrs(i).attr_prompt := rec.display_name;
6452       resp_attrs(i).attr_type := rec.type;
6453       resp_attrs(i).attr_name := rec.name;
6454       resp_attrs(i).attr_format := rec.format;
6455       i := i + 1;
6456     end loop;
6457 
6458     -- Bug 2375920 get the signature policy for the notification and
6459     -- raise error is the policy is invalid
6460     Wf_Mail.GetSignaturePolicy(nid, n_sig_policy);
6461     if (n_sig_policy is not NULL and upper(n_sig_policy) <> 'DEFAULT') then
6462       if(upper(n_sig_policy) = 'PSIG_ONLY') then
6463          wf_core.context('WF_MAIL', 'PutMessage', to_char(nid), node, from_addr);
6464          wf_core.token('NID', to_char(nid));
6465          wf_core.raise('WFRSPR_PWD_SIGNATURE');
6466       else
6467          wf_core.context('WF_MAIL', 'PutMessage', to_char(nid), node, from_addr);
6468          wf_core.token('NID', to_char(nid));
6469          wf_core.token('POLICY', n_sig_policy);
6470          wf_core.raise('WFMLR_INVALID_SIG_POLICY');
6471       end if;
6472     end if;
6473 
6474     -- all database-friendly RemoveSpace
6475     buffer := RemoveSpace(resp_body, resp_attrs);
6476 
6477     if (buffer is not null or buffer <> '') then
6478       -- separate the mail content into tokens based on Content-Type
6479       -- to eliminate v-card interference.
6480       stk := 1;
6481       i := 1;
6482       loc := 1;
6483       token := '';
6484       while (i <= length(buffer)) loop
6485         -- check if we are at the beginning of a Content-Type
6486         prompt := substrb(buffer, i, length('Content-Type'));
6487         if (upper(prompt) = 'CONTENT-TYPE') then
6488           if (token is not null or token <> '') then
6489             -- push the buffer to the stack and start again
6490             contentStack(stk) := token;
6491             stk := stk + 1;
6492             token := '';
6493             loc := i;
6494           end if;
6495         end if;
6496         token := substrb(buffer, loc, (i - loc) + 1);
6497         i := i + 1;
6498       end loop;
6499       if (token is not null or token <> '') then
6500         contentStack(stk) := token;
6501       end if;
6502 
6503       -- now look for response attributes within tokens based on
6504       -- Content-Type. Only the body will contain the response though.
6505       stk := 1;
6506       for k in 1..contentStack.count loop
6507          for i in 1..resp_attrs.count loop
6508             prompPos := instrb(contentStack(k), resp_attrs(i).attr_prompt||':', 1);
6509             nextPos := 0;
6510             -- get the position of the next nearest prompt
6511             for j in 1..resp_attrs.count loop
6512                tmpPos := instrb(contentStack(k), resp_attrs(j).attr_prompt||':', prompPos + 1);
6513                if (tmpPos > prompPos + 1) then
6514                   if (nextPos = 0) then
6515                      nextPos := tmpPos;
6516                   elsif (tmpPos <> 0 and tmpPos < nextPos) then
6517                      nextPos := tmpPos;
6518                   end if;
6519                end if;
6520             end loop;
6521             -- if nextPos is 0, then we are at the last or only response prompt
6522             -- push to stack only if there was a prompt found within the body
6523             -- hoping to avoid v-card here
6524             if (prompPos > 0) then
6525                if (nextPos = 0) then
6526                   if (resp_attrs(i).attr_type in ('LOOKUP', 'NUMBER')) then
6527                      stack(stk) := substrb(contentStack(k), prompPos,
6528                                           length(resp_attrs(i).attr_prompt) + 40);
6529                   else
6530                      stack(stk) := substrb(contentStack(k), prompPos,
6531                                           length(resp_attrs(i).attr_prompt) + 2000);
6532                   end if;
6533                else
6534                   stack(stk) := substrb(contentStack(k), prompPos, (nextPos - prompPos) - 1);
6535                end if;
6536                stk := stk + 1;
6537             end if;
6538          end loop;
6539       end loop;
6540     end if;
6541 
6542     -- process the response values from the stack
6543     for i in 1..stack.count loop
6544       for j in 1..resp_attrs.count loop
6545         -- check if we are at the beginning of a response prompt
6546         prompt := substrb(stack(i), 1, length(resp_attrs(j).attr_prompt));
6547         if (upper(prompt) = upper(resp_attrs(j).attr_prompt)) then
6548           -- remove all the following occurences of the prompt
6549           -- within the stack
6550           for k in i..stack.count loop
6551             stack(k) := replace(stack(k), resp_attrs(j).attr_prompt||':');
6552           end loop;
6553           -- check for double quotes from both ends
6554           dleft := instrb(stack(i), '"', 1, 1);
6555           dright := instrb(stack(i), '"', -1, 1);
6556 
6557           -- check for single quotes from both ends
6558           sleft := instrb(stack(i), '''', 1, 1);
6559           sright := instrb(stack(i), '''', -1, 1);
6560 
6561           if (dleft <> 0 and (sleft = 0 or dleft < sleft)) then
6562             left := dleft;
6563           else
6564             left := sleft;
6565           end if;
6566           if (dright > sright) then
6567             right := dright;
6568           else
6569             right := sright;
6570           end if;
6571           if ((right - left) > 1) then
6572             value := substrb(stack(i), left+1, (right - left)-1);
6573             if (resp_attrs(j).attr_type = 'LOOKUP') then
6574               lk_type := resp_attrs(j).attr_format;
6575               lk_meaning := value;
6576               value := GetLovCode(resp_attrs(j).attr_format, value);
6577             end if;
6578 
6579             --  Process this notification only if it has a status of 'OPEN'
6580             --  otherwise do nothing. Fix for bug 2202392.
6581             if (stat = 'OPEN') then
6582               -- Save the new attribute value for nid.
6583               Wf_Notification.SetAttrText(nid, resp_attrs(j).attr_name, value);
6584             end if;
6585             response := TRUE;
6586           end if;
6587         end if;
6588       end loop;
6589    end loop;
6590 
6591    -- Do not need to preserve context
6592    wf_engine.preserved_context := FALSE;
6593 
6594    -- Complete the response.
6595    if response then
6596      Wf_Notification.Respond(nid, NULL, 'email:'||from_addr);
6597    else
6598      wf_core.context('WF_MAIL', 'PutMessage', to_char(nid), node, from_addr);
6599      wf_core.raise('WFRSPR_NORESPONSE');
6600    end if;
6601 
6602 exception
6603    when no_program_unit then
6604      wf_core.context('WF_MAIL','PutMessage', to_char(nid));
6605      raise;
6606    when OTHERS then
6607      wf_core.context('WF_MAIL','PutMessage', to_char(nid));
6608      -- Save error message and set status to INVALID so mailer will
6609      -- bounce an "invalid reply" message to sender.
6610      HandleResponseError(nid, lk_type, lk_meaning, error_result);
6611 end PutMessage;
6612 
6613 -- PutDirectMessage
6614 --   Direct reply processor.  Read body of a notification reply, set any
6615 --   response attributes, and complete response.
6616 --   Used by the notification mail response processor.
6617 -- IN
6618 --   notification id
6619 --   mailer node name
6620 --   response body text
6621 --   email 'from' address
6622 procedure PutDirectMessage(
6623     nid       in  number,
6624     node      in  varchar2,
6625     resp_body in  varchar2,
6626     from_addr in  varchar2,
6627     error_result in out NOCOPY varchar2)
6628 as
6629     buffer   varchar2(32000);
6630     msg_name varchar2(30);
6631     msg_type varchar2(8);
6632     stat     varchar2(8);
6633     use_default boolean;
6634     first_blank_line boolean := true;
6635     response boolean;
6636 
6637     -- Select msg response attrs.
6638     -- Order-by is to insure longest prompts are processed first to prevent
6639     -- problems where one prompt is a substring of another prompt.
6640     cursor c1 is
6641     select WMA.NAME, WMA.TYPE, WMA.FORMAT,
6642            decode(WMA.TYPE,
6643              'VARCHAR2', decode(WMA.FORMAT,
6644                            '', WNA.TEXT_VALUE,
6645                            substr(WNA.TEXT_VALUE, 1, to_number(WMA.FORMAT))),
6646              'NUMBER', decode(WMA.FORMAT,
6647                          '', to_char(WNA.NUMBER_VALUE),
6648                          to_char(WNA.NUMBER_VALUE, WMA.FORMAT)),
6649              'DATE', decode(WMA.FORMAT,
6650                        '', to_char(WNA.DATE_VALUE),
6651                        to_char(WNA.DATE_VALUE, WMA.FORMAT)),
6652              'LOOKUP', WNA.TEXT_VALUE,
6653              WNA.TEXT_VALUE) VALUE
6654     from   WF_NOTIFICATION_ATTRIBUTES WNA,
6655            WF_NOTIFICATIONS WN,
6656            WF_MESSAGE_ATTRIBUTES_VL WMA
6657     where  WNA.NOTIFICATION_ID = nid
6658     and    WN.NOTIFICATION_ID = WNA.NOTIFICATION_ID
6659     and    WN.MESSAGE_TYPE = WMA.MESSAGE_TYPE
6660     and    WN.MESSAGE_NAME = WMA.MESSAGE_NAME
6661     and    WMA.NAME = WNA.NAME
6662     and    WMA.SUBTYPE = 'RESPOND'
6663     and    WMA.TYPE not in ('FORM', 'URL')
6664     order  by WMA.SEQUENCE;
6665 
6666     new_value varchar2(4000);
6667     new_start pls_integer;
6668     new_end pls_integer;
6669     no_program_unit exception;
6670     pragma exception_init(no_program_unit, -6508);
6671     -- Bug# 2301881
6672     lk_type varchar2(30);
6673     lk_meaning varchar2(80);
6674     -- Bug 2375920
6675     n_sig_policy varchar2(100);
6676 begin
6677     buffer := resp_body;
6678     response := false;
6679 
6680     -- Get notification message and status
6681     begin
6682       select MESSAGE_NAME, MESSAGE_TYPE, STATUS
6683       into   msg_name, msg_type, stat
6684       from   WF_NOTIFICATIONS
6685       where  NOTIFICATION_ID = nid;
6686     exception
6687       when no_data_found then
6688         wf_core.token('NID', to_char(nid));
6689         wf_core.raise('WFNTF_NID');
6690     end;
6691 
6692     -- Bug 2375920 get the signature policy for the notification and
6693     -- raise error is the policy is invalid
6694     Wf_Mail.GetSignaturePolicy(nid, n_sig_policy);
6695     if (n_sig_policy is not NULL and upper(n_sig_policy) <> 'DEFAULT') then
6696       if(upper(n_sig_policy) = 'PSIG_ONLY') then
6697          wf_core.context('WF_MAIL', 'PutDirectMessage', to_char(nid), node, from_addr);
6698          wf_core.token('NID', to_char(nid));
6699          wf_core.raise('WFRSPR_PWD_SIGNATURE');
6700       else
6701          wf_core.context('WF_MAIL', 'PutDirectMessage', to_char(nid), node, from_addr);
6702          wf_core.token('NID', to_char(nid));
6703          wf_core.token('POLICY', n_sig_policy);
6704          wf_core.raise('WFMLR_INVALID_SIG_POLICY');
6705       end if;
6706     end if;
6707 
6708     -- Process all Response attributes.
6709     -- The first line in the mail body should be the answer for the first
6710     -- response attribute. And the second line should be for the second
6711     -- response attribute....
6712     -- Blank line means take the default value.
6713     -- The mailer is assuming that blank line will be inserted by user
6714     -- when they want to take the default value.
6715     -- When an answer is too long, double quote should be enclosed at both
6716     -- beginning and the end of the answer.
6717     for rec in c1 loop
6718 
6719       -- Bug# 2301881 These values are needed in HandleResponseError
6720       -- and FormatErrorMessage to bounce invalid response mail
6721       lk_type := rec.format;
6722       lk_meaning := g_none;
6723 
6724       -- GetDirectAnswer() will take the next line from the mail body.
6725       -- GetDirectAnswer() takes care multiple lines answer.(answer enclosed
6726       --                    by double quote
6727       use_default := false;
6728       GetDirectAnswer(buffer, new_value);
6729 
6730       if (new_value is null) then
6731         -- check if is leading blank line
6732         if (not(first_blank_line)) then
6733           new_value := rec.value;
6734           use_default := true;
6735         else
6736           while (new_value is null) loop
6737             GetDirectAnswer(buffer, new_value);
6738           end loop;
6739           first_blank_line := false;
6740         end if;
6741       else
6742         first_blank_line := false;
6743       end if;
6744 
6745       -- Bug# 2301881
6746       lk_meaning := new_value;
6747       -- If this is a lookup, replace displayed meaning with code.
6748       if (rec.type = 'LOOKUP' AND use_default = false) then
6749         new_value := GetLovCode(rec.format, new_value);
6750       end if;
6751 
6752       --  Process this notification only if it has a status of 'OPEN'
6753       --  otherwise do nothing. Fix for bug 2202392.
6754 
6755       if (stat = 'OPEN') then
6756       -- Save the new attribute value for nid.
6757          Wf_Notification.SetAttrText(nid, rec.name, new_value);
6758       end if;
6759       response := TRUE;
6760     end loop;
6761 
6762     -- Do not need to preserve context
6763     wf_engine.preserved_context := FALSE;
6764 
6765     -- Complete the response.
6766     if response then
6767        Wf_Notification.Respond(nid, NULL, 'email:'||from_addr);
6768     else
6769        wf_core.context('WF_MAIL', 'PutDirectMessage', to_char(nid),
6770                        node, from_addr);
6771        wf_core.raise('WFRSPR_NORESPONSE');
6772     end if;
6773 exception
6774     when no_program_unit then
6775         wf_core.context('WF_MAIL','PutDirectMessage', to_char(nid));
6776         raise;
6777     when OTHERS then
6778         wf_core.context('WF_MAIL','PutDirectMessage', to_char(nid));
6779       -- Save error message and set status to INVALID so mailer will
6780       -- bounce an "invalid reply" message to sender.
6781       HandleResponseError(nid, lk_type, lk_meaning, error_result);
6782 end PutDirectMessage;
6783 
6784 -- PutMoreInfoRequest
6785 --   Reply processor.  Read body of a request for more information
6786 --   parse the body for the role to send the request to.
6787 --   Used by the notification mail response processor.
6788 -- IN
6789 --   notification id
6790 --   mailer node name
6791 --   response body text
6792 --   email 'from' address
6793 procedure PutMoreInfoRequest(
6794     nid       in  number,
6795     node      in  varchar2,
6796     resp_body in  varchar2,
6797     from_addr in  varchar2,
6798     error_result in out nocopy varchar2)
6799 as
6800     buffer   varchar2(32000);
6801     start_pos number;
6802     end_pos number;
6803     prompt varchar2(200);
6804     comment varchar2(200);
6805     to_user varchar2(320);
6806     dummy varchar2(200);
6807     no_program_unit exception;
6808     pragma exception_init(no_program_unit, -6508);
6809 begin
6810     buffer := resp_body;
6811     -- Remove line feeds incase there
6812     -- has been a line wrap on the response
6813     buffer := replace(buffer, g_newLine, '');
6814 
6815     -- Locate who the response is to.
6816     prompt := g_moreInfoFrom||': ''';
6817     start_pos := instr(buffer, prompt, 1, 1);
6818     if start_pos <> 0 then
6819        start_pos := start_pos + length(prompt);
6820        end_pos := instr(buffer, '''',  start_pos, 1);
6821        to_user := substr(buffer, start_pos, end_pos - start_pos);
6822     else
6823        to_user := '';
6824     end if;
6825 
6826     -- More info request can come only from a HTML mail in which
6827     -- a template is provided with single quotes
6828     prompt := '';
6829     start_pos := 0;
6830     end_pos := 0;
6831     prompt := g_moreInfoQPrompt||': ''';
6832 
6833     -- checking only for single quote, as the template is generated by the
6834     -- mailer with a single quote on clicking on the link
6835     start_pos := instr(buffer, prompt, 1, 1);
6836     if start_pos <> 0 then
6837        start_pos := start_pos + length(prompt);
6838        end_pos := instr(buffer, '''', start_pos, 1);
6839        if end_pos <> 0 then
6840           comment := substr(buffer, start_pos, end_pos - start_pos);
6841           if comment = g_moreInfoQuestion and
6842              length(comment) =
6843                  length(g_moreInfoQuestion) then
6844              comment := '';
6845           end if;
6846 
6847        end if;
6848     else
6849        comment := 'NULL';
6850     end if;
6851     -- validate the role before calling updateinfo
6852 
6853     -- update wf_notifications and wf_comments in QUESTION mode
6854     if (length(to_user) > 0  and length(comment) > 0) then
6855        wf_notification.UpdateInfo2(nid, to_user, from_addr, comment);
6856     end if;
6857 exception
6858     when no_program_unit then
6859       wf_core.context('WF_MAIL','PutMoreInfoRequest', to_char(nid));
6860       raise;
6861     when OTHERS then
6862       wf_core.context('WF_MAIL','PutMoreInfoRequest', to_char(nid));
6863       -- Save error message and set status to INVALID so mailer will
6864       -- bounce an "invalid reply" message to sender.
6865       HandleResponseError(nid, dummy, dummy, error_result);
6866 end PutMoreInfoRequest;
6867 
6868 -- PutMoreInfoMessage
6869 --   Reply processor.  Read body of a reply for more information
6870 --   request, parse the body for the comments from the user and
6871 --   update wf_notification and wf_comments apropriately
6872 --   Used by the notification mail response processor.
6873 -- IN
6874 --   notification id
6875 --   mailer node name
6876 --   response body text
6877 --   email 'from' address
6878 procedure PutMoreInfoMessage(
6879     nid       in  number,
6880     node      in  varchar2,
6881     resp_body in  varchar2,
6882     from_addr in  varchar2,
6883     error_result in out nocopy varchar2)
6884 as
6885     buffer   varchar2(32000);
6886     start_pos number;
6887     end_pos number;
6888     prompt varchar2(200);
6889     comment varchar2(200);
6890     to_user varchar2(320);
6891     dummy varchar2(200);
6892     no_program_unit exception;
6893     pragma exception_init(no_program_unit, -6508);
6894 begin
6895     buffer := resp_body;
6896     -- Remove line feeds incase there
6897     -- has been a line wrap on the response
6898     buffer := replace(buffer, g_newLine, '');
6899 
6900     prompt := '';
6901     start_pos := 0;
6902     end_pos := 0;
6903     prompt := g_moreInfoAPrompt||': ''';
6904 
6905     start_pos := instr(buffer, prompt, 1, 1);
6906     if start_pos <> 0 then
6907        start_pos := start_pos + length(prompt);
6908        end_pos := instr(buffer, '''', start_pos, 1);
6909        if end_pos <> 0 then
6910           comment := substr(buffer, start_pos, end_pos - start_pos);
6911           if comment = g_moreInfoAnswer and
6912              length(comment) =
6913                  length(g_moreInfoAnswer) then
6914              comment := '';
6915           end if;
6916 
6917        end if;
6918     else
6919        prompt := '';
6920        start_pos := 0;
6921        end_pos := 0;
6922        prompt := g_moreInfoAPrompt||': "';
6923        start_pos := instr(buffer, prompt, 1, 1);
6924        if (start_pos <> 0) then
6925           start_pos := start_pos + length(prompt);
6926           end_pos := instr(buffer, '''', start_pos, 1);
6927           if (end_pos <> 0) then
6928              comment := substr(buffer, start_pos, end_pos - start_pos);
6929              if ((comment = g_moreInfoAnswer) and
6930                   (length(comment) =
6931                       length(g_moreInfoAnswer))) then
6932                 comment := '';
6933              end if;
6934           end if;
6935        end if;
6936     end if;
6937     -- update wf_notifications and wf_comments in ANSWER mode
6938     to_user := NULL;
6939     wf_notification.UpdateInfo2(nid, to_user, from_addr, comment);
6940 exception
6941     when no_program_unit then
6942       wf_core.context('WF_MAIL','PutMoreInfoMessage', to_char(nid));
6943       raise;
6944     when OTHERS then
6945       wf_core.context('WF_MAIL','PutMoreInfoMessage', to_char(nid));
6946       -- Save error message and set status to INVALID so mailer will
6947       -- bounce an "invalid reply" message to sender.
6948       HandleResponseError(nid, dummy, dummy, error_result);
6949 end PutMoreInfoMessage;
6950 
6951 -- GetURLAttachment - Return the attached URLS as a list on an attachment
6952 -- IN
6953 --    NID Notificaiton ID
6954 -- OUT
6955 --   BUFFER containing the attachment body
6956 --   ERROR_RESULT - Errorstack if requried
6957 procedure GetUrlAttachment (nid in number,
6958                             buffer out NOCOPY varchar2,
6959                             error_result out NOCOPY varchar2)
6960 is
6961     l_subject     varchar2(2000);
6962     l_html_body   varchar2(32000);
6963     l_url         varchar2(2000);
6964     l_urllist     varchar2(32000);
6965     l_urlcount    integer;
6966 
6967     err_name      varchar2(30);
6968     err_message   varchar2(2000);
6969     err_stack     varchar2(4000);
6970 
6971     cursor ntf is
6972     select WMA.TYPE, WMA.DISPLAY_NAME,
6973        decode(WMA.TYPE, 'URL', WF_NOTIFICATION.GetUrlText(WNA.TEXT_VALUE,
6974               GetURLAttachment.nid), WNA.TEXT_VALUE) URL,
6975        WNA.NAME
6976        from WF_NOTIFICATION_ATTRIBUTES WNA, WF_NOTIFICATIONS WN,
6977             WF_MESSAGE_ATTRIBUTES_VL WMA
6978        where WNA.NOTIFICATION_ID = GetURLAttachment.nid
6979          and WN.NOTIFICATION_ID = WNA.NOTIFICATION_ID
6980          and WN.MESSAGE_TYPE = WMA.MESSAGE_TYPE
6981          and WN.MESSAGE_NAME = WMA.MESSAGE_NAME
6982          and (WMA.TYPE = 'URL')
6983          and WMA.ATTACH = 'Y'
6984          and WMA.NAME = WNA.NAME;
6985 
6986 
6987     no_program_unit exception;
6988     pragma exception_init(no_program_unit, -6508);
6989 
6990 begin
6991 
6992    -- Get the template.
6993    begin
6994      select SUBJECT, HTML_BODY
6995      into   l_subject, l_html_body
6996      from   WF_MESSAGES_VL
6997      where  NAME = 'ATTACHED_URLS' and TYPE = 'WFMAIL';
6998    exception
6999      when no_data_found then
7000        -- If the template has not been installed, then construct a
7001        -- default, minimem template.
7002        l_html_body := '<HTML><BODY><B><FONT SIZE=+1>'||
7003                       'Notificaiton References</FONT></B>'||
7004                       '<BR>Default Template<BR>'||
7005                       '&'||'URLLIST</BODY><HTML>';
7006 
7007    end;
7008 
7009    -- Build the list of URLs
7010    l_urlcount := 0;
7011    l_urllist := '<BR>';
7012    for urlattr in ntf loop
7013       l_url := wf_notification.SetFrameworkAgent(urlattr.url);
7014       l_urllist := l_urllist || '<A class="OraLink" HREF="' || l_url || '">' ||
7015                    urlattr.display_name || '</A><BR>'||g_newLine;
7016       l_urlcount := l_urlcount + 1;
7017    end loop;
7018 
7019 
7020    -- Substitute the list
7021    if l_urlcount > 0 then
7022       buffer := substrb(replace(l_html_body, '&'||'URLLIST', l_urllist), 1, 32000);
7023     else
7024        buffer := '';
7025     end if;
7026 exception
7027   when no_program_unit then
7028     wf_core.context('WF_MAIL', 'GetURLAttachment', to_char(nid));
7029     raise;
7030   when others then
7031     -- First look for a wf_core error.
7032     wf_core.get_error(err_name, err_message, err_stack);
7033 
7034     -- If no wf_core error look for a sql error.
7035     if (err_name is null) then
7036       err_message := sqlerrm;
7037     end if;
7038 
7039     error_result := err_message;
7040     wf_core.context('WF_MAIL', 'GetURLAttachment', to_char(nid));
7041 
7042 end;
7043 
7044 
7045 
7046 -- Direct_Response - Return the value of the direct response flag
7047 --
7048 -- OUT
7049 --   Direct Response as [TRUE|FALSE]
7050 function Direct_Response return boolean
7051 is
7052 begin
7053    return g_direct_response;
7054 end;
7055 
7056 -- Send_Accesskey - Return the value of the send access key flag
7057 --
7058 -- OUT
7059 --   Direct Response as [TRUE|FALSE]
7060 function Send_Accesskey return boolean
7061 is
7062    enabled varchar2(1);
7063    flag boolean;
7064 begin
7065    if g_install = 'EMBEDDED' then
7066       enabled := FND_PROFILE.Value('WF_VALIDATE_NTF_ACCESS');
7067       if enabled = 'Y' then
7068          flag := TRUE;
7069       else
7070          flag := FALSE;
7071       end if;
7072    else
7073       flag := g_send_accesskey;
7074    end if;
7075 
7076    return flag;
7077 
7078 end;
7079 
7080 -- autoclose_fyi - Return the value of the autoclose fyi flag
7081 --
7082 -- OUT
7083 --   AUTOCLOSE_FYI as [TRUE|FALSE]
7084 function Autoclose_FYI return boolean
7085 is
7086 begin
7087    return g_autoclose_fyi;
7088 end;
7089 
7090 -- Direct_Response_On - Set the value of the direct response flag to TRUE
7091 --
7092 procedure Direct_Response_On
7093 is
7094 begin
7095    g_direct_response := TRUE;
7096 end;
7097 
7098 -- Direct_Response_Off - Set the value of the direct response flag to FALSE
7099 --
7100 procedure Direct_Response_off
7101 is
7102 begin
7103    g_direct_response := FALSE;
7104 end;
7105 
7106 -- send_accesskey_on - Set the value of the send acces key flag to TRUE
7107 --
7108 procedure Send_Access_Key_On
7109 is
7110 begin
7111    g_send_accesskey := TRUE;
7112 end;
7113 
7114 -- Send_Accesskey_oOf - Set the value of the send acces key flag to FALSE
7115 --
7116 procedure Send_Access_Key_Off
7117 is
7118 begin
7119    g_send_accesskey := FALSE;
7120 end;
7121 
7122 -- Autoclose_FYI_On - Set the value of the autoclose FYI flag to TRUE
7123 --
7124 procedure Autoclose_FYI_On
7125 is
7126 begin
7127    g_autoclose_fyi := TRUE;
7128 end;
7129 
7130 -- Autoclose_FYI_Off - Set the value of the autoclose FYI flag to FALSE
7131 --
7132 procedure Autoclose_FYI_Off
7133 is
7134 begin
7135    g_autoclose_fyi := FALSE;
7136 end;
7137 
7138 -- set_template - Set the mail template
7139 -- if nothing is specify, it will clear the mail template value.
7140 procedure set_template(name in varchar2)
7141 is
7142 begin
7143    g_template := substr(name, 1, 30);
7144 end set_template;
7145 
7146 --
7147 -- GetCharset (PRIVATE)
7148 --   Get the character set base of the language and territory info.
7149 -- NOTE
7150 --   We may do more in the future to find the character set.
7151 --
7152 procedure GetCharset(lang in varchar2,
7153                      terr in varchar2,
7154                      charset out NOCOPY varchar2)
7155 is
7156 begin
7157   begin
7158     if (terr is null) then
7159       raise NO_DATA_FOUND;
7160     end if;
7161     select NLS_CODESET
7162       into charset
7163       from WF_LANGUAGES
7164      where NLS_LANGUAGE = lang
7165        and NLS_TERRITORY = terr;
7166   exception
7167     when NO_DATA_FOUND then
7168       -- try to find the character set base on language alone
7169       select NLS_CODESET
7170         into charset
7171         from WF_LANGUAGES
7172        where NLS_LANGUAGE = lang
7173          and rownum < 2;
7174   end;
7175 exception
7176   when OTHERS then
7177     wf_core.context('WF_MAIL', 'GetCharset', lang, terr);
7178     raise;
7179 end GetCharset;
7180 
7181 -- GetSessionLanguage
7182 -- Get the session language and territory for the
7183 -- current session
7184 --
7185 -- OUT
7186 -- Language
7187 -- Territory
7188 -- codeset
7189 procedure GetSessionLanguage(lang out NOCOPY varchar2,
7190                              terr out NOCOPY varchar2,
7191                              codeset out NOCOPY varchar2)
7192 is
7193    nls_str varchar2(1000);
7194    underscore integer;
7195    dot integer;
7196 begin
7197 
7198    select userenv('LANGUAGE')
7199    into nls_str
7200    from sys.dual;
7201 
7202    underscore := instr(nls_str,'_',1,1);
7203    dot := instr(nls_str, '.',1,1);
7204 
7205    lang := substr(nls_str, 1, underscore-1);
7206    terr := substr(nls_str, underscore+1, dot - underscore -1);
7207    codeset := substr(nls_str, dot+1);
7208 
7209 exception
7210   when others then
7211     wf_core.context('WF_MAIL', 'GetSessionLanguage');
7212     raise;
7213 end GetSessionLanguage;
7214 
7215 -- Bug 2375920
7216 -- GetSignaturePolicy (PUBLIC)
7217 --    Get the signature policy for the notification from
7218 --    the notification attribute
7219 -- IN
7220 --   nid  -  Notification id
7221 -- OUT
7222 --   sig_policy  - Signature policy
7223 
7224 procedure GetSignaturePolicy(nid        in  number,
7225                              sig_policy out NOCOPY varchar2)
7226 is
7227 begin
7228   -- Get value for signature policy for the notification from
7229   -- notification attribute
7230   sig_policy := Wf_Notification.GetAttrText(nid, '#WF_SIG_POLICY');
7231 
7232 exception
7233   when others then
7234     if (wf_core.error_name = 'WFNTF_ATTR') then
7235       wf_core.clear;
7236       sig_policy := 'DEFAULT';
7237     else
7238       raise;
7239     end if;
7240 end GetSignaturePolicy;
7241 
7242 -- gets the size of the current LOB table
7243 function getLobTableSize return number
7244 is
7245 begin
7246    return g_LOBTable.COUNT;
7247 end;
7248 
7249 --
7250 -- GetSecurityPolicy
7251 --
7252 procedure GetSecurityPolicy(p_nid        in  number,
7253                             p_sec_policy out NOCOPY varchar2)
7254 is
7255 begin
7256   -- Get security policy for the notification
7257   p_sec_policy := Wf_Notification.GetAttrText(p_nid, '#WF_SECURITY_POLICY');
7258   if (p_sec_policy is null) then
7259      p_sec_policy := 'DEFAULT';
7260   end if;
7261 exception
7262   when others then
7263     if (wf_core.error_name = 'WFNTF_ATTR') then
7264       wf_core.clear;
7265       p_sec_policy := 'DEFAULT';
7266     else
7267       raise;
7268     end if;
7269 end GetSecurityPolicy;
7270 
7271 --
7272 -- ProcessSecurityPolicy
7273 --
7274 procedure ProcessSecurityPolicy(p_nid          in  number,
7275                                 p_email        out NOCOPY varchar2,
7276                                 p_message_name out NOCOPY varchar2)
7277 is
7278   l_sec_policy    varchar2(100);
7279   l_email_allowed varchar2(1);
7280 begin
7281   -- Get security policy for the notification
7282   Wf_Mail.GetSecurityPolicy(p_nid, l_sec_policy);
7283 
7284   -- If the policy is not seeded, default it to Y to allow email
7285   begin
7286     SELECT email_allowed
7287     INTO   l_email_allowed
7288     FROM   wf_ntf_security_policies
7289     WHERE  policy_name = l_sec_policy;
7290   exception
7291     when others then
7292       Wf_Core.Token('NID', to_char(p_nid));
7293       Wf_Core.Token('POLICY', l_sec_policy);
7294       Wf_Core.Raise('WFMLR_INVALID_SEC_POLICY');
7295   end;
7296 
7297   -- There should be a way to avoid hard-coding the values !!!
7298   p_email := l_email_allowed;
7299 
7300   -- Policy is either EMAIL_OK or DEFAULT
7301   if (l_email_allowed = 'Y') then
7302      p_message_name := null;
7303 
7304   -- Policy NO_EMAIL, the notification is not to be sent by e-mail
7305   elsif (l_email_allowed = 'N') then
7306      p_message_name := 'OPEN_MAIL_SECURE';
7307 
7308   -- Policy is ENC_EMAIL_ONLY. Requires the notification to be encrypted. Currently not
7309   -- supported, so inform the user to access the online notification
7310   elsif (l_email_allowed = 'E') then
7311      p_message_name := 'OPEN_MAIL_SECURE';
7312   end if;
7313 
7314 exception
7315   when others then
7316     Wf_Core.Context('Wf_Mail', 'ProcessSecurityPolicy', to_char(p_nid));
7317     raise;
7318 end ProcessSecurityPolicy;
7319 
7320 -- Set_FYI_Flag (Private)
7321 --   Sets a global flag to identify if the current e-mail being processed is a
7322 --   FYI notification
7323 -- IN
7324 --   p_fyi  boolean
7325 procedure Set_FYI_Flag(p_fyi in boolean)
7326 is
7327 begin
7328   g_fyi := p_fyi;
7329 end Set_FYI_Flag;
7330 
7331 -- Get_FYI_Flag (Private)
7332 --   Returns a global flag to identify if the current e-mail being processed is
7333 --   a FYI notification
7334 -- OUT
7335 --   Boolean value
7336 function Get_FYI_Flag return boolean
7337 is
7338 begin
7339   return g_fyi;
7340 end Get_FYI_Flag;
7341 
7342 -- Get_Ntf_Language (PRIVATE)
7343 --   Overrides the language and territory setting for the notification based
7344 --   on the #WFM_LANGUAGE and #WFM_TERRITORY attributes. If neither user's
7345 --   preference nor the notification level setting are valid, the base NLS
7346 --   setting is used
7347 -- IN
7348 --   p_nid - Notification Id
7349 -- IN OUT
7350 --   p_language   - NLS Language
7351 --   p_territory  - NLS Territory
7352 --   p_codeset    - NLS Codeset
7353 procedure Get_Ntf_Language(p_nid       in            number,
7354                            p_language  in out nocopy varchar2,
7355                            p_territory in out nocopy varchar2,
7356                            p_codeset   in out nocopy varchar2)
7357 is
7358   l_lang      varchar2(64);
7359   l_terr      varchar2(64);
7360   l_install   varchar2(1);
7361   l_codeset   varchar2(30);
7362 
7363   l_base_lang varchar2(64);
7364   l_base_terr varchar2(64);
7365   l_base_codeset varchar2(30);
7366 
7367   l_base_nlsDateFormat        VARCHAR2(64);
7368   l_base_nlsDateLanguage      varchar2(64);
7369   l_base_nlsCalendar          varchar2(64);
7370   l_base_nlsNumericCharacters varchar2(30);
7371   l_base_nlsSort              varchar2(64);
7372   l_base_nlsCurrency          varchar2(30);
7373 
7374 
7375 begin
7376 
7377   if (wf_log_pkg.level_procedure >= fnd_log.g_current_runtime_level) then
7378     wf_log_pkg.string(wf_log_pkg.level_statement, 'wf.plsql.wf_mail.Get_Ntf_Language.BEGIN',
7379                       'User preferences. LANG {'||p_language||'} TERR {'||p_territory||'}');
7380   end if;
7381 
7382   -- Get notification's language preference
7383   begin
7384     l_lang := upper(Wf_Notification.GetAttrText(p_nid, '#WFM_LANGUAGE'));
7385   exception
7386     when others then
7387       if (wf_core.error_name = 'WFNTF_ATTR') then
7388         wf_core.clear();
7389         l_lang := null;
7390       else
7391         raise;
7392       end if;
7393   end;
7394 
7395   l_install := 'N';
7396   if (l_lang is not null) then
7397     -- Check for validity of notification's language
7398     begin
7399       select nls_codeset, installed_flag
7400       into   l_codeset, l_install
7401       from   wf_languages
7402       where  nls_language = l_lang;
7403     exception
7404       when others then
7405         l_install := 'N';
7406         l_lang := null;
7407         l_codeset := null;
7408     end;
7409   end if;
7410 
7411   -- Notification level language is installed and valid
7412   if (l_install = 'Y') then
7413     p_language := l_lang;
7414     p_codeset := l_codeset;
7415   else
7416     -- Notification level language is not installed, use user's preference
7417     begin
7418       select nls_codeset, installed_flag
7419       into   p_codeset, l_install
7420       from   wf_languages
7421       where  nls_language = p_language;
7422     exception
7423       when others then
7424         l_install := 'N';
7425     end;
7426 
7427     -- If neither notification level nor user's preferences are installed,
7428     -- use default
7429     if (l_install = 'N') then
7430       p_language := null;
7431       p_codeset := null;
7432     end if;
7433   end if;
7434 
7435   -- Get notification's territory preference
7436   begin
7437     l_terr := upper(Wf_Notification.GetAttrText(p_nid, '#WFM_TERRITORY'));
7438   exception
7439     when others then
7440       if (wf_core.error_name = 'WFNTF_ATTR') then
7441         wf_core.clear();
7442         l_terr := null;
7443       else
7444         raise;
7445       end if;
7446   end;
7447 
7448   l_install := 'N';
7449   if (l_terr is not null) then
7450     begin
7451       select 'Y'
7452       into   l_install
7453       from   wf_languages
7454       where  nls_territory = l_terr;
7455       -- Notification level territory is valid
7456       p_territory := l_terr;
7457     exception
7458       when others then
7459         l_install := 'N';
7460     end;
7461   end if;
7462 
7463   -- Use user's territory preference if it is valid
7464   if (l_install = 'N') then
7465     begin
7466       select 'Y'
7467       into   l_install
7468       from   wf_languages
7469       where  nls_territory = p_territory;
7470     exception
7471       when others then
7472         -- Neither notification level nor user's territory preference is
7473         -- valid, use default
7474         p_territory := null;
7475     end;
7476   end if;
7477 
7478   -- If at least one of the value is null, use the base NLS setting
7479   if (p_language is null or p_territory is null) then
7480     if (wf_log_pkg.level_statement >= fnd_log.g_current_runtime_level) then
7481       wf_log_pkg.string(wf_log_pkg.level_statement, 'wf.plsql.wf_mail.Get_Ntf_Language.NULL',
7482                       'Using base NLS setting because p_language is {'||p_language||
7483                       '} and p_territory is {'||p_territory||'}');
7484     end if;
7485 
7486     -- <<sstomar>> :
7487     -- WF_MAIL.GetSessionLanguage(l_base_lang, l_base_terr, l_base_codeset);
7488     -- Note: we should AVOID to call below API here
7489     -- use global variables instead .
7490     WF_NOTIFICATION_UTIL.getNLSContext(
7491                    l_base_lang  ,
7492                    l_base_terr       ,
7493                    l_base_codeset          ,
7494                    l_base_nlsDateFormat ,
7495                    l_base_nlsDateLanguage   ,
7496                    l_base_nlsNumericCharacters ,
7497                    l_base_nlsSort            ,
7498                    l_base_nlsCalendar
7499                    );
7500 
7501     if (p_language is null) then
7502       p_language := l_base_lang;
7503       p_codeset := l_base_codeset;
7504     end if;
7505 
7506     p_territory := nvl(p_territory, l_base_terr);
7507 
7508   end if;
7509 
7510   if (wf_log_pkg.level_procedure >= fnd_log.g_current_runtime_level) then
7511      wf_log_pkg.string(wf_log_pkg.level_procedure, 'wf.plsql.wf_mail.Get_Ntf_Language.END',
7512                       'Email Notification LANG {'||p_language||'} TERR {'||p_territory||'}'||
7513                       ' CODESET {'||p_codeset||'}');
7514   end if;
7515 
7516 exception
7517   when others then
7518     Wf_Core.Context('Wf_Mail', 'Get_Ntf_Language', to_char(p_nid), p_language, p_territory);
7519     raise;
7520 end Get_Ntf_Language;
7521 
7522 --
7523 -- Generic mailer routines
7524 --
7525 
7526 -- Send
7527 --   Sends a e-mail notification to the specified list of recipients.
7528 --   This API unlike wf_notification.send does not require workflow
7529 --   message or workflow roles to send a notification.
7530 
7531 procedure send(p_subject        in varchar2,
7532                p_message        in out nocopy clob,
7533                p_recipient_list in wf_recipient_list_t,
7534                p_module         in varchar2,
7535                p_idstring       in varchar2,
7536                p_from           in varchar2,
7537                p_replyto        in varchar2,
7538                p_language       in varchar2,
7539                p_territory      in varchar2,
7540                p_codeset        in varchar2,
7541                p_content_type   in varchar2,
7542                p_callback_event in varchar2,
7543                p_event_key      in varchar2,
7544                p_fyi_flag       in varchar2)
7545 is
7546   l_module varchar2(10);
7547   l_str  varchar2(4000);
7548   l_attrlist wf_xml.wf_xml_attr_table_type;
7549   l_pos  integer;
7550   l_amt  number;
7551 
7552   l_msg_doc    clob;
7553   l_event      wf_event_t;
7554   l_agent      wf_agent_t;
7555   l_parameter_list wf_parameter_list_t;
7556   l_event_name varchar2(240);
7557   l_event_key  varchar2(240);
7558   l_recp_type  varchar2(10);
7559   l_name       varchar2(360);
7560   l_email      varchar2(240);
7561   l_replyto    varchar2(240);
7562   l_subject    varchar2(2000);
7563   l_from       varchar2(360);
7564   l_nodename   varchar2(240);
7565   l_idstr      varchar2(240);
7566   l_idsize     integer;
7567 
7568   l_recp_clob  clob;
7569   l_recp       clob;
7570   l_recp_tmp   varchar2(32000);
7571   l_recp_txt   varchar2(32000);
7572   l_recp_pos   integer;
7573   l_recp_is_lob boolean;
7574   l_recp_len   integer;
7575   l_hdr_tmp    varchar2(32000);
7576   l_hdr_pos    integer;
7577   l_occurance  integer := 1;
7578   i            integer;
7579 
7580 
7581   l_messageIdx pls_integer;
7582   l_start_cdata VARCHAR2(10) := '<![CDATA[';
7583   l_end_cdata VARCHAR2(4) := ']]>';
7584   l_fyi_flag boolean;
7585 
7586 begin
7587 
7588   if (wf_log_pkg.level_procedure >= fnd_log.g_current_runtime_level) then
7589      wf_log_pkg.string(WF_LOG_PKG.level_procedure,
7590                        'wf.plsql.WF_MAIL.send',
7591                         'BEGIN');
7592   end if;
7593 
7594   -- Test the FYI flag. If set, then treat this message as FYI
7595   -- i.e. No ID string to be appended. Same is true if the ID string is
7596   -- null irresepective of the p_fyi_flag.
7597   if (p_fyi_flag is not null and upper(p_fyi_flag) = 'Y')
7598     or p_idstring is null then
7599      l_fyi_flag := true;
7600   else
7601      l_fyi_flag := false;
7602   end if;
7603 
7604   -- Recipients required
7605   if (p_recipient_list is null) then
7606     if (wf_log_pkg.level_procedure >= fnd_log.g_current_runtime_level) then
7607         wf_log_pkg.string(WF_LOG_PKG.level_procedure,
7608                        'wf.plsql.WF_MAIL.send',
7609                         'Recipient List is empty. ');
7610     end if;
7611 
7612     wf_core.raise('WFMLR_NO_RECIPIENTS');
7613   end if;
7614 
7615   -- At least subject or body is required
7616   if (p_subject is null and (p_message is null or dbms_lob.GetLength(p_message)=0)) then
7617     if (wf_log_pkg.level_procedure >= fnd_log.g_current_runtime_level) then
7618         wf_log_pkg.string(WF_LOG_PKG.level_procedure,
7619                        'wf.plsql.WF_MAIL.send',
7620                         'Subject and Message Contents are blank or null. ');
7621     end if;
7622 
7623     wf_core.raise('WFMLR_MSG_INCOMPLETE');
7624   end if;
7625 
7626   -- For all e-mails, respone or FYI, module name is required
7627   if (p_module is null) then
7628     if (wf_log_pkg.level_procedure >= fnd_log.g_current_runtime_level) then
7629         wf_log_pkg.string(WF_LOG_PKG.level_procedure,
7630                        'wf.plsql.WF_MAIL.send',
7631                         'Message module is not specified. ');
7632     end if;
7633 
7634     wf_core.raise('WFMLR_NO_MODULE');
7635   end if;
7636 
7637   dbms_lob.createTemporary(l_msg_doc, TRUE, dbms_lob.call);
7638   l_str := '<?xml version="1.0" ?>';
7639   l_pos := length(l_str);
7640   dbms_lob.write(l_msg_doc, l_pos, 1, l_str);
7641 
7642   -- <NOTIFICATIONGROUP> begin
7643   wf_xml.AddElementAttribute('maxcount', '1', l_attrlist);
7644   l_pos := wf_xml.NewLOBTag(l_msg_doc, l_pos, 'NOTIFICATIONGROUP', '', l_attrlist);
7645   l_attrlist.DELETE;
7646 
7647   l_nodename := WF_MAILER_PARAMETER.GetValueForCorr(p_module, 'NODENAME');
7648 
7649   -- Id string required for response required alerts.
7650   -- We assume that a null id string constitutes a FYI message
7651   if (p_idstring is not null and l_fyi_flag = false) then
7652      if (trim(p_module) <> g_Alert_Nodename) then
7653         l_module := '#X_'||trim(p_module);
7654      else
7655         l_module := p_module;
7656      end if;
7657      l_idstr := l_module||'['||p_idstring||'/0000@'||l_nodename||']';
7658   else
7659     l_idstr := null;
7660   end if;
7661 
7662   -- <NOTIFICATION> begin
7663   wf_xml.AddElementAttribute('nid', '0', l_attrlist);
7664   wf_xml.AddElementAttribute('nidstr', l_idstr, l_attrlist);
7665   wf_xml.AddElementAttribute('language', p_language, l_attrlist);
7666   wf_xml.AddElementAttribute('territory', p_territory, l_attrlist);
7667   wf_xml.AddElementAttribute('codeset', p_codeset, l_attrlist);
7668 
7669   -- <<sstomar>>:
7670   --     Setting default parameter's value until ALR team starts to pass
7671   --     below parameter's values.
7672   -- TODO>>
7673   wf_xml.AddElementAttribute('nlsDateformat', wf_core.nls_date_format, l_attrlist);
7674   wf_xml.AddElementAttribute('nlsDateLanguage', wf_core.nls_date_language, l_attrlist);
7675   wf_xml.AddElementAttribute('nlsNumericCharacters', wf_core.nls_numeric_characters, l_attrlist);
7676   wf_xml.AddElementAttribute('nlsSort', wf_core.nls_sort, l_attrlist);
7677 
7678   wf_xml.AddElementAttribute('priority', '50', l_attrlist);
7679   wf_xml.AddElementAttribute('accesskey', '0', l_attrlist);
7680   wf_xml.AddElementAttribute('node', l_nodename, l_attrlist);
7681   wf_xml.AddElementAttribute('item_type', 'NULL', l_attrlist);
7682   wf_xml.AddElementAttribute('message_name', 'NULL', l_attrlist);
7683   wf_xml.AddElementAttribute('full-document', 'Y', l_attrlist);
7684 
7685   if (p_callback_event is not null) then
7686     wf_xml.AddElementAttribute('callback', p_callback_event, l_attrlist);
7687   end if;
7688 
7689   l_pos := wf_xml.NewLOBTag(l_msg_doc, l_pos, 'NOTIFICATION', '', l_attrlist);
7690   l_attrlist.DELETE;
7691 
7692   dbms_lob.createTemporary(l_recp_clob, TRUE, dbms_lob.call);
7693 
7694   l_recp_pos := 0;
7695   l_recp_tmp := '';
7696 
7697   i := p_recipient_list.first;
7698   while (i is not null) loop
7699     l_name := replace(p_recipient_list(i).name, g_newline);
7700     l_recp_type := p_recipient_list(i).recipient_type;
7701 
7702     -- <RECIPIENT> begin
7703     wf_xml.AddElementAttribute('name', l_name, l_attrlist);
7704     wf_xml.AddElementAttribute('type', l_recp_type, l_attrlist);
7705     l_recp_pos := wf_xml.NewTag(l_recp_tmp, l_recp_pos, 'RECIPIENT', '', l_attrlist);
7706     l_attrlist.DELETE;
7707 
7708     -- <NAME>
7709     l_recp_pos := wf_xml.NewTag(l_recp_tmp, l_recp_pos, 'NAME', l_name, l_attrlist);
7710     l_recp_pos := wf_xml.SkipTag(l_recp_tmp, 'NAME', l_recp_pos, l_occurance);
7711 
7712     -- <ADDRESS>
7713     l_email := '<![CDATA['||replace(p_recipient_list(i).address, g_newline)||']]>';
7714     l_recp_pos := wf_xml.NewTag(l_recp_tmp, l_recp_pos, 'ADDRESS', l_email, l_attrlist);
7715     l_recp_pos := wf_xml.SkipTag(l_recp_tmp, 'ADDRESS', l_recp_pos, l_occurance);
7716 
7717     -- <RECIPIENT> end
7718     l_recp_pos := wf_xml.SkipTag(l_recp_tmp, 'RECIPIENT', l_recp_pos, l_occurance);
7719     l_attrlist.DELETE;
7720 
7721     i := p_recipient_list.next(i);
7722 
7723     l_recp_len := length(l_recp_tmp);
7724     if (l_recp_len > 30000) then
7725       dbms_lob.WriteAppend(l_recp_clob, l_recp_len, l_recp_tmp);
7726       l_recp_tmp := '';
7727       l_recp_pos := 0;
7728       l_recp_is_lob := true;
7729     end if;
7730   end loop;
7731 
7732   l_recp_len := length(l_recp_tmp);
7733   if(l_recp_len > 0 and l_recp_is_lob) then
7734     dbms_lob.WriteAppend(l_recp_clob, l_recp_len, l_recp_tmp);
7735   end if;
7736 
7737   -- <RECIPIENTLIST> start
7738   l_recp_pos := 0;
7739   if (l_recp_is_lob) then
7740     dbms_lob.createTemporary(l_recp, TRUE, dbms_lob.call);
7741     l_recp_pos := wf_xml.NewLOBTag(l_recp, l_recp_pos, 'RECIPIENTLIST', l_recp_clob, l_attrlist);
7742     l_recp_pos := wf_xml.SkipLOBTag(l_recp, 'RECIPIENTLIST', l_recp_pos, l_occurance);
7743   else
7744     l_recp_pos := wf_xml.NewTag(l_recp_txt, l_recp_pos, 'RECIPIENTLIST', l_recp_tmp, l_attrlist);
7745     l_recp_pos := wf_xml.SkipTag(l_recp_txt, 'RECIPIENTLIST', l_recp_pos, l_occurance);
7746   end if;
7747 
7748   l_hdr_pos := 0;
7749   l_hdr_tmp := '';
7750 
7751   if (p_from is not null or p_replyto is not null) then
7752      -- <FROM> start
7753      l_hdr_pos := wf_xml.NewTag(l_hdr_tmp, l_hdr_pos, 'FROM', '', l_attrlist);
7754 
7755      -- <NAME>
7756      if (p_from is not null) then
7757        l_from := replace(p_from, g_newLine);
7758        l_hdr_pos := wf_xml.NewTag(l_hdr_tmp, l_hdr_pos, 'NAME', l_from, l_attrlist);
7759        l_hdr_pos := wf_xml.SkipTag(l_hdr_tmp, 'NAME', l_hdr_pos, l_occurance);
7760      end if;
7761 
7762      -- <ADDRESS>
7763      if (p_replyto is not null) then
7764        l_replyto := replace(p_replyto, g_newLine);
7765        l_replyto := '<![CDATA['||l_replyto||']]>';
7766        l_hdr_pos := wf_xml.NewTag(l_hdr_tmp, l_hdr_pos, 'ADDRESS', l_replyto, l_attrlist);
7767        l_hdr_pos := wf_xml.SkipTag(l_hdr_tmp, 'ADDRESS', l_hdr_pos, l_occurance);
7768      end if;
7769 
7770      -- <FROM> end
7771      l_hdr_pos := wf_xml.SkipTag(l_hdr_tmp, 'FROM', l_hdr_pos, l_occurance);
7772   end if;
7773 
7774   l_subject := replace(p_subject, g_newLine);
7775   l_subject := '<![CDATA['||l_subject||']]>';
7776   l_hdr_pos := wf_xml.NewTag(l_hdr_tmp, l_hdr_pos, 'SUBJECT', l_subject, l_attrlist);
7777   l_hdr_pos := wf_xml.SkipTag(l_hdr_tmp, 'SUBJECT', l_hdr_pos, l_occurance);
7778 
7779   if (l_recp_is_lob) then
7780     dbms_lob.WriteAppend(l_recp, length(l_hdr_tmp), l_hdr_tmp);
7781     l_pos := wf_xml.NewLOBTag(l_msg_doc, l_pos, 'HEADER', l_recp, l_attrlist);
7782   else
7783     l_hdr_tmp := l_recp_txt || l_hdr_tmp;
7784     l_pos := wf_xml.NewLOBTag(l_msg_doc, l_pos, 'HEADER', l_hdr_tmp, l_attrlist);
7785   end if;
7786   l_pos := wf_xml.SkipLOBTag(l_msg_doc, 'HEADER', l_pos, l_occurance);
7787   l_attrlist.DELETE;
7788 
7789   -- <CONTENT> start
7790   wf_xml.AddElementAttribute('content-type', 'multipart/mixed', l_attrlist);
7791   l_pos := wf_xml.NewLOBTag(l_msg_doc, l_pos, 'CONTENT', '', l_attrlist);
7792   l_attrlist.DELETE;
7793 
7794   -- <BODYPART> start
7795   wf_xml.AddElementAttribute('content-type', p_content_type, l_attrlist);
7796   l_pos := wf_xml.NewLOBTag(l_msg_doc, l_pos, 'BODYPART', '', l_attrlist);
7797 
7798   -- <MESSAGE>
7799 
7800   l_messageIdx := wf_temp_lob.getLOB(g_LOBTable);
7801 
7802   dbms_lob.trim(g_LOBTable(l_messageIdx).temp_lob,0);
7803 
7804   dbms_lob.writeAppend(g_LOBTable(l_messageIdx).temp_lob,
7805                                     length(l_start_cdata), l_start_cdata);
7806 
7807   dbms_lob.append(dest_lob => g_LOBTable(l_messageIdx).temp_lob,
7808                   src_lob => p_message);
7809 
7810   if l_idstr is not null and l_fyi_flag = false then
7811      l_idstr := wf_core.newline||l_idstr;
7812      l_idsize := length(l_idstr);
7813      dbms_lob.writeAppend(lob_loc => g_LOBTable(l_messageIdx).temp_lob,
7814                           amount => l_idsize,
7815                           buffer => l_idstr);
7816   end if;
7817 
7818   dbms_lob.writeAppend(g_LOBTable(l_messageIdx).temp_lob,
7819                                     length(l_end_cdata), l_end_cdata);
7820 
7821   -- Alert Message has been appended in XML Payload, can be released now
7822   l_pos := wf_xml.NewLOBTag(l_msg_doc, l_pos, 'MESSAGE',
7823                             g_LOBTable(l_messageIdx).temp_lob, l_attrlist);
7824   l_pos := wf_xml.SkipLOBTag(l_msg_doc, 'MESSAGE', l_pos, l_occurance);
7825 
7826   l_attrlist.DELETE;
7827 
7828   -- <BODYPART> end
7829   l_pos := wf_xml.SkipLOBTag(l_msg_doc, 'BODYPART', l_pos, l_occurance);
7830 
7831   -- <CONTENT> end
7832   l_pos := wf_xml.SkipLOBTag(l_msg_doc, 'CONTENT', l_pos, l_occurance);
7833 
7834   -- <NOTIFICATION> end
7835   l_pos := wf_xml.SkipLOBTag(l_msg_doc, 'NOTIFICATION', l_pos, l_occurance);
7836 
7837   -- <NOTIFICATIONGROUP> end
7838   l_pos := wf_xml.SkipLOBTag(l_msg_doc, 'NOTIFICATIONGROUP', l_pos, l_occurance);
7839 
7840   wf_event_t.Initialize(l_event);
7841 
7842   l_event.event_name := wf_xml.WF_NTF_SEND_MESSAGE;
7843 
7844   -- Create an event key based firstly on the parameter. If that does not
7845   -- exist, then base it on the ID string. If that too is null (FYI for
7846   -- instance) then create a default, non unique key.
7847   if p_event_key is not null then
7848      l_event.event_key := p_event_key;
7849   elsif p_idstring is not null then
7850      l_event.event_key := p_idstring;
7851   else
7852      l_event.event_key := p_module||':FYI';
7853   end if;
7854 
7855   l_event.SetEventData(l_msg_doc);
7856 
7857   l_agent := wf_agent_t('WF_NOTIFICATION_OUT', wf_event.local_system_name);
7858   l_event.SetFromAgent(l_agent);
7859 
7860   -- Generally it would be 'ALR'
7861   if(instr( send.p_module, ':') = 0) then
7862     l_event.addParameterToList('Q_CORRELATION_ID', send.p_module || ':' );
7863   else
7864    l_event.addParameterToList('Q_CORRELATION_ID', send.p_module  );
7865   END if;
7866 
7867   l_event.addParameterToList('NOTIFICATION_ID', '0');
7868 
7869   wf_event.send(l_event);
7870 
7871   -- Release allocated temp LOBs back to pool
7872   wf_temp_lob.releaseLob(g_LOBTable, l_messageIdx);
7873 
7874   if (wf_log_pkg.level_procedure >= fnd_log.g_current_runtime_level) then
7875      wf_log_pkg.string(WF_LOG_PKG.level_procedure,
7876                        'wf.plsql.WF_MAIL.send',
7877                         'END');
7878   end if;
7879 
7880 exception
7881   when others then
7882      -- Release allocated temp LOBs back to pool
7883      if(l_messageIdx > 0 ) then
7884         wf_temp_lob.releaseLob(g_LOBTable, l_messageIdx);
7885       END if;
7886 
7887 
7888     wf_core.context('Wf_Mail', 'Send', p_idstring, p_subject);
7889     raise;
7890 end Send;
7891 
7892 
7893 --
7894 -- SendMoreInfoResponseWarning
7895 --   procedure to send a warning notification about the answer for a More
7896 --   Informantion request that has already been answered.
7897 -- IN
7898 --   p_nid - Notification Id
7899 --   p_from_email - Email address of the responder
7900 
7901 procedure SendMoreInfoResponseWarning(p_nid NUMBER, p_from_email VARCHAR2)
7902 is
7903   l_to_role VARCHAR2(360);
7904   l_from_role VARCHAR2(360);
7905   l_installed VARCHAR2(64);
7906   l_question VARCHAR2(4000);
7907   l_subject VARCHAR2(360);
7908   l_codeset VARCHAR2(360);
7909   l_orig_lang VARCHAR2(64);
7910   l_orig_terr VARCHAR2(64);
7911   l_orig_chrs VARCHAR2(64);
7912 
7913   l_mail_error_message VARCHAR2(1024);
7914 
7915   l_msg_type VARCHAR2(8);
7916   l_msg_name VARCHAR2(30);
7917   l_nodename VARCHAR2(240);
7918 
7919   l_idstr VARCHAR2(240);
7920   l_str VARCHAR2(64);
7921 
7922   l_description VARCHAR2(1000);
7923   l_start_cdata VARCHAR2(10) := '<![CDATA[';
7924   l_end_cdata VARCHAR2(4) := ']]>';
7925 
7926  -- l_recp_tmp VARCHAR2(4000);
7927   l_text_body VARCHAR2(32000);
7928   l_html_body VARCHAR2(32000);
7929 
7930   --l_recp_clob VARCHAR2(32000);
7931 
7932   l_fax             varchar2(240);
7933   l_expiration_date   date;
7934   l_status            varchar2(8);
7935   l_orig_system       varchar2(30);
7936   l_orig_system_id    number;
7937 
7938   l_msg_doc CLOB;
7939   l_attrlist wf_xml.wf_xml_attr_table_type;
7940 
7941   l_messageidx pls_integer;
7942   l_pos INTEGER;
7943   l_occurance INTEGER := 1;
7944 
7945   l_event wf_event_t;
7946   l_agent wf_agent_t;
7947 
7948   l_start    pls_integer;
7949   l_end      pls_integer;
7950 
7951   hdrxml varchar2(32000);
7952   hdrxmlPos integer;
7953 
7954 
7955   l_display_name wf_roles.display_name%TYPE;
7956   l_to_emailaddress wf_roles.email_address%TYPE;
7957   l_to_ntf_pref wf_roles.notification_preference%TYPE;
7958   l_language wf_roles.LANGUAGE %TYPE;
7959   l_territory wf_roles.territory%TYPE;
7960 
7961   CURSOR c_ques IS
7962     SELECT from_role, to_role, user_comment
7963     FROM   wf_comments
7964     WHERE  notification_id = p_nid
7965     AND    action = 'QUESTION'
7966     ORDER BY comment_date desc;
7967 
7968 BEGIN
7969 
7970   IF(wf_log_pkg.level_procedure >= fnd_log.g_current_runtime_level) THEN
7971      wf_log_pkg.string(wf_log_pkg.level_procedure,
7972      'wf.plsql.WF_MAIL.SendMoreInfoResponseWarning',   'BEGIN');
7973   END IF;
7974 
7975   -- Get details of last question asked from WF_COMMENTS table and
7976   -- Get role language, territory and codeset details.
7977 
7978   OPEN c_ques;
7979     FETCH c_ques INTO l_from_role, l_to_role, l_question;
7980 
7981     if (c_ques%notfound) then
7982       l_question := '';
7983       l_from_role := '';
7984       l_to_role := '';
7985     END if;
7986 
7987   CLOSE c_ques;
7988 
7989   -- Get role detail.
7990   wf_directory.GetRoleInfoMail(l_to_role, l_display_name, l_to_emailaddress, l_to_ntf_pref,
7991                                 l_language, l_territory, l_orig_system,
7992                                 l_orig_system_id,
7993                                 l_installed);
7994 
7995 
7996   -- if role not  exist in wf_comments or email address exist
7997   -- in wf_comments  then use p_from_email to get other details .
7998   --
7999   if( l_to_role = '' OR
8000       instr(l_to_role, '@') > 0 OR
8001       l_to_emailaddress IS null ) then
8002 
8003      -- Stripping off unwanted info from email
8004      l_start := instr(p_from_email, '<', 1, 1);
8005 
8006      if (l_start > 0) then
8007 
8008         l_end := instr(p_from_email, '>', l_start);
8009         l_to_emailaddress := substr(p_from_email, l_start+1, l_end-l_start-1);
8010 
8011      end if;
8012 
8013      -- Get role detail based on email address.
8014      --
8015      WF_DIRECTORY.GetInfoFromMail(l_to_emailaddress, l_to_role, l_display_name,
8016                                  l_description, l_to_ntf_pref, l_language,
8017                                  l_territory, l_fax,l_expiration_date, l_status,
8018                                  l_orig_system, l_orig_system_id);
8019 
8020   end if;
8021 
8022 
8023   begin
8024        SELECT NLS_CODESET, INSTALLED_FLAG
8025        INTO   l_codeset,l_installed
8026        FROM   WF_LANGUAGES
8027        WHERE  NLS_LANGUAGE = l_language
8028        AND    INSTALLED_FLAG = 'Y';
8029   exception
8030        when no_data_found then
8031            l_installed := 'N';
8032   end;
8033 
8034   -- << sstomar>: The content of this warning ntf emil
8035   --       are generated by Mailer, so other NLS parameters ( NLS_DATE_FORMAT etc.
8036   --       because we don;t have any such attributes in message body)
8037   --       are not required to SET/ RESET but if we want to use centralized API
8038   --       wf_notification_util.setNLSContext / getNLSContext then we need to modify.
8039   --
8040 
8041   -- getting the language for the session
8042   wf_notification.getnlslanguage(l_orig_lang, l_orig_terr, l_orig_chrs);
8043 
8044   --
8045   If l_installed = 'Y' then
8046     -- Set user's language
8047     wf_notification.setnlslanguage(l_language, l_territory);
8048 
8049   end if;
8050 
8051   -- Get message details
8052   SELECT message_type,message_name
8053   INTO l_msg_type,l_msg_name
8054   FROM wf_notifications
8055   WHERE notification_id = p_nid;
8056 
8057   -- Get template 'WARNING'
8058   SELECT subject, BODY, HTML_BODY
8059   INTO l_subject,l_text_body,l_html_body
8060   FROM wf_messages_vl
8061   WHERE name = 'MORE_INFO_ANSWERED'
8062   AND type = 'WFMAIL';
8063 
8064   -- Get Error message
8065   begin
8066      SELECT TEXT INTO l_mail_error_message
8067      FROM WF_RESOURCES
8068      WHERE NAME = 'WFNTF_CANNOT_REPLY'
8069      AND LANGUAGE = userenv('LANG');
8070   exception
8071       when NO_DATA_FOUND then
8072         wf_core.raise('WFCORE_NO_MESSAGE');
8073   end;
8074 
8075   l_nodename := wf_mailer_parameter.getvalueforcorr(
8076                              l_msg_type||':'||l_msg_name,'NODENAME');
8077 
8078   l_idstr := '';
8079 
8080   -- Substitute Tags with values
8081   l_subject := SUBSTR(REPLACE(l_subject,'&NOTIFICATION',p_nid ),1, 360);
8082 
8083   l_text_body := SUBSTR(REPLACE(l_text_body,
8084                          '&MAIL_ERROR_MESSAGE',l_mail_error_message),1,32000);
8085 
8086   l_text_body := SUBSTR(REPLACE(l_text_body,'&NOTIFICATION',p_nid),1,32000);
8087 
8088   l_text_body := SUBSTR(REPLACE(l_text_body,'&FROM',l_from_role),1,32000);
8089 
8090   l_text_body := SUBSTR(REPLACE(l_text_body,'&QUESTION',l_question),1,32000);
8091 
8092   -- HTML BODY
8093   l_html_body := SUBSTR(REPLACE(l_html_body,
8094                           '&MAIL_ERROR_MESSAGE',l_mail_error_message),1,32000);
8095 
8096   l_html_body := SUBSTR(REPLACE(l_html_body,'&NOTIFICATION',p_nid),1,32000);
8097 
8098   l_html_body := SUBSTR(REPLACE(l_html_body,'&FROM',l_from_role),1,32000);
8099 
8100   l_html_body := SUBSTR(REPLACE(l_html_body,'&QUESTION',l_question),1,32000);
8101 
8102   -- reset the session language
8103   WF_Notification.SetNLSLanguage(l_orig_lang, l_orig_terr);
8104 
8105   -- LOB to store message payload.
8106   dbms_lob.createtemporary(l_msg_doc, TRUE,dbms_lob.CALL);
8107 
8108   l_str := '<?xml version="1.0" ?>';
8109   l_pos := LENGTH(l_str);
8110   dbms_lob.WRITE(l_msg_doc,   l_pos,   1,   l_str);
8111 
8112   -- <NOTIFICATIONGROUP> begin
8113   wf_xml.AddElementAttribute('maxcount','1',l_attrlist);
8114   l_pos := wf_xml.newlobtag(l_msg_doc,l_pos,'NOTIFICATIONGROUP','',l_attrlist);
8115   l_attrlist.DELETE;
8116 
8117 
8118   -- <NOTIFICATION> begin , set NID as 0 so that
8119   -- WF_NOTIFICATION.MAIL_STAUTS should not affect to deliver
8120   -- this warning message.
8121   wf_xml.AddElementAttribute('nid','0',l_attrlist);
8122   wf_xml.AddElementAttribute('nidstr',l_idstr,l_attrlist);
8123   wf_xml.AddElementAttribute('language',l_language,l_attrlist);
8124   wf_xml.AddElementAttribute('territory',l_territory,l_attrlist);
8125   wf_xml.AddElementAttribute('codeset',l_codeset,l_attrlist);
8126 
8127   -- <<sstomar>>:
8128   --     Setting default parameter's value as for warning, as these parameters
8129   --     won't be used to update AppsContext (non-OAF content)
8130   -- TODO>>
8131   wf_xml.AddElementAttribute('nlsDateformat', wf_core.nls_date_format, l_attrlist);
8132   wf_xml.AddElementAttribute('nlsDateLanguage', wf_core.nls_date_language, l_attrlist);
8133   wf_xml.AddElementAttribute('nlsNumericCharacters', wf_core.nls_numeric_characters, l_attrlist);
8134   wf_xml.AddElementAttribute('nlsSort', wf_core.nls_sort, l_attrlist);
8135 
8136   wf_xml.AddElementAttribute('priority','50',l_attrlist);
8137   wf_xml.AddElementAttribute('accesskey','0',l_attrlist);
8138   wf_xml.AddElementAttribute('node',l_nodename,l_attrlist);
8139   wf_xml.AddElementAttribute('item_type',l_msg_type,l_attrlist);
8140   wf_xml.AddElementAttribute('message_name',l_msg_name,l_attrlist);
8141   wf_xml.AddElementAttribute('full-document','Y',l_attrlist);
8142 
8143   l_pos := wf_xml.NewLOBTag(l_msg_doc, l_pos,'NOTIFICATION','',l_attrlist);
8144   l_attrlist.DELETE;
8145 
8146   -- Below variabkes are just to generate HEADER of
8147   -- xml PAYLOAD
8148   hdrxmlPos := 0;
8149   hdrxml := '';
8150 
8151   hdrxmlPos := WF_XML.NewTag(hdrxml, hdrxmlPos, 'RECIPIENTLIST', '', l_attrlist);
8152 
8153   -- TO
8154   WF_XML.AddElementAttribute('name', l_to_role, l_attrlist);
8155   WF_XML.AddElementAttribute('type', 'to', l_attrlist);
8156 
8157   hdrxmlPos := WF_XML.NewTag(hdrxml, hdrxmlPos, 'RECIPIENT', '', l_attrlist);
8158   l_attrlist.DELETE;
8159 
8160 
8161   if (l_display_name is not null or l_display_name <> '') then
8162       l_display_name := replace(l_display_name, g_newLine);
8163       l_display_name := '<![CDATA[' || l_display_name ||']]>';
8164   else
8165       l_display_name := '';
8166   end if;
8167 
8168   -- attrlist is empty
8169   hdrxmlPos :=  WF_XML.NewTag(hdrxml, hdrxmlPos, 'NAME', l_display_name, l_attrlist);
8170   hdrxmlPos :=  WF_XML.SkipTag(hdrxml, 'NAME', hdrxmlPos, l_occurance);
8171 
8172   l_to_emailaddress := replace(l_to_emailaddress, g_newLine);
8173   l_to_emailaddress := '<![CDATA['||l_to_emailaddress||']]>';
8174 
8175   -- attrlist is empty
8176   hdrxmlPos :=  WF_XML.NewTag(hdrxml, hdrxmlPos, 'ADDRESS', l_to_emailaddress, l_attrlist);
8177   hdrxmlPos :=  WF_XML.SkipTag(hdrxml, 'ADDRESS', hdrxmlPos, l_occurance);
8178 
8179   hdrxmlPos :=  WF_XML.SkipTag(hdrxml, 'RECIPIENT', hdrxmlPos, l_occurance);
8180 
8181   -- end RECIPIENTLIST tag
8182   hdrxmlPos :=  WF_XML.SkipTag(hdrxml, 'RECIPIENTLIST', hdrxmlPos, l_occurance);
8183   l_attrlist.DELETE;
8184 
8185 
8186   -- Use from_role which asked the Question or requested for more info.
8187   -- TODO : we should use Display name of that role.
8188   IF(l_from_role IS NOT NULL AND l_from_role <> '' ) then
8189      -- attrlist is empty
8190     hdrxmlPos :=  WF_XML.NewTag(hdrxml, hdrxmlPos, 'FROM', '', l_attrlist);
8191 
8192     l_from_role := replace(l_from_role, g_newLine);
8193     hdrxmlPos := WF_XML.NewTag(hdrxml, hdrxmlPos, 'NAME', l_from_role, l_attrlist);
8194     hdrxmlPos := WF_XML.SkipTag(hdrxml, 'NAME', hdrxmlPos, l_occurance);
8195 
8196     hdrxmlPos := WF_XML.SkipTag(hdrxml, 'FROM', hdrxmlPos, l_occurance);
8197 
8198   end if;
8199 
8200   l_attrlist.DELETE;
8201   l_subject := replace(l_subject, g_newLine);
8202   l_subject := '<![CDATA['||l_subject||']]>';
8203   hdrxmlPos := WF_XML.NewTag(hdrxml, hdrxmlPos, 'SUBJECT', l_subject, l_attrlist);
8204 
8205   -- Add HEADER tag in LOB.
8206   l_pos := WF_XML.NewLOBTag(l_msg_doc, l_pos, 'HEADER', hdrxml, l_attrlist);
8207   l_pos := WF_XML.SkipLOBTag(l_msg_doc, 'HEADER', l_pos, l_occurance);
8208   l_attrlist.DELETE;
8209 
8210   -- <CONTENT> start
8211   wf_xml.AddElementAttribute('content-type','multipart/mixed',l_attrlist);
8212   l_pos := wf_xml.NewLOBTag(l_msg_doc,l_pos,'CONTENT','',l_attrlist);
8213   l_attrlist.DELETE;
8214 
8215   -- <BODYPART> start
8216   wf_xml.AddElementAttribute('content-type','text/plain', l_attrlist);
8217   l_pos := wf_xml.NewLOBTag(l_msg_doc,l_pos,'BODYPART', '', l_attrlist);
8218 
8219 
8220   l_messageidx := wf_temp_lob.getlob(g_lobtable);
8221 
8222   dbms_lob.TRIM(g_lobtable(l_messageidx).temp_lob,0);
8223 
8224   dbms_lob.writeappend(g_lobtable(l_messageidx).temp_lob,
8225                        LENGTH(l_start_cdata), l_start_cdata);
8226 
8227   dbms_lob.append(dest_lob=>g_lobtable(l_messageidx).temp_lob,
8228                   src_lob=>l_text_body);
8229 
8230   dbms_lob.writeappend(g_lobtable(l_messageidx).temp_lob,
8231                     LENGTH(l_end_cdata),l_end_cdata);
8232 
8233   -- l_attrlist content-type='text/plain' will be used same
8234   -- as for BODYPART
8235   -- -- <MESSAGE>
8236   l_pos :=  wf_xml.NewLOBTag(l_msg_doc,l_pos, 'MESSAGE',
8237                              g_lobtable(l_messageidx).temp_lob,
8238                              l_attrlist);
8239 
8240   l_pos := wf_xml.SkipLOBTag(l_msg_doc,'MESSAGE',l_pos,l_occurance);
8241   l_attrlist.DELETE;
8242 
8243   -- <BODYPART> end
8244   l_pos := wf_xml.SkipLOBTag(l_msg_doc,'BODYPART',l_pos,l_occurance);
8245 
8246   -- TODO : << sstomar>>
8247   -- Later we will consider to send HTML body part
8248   -- commenting below code as of now.
8249 
8250   -- reuse same LOB for html body
8251   -- Check if same can be re-used or not.
8252   --dbms_lob.trim(g_LOBTable(l_messageidx).temp_lob, 0);
8253 
8254   --l_attrlist.DELETE;
8255 
8256   -- <BODYPART> start
8257   --wf_xml.AddElementAttribute('content-type','text/html', l_attrlist);
8258   --l_pos := wf_xml.NewLOBTag(l_msg_doc,l_pos,'BODYPART', '', l_attrlist);
8259 
8260   --dbms_lob.writeappend(g_lobtable(l_messageidx).temp_lob,
8261   --                     LENGTH(l_start_cdata), l_start_cdata);
8262 
8263   --dbms_lob.append(dest_lob=>g_lobtable(l_messageidx).temp_lob,
8264   --               src_lob=>l_html_body);
8265 
8266   --dbms_lob.writeappend(g_lobtable(l_messageidx).temp_lob,
8267   --                  LENGTH(l_end_cdata),l_end_cdata);
8268 
8269   --l_pos :=  wf_xml.NewLOBTag(l_msg_doc,l_pos, 'MESSAGE',
8270   --                           g_lobtable(l_messageidx).temp_lob,
8271   --                           l_attrlist);
8272 
8273   --l_pos := wf_xml.SkipLOBTag(l_msg_doc,'MESSAGE',l_pos,l_occurance);
8274   --l_attrlist.DELETE;
8275 
8276   -- <BODYPART> end
8277   --l_pos := wf_xml.SkipLOBTag(l_msg_doc,'BODYPART',l_pos,l_occurance);
8278 
8279   -- <CONTENT> end
8280   l_pos := wf_xml.skiplobtag(l_msg_doc,'CONTENT',l_pos,l_occurance);
8281   -- <NOTIFICATION> end
8282   l_pos := wf_xml.skiplobtag(l_msg_doc,'NOTIFICATION',l_pos,l_occurance);
8283   -- <NOTIFICATIONGROUP> end
8284   l_pos := wf_xml.skiplobtag(l_msg_doc,'NOTIFICATIONGROUP',l_pos,l_occurance);
8285 
8286 
8287   if(wf_log_pkg.level_procedure >= fnd_log.g_current_runtime_level) then
8288     wf_log_pkg.string(wf_log_pkg.level_procedure,
8289     'wf.plsql.WF_MAIL.SendMoreInfoResponseWarning',
8290     'Initializing ' || wf_xml.wf_ntf_send_message || ' event');
8291   end if;
8292 
8293   wf_event_t.initialize(l_event);
8294   l_event.event_name := wf_xml.wf_ntf_send_message;
8295 
8296   -- Create an event key : make it as 0 so that this warning message
8297   -- waon't rely on wf_notification TABLE.
8298   l_event.event_key := 0;
8299   l_event.seteventdata(l_msg_doc);
8300 
8301   l_agent := wf_agent_t('WF_NOTIFICATION_OUT',wf_event.local_system_name);
8302   l_event.setfromagent(l_agent);
8303 
8304   l_event.addparametertolist('Q_CORRELATION_ID',l_msg_type || ':' ||l_msg_name);
8305   l_event.addparametertolist('NOTIFICATION_ID','0');
8306 
8307   wf_event.send(l_event);
8308 
8309   -- Release allocated temp LOBs back to pool
8310   wf_temp_lob.releaselob(g_lobtable,  l_messageidx);
8311 
8312   if(wf_log_pkg.level_procedure >= fnd_log.g_current_runtime_level) then
8313     wf_log_pkg.string(wf_log_pkg.level_procedure,
8314     'wf.plsql.WF_MAIL.SendMoreInfoResponseWarning', 'END');
8315   end if;
8316 
8317 exception
8318   when others then
8319       --Release allocated temp LOBs back to pool
8320       if (l_orig_lang is not null and l_orig_terr is not null) then
8321         WF_Notification.SetNLSLanguage(l_orig_lang,l_orig_terr);
8322       end IF;
8323 
8324       if(l_messageidx > 0) then
8325         wf_temp_lob.releaselob(g_lobtable,  l_messageidx);
8326       end if;
8327 
8328       raise;
8329 end SendMoreInfoResponseWarning;
8330 
8331 -- SetNtfEventsSubStatus
8332 --   This procedure sets the status of seeded subscription to the event group
8333 --   oracle.apps.wf.notification.send.group. This subscription is responsible
8334 --   for notification XML message generation and presenting it to the mailer for
8335 --   e-mail dispatch. Disabling this subscription causes e-mails not to be sent.
8336 --
8337 --    ENABLED  - E-mails are sent
8338 --    DISABLED - E-mails are not sent
8339 -- IN
8340 --   p_status - Subscription status (Either ENABLED or DISABLED)
8341 procedure SetNtfEventsSubStatus(p_status in varchar2)
8342 is
8343 begin
8344 
8345   if (p_status in ('ENABLED', 'DISABLED')) then
8346     -- Update the notification send event group subscription
8347     -- with the specified status
8348     UPDATE wf_event_subscriptions
8349     SET    status = p_status
8350     WHERE  rule_data = 'MESSAGE'
8351     AND    owner_name = 'Oracle Workflow'
8352     AND    owner_tag = 'FND'
8353     AND    event_filter_guid
8354        IN (SELECT guid
8355            FROM   wf_events
8356            WHERE  name = 'oracle.apps.wf.notification.send.group'
8357            AND    type = 'GROUP')
8358     AND    out_agent_guid
8359        IN (SELECT guid
8360            FROM   wf_agents
8361            WHERE  name = 'WF_NOTIFICATION_OUT'
8362            AND    system_guid = hextoraw(wf_core.translate('WF_SYSTEM_GUID')))
8363     AND    customization_level = 'L';
8364 
8365     -- Inform BES cache manager that BES meta-data state has changed
8366     wf_bes_cache.SetMetaDataUploaded;
8367   end if;
8368 
8369 end SetNtfEventsSubStatus;
8370 
8371 
8372 -- SetResponseDelimiters
8373 -- Sets the package level variables with one procedure call. The
8374 -- response delimiters are used to determine the free form text
8375 -- values in email notification responses.
8376 --
8377 -- IN
8378 -- open_text - Opening text/plain delimiter
8379 -- close_text - Closing text/plain delimiter
8380 -- open_html - Opening text/html delimiter
8381 -- close_html - Closing text/html delimiter
8382 procedure SetResponseDelimiters(open_text in varchar2,
8383                                 close_text in varchar2,
8384                                 open_html in varchar2,
8385                                 close_html in varchar2)
8386 is
8387 begin
8388    if open_text is null then
8389       wf_mail.g_open_text_delimiter := '"';
8390    else
8391       wf_mail.g_open_text_delimiter := open_text;
8392    end if;
8393 
8394    if close_text is null then
8395       wf_mail.g_close_text_delimiter := '"';
8396    else
8397       wf_mail.g_close_text_delimiter := close_text;
8398    end if;
8399 
8400    if open_html is null then
8401       wf_mail.g_open_html_delimiter := '''';
8402    else
8403       wf_mail.g_open_html_delimiter := open_html;
8404    end if;
8405 
8406    if open_html is null then
8407       wf_mail.g_close_html_delimiter := '''';
8408    else
8409       wf_mail.g_close_html_delimiter := close_html;
8410    end if;
8411 
8412 end SetResponseDelimiters;
8413 
8414 
8415 end WF_MAIL;