DBA Data[Home] [Help]

PACKAGE BODY: APPS.WF_MAIL

Source


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