1 package body WF_NOTIFICATION as
2 /* $Header: wfntfb.pls 120.43.12010000.27 2009/02/26 01:39:33 alepe ship $ */
3
4 --
5 -- Constants
6 --
7
8 -- Max_forward - Max number of forwards allowed by routing rules
9 -- before a routing loop is inferred.
10 max_forward number := 10;
11
12 -- Sequence number for comments for notification actions in the same
13 -- session, caused by Routing Rules
14 g_comments_seq pls_integer := 0;
15
16 -- logging variable
17 g_plsqlName varchar2(35) := 'wf.plsql.WF_NOTIFICATION.';
18
19 --
20 -- Private Variables
21 --
22 table_direction varchar2(1) := 'L';
23 table_type varchar2(1) := 'V';
24 table_width varchar2(8) := '100%';
25 table_border varchar2(2) := '0';
26 table_cellpadding varchar2(2) := '1';
27 table_cellspacing varchar2(2) := '0';
28 table_bgcolor varchar2(7) := 'white';
29 th_bgcolor varchar2(7) := '#cfe0f1';
30 th_fontcolor varchar2(7) := '#336699';
31 th_fontface varchar2(80) := 'Arial, Helvetica, Geneva, sans-serif';
32 th_fontsize varchar2(2) := '2';
33 td_bgcolor varchar2(7) := '#f7f7e7';
34 td_fontcolor varchar2(7) := 'black';
35 td_fontface varchar2(80) := 'Arial, Helvetica, Geneva, sans-serif';
36 td_fontsize varchar2(2) := '2';
37 table_summary WF_RESOURCES.TEXT%TYPE := wf_core.translate('ACTION_HISTORY');
38
39 -- Session NLS_DATE_FORMAT
40 -- Use this to fulfill the GSCC requirement
41 s_nls_date_format varchar2(120); -- session parameter value has length of 120
42
43 --Data types for custom columns API
44 type text_array_t is varray(10) of VARCHAR2(4000);
45 type numb_array_t is varray(5) of NUMBER;
46 type date_array_t is varray(5) of DATE;
47
48 -- checks if plsqlclob,plsqlblob exists
49 plsql_clob_exists pls_integer;
50
51 --
52 -- Private Functions
53 --
54
55 -- NTF_TABLE
56 -- Generate a "Browser Look and Feel (BLAF)" look a like table.
57 -- ADA compliance is achieved through "scope".
58 --
59 -- IN
60 -- cells - array of table cells
61 -- col - number of columns
62 -- type - Two character code. First determines header position.
63 -- - optional second denotes direction for Bi-Di support.
64 -- - V to generate a vertical table
65 -- - H to generate a horizontal table
66 -- - N to generate a mailer notification header table which
67 -- is a form of vertical
68 -- - *L Left to Right (default)
69 -- - *R Right to Left
70 -- rs - the result html code for the table
71 --
72 -- NOTE
73 -- type - Vertical table is Header always on the first column
74 -- - Horizontal table is Headers always on first row
75 -- - The direction can be omitted to which the default will be
76 -- - Left to Right.
77 --
78 -- cell has the format:
79 -- R40%:content of the cell here
80 -- ^ ^
81 -- | |
82 -- | + -- width specification
83 -- +-- align specification (L-Left, C-Center, R-Right, S-Start E-End)
84 --
85 procedure NTF_Table(cells in tdType,
86 col in pls_integer,
87 type in varchar2, -- 'V'ertical or 'H'orizontal
88 rs in out nocopy varchar2)
89 is
90 i pls_integer;
91 colon pls_integer;
92 modv pls_integer;
93 alignv varchar2(1);
94 l_align varchar2(8);
95 l_width varchar2(3);
96 l_text varchar2(4000);
97 l_type varchar2(1);
98 l_dir varchar2(1);
99 l_dirAttr varchar2(10);
100
101
102 -- Define a local set and initialize with the default
103 l_table_width varchar2(8);
104 l_table_border varchar2(2);
105 l_table_cellpadding varchar2(2);
106 l_table_cellspacing varchar2(2);
107 l_table_bgcolor varchar2(7);
108 l_th_bgcolor varchar2(7);
109 l_th_fontcolor varchar2(7);
110 l_th_fontface varchar2(80);
111 l_th_fontsize varchar2(2);
112 l_td_bgcolor varchar2(7);
113 l_td_fontcolor varchar2(7);
114 l_td_fontface varchar2(80);
115 l_td_fontsize varchar2(4);
116 l_table_summary WF_RESOURCES.TEXT%TYPE;
117
118 begin
119 l_table_width := table_width;
120 l_table_border := table_border;
121 l_table_cellpadding := table_cellpadding;
122 l_table_cellspacing := table_cellspacing;
123 l_table_bgcolor := table_bgcolor;
124 l_th_bgcolor := th_bgcolor;
125 l_th_fontcolor := th_fontcolor;
126 l_th_fontface := th_fontface;
127 l_th_fontsize := th_fontsize;
128 l_td_bgcolor := td_bgcolor;
129 l_td_fontcolor := td_fontcolor;
130 l_td_fontface := td_fontface;
131 l_td_fontsize := '10pt';
132 l_table_summary := table_summary;
133
134 if length(type) > 1 then
135 l_type := substrb(type, 1, 1);
136 l_dir := substrb(type,2, 1);
137 else
138 l_type := type;
139 l_dir := 'L';
140 end if;
141
142 if l_dir = 'L' then
143 l_dirAttr := NULL;
144 else
145 l_dirAttr := 'dir="RTL"';
146 end if;
147
148 if (l_type = 'N') then
149 -- Notification format. Alter the default colors.
150 l_table_bgcolor := '#FFFFFF';
151 l_th_bgcolor := '#FFFFFF';
152 l_th_fontcolor := '#000000';
153 l_td_bgcolor := '#FFFFFF';
154 l_td_fontcolor := '#000000';
155 l_table_cellpadding := '0';
156 l_table_cellspacing := '0';
157 l_table_width := '100%';
158 end if;
159
160 if (cells.COUNT = 0) then
161 rs := null;
162 return;
163 end if;
164
165 -- << bug 6369346 >> :
166 -- There is no need to increase width of <TD>
167 -- in WF_MAIL.GetHeaderTable from 30% to 50%, just
168 -- increase width of below html table to 100% ,
169 -- irrespective of table Type ('N' -notification, 'H', 'V')
170 rs := '<table width="100%" summary="'||l_table_summary||'" '||l_dirAttr|| '><tr><td>';
171
172 if (l_type = 'N') then
173 rs := rs||wf_core.newline||'<table width="'||l_table_width||'"'||
174 ' border="'||l_table_border||'"'||
175 ' cellpadding="'||l_table_cellpadding||'"'||
176 ' cellspacing="'||l_table_cellspacing||'"'||
177 ' summary="'||l_table_summary||'"'||
178 ' bgcolor="'||l_table_bgcolor||'" '||l_dirAttr||'>';
179
180
181
182 else -- Type ('V' and 'H')
183
184 rs := rs||wf_core.newline||'<table width="'||l_table_width||'"'||
185 ' class="x1h" cellpadding="'||l_table_cellpadding||'"'||
186 ' cellspacing="'||l_table_cellspacing||'"'||
187 ' summary="'||l_table_summary||'"'||
188 ' border="'||l_table_border||'"'||
189 ' '||l_dirAttr||'>';
190 end if;
191
192 for i in 1..cells.LAST loop
193 modv := mod(i, col);
194 if (modv = 1) then
195 rs := rs||wf_core.newline||'<tr>';
196 end if;
197
198 alignv := substrb(cells(i), 1, 1);
199 if (alignv = 'R') then
200 l_align := 'RIGHT';
201 elsif (alignv = 'L') then
202 l_align := 'LEFT';
203 elsif (alignv = 'S') then
204 if (l_dir = 'L') then
205 l_align := 'LEFT';
206 else
207 l_align := 'RIGHT';
208 end if;
209 elsif (alignv = 'E') then
210 if (l_dir = 'L') then
211 l_align := 'RIGHT';
212 else
213 l_align := 'LEFT';
214 end if;
215 else
216 l_align := 'CENTER';
217 end if;
218
219 colon := instrb(cells(i),':');
220 l_width := substrb(cells(i), 2, colon-2);
221 l_text := substrb(cells(i), colon+1); -- what is after the colon
222
223 if ((l_type = 'V' and modv = 1) or (l_type = 'N' and modv = 1)
224 or (l_type = 'H' and i <= col)) then
225 -- this is a header
226 if (l_type = 'N') then
227 rs := rs||wf_core.newline||'<td class="x8" ';
228 elsif (l_type = 'H') then
229 rs := rs||wf_core.newline||'<th class="x1r x4j" ';
230 rs := rs||' scope="col"';
231 else -- if (l_type = 'V') then
232 rs := rs||wf_core.newline||'<th class="x1r x4q" ';
233 rs := rs||' scope="row"';
234 end if;
235
236 if (l_width is not null) then
237 rs := rs||' width="'||l_width||'"';
238 end if;
239 rs := rs||' align="'||l_align||'" valign="baseline" >';
240 if (l_type = 'N') then
241 rs := rs||l_text;
242 rs := rs||'</td><td width="12">&'||'nbsp;</td>';
243 else
244 rs := rs||'<span class="x24">'||l_text||'</span>';
245 rs := rs||'</th>';
246 end if;
247 else
248 -- this is regular data
249 rs := rs||wf_core.newline||'<td';
250 if (l_width is not null) then
251 rs := rs||' width="'||l_width||'"';
252 end if;
253 rs := rs||' align="'||l_align||'" valign="baseline" ';
254
255 if (l_type = 'N') then
256 rs := rs||' class="x2">'||l_text||'</td>';
257 else
258 rs := rs||' class="x1l x4x">'||l_text||'</td>';
259 end if;
260 end if;
261 if (modv = 0) then
262 rs := rs||wf_core.newline||'</tr>';
263 end if;
264 end loop;
265 rs := rs||wf_core.newline||'</table>'||wf_core.newline||'</td></tr></table>';
266
267 exception
268 when OTHERS then
269 wf_core.context('Wf_Notification', 'NTF_Table',to_char(col),l_type);
270 raise;
271 end NTF_Table;
272
273 --
274 -- WF_MSG_ATTR
275 -- Create a table of message attributes
276 -- NOTE
277 -- o Considered using dynamic sql passing in attributes as a comma delimited
278 -- list. The cost of non-reusable sql may be high.
279 -- o Considered using bind variables with dynamic sql. Then we must impose
280 -- a hard limit on the number of bind variables. If a limit exceed we
281 -- need some fall back handling.
282 -- o Parsing the comma delimited list and making individual select is more
283 -- costly. But the sql will be reusable, it may end up cheaper.
284 --
285 function wf_msg_attr(nid in number,
286 attrs in varchar2,
287 disptype in varchar2)
288 return varchar2
289 is
290 l_attr varchar2(30);
291 l_dispname varchar2(80);
292 l_text varchar2(4000);
293
294 l_type varchar2(8);
295 l_cols pls_integer;
296 l_table_direction varchar2(1);
297 l_format varchar2(240);
298 l_textv varchar2(4000);
299 l_numberv number;
300 l_datev date;
301
302 i pls_integer;
303 p1 pls_integer;
304 p2 pls_integer;
305 not_empty boolean := true;
306 role_info_tbl wf_directory.wf_local_roles_tbl_type;
307
308 l_delim varchar2(1);
309 cells tdType;
310 result varchar2(32000);
311 begin
312
313 l_delim := ':';
314
315 l_table_direction := table_direction;
316 if (table_type = 'N') then
317 l_cols := 3;
318 else
319 l_cols := 2;
320 end if;
321
322 i := 1;
323 p1 := 1;
324 while not_empty loop
325 p2 := instrb(attrs,',',p1);
326 if (p2 = 0) then
327 p2 := lengthb(attrs)+1;
328 not_empty := false;
329 end if;
330
331 l_attr := ltrim(substrb(attrs,p1,p2-p1));
332
333 begin
334 select MA.DISPLAY_NAME,
335 MA.TYPE,
336 MA.FORMAT,
337 NA.TEXT_VALUE,
338 NA.NUMBER_VALUE,
339 NA.DATE_VALUE
340 into l_dispname, l_type, l_format, l_textv, l_numberv, l_datev
341 from WF_MESSAGE_ATTRIBUTES_VL MA,
342 WF_NOTIFICATION_ATTRIBUTES NA,
343 WF_NOTIFICATIONS N
344 where NA.NAME = l_attr
345 and NA.NOTIFICATION_ID = nid
346 and NA.NOTIFICATION_ID = N.NOTIFICATION_ID
347 and N.MESSAGE_TYPE = MA.MESSAGE_TYPE
348 and N.MESSAGE_NAME = MA.MESSAGE_NAME
349 and MA.NAME = NA.NAME;
350 exception
351 when NO_DATA_FOUND then
352 -- skip if this attribute or notification does not exist
353 l_dispname := null;
354 l_type := 'VARCHAR2';
355 l_format:= null;
356 l_textv := null;
357 when OTHERS then
358 raise;
359 end;
360
361 if (l_type = 'DATE') then
362 -- <bug 7514495> now as date format we use the first non-null value of:
363 -- l_format, wf_notification_util.G_NLS_DATE_FORMAT (if nid is provided and matches
364 -- wf_notification_util.G_NID), session user's WFDS preference, wf_core.nls_date_format.
365 l_text := wf_notification_util.GetCalendarDate(nid, l_datev, l_format, false);
366 elsif (l_type = 'NUMBER') then
367 if (l_format is null) then
368 l_text := to_char(l_numberv);
369 else
370 l_text := to_char(l_numberv, l_format);
371 end if;
372 elsif (l_type = 'ROLE') then
373 Wf_Directory.GetRoleInfo2(l_textv,role_info_tbl);
374 l_text := role_info_tbl(1).display_name;
375 elsif (l_type = 'LOOKUP') then
376 begin
377 select MEANING
378 into l_text
379 from WF_LOOKUPS
380 where LOOKUP_TYPE = l_format
381 and LOOKUP_CODE = l_textv;
382 exception
383 when no_data_found then
384 -- Use code directly if lookup not found.
385 l_text := l_textv;
386 end;
387 elsif (l_type = 'VARCHAR2') then
388 -- VARCHAR2 is text_value, truncated at format if one provided.
389 if (l_format is null) then
390 l_text := l_textv;
391 else
392 l_text := substrb(l_textv, 1, to_number(l_format));
393 end if;
394 else
395 -- do not do any complicated substitution for URL and FORM
396 -- do nothing for DOCUMENT as it is too costly
397 l_text := l_textv;
398 end if;
399
400 -- make sure the text does not carry any HTML chars... though NUMBER is safe
401 -- others possibly could carry.
402 if (disptype = wf_notification.doc_html) then
403 l_text := substrb(Wf_Core.SubstituteSpecialChars(l_text), 1, 4000);
404 end if;
405
406 -- display
407 if (l_dispname is not null) then
408 if (disptype = wf_notification.doc_html) then
409 l_dispname := substrb(Wf_Core.SubstituteSpecialChars(l_dispname), 1, 80);
410 if (table_type = 'N') then
411 cells(i) := 'E:'||l_dispname;
412 i := i+1;
413 cells(i) := 'S12:';
414 else
415 cells(i) := 'E40%:'||l_dispname;
416 end if;
417 i := i+1;
418 cells(i) := 'S:'||l_text; -- normally align number to the right
419 -- but not in vertical table
420 i := i+1;
421 else
422 result := result||wf_core.newline||l_dispname||l_delim||' '||l_text;
423 end if;
424 end if;
425
426 -- ### implement as generic log in the future
427 -- if (wf_notification.debug) then
428 -- dbms_output.put_line(substrb(l_attr||'/'||l_dispname||': '||l_text,1,250));
429 -- end if;
430
431 p1 := p2+1;
432 end loop;
433
434 if (disptype = wf_notification.doc_html) then
435 if (table_type = 'N') then
436 table_width := '100%';
437 else
438 table_width := '70%';
439 end if;
440 NTF_Table(cells=>cells,
441 col=>l_cols,
442 type=>table_type||l_table_direction,
443 rs=>result);
444 end if;
445
446 return(result);
447
448 exception
449 when OTHERS then
450 wf_core.context('Wf_Notification','Wf_Msg_Attr',to_char(nid),attrs);
451 raise;
452 end wf_msg_attr;
453
454
455 -- Wf_Ntf_History
456 -- Construct Action History table for a given notification from the WF_COMMENTS table
457 -- The table consists of actions like Reassign, More Info Request and Respond and related
458 -- comments. The user can restrict the rows in the table using the following format.
459 -- WF_NOTIFICATION(HISTORY, hide_reassign, hide_requestinfo)
460 -- Example:
461 -- WF_NOTIFICATION(HISTORY, Y, Y) - Hides comments related to Reassign and More Info Reqs
462 -- WF_NOTIFICATION(HISTORY, N, Y) - Hides comments related to More Info Reqs
463 -- WF_NOTIFICATION(HISTORY) - Shows all comments related to the notification
464 --
465 -- InPut
466 -- nid - Notification Id
467 -- disptype - text/plain or text/html
468 -- param - Hide Reassign, Hide Request Info indicators
469
470 function wf_ntf_history(nid in number,
471 disptype in varchar2,
472 param in varchar2)
473 return varchar2
474 is
475 l_param varchar2(100);
476 l_hide_reassign varchar2(1);
477 l_hide_requestinfo varchar2(1);
478 l_action_history varchar2(32000);
479 l_pos pls_integer;
480 begin
481
482 l_hide_reassign := 'N';
483 l_hide_requestinfo := 'N';
484
485 begin
486 if (param is not null) then
487 l_pos := instr(param, ',', 1);
488 l_hide_reassign := trim(substr(param, 1, l_pos-1));
489 l_hide_requestinfo := trim(substr(param, l_pos+1, length(param)-l_pos));
490 end if;
491 exception
492 when others then
493 l_hide_reassign := 'N';
494 l_hide_requestinfo := 'N';
495 end;
496
497 Wf_Notification.GetComments2(p_nid => nid, p_display_type => disptype,
498 p_hide_reassign => l_hide_reassign,
499 p_hide_requestinfo => l_hide_requestinfo,
500 p_action_history => l_action_history);
501 return l_action_history;
502
503 end wf_ntf_history;
504
505 /*
506 ** This Procedure is obsolete. From 11.5.10 onwards, Action History table is based on
507 ** WF_COMMENTS table and on the Notification Activities' history. Hence, WF_NTF_HISTORY
508 ** procedure is reimplemented.
509 **
510 --
511 -- Wf_Ntf_History
512 -- Construct a history table for a notification activity.
513 -- NOTE
514 -- Consist of three sections:
515 -- 1. Current Notification
516 -- 2. Past Notifications in the history table
517 -- 3. The owner role as the submitter and begin date for such item
518 --
519 function wf_ntf_history(nid in number,
520 disptype in varchar2)
521 return varchar2
522 is
523 -- current notification
524 cursor hist0c(x_item_type varchar2, x_item_key varchar2, x_actid number) is
525 select IAS.NOTIFICATION_ID, IAS.ASSIGNED_USER, A.RESULT_TYPE, IAS.ACTIVITY_RESULT_CODE, nvl(IAS.END_DATE, IAS.BEGIN_DATE) ACT_DATE, IAS.EXECUTION_TIME
526 from WF_ITEM_ACTIVITY_STATUSES IAS,
527 WF_ACTIVITIES A,
528 WF_PROCESS_ACTIVITIES PA,
529 WF_ITEM_TYPES IT,
530 WF_ITEMS I
531 where IAS.ITEM_TYPE = x_item_type
532 and IAS.ITEM_KEY = x_item_key
533 and IAS.PROCESS_ACTIVITY = x_actid
534 and IAS.ITEM_TYPE = I.ITEM_TYPE
535 and IAS.ITEM_KEY = I.ITEM_KEY
536 and I.BEGIN_DATE between A.BEGIN_DATE and nvl(A.END_DATE, I.BEGIN_DATE)
537 and I.ITEM_TYPE = IT.NAME
538 and IAS.PROCESS_ACTIVITY = PA.INSTANCE_ID
539 and PA.ACTIVITY_NAME = A.NAME
540 and PA.ACTIVITY_ITEM_TYPE = A.ITEM_TYPE;
541
542 -- past notifications
543 cursor histc(x_item_type varchar2, x_item_key varchar2, x_actid number) is
544 select IAS.NOTIFICATION_ID, IAS.ASSIGNED_USER, A.RESULT_TYPE, IAS.ACTIVITY_RESULT_CODE, nvl(IAS.END_DATE, IAS.BEGIN_DATE) ACT_DATE, IAS.EXECUTION_TIME
545 from WF_ITEM_ACTIVITY_STATUSES_H IAS,
546 WF_ACTIVITIES A,
547 WF_PROCESS_ACTIVITIES PA,
548 WF_ITEM_TYPES IT,
549 WF_ITEMS I
550 where IAS.ITEM_TYPE = x_item_type
551 and IAS.ITEM_KEY = x_item_key
552 and IAS.PROCESS_ACTIVITY = x_actid
553 and IAS.ITEM_TYPE = I.ITEM_TYPE
554 and IAS.ITEM_KEY = I.ITEM_KEY
555 and I.BEGIN_DATE between A.BEGIN_DATE and nvl(A.END_DATE, I.BEGIN_DATE)
556 and I.ITEM_TYPE = IT.NAME
557 and IAS.PROCESS_ACTIVITY = PA.INSTANCE_ID
558 and PA.ACTIVITY_NAME = A.NAME
559 and PA.ACTIVITY_ITEM_TYPE = A.ITEM_TYPE
560 order by IAS.BEGIN_DATE desc , IAS.EXECUTION_TIME desc;
561
562 l_itype varchar2(30);
563 l_ikey varchar2(240);
564 l_actid number;
565 l_result_type varchar2(30);
566 l_result_code varchar2(30);
567 l_action varchar2(80);
568 l_owner_role varchar2(320);
569 l_owner varchar2(320);
570 l_begin_date date;
571 i pls_integer;
572 j pls_integer;
573 role_info_tbl wf_directory.wf_local_roles_tbl_type;
574
575 l_table_direction varchar2(1);
576 l_delim varchar2(1) := ':';
577 cells tdType;
578 result varchar2(32000);
579 l_note varchar2(4000);
580 begin
581
582 l_table_direction := table_direction;
583
584 begin
585 select ITEM_TYPE, ITEM_KEY, PROCESS_ACTIVITY
586 into l_itype, l_ikey, l_actid
587 from WF_ITEM_ACTIVITY_STATUSES
588 where notification_id = nid;
589 exception
590 when NO_DATA_FOUND then
591 begin
592 select ITEM_TYPE, ITEM_KEY, PROCESS_ACTIVITY
593 into l_itype, l_ikey, l_actid
594 from WF_ITEM_ACTIVITY_STATUSES_H
595 where notification_id = nid;
596 exception
597 when NO_DATA_FOUND then
598 null; -- raise a notification not exist message
599 end;
600 end;
601
602 j := 1;
603 -- title
604 cells(j) := wf_core.translate('NUM');
605 if (disptype = wf_notification.doc_html) then
606 cells(j) := 'S10%:'||cells(j);
607 end if;
608 j := j+1;
609 cells(j) := wf_core.translate('NAME');
610 if (disptype = wf_notification.doc_html) then
611 cells(j) := 'S:'||cells(j);
612 end if;
613 j := j+1;
614 cells(j) := wf_core.translate('ACTION');
615 if (disptype = wf_notification.doc_html) then
616 cells(j) := 'S:'||cells(j);
617 end if;
618 j := j+1;
619 cells(j) := wf_core.translate('ACTION_DATE');
620 if (disptype = wf_notification.doc_html) then
621 cells(j) := 'S:'||cells(j);
622 end if;
623 j := j+1;
624 cells(j) := wf_core.translate('NOTE');
625 if (disptype = wf_notification.doc_html) then
626 cells(j) := 'S:'||cells(j);
627 end if;
628 j := j+1;
629
630 i := 0;
631 for histr in hist0c(l_itype, l_ikey, l_actid) loop
632 cells(j) := to_char(histr.notification_id);
633 j := j+1;
634 wf_directory.GetRoleInfo2(histr.assigned_user, role_info_tbl);
635 if (disptype = wf_notification.doc_html) then
636 cells(j) := 'S:'||Wf_Notification.SubstituteSpecialChars(role_info_tbl(1).display_name);
637 else
638 cells(j) := role_info_tbl(1).display_name;
639 end if;
640 j := j+1;
641 if (l_result_type is null or l_result_code is null or
642 histr.result_type <> l_result_type or
643 histr.activity_result_code <> l_result_code) then
644 l_result_type := histr.result_type;
645 l_result_code := histr.activity_result_code;
646 l_action := wf_core.activity_result(l_result_type, l_result_code);
647 end if;
648 if (disptype = wf_notification.doc_html) then
649 if (l_action is null) then
650 cells(j) := 'S: ';
651 else
652 cells(j) := 'S:'||l_action;
653 end if;
654 else
655 cells(j) := l_action;
656 end if;
657 j := j+1;
658 if (disptype = wf_notification.doc_html) then
659 cells(j) := 'S:'||to_char(histr.act_date);
660 else
661 cells(j) := to_char(histr.act_date);
662 end if;
663 j := j+1;
664 begin
665 l_note := Wf_Notification.GetAttrText(histr.notification_id,'WF_NOTE',TRUE);
666 if (disptype = wf_notification.doc_html) then
667 l_note := substrb(Wf_Notification.SubstituteSpecialChars(l_note), 1, 4000);
668 end if;
669 cells(j) := l_note;
670 exception
671 when OTHERS then
672 cells(j) := null;
673 wf_core.clear;
674 end;
675 if (disptype = wf_notification.doc_html) then
676 if (cells(j) is null) then
677 cells(j) := 'S: ';
678 else
679 cells(j) := 'S:'||cells(j);
680 end if;
681 end if;
682 j := j+1;
683
684 i := i+1;
685 end loop;
686
687 for histr in histc(l_itype, l_ikey, l_actid) loop
688 cells(j) := to_char(histr.notification_id);
689 j := j+1;
690 wf_directory.GetRoleInfo2(histr.assigned_user, role_info_tbl);
691 if (disptype = wf_notification.doc_html) then
692 cells(j) := 'S:'||Wf_Notification.SubstituteSpecialChars(role_info_tbl(1).display_name);
693 else
694 cells(j) := role_info_tbl(1).display_name;
695 end if;
696 j := j+1;
697 if (l_result_type is null or l_result_code is null or
698 histr.result_type <> l_result_type or
699 histr.activity_result_code <> l_result_code) then
700 l_result_type := histr.result_type;
701 l_result_code := histr.activity_result_code;
702 l_action := wf_core.activity_result(l_result_type, l_result_code);
703 end if;
704 if (disptype = wf_notification.doc_html) then
705 if (l_action is null) then
706 cells(j) := 'S: ';
707 else
708 cells(j) := 'S:'||l_action;
709 end if;
710 else
711 cells(j) := l_action;
712 end if;
713 j := j+1;
714 if (disptype = wf_notification.doc_html) then
715 cells(j) := 'S:'||to_char(histr.act_date);
716 else
717 cells(j) := to_char(histr.act_date);
718 end if;
719 j := j+1;
720 begin
721 l_note := Wf_Notification.GetAttrText(histr.notification_id,'WF_NOTE',TRUE);
722 if (disptype = wf_notification.doc_html) then
723 l_note := substrb(Wf_Notification.SubstituteSpecialChars(l_note), 1, 4000);
724 end if;
725 cells(j) := l_note;
726 exception
727 when OTHERS then
728 cells(j) := null;
729 wf_core.clear;
730 end;
731 if (disptype = wf_notification.doc_html) then
732 if (cells(j) is null) then
733 cells(j) := 'S: ';
734 else
735 cells(j) := 'S:'||cells(j);
736 end if;
737 end if;
738 j := j+1;
739
740 i := i+1;
741 end loop;
742
743 -- submit row
744 cells(j) := '0';
745 j := j+1;
746 begin
747 select OWNER_ROLE, BEGIN_DATE
748 into l_owner_role, l_begin_date
749 from WF_ITEMS
750 where ITEM_TYPE = l_itype
751 and ITEM_KEY = l_ikey;
752 exception
753 when OTHERS then
754 raise;
755 end;
756 wf_directory.GetRoleInfo2(l_owner_role, role_info_tbl);
757 if (disptype = wf_notification.doc_html) then
758 cells(j) := 'S:'||Wf_Notification.SubstituteSpecialChars(role_info_tbl(1).display_name);
759 else
760 cells(j) := role_info_tbl(1).display_name;
761 end if;
762 j := j+1;
763 if (disptype = wf_notification.doc_html) then
764 cells(j) := 'S:'||wf_core.translate('SUBMIT');
765 else
766 cells(j) := wf_core.translate('SUBMIT');
767 end if;
768 j := j+1;
769 if (disptype = wf_notification.doc_html) then
770 cells(j) := 'S:'||to_char(l_begin_date);
771 else
772 cells(j) := to_char(l_begin_date);
773 end if;
774 j := j+1;
775 if (disptype = wf_notification.doc_html) then
776 cells(j) := 'S: ';
777 else
778 cells(j) := null;
779 end if;
780
781 -- ### implement as generic log in the future
782 -- if (wf_notification.debug) then
783 -- dbms_output.put_line('j = '||to_char(j));
784 -- dbms_output.put_line(substrb('last cell = '||cells(j),1,254));
785 -- end if;
786
787 -- calculate the sequence
788 -- Only after we know the number of rows, then we can put the squence
789 -- number on for each row.
790 for k in 0..i loop
791 if (disptype = wf_notification.doc_html) then
792 cells((k+1)*5+1) := 'C:'||to_char(i-k);
793 else
794 cells((k+1)*5+1) := to_char(i-k);
795 end if;
796 end loop;
797
798 if (disptype = wf_notification.doc_html) then
799 table_width := '100%';
800 NTF_Table(cells=>cells,
801 col=>5,
802 type=>'H'||l_table_direction,
803 rs=>result);
804 else
805 for k in 1..cells.LAST loop
806 if (mod(k, 5) <> 0) then
807 result := result||cells(k)||' '||l_delim||' ';
808 else
809 result := result||cells(k)||wf_core.newline;
810 end if;
811 end loop;
812 end if;
813
814 return(result);
815 exception
816 when OTHERS then
817 wf_core.context('Wf_Notification', 'Wf_NTF_History', to_char(nid));
818 raise;
819 end wf_ntf_history;
820 **
821 ** End of obsoleted procedure WF_NTF_HISTORY
822 **/
823
824 --
825 -- runFuncOnBody
826 -- NOTE
827 -- Attempt to find, parse and replace the string
828 -- WF_NOTIFICATION(F,P1,P2,...)
829 -- F = function to run
830 -- P1,P2,... = comma delimited parameter list
831 --
832 function runFuncOnBody(nid in number,
833 body in varchar2,
834 disptype in varchar2)
835 return varchar2
836 is
837 p1 pls_integer;
838 p2 pls_integer;
839 pp pls_integer;
840 l_body varchar2(32000);
841 rs varchar2(32000);
842 fname varchar2(32);
843 frun varchar2(32);
844 func varchar2(8000);
845 param varchar2(8000);
846 i pls_integer;
847 alldone boolean;
848 begin
849
850 l_body := body;
851
852 p1:=1;
853 alldone:=false;
854 while (not alldone) loop
855 fname := 'WF_NOTIFICATION('; -- lengthb(fname) is 16
856
857 p1 := instrb(l_body, fname, p1);
858 if (p1 <> 0) then
859 p2 := instrb(l_body, ')', p1);
860 if (p2 <> 0) then
861 -- try to separate function to run and parameters
862 func := substrb(l_body, p1, p2-p1+1);
863 pp := instrb(func, ',');
864 if (pp = 0) then
865 pp := lengthb(func); -- only the function to run exist.
866 param := null;
867 else
868 param := substrb(func, pp+1, p2-p1-pp);
869 end if;
870
871 frun := substrb(func, 17, pp-17);
872
873 -- ### implement as generic log in the future
874 -- if (wf_notification.debug) then
875 -- dbms_output.put_line('frun='||frun);
876 -- end if;
877
878 if (frun = 'ATTRS') then
879 rs := wf_msg_attr(nid, param, disptype);
880 elsif (frun = 'HISTORY') then
881 rs := wf_ntf_history(nid, disptype, param);
882 else
883 rs := func;
884 end if;
885
886 -- do not replace a string with itself.
887 -- if rs is null, then there is nothing to display for Action/Notifications History
888 -- or Attributes table. We would not want WF_NOTIFICATION(ATTRS,...) or
889 -- WF_NOTIFICATION(HISTORY) to appear in the notification as is.
890 if (rs is null or rs <> func) then
891 l_body := replace(l_body, func, rs);
892 end if;
893
894 -- now move p1 to the end
895 p1 := p2+1;
896 else
897 -- since we cannot find a closing paranthesis
898 alldone := true;
899 end if;
900 else
901 alldone := true;
902 end if;
903 end loop;
904
905 return(l_body);
906
907 exception
908 when OTHERS then
909 wf_core.context('Wf_Notification', 'runFuncOnBody', to_char(nid), disptype);
910 raise;
911 end runFuncOnBody;
912
913 -- More Info mailer support
914 --
915 -- GetUserfromEmail (PRIVATE)
916 -- from_email - from email id
917 -- user_name - user name
918 -- disp_name - Display name of the user
919 -- found - whether user/role has been reconciled
920 -- NOTE:
921 -- Get the user/role name and display name based on the email id. Else return
922 -- the stripped off email with the found flag FALSE
923
924 procedure GetUserfromEmail (from_email in varchar2,
925 user_name out nocopy varchar2,
926 disp_name out nocopy varchar2,
927 found out nocopy boolean)
928 is
929 l_email varchar2(1000);
930 l_role varchar2(320);
931 l_dname varchar2(360);
932 l_desc varchar2(1000);
933 l_npref varchar2(8);
934 l_terr varchar2(30);
935 l_lang varchar2(30);
936 l_fax varchar2(240);
937 l_expire date;
938 l_status varchar2(8);
939 l_orig_sys varchar2(30);
940 l_orig_sysid number;
941 l_start pls_integer;
942 l_end pls_integer;
943
944 begin
945 -- Stripping off unwanted info from email
946 l_start := instr(from_email, '<', 1, 1);
947 if (l_start > 0) then
948 l_end := instr(from_email, '>', l_start);
949 l_email := substr(from_email, l_start+1, l_end-l_start-1);
950 else
951 l_email := from_email;
952 end if;
953 -- user_name := substr(l_email, 1, instr(l_email, '@') - 1);
954 found := false;
955 user_name := l_email;
956 disp_name := l_email;
957
958 Wf_Directory.GetInfoFromMail(mailid => l_email,
959 role => l_role,
960 display_name => l_dname,
961 description => l_desc,
962 notification_preference => l_npref,
963 language => l_lang,
964 territory => l_terr,
965 fax => l_fax,
966 expiration_date => l_expire,
967 status => l_status,
968 orig_system => l_orig_sys,
969 orig_system_id => l_orig_sysid);
970
971 if (l_role is not null) then
972 user_name := l_role;
973 disp_name := l_dname;
974 found := true;
975 end if;
976 end GetUserfromEmail;
977
978 --
979 -- VALIDATE_CONTEXT
980 -- Introduced because of bug 7914921
981 -- Gets a noification contexts and derives the item type, key and activity ID, if any
982 -- Since the standard format itemtype:itemkey:actid is NOT mandatory this function
983 -- returns null values if the context does not meet the standard. Further validation is
984 -- requried by the calling program
985 -- IN
986 -- context: the notification context to validate
987 -- IN OUT
988 -- itemtype: string before the first colon in the context
989 -- itemkey: string between first and second colons in the contex
990 -- actid: NUMBER after the second colon
991 --
992 procedure validate_context (context IN WF_NOTIFICATIONS.CONTEXT%TYPE,
993 itemtype OUT NOCOPY varchar2,
994 itemkey OUT NOCOPY varchar2,
995 actid OUT NOCOPY number)
996 is
997 col1 number;
998 col2 number;
999 begin
1000 itemtype:=null;
1001 itemkey:=null;
1002 actid:=null;
1003 col1 := instr(context, ':', 1, 1);
1004 col2 := instr(context, ':', -1, 1);
1005 if col1>0 AND col2>col1 then --Context seems to have itemtype and key
1006 itemtype := substr(context, 1, col1-1);
1007 itemkey := substr(context, col1+1, col2-col1-1);
1008 if LENGTH(itemtype)<=8 then --Standard lenght for a valid item type
1009 BEGIN
1010 actid:=to_number(substr(context, col2+1));
1011 EXCEPTION
1012 WHEN OTHERS THEN --covers for an invalid conversion to number.
1013 itemtype:=null;
1014 itemkey:=null;
1015 actid:=null;
1016 END;
1017 end if;
1018 end if;
1019 exception
1020 when OTHERS then --no_data_found or invalid_number
1021 itemtype:=null;
1022 itemkey:=null;
1023 actid:=null;
1024 wf_core.context('Wf_Notification', 'validate_context', context);
1025 raise;
1026 end validate_context;
1027
1028
1029 -- End Private Functions
1030 --
1031
1032 --
1033 -- AddAttr
1034 -- Add a new run-time notification attribute.
1035 -- The attribute will be completely unvalidated. It is up to the
1036 -- user to do any validation and insure consistency.
1037 -- IN:
1038 -- nid - Notification Id
1039 -- aname - Attribute name
1040 --
1041 procedure AddAttr(nid in number,
1042 aname in varchar2)
1043 is
1044 dummy pls_integer;
1045 begin
1046 if ((nid is null) or (aname is null)) then
1047 wf_core.token('NID', to_char(nid));
1048 wf_core.token('ANAME', aname);
1049 wf_core.raise('WFSQL_ARGS');
1050 end if;
1051
1052 -- Insure this is a valid notification.
1053 begin
1054 select 1 into dummy from sys.dual where exists
1055 (select null
1056 from WF_NOTIFICATIONS
1057 where NOTIFICATION_ID = nid);
1058 exception
1059 when no_data_found then
1060 wf_core.token('NID', to_char(nid));
1061 wf_core.raise('WFNTF_NID');
1062 end;
1063
1064 -- Insert new attribute
1065 begin
1066 insert into WF_NOTIFICATION_ATTRIBUTES (
1067 NOTIFICATION_ID,
1068 NAME,
1069 TEXT_VALUE,
1070 NUMBER_VALUE,
1071 DATE_VALUE
1072 ) values (
1073 nid,
1074 aname,
1075 '',
1076 '',
1077 ''
1078 );
1079 exception
1080 when dup_val_on_index then
1081 wf_core.token('NID', to_char(nid));
1082 wf_core.token('ATTRIBUTE', aname);
1083 wf_core.raise('WFNTF_ATTR_UNIQUE');
1084 end;
1085
1086 exception
1087 when others then
1088 wf_core.context('Wf_Notification', 'AddAttr', to_char(nid), aname);
1089 raise;
1090 end AddAttr;
1091
1092 --
1093 -- SetAttrText
1094 -- Set the value of a notification attribute, given text representation.
1095 -- If the attribute is a NUMBER or DATE type, then translate the
1096 -- text-string value to a number/date using attribute format.
1097 -- For all other types, store the value directly.
1098 -- IN:
1099 -- nid - Notification id
1100 -- aname - Attribute Name
1101 -- avalue - New value for attribute
1102 --
1103 procedure SetAttrText(nid in number,
1104 aname in varchar2,
1105 avalue in varchar2)
1106 is
1107 atype varchar2(8);
1108 format varchar2(240);
1109 rname varchar2(320);
1110 role_info_tbl wf_directory.wf_local_roles_tbl_type;
1111 l_parameterlist wf_parameter_list_t := wf_parameter_list_t();
1112 l_language varchar2(30);
1113 l_recipient_role varchar2(320);
1114
1115 begin
1116 if ((nid is null) or (aname is null)) then
1117 wf_core.token('NID', to_char(nid));
1118 wf_core.token('ANAME', aname);
1119 wf_core.raise('WFSQL_ARGS');
1120 end if;
1121
1122 -- Get type and format of attr.
1123 -- This is used for translating number/date strings.
1124 begin
1125 select WMA.TYPE, WMA.FORMAT
1126 into atype, format
1127 from WF_NOTIFICATION_ATTRIBUTES WNA, WF_NOTIFICATIONS WN,
1128 WF_MESSAGE_ATTRIBUTES WMA
1129 where WNA.NOTIFICATION_ID = nid
1130 and WNA.NAME = aname
1131 and WNA.NOTIFICATION_ID = WN.NOTIFICATION_ID
1132 and WN.MESSAGE_NAME = WMA.MESSAGE_NAME
1133 and WN.MESSAGE_TYPE = WMA.MESSAGE_TYPE
1134 and WNA.NAME = WMA.NAME;
1135 exception
1136 when no_data_found then
1137 -- This is an unvalidated runtime attr.
1138 -- Treat it as a varchar2.
1139 atype := 'VARCHAR2';
1140 format := '';
1141 end;
1142
1143 -- Update attribute value in appropriate type column.
1144 if (atype = 'NUMBER') then
1145 update WF_NOTIFICATION_ATTRIBUTES
1146 set NUMBER_VALUE = decode(format,
1147 '', to_number(avalue),
1148 to_number(avalue, format))
1149 where NOTIFICATION_ID = nid
1150 and NAME = aname;
1151 elsif (atype = 'DATE') then
1152 -- 4477386 gscc date format requirement change
1153 -- do not use a cached value, this allows nls change within the
1154 -- same session to be seen right away.
1155 update WF_NOTIFICATION_ATTRIBUTES
1156 set DATE_VALUE = decode(format,
1157 '',to_date(avalue,SYS_CONTEXT('USERENV','NLS_DATE_FORMAT')),
1158 to_date(avalue, format))
1159 where NOTIFICATION_ID = nid
1160 and NAME = aname;
1161 elsif (atype = 'VARCHAR2') then
1162 -- VARCHAR2
1163 -- Set the text value directly with no translation.
1164 -- bug 1996299 - JWSMITH , changes substr to substrb for korean char
1165 update WF_NOTIFICATION_ATTRIBUTES
1166 set TEXT_VALUE = decode(format,
1167 '', avalue,
1168 substrb(avalue, 1, to_number(format)))
1169 where NOTIFICATION_ID = nid
1170 and NAME = aname;
1171 elsif (atype = 'ROLE') then
1172 -- ROLE
1173 -- First check if value is internal name
1174 if (avalue is null) then
1175 -- Null values are ok
1176 rname := '';
1177 else
1178 Wf_Directory.GetRoleInfo2(avalue, role_info_tbl);
1179 rname := role_info_tbl(1).name;
1180
1181 -- If not internal name, check for display_name
1182 if (rname is null) then
1183 begin
1184 -- look into the wf_role_lov_vl based on display name
1185 SELECT name
1186 INTO rname
1187 FROM wf_role_lov_vl
1188 WHERE upper(display_name) = upper(avalue)
1189 AND rownum = 1;
1190 exception
1191 when no_data_found then
1192 -- Not displayed or internal role name, error
1193 wf_core.token('ROLE', avalue);
1194 wf_core.raise('WFNTF_ROLE');
1195 end;
1196 end if;
1197 end if;
1198
1199 -- Set the text value with internal role name
1200 update WF_NOTIFICATION_ATTRIBUTES
1201 set TEXT_VALUE = rname
1202 where NOTIFICATION_ID = nid
1203 and NAME = aname;
1204 else
1205 -- LOOKUP, FORM, URL, DOCUMENT, misc type.
1206 -- Set the text value.
1207 update WF_NOTIFICATION_ATTRIBUTES
1208 set TEXT_VALUE = avalue
1209 where NOTIFICATION_ID = nid
1210 and NAME = aname;
1211 end if;
1212
1213 if (SQL%NOTFOUND) then
1214 wf_core.token('NID', to_char(nid));
1215 wf_core.token('ATTRIBUTE', aname);
1216 wf_core.raise('WFNTF_ATTR');
1217 end if;
1218
1219 -- Redenormalize if attribute being updated is #FROM_ROLE
1220 if (aname = '#FROM_ROLE') then
1221 Wf_Notification.Denormalize_Notification(nid);
1222 end if;
1223
1224 -- Bug 2437347 raising event after DML operation on WF_NOTIFICATION_ATTRIBUTES
1225 if (aname = 'SENDER') then
1226 wf_event.AddParameterToList('NOTIFICATION_ID', nid, l_parameterlist);
1227 wf_event.AddParameterToList(aname, avalue, l_parameterlist);
1228
1229
1230
1231
1232 select WN.RECIPIENT_ROLE
1233 into l_recipient_role
1234 from WF_NOTIFICATIONS WN
1235 where WN.NOTIFICATION_ID = nid ;
1236
1237 Wf_Directory.GetRoleInfo2(l_recipient_role, role_info_tbl);
1238 l_language := role_info_tbl(1).language;
1239
1240 select code into l_language from wf_languages where nls_language = l_language;
1241
1242 -- AppSearch
1243 wf_event.AddParameterToList('OBJECT_NAME',
1244 'oracle.apps.fnd.wf.worklist.server.AllNotificationsVO', l_parameterlist);
1245 wf_event.AddParameterToList('CHANGE_TYPE', 'INSERT',l_parameterlist);
1246 wf_event.AddParameterToList('ID_TYPE', 'PK', l_parameterlist);
1247 wf_event.addParameterToList('PK_NAME_1', 'NOTIFICATION_ID',l_parameterlist);
1248 wf_event.addParameterToList('PK_VALUE_1', nid, l_parameterlist);
1249 wf_event.addParameterToList('PK_NAME_2', 'LANGUAGE',l_parameterlist);
1250 wf_event.addParameterToList('PK_VALUE_2', l_language, l_parameterlist);
1251 -- Raise the event
1252 wf_event.Raise(p_event_name => 'oracle.apps.wf.notification.setattrtext',
1253 p_event_key => to_char(nid),
1254 p_parameters => l_parameterlist);
1255 end if;
1256
1257 exception
1258 when others then
1259 wf_core.context('Wf_Notification', 'SetAttrText', to_char(nid),
1260 aname, avalue);
1261 raise;
1262 end SetAttrText;
1263
1264 --
1265 -- SetAttrNumber
1266 -- Set the value of a number notification attribute.
1267 -- Attribute must be a NUMBER-type attribute.
1268 -- IN:
1269 -- nid - Notification id
1270 -- aname - Attribute Name
1271 -- avalue - New value for attribute
1272 --
1273 procedure SetAttrNumber (nid in number,
1274 aname in varchar2,
1275 avalue in number)
1276 is
1277 begin
1278 if ((nid is null) or (aname is null)) then
1279 wf_core.token('NID', to_char(nid));
1280 wf_core.token('ANAME', aname);
1281 wf_core.raise('WFSQL_ARGS');
1282 end if;
1283
1284 -- Update attribute value
1285 update WF_NOTIFICATION_ATTRIBUTES
1286 set NUMBER_VALUE = avalue
1287 where NOTIFICATION_ID = nid and NAME = aname;
1288
1289 if (SQL%NOTFOUND) then
1290 wf_core.token('NID', to_char(nid));
1291 wf_core.token('ATTRIBUTE', aname);
1292 wf_core.raise('WFNTF_ATTR');
1293 end if;
1294
1295 exception
1296 when others then
1297 wf_core.context('Wf_Notification', 'SetAttrNumber', to_char(nid),
1298 aname, to_char(avalue));
1299 raise;
1300 end SetAttrNumber;
1301
1302 --
1303 -- SetAttrDate
1304 -- Set the value of a date notification attribute.
1305 -- Attribute must be a DATE-type attribute.
1306 -- IN:
1307 -- nid - Notification id
1308 -- aname - Attribute Name
1309 -- avalue - New value for attribute
1310 --
1311 procedure SetAttrDate (nid in number,
1312 aname in varchar2,
1313 avalue in date)
1314 is
1315 begin
1316 if ((nid is null) or (aname is null)) then
1317 wf_core.token('NID', to_char(nid));
1318 wf_core.token('ANAME', aname);
1319 wf_core.raise('WFSQL_ARGS');
1320 end if;
1321
1322 -- Update attribute value
1323 update WF_NOTIFICATION_ATTRIBUTES
1324 set DATE_VALUE = avalue
1325 where NOTIFICATION_ID = nid and NAME = aname;
1326
1327 if (SQL%NOTFOUND) then
1328 wf_core.token('NID', to_char(nid));
1329 wf_core.token('ATTRIBUTE', aname);
1330 wf_core.raise('WFNTF_ATTR');
1331 end if;
1332
1333 exception
1334 when others then
1335 wf_core.context('Wf_Notification', 'SetAttrDate', to_char(nid),
1336 aname, to_char(avalue));
1337 raise;
1338 end SetAttrDate;
1339
1340 --
1341 -- SubstituteSpecialChars (PRIVATE)
1342 -- Substitutes the occurence of special characters like <, >, \, ', " etc
1343 -- with their html codes in any arbitrary string.
1344 -- IN
1345 -- some_text - text to be substituted
1346 -- RETURN
1347 -- substituted text
1348
1349 function SubstituteSpecialChars(some_text in varchar2)
1350 return varchar2 is
1351 l_amp varchar2(1);
1352 buf varchar2(32000);
1353 l_amp_flag boolean;
1354 l_lt_flag boolean;
1355 l_gt_flag boolean;
1356 l_bsl_flag boolean;
1357 l_apos_flag boolean;
1358 l_quot_flag boolean;
1359 begin
1360 l_amp := '&';
1361
1362 buf := some_text;
1363
1364 -- bug 6025162 - This function should substitute only those chars that
1365 -- really require substitution. Any valid occurences should be retained.
1366 -- No validation should be required for calling this function
1367
1368 if (instr(buf, l_amp) > 0) then
1369 l_amp_flag := false;
1370 l_lt_flag := false;
1371 l_gt_flag := false;
1372 l_bsl_flag := false;
1373 l_apos_flag := false;
1374 l_quot_flag := false;
1375
1376 -- mask all valid ampersand containing patterns in the content
1377 -- issue is when ntf body already contains of these reserved words...
1378 if (instr(buf, l_amp||'amp;') > 0) then
1379 buf := replace(buf, l_amp||'amp;', '#AMP#');
1380 l_amp_flag := true;
1381 end if;
1382 if (instr(buf, l_amp||'lt;') > 0) then
1383 buf := replace(buf, l_amp||'lt;', '#LT#');
1384 l_lt_flag := true;
1385 end if;
1386 if (instr(buf, l_amp||'gt;') > 0) then
1387 buf := replace(buf, l_amp||'gt;', '#GT#');
1388 l_gt_flag := true;
1389 end if;
1390 if (instr(buf, l_amp||'#92;') > 0) then
1391 buf := replace(buf, l_amp||'#92;', '#BSL#');
1392 l_bsl_flag := true;
1393 end if;
1394 if (instr(buf, l_amp||'#39;') > 0) then
1395 buf := replace(buf, l_amp||'#39;', '#APO#');
1396 l_apos_flag := true;
1397 end if;
1398 if (instr(buf, l_amp||'quot;') > 0) then
1399 buf := replace(buf, l_amp||'quot;', '#QUOT#');
1400 l_quot_flag := true;
1401 end if;
1402
1403 buf := replace(buf, l_amp, l_amp||'amp;');
1404
1405 -- put the masked valid ampersand containing patterns back
1406 if (l_amp_flag) then
1407 buf := replace(buf, '#AMP#', l_amp||'amp;');
1408 end if;
1409 if (l_lt_flag) then
1410 buf := replace(buf, '#LT#', l_amp||'lt;');
1411 end if;
1412 if (l_gt_flag) then
1413 buf := replace(buf, '#GT#', l_amp||'gt;');
1414 end if;
1415 if (l_bsl_flag) then
1416 buf := replace(buf, '#BSL#', l_amp||'#92;');
1417 end if;
1418 if (l_apos_flag) then
1419 buf := replace(buf, '#APO#', l_amp||'#39;');
1420 end if;
1421 if (l_quot_flag) then
1422 buf := replace(buf, '#QUOT#', l_amp||'quot;');
1423 end if;
1424 end if;
1425
1426 buf := replace(buf, '<', l_amp||'lt;');
1427 buf := replace(buf, '>', l_amp||'gt;');
1428 buf := replace(buf, '\', l_amp||'#92;');
1429 buf := replace(buf, '''', l_amp||'#39;');
1430 buf := replace(buf, '"', l_amp||'quot;');
1431 return buf;
1432 exception
1433 when others then
1434 wf_core.context('Wf_Notification', 'SubstituteSpecialChars');
1435 raise;
1436 end SubstituteSpecialChars;
1437
1438 --
1439 -- GetTextInternal (PRIVATE)
1440 -- Substitute tokens in text (pragma-friendly).
1441 -- This is used in forms which only accept 1950 character strings
1442 -- and in views hence document type is not supported
1443 -- DOCUMENT-type attributes not supported.
1444 -- IN:
1445 -- some_text - Text to be substituted
1446 -- nid - Notification id of notification to use for token values
1447 -- target - Frame target
1448 -- urlmode - Look for url tokens with dashes
1449 -- subparams - Recursively substitute FORM/URL parameters
1450 -- (to prevent infinite recursion)
1451 -- disptype - display type
1452 -- ### This only consoliates GetShortText and GetUrlText.
1453 -- ### GetText is a separate procedure and must be double-maintained.
1454 -- ### This is so GetShortText can be pragma'd, and the DOCUMENT
1455 -- ### attribute type uses dbms_sql, which violates pragmas.
1456 --
1457 function GetTextInternal(
1458 some_text in varchar2,
1459 nid in number,
1460 target in out nocopy varchar2,
1461 urlmode in boolean,
1462 subparams in boolean,
1463 disptype in varchar2 default 'text/html')
1464 return varchar2 is
1465 role_name varchar2(320);
1466 email_address varchar2(320);
1467 username varchar2(320);
1468 local_text varchar2(2000);
1469 buf varchar2(2000);
1470 value varchar2(32000);
1471 colon pls_integer;
1472 params pls_integer;
1473 l_document_attributes fnd_document_management.fnd_document_attributes;
1474 buf varchar2(2000);
1475 -- Select attr values, formatting numbers and dates as requested.
1476 -- The order-by is to handle cases where one attr name is a substring
1477 -- of another.
1478 cursor notification_attrs_cursor(nid number) is
1479 select WNA.NAME, WMA.TYPE, WMA.FORMAT, WMA.DISPLAY_NAME,
1480 WNA.TEXT_VALUE, WNA.NUMBER_VALUE, WNA.DATE_VALUE
1481 from WF_NOTIFICATION_ATTRIBUTES WNA, WF_NOTIFICATIONS WN,
1482 WF_MESSAGE_ATTRIBUTES_VL WMA
1483 where WNA.NOTIFICATION_ID = nid
1484 and WN.NOTIFICATION_ID = WNA.NOTIFICATION_ID
1485 and WN.MESSAGE_TYPE = WMA.MESSAGE_TYPE
1486 and WN.MESSAGE_NAME = WMA.MESSAGE_NAME
1487 and WMA.NAME = WNA.NAME
1488 order by decode(wma.type,'URL',length(WNA.NAME),length(WNA.NAME)+1000) desc;
1489 -- order by length(WNA.NAME) desc;
1490 begin
1491 -- make sure text never exceeds 1950 bytes
1492 local_text := substrb(some_text,1,1950);
1493
1494 for not_attr_row in notification_attrs_cursor(nid) loop
1495 if (urlmode) then
1496 if (instr(local_text, '-&'||not_attr_row.name||'-') = 0) then
1497 goto nextattr;
1498 end if;
1499 else
1500 -- Bug 2843136 Check not only '&' but also '&'
1501 if ((instr(local_text, '&'||not_attr_row.name) = 0) AND
1502 (instr(local_text, '&'||not_attr_row.name) = 0)) then
1503 goto nextattr;
1504 end if;
1505 end if;
1506
1507 -- Find displayed value of token depending on type
1508 if (not_attr_row.type = 'LOOKUP') then
1509 -- LOOKUP type select meaning from wf_lookups.
1510 begin
1511 select MEANING
1512 into value
1513 from WF_LOOKUPS
1514 where LOOKUP_TYPE = not_attr_row.format
1515 and LOOKUP_CODE = not_attr_row.text_value;
1516 exception
1517 when no_data_found then
1518 -- Use code directly if lookup not found.
1519 value := not_attr_row.text_value;
1520 end;
1521 elsif (not_attr_row.type = 'VARCHAR2') then
1522 -- VARCHAR2 is text_value, truncated at format if one provided.
1523 if (not_attr_row.format is null) then
1524 value := not_attr_row.text_value;
1525 else
1526 value := substrb(not_attr_row.text_value, 1,
1527 to_number(not_attr_row.format));
1528 end if;
1529
1530 -- Bug 2843136
1531 -- Replace '&' but also '&' only if it hasn't been already substituted
1532 -- This is to prevent something like '&' from happening
1533 if (disptype = wf_notification.doc_html) then
1534 -- (instr(local_text,'&'||not_attr_row.name) = 0) AND
1535 -- (instr(value,'&') = 0)) then
1536
1537 -- bug 6025162 - SubstituteSpecialChars function substitutes only those
1538 -- characters that really require substitution. Any valid occurences
1539 -- will be retained. No validation required from calling program.
1540
1541 value := wf_core.SubstituteSpecialChars(value);
1542 end if;
1543
1544 elsif (not_attr_row.type = 'NUMBER') then
1545 -- NUMBER is number_value, with format if provided.
1546 if (not_attr_row.format is null) then
1547 value := to_char(not_attr_row.number_value);
1548 else
1549 value := to_char(not_attr_row.number_value, not_attr_row.format);
1550 end if;
1551 elsif (not_attr_row.type = 'DATE') then
1552 -- DATE is date_value, with format if provided.
1553 if (not_attr_row.format is null) then
1554 value := to_char(not_attr_row.date_value);
1555 else
1556 value := to_char(not_attr_row.date_value, not_attr_row.format);
1557 end if;
1558 elsif (not_attr_row.type = 'FORM') then
1559 -- FORM is display_name (function), with parameters of function
1560 -- recursively token-substituted if needed.
1561 value := not_attr_row.text_value;
1562 if (subparams) then
1563 params := instr(value, ':');
1564 if (params <> 0) then
1565 value := not_attr_row.display_name||' ( '||
1566 substr(value, 1, params)||
1567 wf_notification.GetTextInternal(substr(value, params+1), nid,
1568 target, FALSE, FALSE, 'text/plain')||' )';
1569 end if;
1570 end if;
1571 elsif ((not_attr_row.type = 'URL') and (not urlmode) ) then
1572 -- URL is display_name (url), with parameters of url
1573 -- recursively token-substituted if needed.
1574 value := not_attr_row.text_value;
1575 -- Default value of target is "_top" (all lower case)
1576 target := substr(nvl(not_attr_row.format, '_top'), 1, 16);
1577 if (subparams) then
1578 params := instr(value, '?');
1579 if (params <> 0) then
1580 value := not_attr_row.display_name||' ( '||
1581 substr(value, 1, params)||
1582 wf_notification.GetTextInternal(substr(value, params+1), nid,
1583 target, TRUE, FALSE, 'text/plain')||' )';
1584 end if;
1585 end if;
1586 elsif (not_attr_row.type = 'ROLE') then
1587 -- ROLE type, get display_name of role
1588 begin
1589 -- NOTE: cannot use wf_directory.getroleinfo2 because of the
1590 -- pragma WNPS.
1591 -- Decode into orig_system if necessary for indexes
1592 colon := instr(not_attr_row.text_value, ':');
1593 if (colon = 0) then
1594 select WR.DISPLAY_NAME
1595 into value
1596 from WF_ROLES WR
1597 where WR.NAME = not_attr_row.text_value
1598 and WR.ORIG_SYSTEM NOT IN ('HZ_PARTY','POS','ENG_LIST','AMV_CHN',
1599 'HZ_GROUP','CUST_CONT');
1600 else
1601 select WR.DISPLAY_NAME
1602 into value
1603 from WF_ROLES WR
1604 where WR.ORIG_SYSTEM = substr(not_attr_row.text_value, 1, colon-1)
1605 and WR.ORIG_SYSTEM_ID = substr(not_attr_row.text_value, colon+1)
1606 and WR.NAME = not_attr_row.text_value;
1607 end if;
1608 exception
1609 when no_data_found then
1610 -- Use code directly if role not found.
1611 value := not_attr_row.text_value;
1612 end;
1613
1614 elsif ((not_attr_row.type = 'DOCUMENT') and (not urlmode)) then
1615 /*
1616 ** Only execute this function if this attribute is definitely
1617 ** in the subject
1618 */
1619 if (INSTR(local_text, '&'||not_attr_row.name) > 0) then
1620
1621 if (SUBSTR(not_attr_row.text_value, 1, 2) = 'DM') then
1622 /*
1623 ** get the document name from the attribute. We used
1624 ** to go fetch the document name from the DM system
1625 ** but that just kills performance because you have to
1626 ** bounce around to a bunch of different nodes using
1627 ** URLS
1628 */
1629 value := not_attr_row.display_name;
1630 else
1631 -- All others default to null since this is a plsql document
1632 value := null;
1633 end if;
1634 end if;
1635 else
1636 -- All others default to text_value
1637 value := not_attr_row.text_value;
1638 end if;
1639
1640 --
1641 -- Substitute all occurrences of SEND tokens with values.
1642 -- Limit to 1950 chars to avoid value errors if substitution pushes
1643 -- it over the edge.
1644 --
1645 if (urlmode) then
1646 local_text := substrb(replace(local_text, '-&'||
1647 not_attr_row.name||'-',
1648 wf_mail.UrlEncode(value)), 1, 1950);
1649
1650 --Bug 2346237
1651 --The target is set to the attribute format only
1652 --if the attribute is of type URL
1653 if (not_attr_row.type = 'URL') then
1654 target := substr(nvl(not_attr_row.format, '_top'), 1, 16);
1655 end if;
1656
1657 else
1658 --Bug 2843136
1659 --Replace & or &
1660 local_text := substrb(replace(local_text, '&'||not_attr_row.name,
1661 value), 1, 1950);
1662 --Now replace any equivalen &sametoken
1663 local_text := substrb(replace(local_text, '&'||not_attr_row.name,
1664 value), 1, 1950);
1665 end if;
1666
1667 <<nextattr>>
1668 null;
1669 end loop;
1670
1671 --
1672 -- Process special '#' internal tokens. Supported tokens are:
1673 -- NID - Notification id
1674 --
1675 if (urlmode) then
1676 local_text := substrb(replace(local_text, '-&'||'#NID-',
1677 to_char(nid)), 1, 1950);
1678 else
1679 local_text := substrb(replace(local_text, '&'||'#NID',
1680 to_char(nid)), 1, 1950);
1681 end if;
1682 return(local_text);
1683 exception
1684 when others then
1685 wf_core.context('Wf_Notification','GetTextInternal', to_char(nid), disptype);
1686 raise;
1687 end GetTextInternal;
1688
1689
1690 --
1691 -- SetFrameworkAgent
1692 -- Check the URL for a JSP: entry and then substitute
1693 -- it with the value of the APPS_FRAMEWORK_AGENT
1694 -- profile option.
1695 -- IN:
1696 -- URL - URL to be ckecked
1697 -- RETURNS:
1698 -- URL with Frame work agent added
1699 -- NOTE:
1700 -- If errors are detected this routine returns some_text untouched
1701 -- instead of raising exceptions.
1702 --
1703 function SetFrameworkAgent(url in varchar2)
1704 return varchar2
1705 is
1706 value varchar2(32000);
1707 params integer;
1708 apps_fwk_agent varchar2(256);
1709 begin
1710 value := url;
1711 --Bug 2276779
1712 --Check if the URL is a javascript call.
1713 if ((lower(substr(value,1,11))) = 'javascript:') then
1714 --If the URL is a javascript function then
1715 --do not prefix the web agent to the URL.
1716 return value;
1717 end if;
1718 if ((wf_core.Translate('WF_INSTALL')='EMBEDDED') AND
1719 (substr(value, 1, 4) = 'JSP:')) then
1720 -- The URL is a APPS Framework reference and will need
1721 -- the JSP Agent rather than the WEB Agent
1722 value := substr(value, 5);
1723 value := '/' || ltrim(value, '/');
1724 apps_fwk_agent := rtrim(fnd_profile.Value('APPS_FRAMEWORK_AGENT'), '/');
1725 value := apps_fwk_agent || value;
1726 params := instr(value,'?');
1727 if (params <> 0) then
1728 value := value||'&'||'dbc='||fnd_web_config.Database_ID;
1729 else
1730 value := value||'?'||'dbc='||fnd_web_config.Database_ID;
1731 end if;
1732 else
1733 if instr(value,'//',1,1)=0 then
1734 -- CTILLEY: Added additional check to make sure a trailing slash
1735 -- is added to the WF_WEB_AGENT if it isn't the first character
1736 -- in the value. Fix for bug 2207322.
1737 if substr(value,1,1)='/' then
1738 value := wf_core.translate('WF_WEB_AGENT')||value;
1739 else
1740 value := wf_core.translate('WF_WEB_AGENT')||'/'||value;
1741 end if;
1742 end if;
1743 end if;
1744 return value;
1745
1746 exception
1747 when others then
1748 wf_core.context('Wf_Notification', 'SetFrameworkAgent', url);
1749 end;
1750
1751
1752
1753 --
1754 -- GetText
1755 -- Substitute tokens in an arbitrary text string.
1756 -- This function may return up to 32K chars. It canNOT be used in a view
1757 -- definition or in a Form. For views and forms, use GetShortText, which
1758 -- truncates values at 2000 chars.
1759 -- IN:
1760 -- some_text - Text to be substituted
1761 -- nid - Notification id of notification to use for token values
1762 -- disptype - Requested display type. Valid values:
1763 -- wf_notification.doc_text - 'text/plain'
1764 -- wf_notification.doc_html - 'text/html'
1765 -- RETURNS:
1766 -- Some_text with tokens substituted.
1767 -- NOTE:
1768 -- If errors are detected this routine returns some_text untouched
1769 -- instead of raising exceptions.
1770 function GetText(some_text in varchar2,
1771 nid in number,
1772 disptype in varchar2)
1773 return varchar2
1774 is
1775 role_name varchar2(320);
1776 email_address varchar2(320);
1777 buf varchar2(2000);
1778 local_text varchar2(32000);
1779 value varchar2(32000);
1780 params pls_integer;
1781 target varchar2(16);
1782
1783 extPos pls_integer; -- Image file extension position
1784 extStr varchar2(1000); -- Image file extention
1785
1786 renderType varchar2(10); -- 4713416 Explicit rendering of URL html.
1787
1788
1789 -- Select attr values, formatting numbers and dates as requested.
1790 -- The order-by is to handle cases where one attr name is a substring
1791 -- of another.
1792 cursor notification_attrs_cursor(nid number) is
1793 select WNA.NAME, WMA.TYPE, WMA.FORMAT, WMA.DISPLAY_NAME,
1794 WNA.TEXT_VALUE, WNA.NUMBER_VALUE, WNA.DATE_VALUE
1795 from WF_NOTIFICATION_ATTRIBUTES WNA, WF_NOTIFICATIONS WN,
1796 WF_MESSAGE_ATTRIBUTES_VL WMA
1797 where WNA.NOTIFICATION_ID = nid
1798 and WN.NOTIFICATION_ID = WNA.NOTIFICATION_ID
1799 and WN.MESSAGE_TYPE = WMA.MESSAGE_TYPE
1800 and WN.MESSAGE_NAME = WMA.MESSAGE_NAME
1801 and WMA.NAME = WNA.NAME
1802 -- order by length(WNA.NAME) desc;
1803 order by decode(wma.type,'URL',length(WNA.NAME),length(WNA.NAME)+1000) desc;
1804
1805 role_info_tbl wf_directory.wf_local_roles_tbl_type;
1806
1807 error_name varchar2(30);
1808 error_stack varchar2(32000);
1809 l_dummy boolean;
1810
1811 begin
1812 local_text := some_text;
1813
1814
1815 for not_attr_row in notification_attrs_cursor(nid) loop
1816
1817 -- only bother to find attribute value if it exists in the string
1818 -- dont place in select as each replace can introduce a new token
1819 --
1820 --Bug 2843136
1821 --Check not only '&' but also '&'
1822 if ((instr(local_text, '&'||not_attr_row.name)>0) OR (instr(local_text, '&'||not_attr_row.name)>0)) then
1823
1824 -- Find displayed value of token depending on type
1825 if (not_attr_row.type = 'LOOKUP') then
1826 -- LOOKUP type select meaning from wf_lookups.
1827 begin
1828 select MEANING
1829 into value
1830 from WF_LOOKUPS
1831 where LOOKUP_TYPE = not_attr_row.format
1832 and LOOKUP_CODE = not_attr_row.text_value;
1833 exception
1834 when no_data_found then
1835 -- Use code directly if lookup not found.
1836 value := not_attr_row.text_value;
1837 end;
1838 elsif (not_attr_row.type = 'VARCHAR2') then
1839 -- VARCHAR2 is text_value, truncated at format if one provided.
1840 if (not_attr_row.format is null) then
1841 value := not_attr_row.text_value;
1842 else
1843 value := substrb(not_attr_row.text_value, 1,
1844 to_number(not_attr_row.format));
1845 end if;
1846
1847 -- JWSMITH bug 1725916 - add BR to attribute value
1848 if (disptype=wf_notification.doc_html) then
1849 --Check if we already have and '&' in which
1850 --case don't resubstitute it.
1851 -- if ((instr(local_text,'&'||not_attr_row.name) = 0)
1852 -- AND (instr(value,'&') = 0)) then
1853
1854 -- bug 6025162 - SubstituteSpecialChars function substitutes only those
1855 -- characters that really require subsstitution. Any valid occurences
1856 -- will be retained. No validation required from calling program.
1857
1858 value := wf_core.SubstituteSpecialChars(value);
1859
1860 -- end if;
1861 value := substrb(replace(value, wf_core.newline,
1862 '<BR>'||wf_core.newline),1, 32000);
1863 end if;
1864
1865 elsif (not_attr_row.type = 'NUMBER') then
1866 -- NUMBER is number_value, with format if provided.
1867 if (not_attr_row.format is null) then
1868 value := to_char(not_attr_row.number_value);
1869 else
1870 value := to_char(not_attr_row.number_value, not_attr_row.format);
1871 end if;
1872 elsif (not_attr_row.type = 'DATE') then
1873 -- <bug 7514495> now as date format we use the first non-null value of:
1874 -- not_attr_row.format, wf_notification_util.G_NLS_DATE_FORMAT (if nid is provided
1875 -- and matches wf_notification_util.G_NID), session user's WFDS preference,
1876 -- and wf_core.nls_date_format.
1877 value := wf_notification_util.GetCalendarDate(nid, not_attr_row.date_value
1878 , not_attr_row.format, false);
1879
1880 elsif (not_attr_row.type = 'FORM') then
1881 -- FORM is display_name (function), with parameters of function
1882 -- recursively token-substituted if needed.
1883 value := not_attr_row.text_value;
1884 params := instr(value, ':');
1885 if (params <> 0) then
1886 value := not_attr_row.display_name||' ( '||
1887 substr(value, 1, params)||
1888 wf_notification.GetTextInternal(substr(value,params+1), nid,
1889 target, FALSE, FALSE, 'text/plain')||' )';
1890 end if;
1891
1892 if (disptype = wf_notification.doc_html) then
1893 -- Bug 4634849
1894 -- Do not display potentially harmful text
1895 begin
1896 l_dummy := wf_core.CheckIllegalChars(value,true,';<>()');
1897 exception
1898 when OTHERS then
1899 wf_core.get_error(error_name, value, error_stack);
1900
1901 value :=wf_core.substitutespecialchars(value);
1902 error_stack:= '';
1903
1904 end;
1905 end if;
1906
1907 elsif (not_attr_row.type = 'URL') then
1908 -- URL is display_name (url), with parameters of url
1909 -- recursively token-substituted if needed.
1910 value := not_attr_row.text_value;
1911 target := substr(nvl(not_attr_row.format, '_top'), 1, 16);
1912 value := wf_notification.SetFrameworkAgent(value);
1913 params := instr(value, '?');
1914 if (params <> 0) then
1915 value := substr(value, 1, params)||
1916 wf_notification.GetTextInternal(substr(value,params+1), nid,
1917 target, TRUE, FALSE, 'text/plain');
1918 end if;
1919
1920 if (disptype = wf_notification.doc_html) then
1921 -- Bug 4634849
1922 -- Do not display potentially harmful url
1923 begin
1924 if (not wf_core.CheckIllegalChars(value,true, ';<>"')) then
1925
1926 -- 4713416 Determine the display formatting for the URI
1927 -- First validate the prefix to the known types.
1928 renderType := upper(substr(value, 1, 4));
1929 if renderType in ('IMG:','LNK:') then
1930 -- Remove the prefix
1931 value := substr(value, 5);
1932 else
1933 -- Explicitly reset the type so that the file extention will
1934 -- dictate the render type.
1935 renderType := NULL;
1936 end if;
1937
1938 -- Check the extention of the URI file but only if
1939 -- there are no URL parameters and either a render prefix has
1940 -- been added or no prefix at all.
1941 extPos := instrb(value, '.', -1, 1) + 1;
1942 extStr := lower(substrb(value, extPos));
1943 if (params = 0 and
1944 ( renderType is null or renderType = 'IMG:' ) and
1945 extStr in ('gif','jpg','png','tif','bmp','jpeg'))
1946 then
1947 value := '<IMG SRC="'||value||
1948 '" alt="'|| not_attr_row.display_name||'"></IMG>';
1949 -- Set the renderType used to inform the next condition
1950 renderType := 'IMG:';
1951 else
1952 -- No IMG was rendered so set the type to a normal link.
1953 renderType := 'LNK:';
1954 end if;
1955
1956 if renderType = 'LNK:' then
1957 -- If no render prefix was given or an explicit LNK: prefix
1958 -- then render as a normal anchor.
1959
1960 -- For URL type display as an anchor
1961 value := '<A class="OraLink" HREF="'||value||'" TARGET="'||target||'">'||
1962 not_attr_row.display_name||'</A>';
1963 end if;
1964 end if;
1965 exception
1966 when OTHERS then
1967 wf_core.get_error(error_name, value, error_stack);
1968
1969 value :=wf_core.substitutespecialchars(value);
1970 error_stack:= '';
1971
1972 end;
1973 else
1974 -- Other types get a text representation
1975 value := not_attr_row.display_name||' : '||value;
1976 end if;
1977
1978 elsif (not_attr_row.type = 'DOCUMENT') then
1979 --skilaru 28-July-03 fix for bug 3042471
1980 if( instr(not_attr_row.text_value, fwk_region_start) = 1 ) then
1981 wf_core.token('ANAME', not_attr_row.name );
1982 wf_core.token('FWK_CONTENT', not_attr_row.text_value );
1983 value := wf_core.translate('WFUNSUP_FWK_CONTENT');
1984 else
1985 -- DOCUMENT type retrieve document contents
1986 -- Bug 2879507 if doc generation fails, let the error propagate
1987 -- to the caller
1988 value := GetAttrDoc(nid, not_attr_row.name, disptype);
1989 end if;
1990 elsif (not_attr_row.type = 'ROLE') then
1991 -- ROLE type, get display_name of role
1992 Wf_Directory.GetRoleInfo2(not_attr_row.text_value,role_info_tbl);
1993 -- Use code directly if role not found.
1994 value := nvl(role_info_tbl(1).display_name,not_attr_row.text_value);
1995
1996 -- Retrieve role information
1997 if (not_attr_row.text_value is not null) then
1998 -- Default role info to recipient if role cannot be found
1999 role_name := nvl(role_info_tbl(1).display_name,
2000 not_attr_row.text_value);
2001 email_address := nvl(role_info_tbl(1).email_address,
2002 not_attr_row.text_value);
2003 end if;
2004
2005 if (disptype = wf_notification.doc_html) then
2006
2007 value := '<A class="OraLink" HREF="mailto:'||email_address||'" TARGET="_top">'||value||'</A>';
2008
2009 end if;
2010
2011 else
2012 -- All others default to text_value
2013 value := not_attr_row.text_value;
2014
2015 if (disptype = wf_notification.doc_html) then
2016 value := wf_core.substitutespecialchars(value);
2017 end if;
2018 end if;
2019
2020 -- Substitute all occurrences of SEND tokens with values.
2021 -- Limit to 32000 chars to avoid value errors if substitution pushes
2022 -- it over the edge.
2023 --
2024 --Bug 2594012/2843136
2025 -- Bug 2917787 - added code to check if value is null.
2026 if ((value is null) or (lengthb(local_text) + (lengthb(value) - length('&'||not_attr_row.name))
2027 <= 32000)) then
2028 local_text := replace(local_text, '&'||not_attr_row.name, value);
2029 local_text := replace(local_text, '&'||not_attr_row.name, value);
2030 end if;
2031 end if;-- if instr(..
2032 end loop;
2033
2034 --
2035 -- Process special '#' internal tokens. Supports tokens are:
2036 -- NID - Notification id
2037 --
2038 local_text := substrb(replace(local_text, '&'||'#NID',
2039 to_char(nid)), 1, 32000);
2040 return(local_text);
2041 exception
2042 when others then
2043 wf_core.context('Wf_Notification','GetText', to_char(nid), disptype);
2044 raise;
2045 -- return(some_text);
2046 end GetText;
2047
2048 --
2049 -- GetUrlText
2050 -- Substitute url-style tokens (with dashes) in an arbitrary text string.
2051 -- This function may return up to 32K chars. It can NOT be used in a view
2052 -- definition or in a Form. For views and forms, use GetShortText, which
2053 -- truncates values at 2000 chars.
2054 -- IN:
2055 -- some_text - Text to be substituted
2056 -- nid - Notification id of notification to use for token values
2057 -- RETURNS:
2058 -- Some_text with tokens substituted.
2059 -- NOTE:
2060 -- If errors are detected this routine returns some_text untouched
2061 -- instead of raising exceptions.
2062 --
2063 function GetUrlText(some_text in varchar2,
2064 nid in number)
2065 return varchar2
2066 is
2067 target varchar2(16);
2068 l_error varchar2(32000);
2069 begin
2070 return(GetTextInternal(some_text, nid, target, TRUE, TRUE, 'text/plain'));
2071 exception
2072 when others then
2073 -- Return the error message with error stack
2074 l_error := wf_core.translate('ERROR') || wf_core.newline;
2075 if (wf_core.error_name is not null) then
2076 l_error := l_error || wf_core.error_message || wf_core.newline;
2077 l_error := l_error || wf_core.translate('WFENG_ERRNAME') || ': ' ||
2078 wf_core.error_name || wf_core.newline;
2079 else
2080 l_error := l_error || sqlerrm || wf_core.newline;
2081 l_error := l_error || wf_core.translate('WFENG_ERRNAME') || ': ' ||
2082 to_char(sqlcode) || wf_core.newline;
2083 end if;
2084 l_error := l_error || wf_core.translate('WFENG_ERRSTACK') || ': ' ||
2085 wf_core.error_stack || wf_core.newline;
2086 return (substrb(l_error, 1, 1950));
2087 end GetUrlText;
2088
2089 --
2090 -- GetShortText
2091 -- Substitute tokens in an arbitrary text string, limited to 2000 chars.
2092 -- (actually 1950, because of forms overhead).
2093 -- This function is meant to be used in view definitions and Forms, where
2094 -- the field size must be limited to 2000 chars. Use GetText() to retrieve
2095 -- up to 32K if the text may be longer.
2096 -- IN:
2097 -- some_text - Text to be substituted
2098 -- nid - Notification id of notification to use for token values
2099 -- RETURNS:
2100 -- Some_text with tokens substituted.
2101 -- NOTE:
2102 -- If errors are detected this routine returns some_text untouched
2103 -- instead of raising exceptions.
2104 function GetShortText(some_text in varchar2,
2105 nid in number)
2106 return varchar2
2107 is
2108 target varchar2(16);
2109 l_error varchar2(32000);
2110
2111 begin
2112 -- gettextinternal will truncate to 1950 characters.
2113 return(GetTextInternal(some_text, nid, target, FALSE, TRUE));
2114 exception
2115 when others then
2116 -- Return the error message with error stack if GetTextInternal raises
2117 l_error := wf_core.translate('ERROR') || wf_core.newline;
2118 if (wf_core.error_name is not null) then
2119 l_error := l_error || wf_core.error_message || wf_core.newline;
2120 l_error := l_error || wf_core.translate('WFENG_ERRNAME') || ': ' ||
2121 wf_core.error_name || wf_core.newline;
2122 else
2123 l_error := l_error || sqlerrm || wf_core.newline;
2124 l_error := l_error || wf_core.translate('WFENG_ERRNAME') || ': ' ||
2125 to_char(sqlcode) || wf_core.newline;
2126 end if;
2127 l_error := l_error || wf_core.translate('WFENG_ERRSTACK') || ': ' ||
2128 wf_core.error_stack||wf_core.newline;
2129 return (substrb(l_error, 1, 1950));
2130
2131 end GetShortText;
2132
2133 --
2134 -- GetAttrInfo
2135 -- Get type information about a notification attribute.
2136 -- IN:
2137 -- nid - Notification id
2138 -- aname - Attribute name
2139 -- OUT:
2140 -- atype - Attribute type
2141 -- subtype - 'SEND' or 'RESPOND',
2142 -- format - Attribute format
2143 --
2144 procedure GetAttrInfo(nid in number,
2145 aname in varchar2,
2146 atype out nocopy varchar2,
2147 subtype out nocopy varchar2,
2148 format out nocopy varchar2)
2149 is
2150 begin
2151 if ((nid is null) or (aname is null)) then
2152 wf_core.token('NID', to_char(nid));
2153 wf_core.token('ANAME', aname);
2154 wf_core.raise('WFSQL_ARGS');
2155 end if;
2156
2157 begin
2158 select WMA.TYPE, WMA.SUBTYPE, WMA.FORMAT
2159 into atype, subtype, format
2160 from WF_NOTIFICATION_ATTRIBUTES WNA, WF_NOTIFICATIONS WN,
2161 WF_MESSAGE_ATTRIBUTES WMA
2162 where WNA.NOTIFICATION_ID = nid
2163 and WNA.NAME = aname
2164 and WNA.NOTIFICATION_ID = WN.NOTIFICATION_ID
2165 and WN.MESSAGE_TYPE = WMA.MESSAGE_TYPE
2166 and WN.MESSAGE_NAME = WMA.MESSAGE_NAME
2167 and WMA.NAME = WNA.NAME;
2168 exception
2169 when no_data_found then
2170 wf_core.token('NID', to_char(nid));
2171 wf_core.token('ATTRIBUTE', aname);
2172 wf_core.raise('WFNTF_ATTR');
2173 end;
2174
2175 exception
2176 when others then
2177 wf_core.context('Wf_Notification', 'GetAttrInfo', to_char(nid),
2178 aname);
2179 raise;
2180 end GetAttrInfo;
2181
2182 --
2183 -- GetAttrText
2184 -- Get the value of a text notification attribute.
2185 -- If the attribute is a NUMBER or DATE type, then translate the
2186 -- number/date value to a text-string representation using attrbute format.
2187 -- For all other types, get the value directly.
2188 -- IN:
2189 -- nid - Notification id
2190 -- aname - Attribute Name
2191 -- RETURNS:
2192 -- Attribute value
2193 --
2194 function GetAttrText (nid in number,
2195 aname in varchar2,
2196 ignore_notfound in boolean)
2197 return varchar2 is
2198 atype varchar2(8);
2199 format varchar2(240);
2200 lvalue varchar2(4000);
2201 params pls_integer;
2202 l_valDate date;
2203 begin
2204 if ((nid is null) or (aname is null)) then
2205 wf_core.token('NID', to_char(nid));
2206 wf_core.token('ANAME', aname);
2207 wf_core.raise('WFSQL_ARGS');
2208 end if;
2209
2210 -- Get type and format of attr.
2211 -- This is used for translating number/date strings.
2212 begin
2213 select WMA.TYPE, WMA.FORMAT
2214 into atype, format
2215 from WF_NOTIFICATION_ATTRIBUTES WNA, WF_NOTIFICATIONS WN,
2216 WF_MESSAGE_ATTRIBUTES WMA
2217 where WNA.NOTIFICATION_ID = nid
2218 and WNA.NAME = aname
2219 and WNA.NOTIFICATION_ID = WN.NOTIFICATION_ID
2220 and WN.MESSAGE_NAME = WMA.MESSAGE_NAME
2221 and WN.MESSAGE_TYPE = WMA.MESSAGE_TYPE
2222 and WNA.NAME = WMA.NAME;
2223 exception
2224 when no_data_found then
2225 -- This is an unvalidated runtime attr.
2226 -- Treat it as a varchar2.
2227 atype := 'VARCHAR2';
2228 format := '';
2229 end;
2230
2231 -- Select value from appropriate type column.
2232 begin
2233 if (atype = 'NUMBER') then
2234 select decode(format,
2235 '', to_char(WNA.NUMBER_VALUE),
2236 to_char(WNA.NUMBER_VALUE, format))
2237 into lvalue
2238 from WF_NOTIFICATION_ATTRIBUTES WNA
2239 where WNA.NOTIFICATION_ID = nid and WNA.NAME = aname;
2240 elsif (atype = 'DATE') then
2241 -- <bug 7514495> apply format precedence to get date text
2242 select DATE_VALUE into l_valDate
2243 from WF_NOTIFICATION_ATTRIBUTES WNA
2244 where WNA.NOTIFICATION_ID = nid and WNA.NAME = aname;
2245
2246 lvalue := wf_notification_util.GetCalendarDate(nid, l_valDate, format, false);
2247
2248 else
2249 -- VARCHAR2, LOOKUP, FORM, or URL type.
2250 select WNA.TEXT_VALUE
2251 into lvalue
2252 from WF_NOTIFICATION_ATTRIBUTES WNA
2253 where WNA.NOTIFICATION_ID = nid and WNA.NAME = aname;
2254
2255 -- Recursively substitute attributes in parameter portion of
2256 -- FORM and URL type attributes.
2257 -- Note a slight chance of infinite recursion here if
2258 -- parameters are defined perversely.
2259 if (atype = 'FORM') then
2260 -- FORM params are after ':'
2261 params := instr(lvalue, ':');
2262 if (params <> 0) then
2263 lvalue := substr(lvalue, 1, params)||
2264 wf_notification.GetShortText(substr(lvalue,
2265 params+1), nid);
2266 end if;
2267 elsif (atype = 'URL') then
2268 -- URL params are after '?'
2269 params := instr(lvalue, '?');
2270 if (params <> 0) then
2271 lvalue := substr(lvalue, 1, params)||
2272 wf_notification.GetUrlText(substr(lvalue,
2273 params+1), nid);
2274 end if;
2275 end if;
2276 end if;
2277 exception
2278 when no_data_found then
2279 if (ignore_notfound) then
2280 return(null);
2281 else
2282 wf_core.token('NID', to_char(nid));
2283 wf_core.token('ATTRIBUTE', aname);
2284 wf_core.raise('WFNTF_ATTR');
2285 end if;
2286 end;
2287
2288 return(lvalue);
2289 exception
2290 when others then
2291 wf_core.context('Wf_Notification', 'GetAttrText', to_char(nid), aname);
2292 raise;
2293 end GetAttrText;
2294
2295 --
2296 -- GetAttrNumber
2297 -- Get the value of a number notification attribute.
2298 -- Attribute must be a NUMBER-type attribute.
2299 -- IN:
2300 -- nid - Notification id
2301 -- aname - Attribute Name
2302 -- RETURNS:
2303 -- Attribute value
2304 --
2305 function GetAttrNumber (nid in number,
2306 aname in varchar2)
2307 return number is
2308 lvalue number;
2309 begin
2310 if ((nid is null) or (aname is null)) then
2311 wf_core.token('NID', to_char(nid));
2312 wf_core.token('ANAME', aname);
2313 wf_core.raise('WFSQL_ARGS');
2314 end if;
2315
2316 begin
2317 select WNA.NUMBER_VALUE
2318 into lvalue
2319 from WF_NOTIFICATION_ATTRIBUTES WNA
2320 where WNA.NOTIFICATION_ID = nid and WNA.NAME = aname;
2321 exception
2322 when no_data_found then
2323 wf_core.token('NID', to_char(nid));
2324 wf_core.token('ATTRIBUTE', aname);
2325 wf_core.raise('WFNTF_ATTR');
2326 end;
2327
2328 return(lvalue);
2329 exception
2330 when others then
2331 wf_core.context('Wf_Notification', 'GetAttrNumber', to_char(nid), aname);
2332 raise;
2333 end GetAttrNumber;
2334
2335 --
2336 -- GetAttrDate
2337 -- Get the value of a date notification attribute.
2338 -- Attribute must be a DATE-type attribute.
2339 -- IN:
2340 -- nid - Notification id
2341 -- aname - Attribute Name
2342 -- RETURNS:
2343 -- Attribute value
2344 --
2345 function GetAttrDate (nid in number,
2346 aname in varchar2)
2347 return date is
2348 lvalue date;
2349 begin
2350 if ((nid is null) or (aname is null)) then
2351 wf_core.token('NID', to_char(nid));
2352 wf_core.token('ANAME', aname);
2353 wf_core.raise('WFSQL_ARGS');
2354 end if;
2355
2356 begin
2357 select WNA.DATE_VALUE
2358 into lvalue
2359 from WF_NOTIFICATION_ATTRIBUTES WNA
2360 where WNA.NOTIFICATION_ID = nid and WNA.NAME = aname;
2361 exception
2362 when no_data_found then
2363 wf_core.token('NID', to_char(nid));
2364 wf_core.token('ATTRIBUTE', aname);
2365 wf_core.raise('WFNTF_ATTR');
2366 end;
2367
2368 return(lvalue);
2369 exception
2370 when others then
2371 wf_core.context('Wf_Notification', 'GetAttrDate', to_char(nid), aname);
2372 raise;
2373 end GetAttrDate;
2374
2375 --
2376 --
2377 -- GetAttrDoc
2378 -- Get the displayed value of a DOCUMENT-type attribute.
2379 -- Returns referenced document in format requested.
2380 -- Use GetAttrText to get retrieve the actual attr value (i.e. the
2381 -- document key string instead of the actual document).
2382 -- NOTE:
2383 -- Only PLSQL document type is implemented.
2384 -- This function will call a revised implementation of procedure GetAttrDoc2
2385 -- which will return the document type also.
2386 -- IN:
2387 -- nid - Notification id
2388 -- aname - Attribute Name
2389 -- disptype - Requested display type. Valid values:
2390 -- wf_notification.doc_text - 'text/plain'
2391 -- wf_notification.doc_html - 'text/html'
2392 -- RETURNS:
2393 -- Referenced document in format requested.
2394 --
2395 function GetAttrDoc(
2396 nid in number,
2397 aname in varchar2,
2398 disptype in varchar2)
2399 return varchar2
2400 is
2401 document varchar2(32000);
2402 doctype varchar2(255);
2403 begin
2404
2405 -- call the procedure to get the Document Content and return to the caller.
2406 wf_notification.GetAttrDoc2(nid, aname, disptype, document, doctype);
2407 return (document);
2408
2409 exception
2410 when others then
2411 wf_core.context('Wf_Notification', 'GetAttrDoc', to_char(nid), aname,
2412 disptype);
2413 raise;
2414 end GetAttrDoc;
2415
2416 --
2417 -- GetAttrDoc2 - <explained in wfntfs.pls>
2418 --
2419 procedure GetAttrDoc2(
2420 nid in number,
2421 aname in varchar2,
2422 disptype in varchar2,
2423 document out nocopy varchar2,
2424 doctype out nocopy varchar2)
2425 is
2426 key varchar2(4000);
2427 colon pls_integer;
2428 slash pls_integer;
2429 dmstype varchar2(30);
2430 display_name varchar2(80);
2431 procname varchar2(240);
2432 launch_url varchar2(4000);
2433 procarg varchar2(32000);
2434 username varchar2(320);
2435 sqlbuf varchar2(2000);
2436 target varchar2(240);
2437 l_charcheck boolean;
2438
2439 begin
2440
2441 -- Check args
2442 if ((nid is null) or (aname is null) or
2443 (disptype not in (wf_notification.doc_text,
2444 wf_notification.doc_html))) then
2445 wf_core.token('NID', to_char(nid));
2446 wf_core.token('ANAME', aname);
2447 wf_core.token('DISPTYPE', disptype);
2448 wf_core.raise('WFSQL_ARGS');
2449 end if;
2450
2451 -- Retrieve key string
2452 key := GetAttrText(nid, aname);
2453
2454 -- If the key is empty then return a null string
2455 if (key is null) then
2456 document := '';
2457 return;
2458 end if;
2459
2460 -- Parse doc mgmt system type from key
2461 colon := instr(key, ':');
2462 if ((colon <> 0) and (colon < 30)) then
2463 dmstype := upper(substr(key, 1, colon-1));
2464 end if;
2465
2466 if (dmstype in ('PLSQLCLOB','PLSQLBLOB')) then
2467 document := '&'||aname;
2468 plsql_clob_exists := 1;
2469 return;
2470 elsif (dmstype = 'PLSQL') then
2471 -- Parse out procedure name and arg
2472 slash := instr(key, '/');
2473 if (slash = 0) then
2474 procname := substr(key, colon+1);
2475 procarg := '';
2476 else
2477 procname := substr(key, colon+1, slash-colon-1);
2478 procarg := substr(key, slash+1);
2479 end if;
2480
2481 -- Dynamic sql call to procedure
2482 if (procarg is null) then
2483 --force a dummy value since no doc id to pass
2484 procarg := NULL;
2485 else
2486 -- Substitute refs to other attributes in argument
2487 -- NOTE: There is a slight chance of recursive loop here,
2488 -- if the substituted string eventually contains a reference
2489 -- back to this same docattr.
2490 procarg := Wf_Notification.GetTextInternal(procarg, nid, target, FALSE,
2491 FALSE, 'text/plain');
2492 end if;
2493
2494 -- ### Review Note 4
2495
2496 l_charcheck := wf_notification_util.CheckIllegalChar(procname);
2497 --Throw the Illegal exception when the check fails
2498
2499 if (wf_log_pkg.level_statement >= fnd_log.g_current_runtime_level) then
2500 wf_log_pkg.string2(wf_log_pkg.level_statement,
2501 'wf.plsql.wf_notification.GetAttrDoc2.plsqldoc_callout',
2502 'Start executing PLSQL Doc procedure - '||procname, true);
2503 end if;
2504
2505 sqlbuf := 'begin '||procname||'(:p1, :p2, :p3, :p4); end;';
2506 execute immediate sqlbuf using
2507 in procarg,
2508 in disptype,
2509 in out document,
2510 in out doctype;
2511
2512 if (wf_log_pkg.level_statement >= fnd_log.g_current_runtime_level) then
2513 wf_log_pkg.string2(wf_log_pkg.level_statement,
2514 'wf.plsql.wf_notification.GetAttrDoc2.plsqldoc_callout',
2515 'End executing PLSQL Doc procedure - '||procname, false);
2516 end if;
2517
2518
2519 -- Translate doc types if needed
2520 if ((disptype = wf_notification.doc_html) and
2521 (doctype = wf_notification.doc_text)) then
2522 -- Change plain text to html by wrapping in preformatted tags
2523 document := '<PRE>'||document||'</PRE>';
2524 end if;
2525 return;
2526
2527 else
2528 /*
2529 ** Get the attribute display name
2530 */
2531 -- Get type and format of attr.
2532 -- This is used for translating number/date strings.
2533 begin
2534 select WMATL.DISPLAY_NAME, NVL(WMA.FORMAT, '_blank')
2535 into display_name, target
2536 from WF_NOTIFICATION_ATTRIBUTES WNA, WF_NOTIFICATIONS WN,
2537 WF_MESSAGE_ATTRIBUTES_TL WMATL, WF_MESSAGE_ATTRIBUTES WMA
2538 where WNA.NOTIFICATION_ID = nid
2539 and WNA.NAME = aname
2540 and WNA.NOTIFICATION_ID = WN.NOTIFICATION_ID
2541 and WN.MESSAGE_NAME = WMATL.MESSAGE_NAME
2542 and WN.MESSAGE_TYPE = WMATL.MESSAGE_TYPE
2543 and WNA.NAME = WMATL.NAME
2544 and WN.MESSAGE_NAME = WMA.MESSAGE_NAME
2545 and WN.MESSAGE_TYPE = WMA.MESSAGE_TYPE
2546 and WNA.NAME = WMA.NAME
2547 and WMATL.LANGUAGE = userenv('LANG');
2548 exception
2549 when no_data_found then
2550 display_name := null;
2551 when others then
2552 raise;
2553 end;
2554
2555 /*
2556 ** If this is a plain text request then just return the display
2557 ** name for the attribute. If it is html then get the attachment
2558 ** url link and return it.
2559 */
2560 if (disptype = wf_notification.doc_html) THEN
2561
2562 -- Returns session user name if available
2563 username := Wfa_Sec.GetUser;
2564
2565 fnd_document_management.get_launch_document_url (
2566 username, key, FALSE, launch_url);
2567
2568 document := '<A class="OraLink" HREF="'||launch_url|| '" TARGET="'||target||'">'||
2569 display_name||'</A>';
2570
2571 ELSE
2572 document := display_name;
2573 END IF;
2574 return;
2575 end if;
2576 document := null;
2577 exception
2578 when others then
2579 wf_core.context('wf_notification', 'GetAttrDoc2', to_char(nid), aname, disptype);
2580 raise;
2581 end GetAttrDoc2;
2582
2583 -- bug 2581129
2584 -- GetSubject
2585 -- Get subject of notification message with token values substituted
2586 -- from notification attributes. Takes disptype as input.
2587 -- IN:
2588 -- nid - Notification Id
2589 -- disptype - Display Type
2590 -- RETURNS:
2591 -- Substituted message subject
2592 -- NOTE:
2593 -- If errors are detected this routine returns the subject unsubstituted,
2594 -- or null if all else fails, instead of raising exceptions.
2595 --
2596 function GetSubject(
2597 nid in number,
2598 disptype in varchar2)
2599 return varchar2 is
2600 local_subject varchar2(240);
2601 target varchar2(16);
2602 l_error varchar2(32000);
2603
2604 begin
2605 -- Get subject
2606 select WM.SUBJECT
2607 into local_subject
2608 from WF_NOTIFICATIONS N, WF_MESSAGES_VL WM
2609 where N.NOTIFICATION_ID = nid
2610 and N.MESSAGE_NAME = WM.NAME
2611 and N.MESSAGE_TYPE = WM.TYPE;
2612
2613 -- Return substituted subject, limited to 240 chars in case
2614 -- tokens exceed length.
2615 -- return(substrb(GetTextInternal(local_subject, nid, target, FALSE,
2616 -- TRUE, disptype), 1, 240));
2617 -- Allow PLSQL Document attributes within Subject
2618 return(substrb(GetText(local_subject, nid, disptype), 1, 240));
2619
2620 exception
2621 when others then
2622 -- Return the error message with error stack
2623 l_error := wf_core.translate('ERROR') || wf_core.newline;
2624 if (wf_core.error_name is not null) then
2625 l_error := l_error || wf_core.error_message || wf_core.newline;
2626 l_error := l_error || wf_core.translate('WFENG_ERRNAME') || ': ' ||
2627 wf_core.error_name || wf_core.newline;
2628 else
2629 l_error := l_error || sqlerrm || wf_core.newline;
2630 l_error := l_error || wf_core.translate('WFENG_ERRNAME') || ': ' ||
2631 to_char(sqlcode) || wf_core.newline;
2632 end if;
2633 l_error := l_error || wf_core.translate('WFENG_ERRSTACK') || ': ' ||
2634 wf_core.error_stack || wf_core.newline;
2635 return (substrb(l_error, 1, 240));
2636 end GetSubject;
2637
2638 -- GetSubject
2639 -- Get subject of notification message with token values substituted
2640 -- from notification attributes.
2641 -- IN:
2642 -- nid - Notification Id
2643 -- RETURNS:
2644 -- Substituted message subject
2645 -- NOTE:
2646 -- If errors are detected this routine returns the subject unsubstituted,
2647 -- or null if all else fails, instead of raising exceptions.
2648 --
2649 function GetSubject(nid in number)
2650 return varchar2 is
2651 local_subject varchar2(240);
2652 begin
2653 return (Wf_Notification.GetSubject(nid, 'text/html'));
2654 end GetSubject;
2655
2656 --
2657 -- GetBody
2658 -- Get body of notification message with token values substituted
2659 -- from notification attributes.
2660 -- This function may return up to 32K chars. It can NOT be used in a view
2661 -- definition or in a Form. For views and forms, use GetShortBody, which
2662 -- truncates values at 1950 chars.
2663 -- IN:
2664 -- nid - Notification Id
2665 -- disptype - Requested display type. Valid values:
2666 -- wf_notification.doc_text - 'text/plain'
2667 -- wf_notification.doc_html - 'text/html'
2668 -- wf_notification.doc_attach - ''
2669 -- RETURNS:
2670 -- Substituted message body
2671 -- NOTE:
2672 -- If errors are detected this routine returns the body unsubstituted,
2673 -- or null if all else fails, instead of raising exceptions.
2674 --
2675 function GetBody(
2676 nid in number,
2677 disptype in varchar2)
2678 return varchar2 is
2679 local_body varchar2(32000);
2680 local_html_body varchar2(32000);
2681
2682 -- To check if Reassign or Request Info is performed on a FYI Notification
2683 CURSOR c_comm IS
2684 SELECT count(1)
2685 FROM wf_comments
2686 WHERE action_type in ('REASSIGN', 'QA')
2687 AND notification_id = nid
2688 AND rownum = 1;
2689
2690 l_resp_cnt number;
2691 l_fyi boolean;
2692 l_comm_cnt pls_integer;
2693 l_html_hist boolean;
2694 l_cust_hist varchar2(4000);
2695 l_action_hist varchar2(240);
2696 begin
2697 -- Get body
2698 select WM.BODY, WM.HTML_BODY
2699 into local_body, local_html_body
2700 from WF_NOTIFICATIONS N, WF_MESSAGES_VL WM
2701 where N.NOTIFICATION_ID = nid
2702 and N.MESSAGE_NAME = WM.NAME
2703 and N.MESSAGE_TYPE = WM.TYPE;
2704
2705 -- If user has not used WF_NOTIFICATION(HISTORY) or #HISTORY, append Action History in the
2706 -- notification body by default if this is a...
2707 -- 1. Response required notification
2708 -- 2. FYI notification with at least one Reassign action
2709 -- Query to check if the ntf is FYI or not
2710 SELECT count(1)
2711 INTO l_resp_cnt
2712 FROM wf_message_attributes wma,
2713 wf_notifications wn
2714 WHERE wn.notification_id = nid
2715 AND wma.message_type = wn.message_type
2716 AND wma.message_name = wn.message_name
2717 AND wma.subtype = 'RESPOND'
2718 AND rownum = 1;
2719
2720 if (l_resp_cnt = 0) then
2721 l_fyi := true;
2722 else
2723 l_fyi := false;
2724 end if;
2725
2726 -- If this is FYI, get the count of Reassign and Request Info actions
2727 if (l_fyi) then
2728 l_comm_cnt := 0;
2729 open c_comm;
2730 fetch c_comm into l_comm_cnt;
2731 if (c_comm%notfound) then
2732 l_comm_cnt := 0;
2733 end if;
2734 close c_comm;
2735 end if;
2736
2737 l_html_hist := false;
2738 if ((l_fyi and l_comm_cnt > 0) or not l_fyi) then
2739 -- According to bug 3612609, if the user just defines #HISTORY, but does not place it in the
2740 -- message body, even then it should be used instead of default WF Action History
2741 begin
2742 l_cust_hist := Wf_Notification.GetAttrText(nid, '#HISTORY');
2743 exception
2744 when others then
2745 l_cust_hist := '';
2746 Wf_Core.Clear;
2747 end;
2748 -- Validate if l_cust_hist has a valid PLSQL doc api attached to it. If it ever has JSP: we
2749 -- would not be here.
2750 if (l_cust_hist is not null and upper(trim(substr(l_cust_hist, 1, 5))) = 'PLSQL') then
2751 l_action_hist := 'HISTORY';
2752 else
2753 l_action_hist := 'WF_NOTIFICATION(HISTORY)';
2754 end if;
2755
2756 -- Either a FYI with at least one reassign/Request Info or a Response notification.
2757 -- So, append Action History
2758 if (local_body is not null and instrb(local_body, 'WF_NOTIFICATION(HISTORY)') = 0 and
2759 instrb(local_body, 'HISTORY') = 0) then
2760
2761 local_body := local_body || Wf_Core.newline || l_action_hist;
2762 end if;
2763
2764 if (local_html_body is not null and instrb(local_html_body, 'WF_NOTIFICATION(HISTORY)') = 0 and
2765 instrb(local_html_body, 'HISTORY') = 0) then
2766 -- Defer adding history macro until after stripping off BODY tags
2767 l_html_hist := true;
2768 end if;
2769 end if;
2770
2771 -- Return substituted body.
2772 if (disptype = wf_notification.doc_text) then
2773 local_body := GetText(local_body, nid, disptype);
2774
2775 -- replace the functions here
2776 local_body := runFuncOnBody(nid, local_body, disptype);
2777
2778 return(local_body);
2779 else
2780 if (local_html_body is null) then
2781 --use the plain text body but fake it as html by adding <BR>
2782 local_body := substrb(replace(local_body, wf_core.newline,
2783 '<BR>'||wf_core.newline),1, 32000);
2784
2785 -- get the attribute values
2786 local_body := GetText(local_body, nid, disptype);
2787
2788 -- replace the functions here
2789 local_body := runFuncOnBody(nid, local_body, disptype);
2790
2791 return(local_body);
2792 else
2793 if instr(upper(local_html_body),'<BODY')>0 then
2794 --strip out the Body tag
2795
2796 local_html_body:= substr(local_html_body,
2797 instr(local_html_body,'>',
2798 instr(upper(local_html_body),'<BODY'))+1);
2799 end if;
2800
2801 if instr(upper(local_html_body),'</BODY')>0 then
2802 local_html_body:= substr(local_html_body,1,
2803 instr(upper(local_html_body),'</BODY')-1);
2804 end if;
2805
2806 if (l_html_hist) then
2807 local_html_body := local_html_body || Wf_Core.newline || l_action_hist;
2808 end if;
2809
2810 local_html_body := GetText(local_html_body, nid, disptype);
2811
2812 -- replace the functions here
2813 local_html_body := runFuncOnBody(nid, local_html_body, disptype);
2814
2815 return(local_html_body);
2816 end if;
2817 end if;
2818
2819 exception
2820 when others then
2821 wf_core.context('Wf_Notification', 'GetBody', to_char(nid), disptype);
2822 raise;
2823 end GetBody;
2824
2825 --
2826 -- GetShortBody
2827 -- Get body of notification message with token values substituted
2828 -- from notification attributes.
2829 -- This function is meant to be used in view definitions and Forms, where
2830 -- the field size must be limited to 2000 chars. Use GetBody() to retrieve
2831 -- up to 32K if the text may be longer.
2832 -- IN:
2833 -- nid - Notification Id
2834 -- RETURNS:
2835 -- Substituted message body
2836 -- NOTE:
2837 -- If errors are detected this routine returns the body unsubstituted,
2838 -- or null if all else fails, instead of raising exceptions. It must do
2839 -- this so the routine can be pragma'd and used in the
2840 -- wf_notifications_view view.
2841 --
2842 function GetShortBody(nid in number)
2843 return varchar2 is
2844 local_body varchar2(4000);
2845 l_error varchar2(32000);
2846 begin
2847 -- Get body
2848 select WM.BODY
2849 into local_body
2850 from WF_NOTIFICATIONS N, WF_MESSAGES_VL WM
2851 where N.NOTIFICATION_ID = nid
2852 and N.MESSAGE_NAME = WM.NAME
2853 and N.MESSAGE_TYPE = WM.TYPE;
2854
2855 -- Return substituted body.
2856 -- GetShortText already limits to 1950 chars, so further limit is not needed.
2857 return(GetShortText(local_body, nid));
2858 exception
2859 when others then
2860 -- If there is a failure in GetShortText, the error message is returned
2861 -- Return the error message with error stack
2862 l_error := wf_core.translate('ERROR') || wf_core.newline;
2863 if (wf_core.error_name is not null) then
2864 l_error := l_error || wf_core.error_message || wf_core.newline;
2865 l_error := l_error || wf_core.translate('WFENG_ERRNAME') || ': ' ||
2866 wf_core.error_name || wf_core.newline;
2867 else
2868 l_error := l_error || sqlerrm || wf_core.newline;
2869 l_error := l_error || wf_core.translate('WFENG_ERRNAME') || ': ' ||
2870 to_char(sqlcode) || wf_core.newline;
2871 end if;
2872 l_error := l_error || wf_core.translate('WFENG_ERRSTACK') || ': ' ||
2873 wf_core.error_stack || wf_core.newline;
2874 return (substrb(l_error, 1, 1950));
2875 end GetShortBody;
2876
2877 --
2878 -- GetInfo
2879 -- Return info about notification
2880 -- IN:
2881 -- nid - Notification Id
2882 -- OUT:
2883 -- role - Role notification is sent to
2884 -- message_type - Type flag of message
2885 -- message_name - Message name
2886 -- priority - Notification priority
2887 -- due_date - Due date
2888 -- status - Notification status (OPEN, CLOSED, CANCELED)
2889 --
2890 procedure GetInfo(nid in number,
2891 role out nocopy varchar2,
2892 message_type out nocopy varchar2,
2893 message_name out nocopy varchar2,
2894 priority out nocopy varchar2,
2895 due_date out nocopy varchar2,
2896 status out nocopy varchar2)
2897 is
2898 begin
2899
2900 begin
2901 select
2902 N.RECIPIENT_ROLE,
2903 N.MESSAGE_TYPE,
2904 N.MESSAGE_NAME,
2905 N.PRIORITY,
2906 N.DUE_DATE,
2907 N.STATUS
2908 into
2909 GetInfo.role,
2910 GetInfo.message_type,
2911 GetInfo.message_name,
2912 GetInfo.priority,
2913 GetInfo.due_date,
2914 GetInfo.status
2915 from WF_NOTIFICATIONS N
2916 where N.NOTIFICATION_ID = nid;
2917 exception
2918 when no_data_found then
2919 wf_core.token('NID', to_char(nid));
2920 wf_core.raise('WFNTF_NID');
2921 end;
2922
2923 exception
2924 when others then
2925 wf_core.context('Wf_Notification', 'GetInfo', to_char(nid));
2926 raise;
2927 end GetInfo;
2928
2929 --
2930 -- Responder
2931 -- Return responder of closed notification.
2932 -- IN
2933 -- nid - Notification Id
2934 -- RETURNS
2935 -- Responder to notification. If no responder was set or notification
2936 -- not yet closed, return null.
2937 --
2938 function Responder(
2939 nid in number)
2940 return varchar2
2941 is
2942 respbuf varchar2(240);
2943 begin
2944 if (nid is null) then
2945 wf_core.token('NID', to_char(nid));
2946 wf_core.raise('WFSQL_ARGS');
2947 end if;
2948
2949 -- Get responder
2950 begin
2951 select WN.RESPONDER
2952 into respbuf
2953 from WF_NOTIFICATIONS WN
2954 where WN.NOTIFICATION_ID = nid;
2955 exception
2956 when no_data_found then
2957 wf_core.token('NID', to_char(nid));
2958 wf_core.raise('WFNTF_NID');
2959 end;
2960
2961 return(respbuf);
2962 exception
2963 when others then
2964 Wf_Core.Context('Wf_Notification', 'Responder', to_char(nid));
2965 raise;
2966 end Responder;
2967
2968 -- AccessCheck
2969 -- Check that the access key is valid for this notification.
2970 -- IN
2971 -- Access string <nid>/<nkey>
2972 -- RETURNS
2973 -- user name (or NULL)
2974 function AccessCheck(access_str in varchar2) return varchar2
2975 is
2976 pos pls_integer;
2977 nid pls_integer;
2978 nkey varchar2(80);
2979 uname varchar2(320);
2980 begin
2981 pos := instr(access_str, '/');
2982 nid := to_number(substr(access_str, 1, pos-1));
2983 nkey := substr(access_str, pos+1);
2984
2985 select recipient_role
2986 into uname
2987 from WF_NOTIFICATIONS
2988 where NOTIFICATION_ID = nid
2989 and ACCESS_KEY = nkey;
2990
2991 return uname;
2992 exception
2993 when others then
2994 return NULL;
2995 end AccessCheck;
2996
2997 --
2998 -- GetMailPreference (PRIVATE)
2999 -- Get the mail preference of a role
3000 -- IN
3001 -- role - role notification being sent to
3002 -- callback - engine callback
3003 -- context - engine callback context
3004 -- RETURNS
3005 -- mail preference of role
3006 --
3007 function GetMailPreference(
3008 role in varchar2,
3009 callback in varchar2,
3010 context in varchar2)
3011 return varchar2
3012 is
3013 colon pls_integer;
3014 mailpref varchar2(8);
3015
3016 sqlbuf varchar2(2000);
3017 tvalue varchar2(4000);
3018 nvalue number;
3019 dvalue date;
3020 l_language varchar2(80);
3021 l_territory varchar2(80);
3022 l_email varchar2(320);
3023 l_dname varchar2(360);
3024 l_charcheck boolean;
3025
3026 begin
3027
3028 -- ROLE type, get display_name of role
3029 wf_directory.getroleinfo (GetMailPreference.role, l_dname,
3030 l_email, mailpref,
3031 l_language, l_territory);
3032
3033 --
3034 -- Check for the "special" mail suppression item attribute.
3035 -- This attribute is set to the process originator in the Process
3036 -- Navigator, so that the originator doesn't receive mail generated
3037 -- by that process.
3038 --
3039 if (callback is not null) then
3040 -- ### Review Note 3 - private function
3041 l_charcheck := WF_NOTIFICATION_UTIL.CheckIllegalChar(callback);
3042
3043
3044 -- BINDVAR_SCAN_IGNORE
3045 sqlbuf := 'begin '||callback||
3046 '(:p1, :p2, :p3, :p4, :p5, :p6, :p7); end;';
3047 begin
3048 execute immediate sqlbuf using
3049 in 'GET',
3050 in context,
3051 in '.MAIL_QUERY',
3052 in 'VARCHAR2',
3053 in out tvalue,
3054 in out nvalue,
3055 in out dvalue;
3056 exception
3057 when others then
3058 -- Ignore cases where no attribute is defined
3059 if (wf_core.error_name = 'WFENG_ITEM_ATTR') then
3060 wf_core.clear;
3061 else
3062 raise;
3063 end if;
3064 end;
3065
3066 -- We have a match, this is the originator. No mail for you.
3067 if (tvalue = role) then
3068 mailpref := 'QUERY';
3069 end if;
3070 end if;
3071
3072 return mailpref;
3073
3074 exception
3075 when others then
3076 Wf_Core.Context('Wf_Notification', 'GetMailPreference', role);
3077 raise;
3078 end GetMailPreference;
3079
3080 --
3081 -- Route (PRIVATE)
3082 -- Auto-forward or respond to notification according to routing rules
3083 -- when notification is sent or forwarded.
3084 -- Called from SendSingle and Forward.
3085 -- IN
3086 -- nid - Notification id
3087 --
3088 procedure Route(
3089 nid in number,
3090 cnt in number)
3091 is
3092 recip varchar2(320);
3093 o_recip varchar2(320);
3094 msgtype varchar2(8);
3095 msgname varchar2(30);
3096
3097 newcomment varchar2(4000);
3098
3099 badfwd exception; -- bad Forward/Transfer happened
3100 inactive_role exception; -- Notification not routed if role is inactive
3101 errmsg varchar2(4000);
3102 dummy varchar2(4000);
3103 l_hide_reassign varchar(1);
3104
3105 cursor rulecurs is
3106 select WRR.RULE_ID, WRR.ACTION, WRR.ACTION_ARGUMENT, WRR.RULE_COMMENT
3107 from WF_ROUTING_RULES WRR
3108 where WRR.ROLE = recip
3109 and sysdate between nvl(WRR.BEGIN_DATE, sysdate-1) and
3110 nvl(WRR.END_DATE, sysdate+1)
3111 and nvl(WRR.MESSAGE_TYPE, msgtype) = msgtype
3112 and nvl(WRR.MESSAGE_NAME, msgname) = msgname
3113 order by WRR.MESSAGE_TYPE, WRR.MESSAGE_NAME;
3114
3115 rulerec rulecurs%rowtype;
3116
3117 cursor attrcurs(ruleid in number) is
3118 select WRRA.NAME, WRRA.TEXT_VALUE, WRRA.NUMBER_VALUE, WRRA.DATE_VALUE,
3119 WMA.TYPE
3120 from WF_ROUTING_RULE_ATTRIBUTES WRRA, WF_ROUTING_RULES WRR,
3121 WF_MESSAGE_ATTRIBUTES WMA
3122 where WRRA.RULE_ID = ruleid
3123 and WRRA.RULE_ID = WRR.RULE_ID
3124 and WRR.MESSAGE_TYPE = WMA.MESSAGE_TYPE
3125 and WRR.MESSAGE_NAME = WMA.MESSAGE_NAME
3126 and WRRA.NAME = WMA.NAME;
3127
3128 begin
3129 -- Get ntf current recipient and message
3130 begin
3131 select WN.RECIPIENT_ROLE, WN.MESSAGE_TYPE, WN.MESSAGE_NAME
3132 into recip, msgtype, msgname
3133 from WF_NOTIFICATIONS WN
3134 where WN.NOTIFICATION_ID = nid;
3135
3136 o_recip := recip; -- set original recipient
3137 exception
3138 when no_data_found then
3139 wf_core.token('NID', to_char(nid));
3140 wf_core.raise('WFNTF_NID');
3141 end;
3142
3143 /* implement the above loop recursively */
3144 if (cnt > wf_notification.max_forward) then
3145 -- it means max_forward must have been exceeded. Treat as a loop error.
3146 wf_core.token('NID', to_char(nid));
3147 wf_core.raise('WFNTF_ROUTE_LOOP');
3148 end if;
3149
3150 -- Select one routing rule to execute
3151 open rulecurs;
3152 fetch rulecurs into rulerec;
3153 if (rulecurs%notfound) then
3154 -- No routing rules found - treat like a NOOP
3155 rulerec.action := 'NOOP';
3156 rulerec.rule_comment := '';
3157 end if;
3158 close rulecurs;
3159
3160 -- If rule has a comment append it to the buffer
3161 -- if (rulerec.rule_comment is not null) then
3162 -- if (newcomment is not null) then
3163 -- newcomment := substrb(newcomment||wf_core.newline, 1, 4000);
3164 -- end if;
3165 -- newcomment := substrb(newcomment||recip||': '||rulerec.rule_comment,
3166 -- 1, 4000);
3167 -- end if;
3168
3169 -- Check for value in #HIDE_REASSIGN attribute if defined
3170 -- Y: Donot allow Reassign
3171 -- N: Allow Reassign
3172 -- B: Allow Reassign only through Routing Rule
3173 l_hide_reassign := 'N';
3174 begin
3175 l_hide_reassign := Wf_Notification.GetAttrText(nid, '#HIDE_REASSIGN');
3176 exception
3177 when others then
3178 -- Clear the error stack since we ignore the error
3179 Wf_Core.Clear;
3180 end;
3181 -- Bug 7358225: If the recipient role of the routing rule is inactive then update the user_comment
3182 -- for the notification and return without executing the routing rule
3183 if LENGTH(rulerec.action_argument) > 0 then
3184 if Not Wf_Directory.UserActive(rulerec.action_argument) then
3185 raise inactive_role;
3186 end if;
3187 end if;
3188 newcomment := rulerec.rule_comment;
3189
3190 if (rulerec.action = 'FORWARD' and l_hide_reassign in ('N', 'B')) then
3191 -- FORWARD
3192 -- Set savepoint before doing anything.
3193 -- savepoint fwd_ntf;
3194
3195 -- Reset recipient and cycle through the loop again to check
3196 -- for another forward.
3197 recip := rulerec.action_argument;
3198
3199 begin
3200 -- ### implement this in next release
3201 -- Wf_Notification.Forward(nid, recip, newcomment, o_recip, cnt+1);
3202 Wf_Notification.Forward(nid, recip, newcomment, o_recip, cnt+1, 'RULE');
3203 exception
3204 when others then
3205 raise badfwd;
3206 end;
3207 elsif (rulerec.action = 'TRANSFER' and l_hide_reassign in ('N', 'B')) then
3208 -- TRANSFER
3209 -- Set savepoint before doing anything.
3210 -- savepoint fwd_ntf;
3211
3212 -- Reset recipient and cycle through the loop again to check
3213 -- for another transfer.
3214 recip := rulerec.action_argument;
3215
3216 begin
3217 -- ### implement this in next release
3218 -- Wf_Notification.Transfer(nid, recip, newcomment, o_recip, cnt+1);
3219 Wf_Notification.Transfer(nid, recip, newcomment, o_recip, cnt+1, 'RULE');
3220 exception
3221 when others then
3222 raise badfwd;
3223 end;
3224 elsif (rulerec.action = 'RESPOND') then
3225 -- RESPOND
3226 -- Query response values for this rule and set attrs accordingly
3227 for respattr in attrcurs(rulerec.rule_id) loop
3228 if (respattr.type = 'NUMBER') then
3229 Wf_Notification.SetAttrNumber(nid, respattr.name,
3230 respattr.number_value);
3231 elsif (respattr.type = 'DATE') then
3232 Wf_Notification.SetAttrDate(nid, respattr.name,
3233 respattr.date_value);
3234 else -- All other types use text
3235 Wf_Notification.SetAttrText(nid, respattr.name,
3236 respattr.text_value);
3237 end if;
3238 end loop;
3239
3240 -- Complete response
3241 Wf_Notification.Respond(nid, newcomment, recip, 'RULE');
3242 else
3243 -- This must be one of:
3244 -- a. NOOP rule
3245 -- b. No routing rule found
3246 -- c. Unimplemented rule type
3247 -- In any case, just return
3248 return;
3249 end if;
3250 return;
3251 exception
3252 when inactive_role then
3253 begin
3254 update WF_NOTIFICATIONS set
3255 USER_COMMENT = substr(USER_COMMENT||decode(nvl(USER_COMMENT,'T'),
3256 'T', null, wf_core.newline)||wf_core.translate('INACTIVE_ROLE'), 1, 4000)
3257 where NOTIFICATION_ID = nid;
3258 exception
3259 when others then
3260 wf_core.context('Wf_Notification', 'Route (update comment)',to_char(nid));
3261 raise;
3262 end;
3263 when badfwd then
3264 Wf_Core.Get_Error(dummy, errmsg, dummy);
3265 Wf_Core.Clear;
3266 if (newcomment is not null) then
3267 newcomment := newcomment||wf_core.newline;
3268 end if;
3269 Wf_Core.Token('TO_ROLE', WF_Directory.GetRoleDisplayName(recip));
3270 newcomment := substrb(newcomment||
3271 Wf_Core.Translate('AUTOROUTE_FAIL')||
3272 wf_core.newline||errmsg,
3273 1, 4000);
3274 begin
3275 -- append newcomment to the existing comment.
3276 -- need to add a newline character if user_comment is not null.
3277 update WF_NOTIFICATIONS set
3278 USER_COMMENT = substr(USER_COMMENT||
3279 decode(nvl(USER_COMMENT,'T'),
3280 'T', null, wf_core.newline)||
3281 Route.newcomment, 1, 4000)
3282 where NOTIFICATION_ID = nid;
3283 exception
3284 when OTHERS then
3285 wf_core.context('Wf_Notification', 'Route (update comment)',
3286 to_char(nid));
3287 raise;
3288 end;
3289
3290 when others then
3291 if (rulecurs%isopen) then
3292 close rulecurs;
3293 end if;
3294 wf_core.context('Wf_Notification', 'Route', to_char(nid));
3295 raise;
3296 end Route;
3297
3298 --
3299 -- First_Execution (Private)
3300 -- Checks if the given item activity is executed for the first time
3301 -- or was executed already
3302 -- IN
3303 -- context - Activity Context
3304 -- RETURN
3305 -- boolean status, TRUE or FALSE
3306 --
3307 function First_Execution(p_context in varchar2)
3308 return boolean
3309 is
3310 l_count pls_integer;
3311 l_item_type varchar2(8);
3312 l_item_key varchar2(240);
3313 l_actid number;
3314 begin
3315
3316 -- Derive item type, item key and activity id from the context
3317 -- when no ':' or just one ':', context does not conform to WF standard
3318 -- it could be sent by calling wf_notification.send directly
3319 -- in this case, we just return false to preserve the old behavior
3320 validate_context (p_context, l_item_type, l_item_key, l_actid);
3321 if (l_item_type is null or l_item_key is null or l_actid is null) then
3322 return false;
3323 end if;
3324
3325 -- If a record exists in history table for this item activity, it has already
3326 -- been executed
3327 SELECT count(1)
3328 INTO l_count
3329 FROM wf_item_activity_statuses_h
3330 WHERE item_type = l_item_type
3331 AND item_key = l_item_key
3332 AND process_activity = l_actid
3333 AND rownum = 1;
3334
3335 if (l_count > 0) then
3336 return false;
3337 end if;
3338 return true;
3339
3340 exception
3341 when others then
3342 Wf_Core.Context('Wf_Notification', 'First_Execution', p_context);
3343 raise;
3344 end First_Execution;
3345
3346 -- Denormalize_Columns_Internal(PRIVATE)
3347 -- Custom Columns
3348 procedure denormalize_columns_internal(p_item_key in varchar2,
3349 p_user_key in varchar2,
3350 p_nid in number)
3351 is
3352 cursor c_custom_cols is
3353 select to_number(
3354 substr(wnrm.column_name,instr(wnrm.column_name,'UTE')+3)) idx,
3355 wnrm.column_name,
3356 wna.text_value,
3357 wna.number_value,
3358 wna.date_value
3359 from wf_ntf_rules wnr,
3360 wf_ntf_rule_maps wnrm,
3361 wf_ntf_rule_criteria wnrc,
3362 wf_notification_attributes wna,
3363 wf_notifications wn
3364 where wnr.rule_name = wnrc.rule_name
3365 and wnrc.message_type = wn.message_type
3366 and wnr.status = 'ENABLED'
3367 and wnrc.rule_name = wnrm.rule_name
3368 and wnrm.attribute_name = wna.name
3369 and wna.notification_id = wn.notification_id
3370 and wn.notification_id = p_nid
3371 order by wnr.phase;
3372
3373 pta text_array_t;
3374 pfa text_array_t;
3375 pua text_array_t;
3376 pda date_array_t;
3377 pna numb_array_t;
3378 ta text_array_t;
3379 fa text_array_t;
3380 ua text_array_t;
3381 da date_array_t;
3382 na numb_array_t;
3383
3384 nd date; /* null date */
3385 nn number; /* null number */
3386 begin
3387
3388 -- initialize the varrays
3389 pta := text_array_t('','','','','','','','','','');
3390 pfa := text_array_t('','','','','');
3391 pua := text_array_t('','','','','');
3392 pda := date_array_t(nd,nd,nd,nd,nd);
3393 pna := numb_array_t(nn,nn,nn,nn,nn);
3394 ta := text_array_t('','','','','','','','','','');
3395 fa := text_array_t('','','','','');
3396 ua := text_array_t('','','','','');
3397 da := date_array_t(nd,nd,nd,nd,nd);
3398 na := numb_array_t(nn,nn,nn,nn,nn);
3399
3400 for c in c_custom_cols
3401 loop
3402 if (c.column_name like 'PROTECTED_TEXT%') then
3403 pta(c.idx) := c.text_value;
3404 elsif (c.column_name like 'PROTECTED_FORM%') then
3405 pfa(c.idx) := c.text_value;
3406 elsif (c.column_name like 'PROTECTED_URL%') then
3407 pua(c.idx) := c.text_value;
3408 elsif (c.column_name like 'PROTECTED_DATE%') then
3409 pda(c.idx) := c.date_value;
3410 elsif (c.column_name like 'PROTECTED_NUMBER%') then
3411 pna(c.idx) := c.number_value;
3412 elsif (c.column_name like 'TEXT%') then
3413 ta(c.idx) := c.text_value;
3414 elsif (c.column_name like 'FORM%') then
3415 fa(c.idx) := c.text_value;
3416 elsif (c.column_name like 'URL%') then
3417 ua(c.idx) := c.text_value;
3418 elsif (c.column_name like 'DATE%') then
3419 da(c.idx) := c.date_value;
3420 elsif (c.column_name like 'NUMBER%') then
3421 na(c.idx) := c.number_value;
3422 end if;
3423 end loop;
3424
3425 update WF_NOTIFICATIONS
3426 set
3427 PROTECTED_TEXT_ATTRIBUTE1 = pta(1)
3428 ,PROTECTED_TEXT_ATTRIBUTE2 = pta(2)
3429 ,PROTECTED_TEXT_ATTRIBUTE3 = pta(3)
3430 ,PROTECTED_TEXT_ATTRIBUTE4 = pta(4)
3431 ,PROTECTED_TEXT_ATTRIBUTE5 = pta(5)
3432 ,PROTECTED_TEXT_ATTRIBUTE6 = pta(6)
3433 ,PROTECTED_TEXT_ATTRIBUTE7 = pta(7)
3434 ,PROTECTED_TEXT_ATTRIBUTE8 = pta(8)
3435 ,PROTECTED_TEXT_ATTRIBUTE9 = pta(9)
3436 ,PROTECTED_TEXT_ATTRIBUTE10 = pta(10)
3437 ,PROTECTED_FORM_ATTRIBUTE1 = pfa(1)
3438 ,PROTECTED_FORM_ATTRIBUTE2 = pfa(2)
3439 ,PROTECTED_FORM_ATTRIBUTE3 = pfa(3)
3440 ,PROTECTED_FORM_ATTRIBUTE4 = pfa(4)
3441 ,PROTECTED_FORM_ATTRIBUTE5 = pfa(5)
3442 ,PROTECTED_URL_ATTRIBUTE1 = pua(1)
3443 ,PROTECTED_URL_ATTRIBUTE2 = pua(2)
3444 ,PROTECTED_URL_ATTRIBUTE3 = pua(3)
3445 ,PROTECTED_URL_ATTRIBUTE4 = pua(4)
3446 ,PROTECTED_URL_ATTRIBUTE5 = pua(5)
3447 ,PROTECTED_DATE_ATTRIBUTE1 = pda(1)
3448 ,PROTECTED_DATE_ATTRIBUTE2 = pda(2)
3449 ,PROTECTED_DATE_ATTRIBUTE3 = pda(3)
3450 ,PROTECTED_DATE_ATTRIBUTE4 = pda(4)
3451 ,PROTECTED_DATE_ATTRIBUTE5 = pda(5)
3452 ,PROTECTED_NUMBER_ATTRIBUTE1= pna(1)
3453 ,PROTECTED_NUMBER_ATTRIBUTE2= pna(2)
3454 ,PROTECTED_NUMBER_ATTRIBUTE3= pna(3)
3455 ,PROTECTED_NUMBER_ATTRIBUTE4= pna(4)
3456 ,PROTECTED_NUMBER_ATTRIBUTE5= pna(5)
3457 ,TEXT_ATTRIBUTE1 = ta(1)
3458 ,TEXT_ATTRIBUTE2 = ta(2)
3459 ,TEXT_ATTRIBUTE3 = ta(3)
3460 ,TEXT_ATTRIBUTE4 = ta(4)
3461 ,TEXT_ATTRIBUTE5 = ta(5)
3462 ,TEXT_ATTRIBUTE6 = ta(6)
3463 ,TEXT_ATTRIBUTE7 = ta(7)
3464 ,TEXT_ATTRIBUTE8 = ta(8)
3465 ,TEXT_ATTRIBUTE9 = ta(9)
3466 ,TEXT_ATTRIBUTE10 = ta(10)
3467 ,FORM_ATTRIBUTE1 = fa(1)
3468 ,FORM_ATTRIBUTE2 = fa(2)
3469 ,FORM_ATTRIBUTE3 = fa(3)
3470 ,FORM_ATTRIBUTE4 = fa(4)
3471 ,FORM_ATTRIBUTE5 = fa(5)
3472 ,URL_ATTRIBUTE1 = ua(1)
3473 ,URL_ATTRIBUTE2 = ua(2)
3474 ,URL_ATTRIBUTE3 = ua(3)
3475 ,URL_ATTRIBUTE4 = ua(4)
3476 ,URL_ATTRIBUTE5 = ua(5)
3477 ,DATE_ATTRIBUTE1 = da(1)
3478 ,DATE_ATTRIBUTE2 = da(2)
3479 ,DATE_ATTRIBUTE3 = da(3)
3480 ,DATE_ATTRIBUTE4 = da(4)
3481 ,DATE_ATTRIBUTE5 = da(5)
3482 ,NUMBER_ATTRIBUTE1= na(1)
3483 ,NUMBER_ATTRIBUTE2= na(2)
3484 ,NUMBER_ATTRIBUTE3= na(3)
3485 ,NUMBER_ATTRIBUTE4= na(4)
3486 ,NUMBER_ATTRIBUTE5= na(5)
3487 ,ITEM_KEY = p_item_key
3488 ,USER_KEY = p_user_key
3489 where notification_id = p_nid;
3490
3491 exception
3492 when OTHERS then
3493 wf_core.context('Wf_Notification', 'denormalize_columns_internal',
3494 p_item_key, p_user_key, to_char(p_nid));
3495 raise;
3496 end Denormalize_columns_internal;
3497
3498 --
3499 -- Denormalize Custom columns(PUBLIC)
3500 -- Called by FNDWFDCC concurrent program
3501 --
3502 procedure denormalizeColsConcurrent(retcode out nocopy varchar2,
3503 errbuf out nocopy varchar2,
3504 p_item_type in varchar2,
3505 p_status in varchar2,
3506 p_recipient in varchar2)
3507 is
3508 cursor c_notifications is
3509 select wi.item_key, wi.user_key, wn.notification_id
3510 from wf_items wi,
3511 wf_item_activity_statuses wias,
3512 wf_notifications wn
3513 where wi.item_key = wias.item_key
3514 and wi.item_type = wias.item_type
3515 and wias.notification_id = wn.group_id
3516 and (wn.message_type = p_item_type or p_item_type is null)
3517 and wn.status = nvl(p_status, 'OPEN')
3518 and (wn.recipient_role = p_recipient or p_recipient is null)
3519 order by wi.item_type;
3520
3521 errname varchar2(30);
3522 errmsg varchar2(2000);
3523 errstack varchar2(4000);
3524 begin
3525
3526 for v_ntf in c_notifications
3527 loop
3528 denormalize_columns_internal(v_ntf.item_key, v_ntf.user_key, v_ntf.notification_id);
3529 end loop;
3530
3531 -- Return 0 for successful completion.
3532 errbuf := '';
3533 retcode := '0';
3534
3535 exception
3536 when others then
3537 -- Retrieve error message into errbuf
3538 wf_core.get_error(errname, errmsg, errstack);
3539 if (errmsg is not null) then
3540 errbuf := errmsg;
3541 else
3542 errbuf := sqlerrm;
3543 end if;
3544
3545 -- Return 2 for error.
3546 retcode := '2';
3547 end denormalizeColsConcurrent;
3548
3549 --
3550 -- SendSingle (PRIVATE)
3551 -- Send a single notification.
3552 -- Called by Send and SendGroup public functions.
3553 -- Argument error checking should be done by Send and SendGroup before
3554 -- calling this function.
3555 -- IN:
3556 -- role - Role to send notification to
3557 -- msg_type - Message type
3558 -- msg_name - Message name
3559 -- due_date - Date due
3560 -- callback - Callback function
3561 -- context - Data for callback
3562 -- send_comment - Comment to add to notification
3563 -- priority - Notification priority
3564 -- group_id - Id of notification group
3565 -- (If null use not_id of notification sent)
3566 -- RETURNS:
3567 -- Notification id
3568 --
3569 function SendSingle(role in varchar2,
3570 msg_type in varchar2,
3571 msg_name in varchar2,
3572 due_date in date,
3573 callback in varchar2,
3574 context in varchar2,
3575 send_comment in varchar2,
3576 priority in number,
3577 group_id in number)
3578 return number is
3579 mailpref varchar2(8);
3580 nid pls_integer;
3581 attr_name varchar2(30);
3582 attr_type varchar2(8);
3583 attr_tvalue varchar2(4000);
3584 attr_nvalue number;
3585 attr_dvalue date;
3586 -- Bug 2376033
3587 attr_evalue wf_event_t;
3588 -- Bug 2283697
3589 l_parameterlist wf_parameter_list_t := wf_parameter_list_t();
3590 -- the following variables for Dynamic SQL
3591 sqlbuf varchar2(2000);
3592 l_from_role varchar2(320);
3593 l_send_comment varchar2(4000);
3594 l_send_source varchar2(30);
3595 l_charcheck boolean;
3596 -- Custom columns fix
3597 l_itemkey varchar2(240);
3598 l_userkey varchar2(240);
3599 l_itemtype varchar2(8);
3600 col1 pls_integer;
3601 col2 pls_integer;
3602 l_language varchar2(30);
3603 role_info_tbl wf_directory.wf_local_roles_tbl_type;
3604
3605 cursor message_attrs_cursor(msg_type varchar2, msg_name varchar2) is
3606 select NAME, TYPE, SUBTYPE, VALUE_TYPE,
3607 TEXT_DEFAULT, NUMBER_DEFAULT, DATE_DEFAULT
3608 from WF_MESSAGE_ATTRIBUTES
3609 where MESSAGE_TYPE = msg_type
3610 and MESSAGE_NAME = msg_name;
3611 begin
3612 -- Check role is valid and get mail preference
3613 mailpref := Wf_Notification.GetMailPreference(role, callback, context);
3614
3615 -- Create new nid and insert notification
3616 select WF_NOTIFICATIONS_S.NEXTVAL
3617 into nid
3618 from SYS.DUAL;
3619
3620 insert into WF_NOTIFICATIONS (
3621 NOTIFICATION_ID,
3622 GROUP_ID,
3623 MESSAGE_TYPE,
3624 MESSAGE_NAME,
3625 RECIPIENT_ROLE,
3626 ORIGINAL_RECIPIENT,
3627 STATUS,
3628 ACCESS_KEY,
3629 MAIL_STATUS,
3630 PRIORITY,
3631 BEGIN_DATE,
3632 END_DATE,
3633 DUE_DATE,
3634 -- USER_COMMENT,
3635 CALLBACK,
3636 CONTEXT
3637 ) select
3638 sendsingle.nid,
3639 nvl(sendsingle.group_id, sendsingle.nid),
3640 sendsingle.msg_type,
3641 sendsingle.msg_name,
3642 sendsingle.role,
3643 sendsingle.role,
3644 'OPEN',
3645 wf_core.random,
3646 decode(sendsingle.mailpref, 'QUERY', '',
3647 'SUMMARY', '',
3648 'SUMHTML', '',
3649 'DISABLED', 'FAILED',
3650 null, '', 'MAIL'),
3651 nvl(SendSingle.priority, WM.DEFAULT_PRIORITY),
3652 sysdate,
3653 null,
3654 sendsingle.due_date,
3655 -- sendsingle.send_comment,
3656 sendsingle.callback,
3657 sendsingle.context
3658 from WF_MESSAGES WM
3659 where WM.TYPE = sendsingle.msg_type
3660 and WM.NAME = sendsingle.msg_name;
3661
3662 -- Open and parse cursor for dynamic sql for getting attr values
3663 -- Bug 2376033 added event value in call to CB
3664 if (callback is not null) then
3665 -- ### Review Note 2
3666 l_charcheck := wf_notification_util.CheckIllegalChar(callback);
3667 --Throw the Illegal exception when the check fails
3668
3669
3670 -- BINDVAR_SCAN_IGNORE
3671 sqlbuf := 'begin '||callback||
3672 '(:p1, :p2, :p3, :p4, :p5, :p6, :p7, :p8); end;';
3673
3674 end if;
3675
3676 --
3677 -- Get and insert notification attributes
3678 --
3679 for message_attr_row in message_attrs_cursor(msg_type, msg_name) loop
3680
3681 attr_name := message_attr_row.name;
3682 attr_type := message_attr_row.type;
3683
3684 -- Set up default values for attributes
3685 if (message_attr_row.value_type = 'CONSTANT') then
3686 -- Constant default values used directly
3687 attr_tvalue := message_attr_row.text_default;
3688 attr_nvalue := message_attr_row.number_default;
3689 attr_dvalue := message_attr_row.date_default;
3690 else
3691 -- Defaults to be fetched from cb - default to null
3692 attr_tvalue := '';
3693 attr_nvalue := '';
3694 attr_dvalue := '';
3695 end if;
3696
3697 -- Bug 2376033 initialize event to fetch value using cb
3698 if (attr_type = 'EVENT') then
3699 wf_event_t.initialize(attr_evalue);
3700 end if;
3701
3702 -- If there is a cb defined and the default vtype is ITEMATTR
3703 -- then call the cb to fetch possible item attribute value.
3704 -- Bug 2376033 execute call to CB with event value
3705 if ((callback is not null) and
3706 (message_attr_row.value_type = 'ITEMATTR')) then
3707 begin
3708 execute immediate sqlbuf using
3709 in 'GET',
3710 in context,
3711 in message_attr_row.text_default,
3712 in attr_type,
3713 in out attr_tvalue,
3714 in out attr_nvalue,
3715 in out attr_dvalue,
3716 in out attr_evalue;
3717
3718 exception
3719 when others then
3720 -- Ignore cases where no attribute is defined
3721 if (wf_core.error_name = 'WFENG_ITEM_ATTR') then
3722 wf_core.clear;
3723 else
3724 -- Bug 2580807 call with original signature for backward
3725 -- compatibility
3726 -- ### Review Note 2 - callback is from table
3727 l_charcheck := wf_notification_util.CheckIllegalChar(callback);
3728 --Throw the Illegal exception when the check fails
3729
3730
3731 -- BINDVAR_SCAN_IGNORE
3732 sqlbuf := 'begin '||callback||
3733 '(:p1, :p2, :p3, :p4, :p5, :p6, :p7); end;';
3734 begin
3735 execute immediate sqlbuf using
3736 in 'GET',
3737 in context,
3738 in message_attr_row.text_default,
3739 in attr_type,
3740 in out attr_tvalue,
3741 in out attr_nvalue,
3742 in out attr_dvalue;
3743 exception
3744 when others then
3745 if (wf_core.error_name = 'WFENG_ITEM_ATTR') then
3746 wf_core.clear;
3747 else
3748 raise;
3749 end if;
3750 end;
3751 end if;
3752 end;
3753 end if;
3754
3755 --
3756 -- Insert notification attribute
3757 -- Bug 2376033 insert the event value
3758 --
3759 insert into WF_NOTIFICATION_ATTRIBUTES (
3760 NOTIFICATION_ID,
3761 NAME,
3762 TEXT_VALUE,
3763 NUMBER_VALUE,
3764 DATE_VALUE,
3765 EVENT_VALUE
3766 ) values (
3767 nid,
3768 attr_name,
3769 attr_tvalue,
3770 attr_nvalue,
3771 attr_dvalue,
3772 attr_evalue
3773 );
3774 end loop;
3775
3776 l_send_source := '';
3777 -- Notification sender comment
3778 if (send_comment is not null) then
3779 l_send_comment := send_comment;
3780 else
3781 -- Look for the sender comment in #SUBMIT_COMMENTS and store it
3782 -- only for the first time
3783 if (First_Execution(context)) then
3784 l_send_source := 'FIRST';
3785 begin
3786 l_send_comment := Wf_Notification.GetAttrText(nid, '#SUBMIT_COMMENTS');
3787 exception
3788 when others then
3789 if(Wf_Core.Error_Name = 'WFNTF_ATTR') then
3790 Wf_Core.Clear();
3791 l_send_comment := '';
3792 else
3793 raise;
3794 end if;
3795 end;
3796 end if;
3797 end if;
3798
3799 -- If #FROM_ROLE is defined, we will get the value in this attribute
3800 begin
3801 l_from_role := Wf_Notification.GetAttrText(nid, '#FROM_ROLE');
3802 exception
3803 when OTHERS then
3804 wf_core.clear;
3805 -- Check if the notification is sent under a valid Fwk Session
3806 l_from_role := Wfa_Sec.GetUser();
3807 end;
3808
3809 -- Use dummy user WF_SYSTEM as last resort
3810 if (l_from_role is null) then
3811 l_from_role := 'WF_SYSTEM';
3812 end if;
3813 Wf_Notification.SetComments(nid, l_from_role, role, 'SEND', l_send_source, l_send_comment);
3814
3815 -- Check for auto-routing of notification just sent
3816 Wf_Notification.Route(nid, 0);
3817
3818 -- Denormalize the Notification after auto-routing is done
3819 Wf_Notification.Denormalize_Notification(nid);
3820
3821 -- Denormalize custom columns
3822 -- Derive item type, item key and activity id from the context
3823 if context is not null then
3824 col1 := instr(context, ':', 1, 1);
3825 col2 := instr(context, ':', -1, 1);
3826
3827 l_itemtype := substr(substr(context, 1, col1-1),1,8);
3828 l_itemkey := substr(substr(context, col1+1, col2-col1-1),1,240);
3829 begin
3830 l_userkey := wf_engine.GetItemUserKey(l_itemtype,l_itemkey);
3831 exception
3832 when others then
3833 l_userkey := null;
3834 end;
3835 else
3836 l_itemkey := null;
3837 l_userkey := null;
3838 end if;
3839 wf_notification.denormalize_columns_internal(l_itemkey, l_userkey, nid);
3840
3841
3842 -- DL: Move this to be the last step before returning the nid
3843 -- The recipient_role could be updated during auto-routing
3844 -- EnqueueNotification maybe able to take advantage of
3845 -- denormalization in the future.
3846 -- Push the notification to the outbound queue
3847 -- Enqueuing has been moved to a subscription for forward
3848 -- compatability. The subscription need only be enabled to use
3849 -- the older mailer. The subscription will call the
3850 -- wf_xml.enqueueNotification API.
3851 -- wf_xml.EnqueueNotification(nid);
3852
3853 --Bug 2283697
3854 --To raise an EVENT whenever DML operation is performed on
3855 --WF_NOTIFICATIONS and WF_NOTIFICATION_ATTRIBUTES table.
3856 wf_event.AddParameterToList('NOTIFICATION_ID',nid,l_parameterlist);
3857 wf_event.AddParameterToList('ROLE',role,l_parameterlist);
3858 wf_event.AddParameterToList('GROUP_ID',nvl(group_id,nid),l_parameterlist);
3859
3860 wf_event.addParameterToList('Q_CORRELATION_ID', sendsingle.msg_type||':'||
3861 sendsingle.msg_name, l_parameterlist);
3862
3863 Wf_Directory.GetRoleInfo2(sendsingle.role, role_info_tbl);
3864 l_language := role_info_tbl(1).language;
3865
3866 select code into l_language from wf_languages where nls_language = l_language;
3867
3868 -- AppSearch
3869 wf_event.AddParameterToList('OBJECT_NAME',
3870 'oracle.apps.fnd.wf.worklist.server.AllNotificationsVO', l_parameterlist);
3871 wf_event.AddParameterToList('CHANGE_TYPE', 'INSERT',l_parameterlist);
3872 wf_event.AddParameterToList('ID_TYPE', 'PK', l_parameterlist);
3873 wf_event.addParameterToList('PK_NAME_1', 'NOTIFICATION_ID',l_parameterlist);
3874 wf_event.addParameterToList('PK_VALUE_1', nid, l_parameterlist);
3875 wf_event.addParameterToList('PK_NAME_2', 'LANGUAGE',l_parameterlist);
3876 wf_event.addParameterToList('PK_VALUE_2', l_language, l_parameterlist);
3877
3878
3879
3880 --Raise the event
3881 if wf_event.phase_maxthreshold is null then
3882 -- means deferred mode , avoid synchronous processing of Send event
3883 wf_event.SetDispatchMode('ASYNC');
3884
3885 wf_event.Raise(p_event_name => 'oracle.apps.wf.notification.send',
3886 p_event_key => to_char(nid),
3887 p_parameters => l_parameterlist);
3888
3889 wf_event.phase_maxthreshold := null;
3890 else
3891 wf_event.Raise(p_event_name => 'oracle.apps.wf.notification.send',
3892 p_event_key => to_char(nid),
3893 p_parameters => l_parameterlist);
3894
3895 end if;
3896
3897 return (nid);
3898
3899 exception
3900 when others then
3901 wf_core.context('Wf_Notification', 'SendSingle', role, msg_type,
3902 msg_name, due_date, callback);
3903 raise;
3904 end SendSingle;
3905
3906 --
3907 -- Send
3908 -- Send the role the specified message.
3909 -- Insert a single notification in the notifications table, and set
3910 -- the default send and respond attributes for the notification.
3911 -- IN:
3912 -- role - Role to send notification to
3913 -- msg_type - Message type
3914 -- msg_name - Message name
3915 -- due_date - Date due
3916 -- callback - Callback function
3917 -- context - Data for callback
3918 -- send_comment - Comment to add to notification
3919 -- priority - Notification priority
3920 -- RETURNS:
3921 -- Notification ID
3922 --
3923 function Send(role in varchar2,
3924 msg_type in varchar2,
3925 msg_name in varchar2,
3926 due_date in date,
3927 callback in varchar2,
3928 context in varchar2,
3929 send_comment in varchar2,
3930 priority in number)
3931 return number is
3932 dummy pls_integer;
3933 nid pls_integer;
3934 rorig_system varchar2(30);
3935 rorig_system_id pls_integer;
3936 --Bug 4050078
3937 itemtype varchar2(8);
3938 itemkey varchar2(240);
3939 actid number;
3940 col1 pls_integer;
3941 col2 pls_integer;
3942 prev_nid pls_integer;
3943 begin
3944 if ((role is null) or (msg_type is null) or (msg_name is null)) then
3945 wf_core.token('ROLE', role);
3946 wf_core.token('TYPE', msg_type);
3947 wf_core.token('NAME', msg_name);
3948 wf_core.raise('WFSQL_ARGS');
3949 end if;
3950
3951 -- Check message is valid
3952 begin
3953 select 1 into dummy from sys.dual where exists
3954 (select null
3955 from WF_MESSAGES M
3956 where M.TYPE = msg_type
3957 and M.NAME = msg_name);
3958 exception
3959 when no_data_found then
3960 wf_core.token('TYPE', msg_type);
3961 wf_core.token('NAME', msg_name);
3962 wf_core.raise('WFNTF_MESSAGE');
3963 end;
3964
3965 Wf_Directory.GetRoleOrigSysInfo(role,rorig_system,rorig_system_id);
3966
3967 -- if ORIG_SYSTEM is null, there is no data found for this role
3968 if (rorig_system is null) then
3969 wf_core.token('ROLE', role);
3970 wf_core.raise('WFNTF_ROLE');
3971 end if;
3972
3973 -- Call SendSingle to complete notification,
3974 -- using group_id = null to create group of one.
3975 nid := SendSingle(role, msg_type, msg_name, due_date, callback,
3976 context, send_comment, priority, null);
3977
3978 -- Update action history of notifications in approval chain
3979 -- Bug 4050078
3980 begin
3981 -- Derive item type, item key and activity id from the context
3982 validate_context(context, itemtype, itemkey, actid);
3983 -- Bug 7914921. The context not always comes in format itemtype:itemkey:actid
3984 if (itemtype is not null AND itemkey is not null AND actid is not null) then
3985 SELECT wn.notification_id
3986 INTO prev_nid
3987 FROM wf_notifications wn,
3988 wf_comments wc
3989 WHERE
3990 EXISTS ( SELECT /*+ NO_UNNEST */ 'x'
3991 FROM wf_item_activity_statuses_h wiash
3992 WHERE wiash.notification_id= wn.notification_id
3993 AND wiash.item_type = wn.message_type
3994 AND wiash.item_type = itemtype
3995 AND wiash.item_key = itemkey
3996 AND wiash.process_activity = actid)
3997 AND wn.status = 'CLOSED'
3998 AND wn.notification_id = wc.notification_id
3999 AND wc.to_role = 'WF_SYSTEM'
4000 AND wc.action_type = 'RESPOND';
4001
4002 UPDATE wf_comments
4003 SET to_role = role,
4004 to_user = nvl(Wf_Directory.GetRoleDisplayname(role), role)
4005 WHERE notification_id = prev_nid
4006 AND to_role = 'WF_SYSTEM'
4007 AND action_type = 'RESPOND';
4008 end if;
4009 exception
4010 when no_data_found then
4011 null;
4012 end;
4013
4014 return (nid);
4015 exception
4016 when others then
4017 wf_core.context('Wf_Notification', 'Send', role, msg_type,
4018 msg_name, due_date, callback);
4019 raise;
4020 end Send;
4021
4022 --
4023 -- SendGroup
4024 -- Send the role users the specified message.
4025 -- Send a separate notification to every user assigned to the role.
4026 -- IN:
4027 -- role - Role of users to send notification to
4028 -- msg_type - Message type
4029 -- msg_name - Message name
4030 -- due_date - Date due
4031 -- callback - Callback function
4032 -- context - Data for callback
4033 -- send_comment - Comment to add to notification
4034 -- priority - Notification priority
4035 -- RETURNS:
4036 -- Group ID - Id of notification group
4037 --
4038 function SendGroup(role in varchar2,
4039 msg_type in varchar2,
4040 msg_name in varchar2,
4041 due_date in date,
4042 callback in varchar2,
4043 context in varchar2,
4044 send_comment in varchar2,
4045 priority in number)
4046 return number is
4047 dummy pls_integer;
4048 nid pls_integer;
4049 gid pls_integer;
4050
4051 rorig_system varchar2(30);
4052 rorig_system_id pls_integer;
4053
4054 cursor role_users_curs is
4055 select WUR.USER_NAME
4056 from WF_USER_ROLES WUR
4057 where WUR.ROLE_ORIG_SYSTEM = rorig_system
4058 and WUR.ROLE_ORIG_SYSTEM_ID = rorig_system_id
4059 and WUR.ROLE_NAME = role;
4060
4061 begin
4062 if ((role is null) or (msg_type is null) or (msg_name is null)) then
4063 wf_core.token('ROLE', role);
4064 wf_core.token('TYPE', msg_type);
4065 wf_core.token('NAME', msg_name);
4066 wf_core.raise('WFSQL_ARGS');
4067 end if;
4068
4069 -- Check message is valid
4070 begin
4071 select 1 into dummy from sys.dual where exists
4072 (select null
4073 from WF_MESSAGES M
4074 where M.TYPE = msg_type
4075 and M.NAME = msg_name);
4076 exception
4077 when no_data_found then
4078 wf_core.token('TYPE', msg_type);
4079 wf_core.token('NAME', msg_name);
4080 wf_core.raise('WFNTF_MESSAGE');
4081 end;
4082
4083 -- Get the orig system ids for the role.
4084 -- Do this instead of using role_name directly so that indexes on
4085 -- the original tables are used when selecting through the view.
4086 Wf_Directory.GetRoleOrigSysInfo(role,rorig_system,rorig_system_id);
4087
4088 -- if ORIG_SYSTEM is null, there is no data found for this role
4089 if (rorig_system is null) then
4090 wf_core.token('ROLE', role);
4091 wf_core.raise('WFNTF_ROLE');
4092 end if;
4093
4094 -- Loop through users of role, sending notification to each one.
4095 gid := '';
4096 for user in role_users_curs loop
4097 -- Send Notification to only active users - Bug 7413050
4098 if Wf_Directory.UserActive(user.user_name) then
4099 -- Call SendSingle to complete notification,
4100 nid := SendSingle(user.user_name, msg_type, msg_name, due_date, callback,
4101 context, send_comment, priority, gid);
4102
4103 -- Use nid of the first notification as group id for the rest.
4104 if (gid is null) then
4105 gid := nid;
4106 end if;
4107 end if;
4108 end loop;
4109
4110 -- Raise error if no users found for role.
4111 -- Most probable cause is role argument is invalid.
4112 if (gid is null) then
4113 wf_core.token('ROLE', role);
4114 wf_core.raise('WFNTF_ROLE');
4115 end if;
4116
4117 return (gid);
4118 exception
4119 when others then
4120 wf_core.context('Wf_Notification', 'SendGroup', role, msg_type,
4121 msg_name, due_date, callback);
4122 raise;
4123 end SendGroup;
4124
4125 --
4126 -- ForwardInternal (PRIVATE)
4127 -- Forward a notification, identified by NID to another user. Validate
4128 -- the user and Return error messages ...
4129 -- Depend on which mode 'FORWARD' or 'TRANSFER', it calls the call-back
4130 -- function differently.
4131 -- IN:
4132 -- nid - Notification Id
4133 -- new_role - Role to forward notification to
4134 -- fmode - Callback mode: 'FORWARD', 'TRANSFER'
4135 -- forward_comment - comment to append to notification
4136 -- user - role who perform this action if provided
4137 -- cnt - count for recursive purpose
4138 -- action_source - Source from where the action is performed
4139 --
4140 procedure ForwardInternal(
4141 nid in number,
4142 new_role in varchar2,
4143 fmode in varchar2,
4144 forward_comment in varchar2,
4145 user in varchar2,
4146 cnt in number,
4147 action_source in varchar2)
4148 is
4149 mailpref varchar2(8);
4150 newcomment varchar2(4000);
4151 old_role varchar2(320);
4152 old_origrole varchar2(320);
4153 status varchar2(8);
4154 cb varchar2(240);
4155 context varchar2(2000);
4156 sqlbuf varchar2(2000);
4157 tvalue varchar2(4000);
4158 nvalue number;
4159 dvalue date;
4160
4161
4162 -- Bug 2331070
4163 l_from_role varchar2(320);
4164
4165 --Bug 2283697
4166 l_parameterlist wf_parameter_list_t := wf_parameter_list_t();
4167
4168 --Bug 2474770
4169 l_more_info_role VARCHAR2(320);
4170 l_dispname VARCHAR2(360);
4171 l_username varchar2(320);
4172 l_found boolean;
4173 l_dummy varchar2(1);
4174
4175 l_language varchar2(30);
4176 l_recipient_role varchar2(320);
4177 role_info_tbl wf_directory.wf_local_roles_tbl_type;
4178
4179 -- Bug 3827935
4180 l_charcheck boolean;
4181
4182 -- <bug 7641725>
4183 l_msgType varchar2(8);
4184 l_msgName varchar2(30);
4185
4186 begin
4187 if ((nid is null) or (new_role is null)) then
4188 wf_core.token('NID', to_char(nid));
4189 wf_core.token('NEW_ROLE', new_role);
4190 wf_core.raise('WFSQL_ARGS');
4191 end if;
4192
4193 -- Check the notification exists and is open
4194 begin
4195 --Bug 2474770
4196 --Obtain the more_info_role in addition
4197 select WN.STATUS, WN.CALLBACK, WN.CONTEXT, -- , WN.USER_COMMENT
4198 WN.RECIPIENT_ROLE, WN.ORIGINAL_RECIPIENT,WN.MORE_INFO_ROLE,
4199 WN.FROM_ROLE
4200 , wn.message_type, wn.message_name -- <7641725>
4201 into status, cb, context, old_role, -- newcomment,
4202 old_origrole,l_more_info_role,l_from_role
4203 , l_msgType, l_msgName
4204 from WF_NOTIFICATIONS WN
4205 where WN.NOTIFICATION_ID = nid
4206 for update nowait;
4207 exception
4208 when no_data_found then
4209 wf_core.token('NID', to_char(nid));
4210 wf_core.raise('WFNTF_NID');
4211 end;
4212 if (status <> 'OPEN') then
4213 wf_core.token('NID', to_char(nid));
4214 wf_core.raise('WFNTF_NID_OPEN');
4215 end if;
4216
4217 -- If we are in a different Fwk session, need to clear Workflow PLSQL state
4218 if (not Wfa_Sec.CheckSession) then
4219 Wf_Global.Init;
4220 end if;
4221
4222 -- Check role is valid and get mail preference
4223 begin
4224 mailpref := Wf_Notification.GetMailPreference(new_role, cb, context);
4225 exception
4226 when others then
4227 wf_core.token('ROLE', new_role);
4228 if (fmode = 'FORWARD') then
4229 wf_core.raise('WFNTF_DELEGATE_FAIL');
4230 elsif (fmode = 'TRANSFER') then
4231 wf_core.raise('WFNTF_TRANSFER_FAIL');
4232 end if;
4233 end;
4234
4235 -- Bug 3065814
4236 -- Set the global context variables to appropriate values for this mode
4237 if (forward_comment is not null and
4238 substr(forward_comment, 1, 6) = 'email:') then
4239 -- If responded through mail then get the username from the email
4240 GetUserFromEmail(forward_comment, l_username, l_dispname, l_found);
4241 if (l_found) then
4242 g_context_user := l_username;
4243 else
4244 g_context_user := 'email:'||l_username;
4245 end if;
4246 else
4247 if (action_source = 'WA') then
4248 -- notification is reassigned by a proxy who is logged in and has a Fwk session
4249 g_context_proxy := Wfa_Sec.GetUser();
4250 g_context_user := ForwardInternal.user;
4251 elsif (action_source = 'RULE') then
4252 -- notification is reassigned by Routing Rule, the context user should be the user
4253 -- to whom the rule belongs and to whom the notification is reassigned
4254 g_context_proxy := null;
4255 g_context_user := ForwardInternal.user;
4256 else
4257 -- notification is reassigned by the recipient
4258 g_context_proxy := null;
4259 g_context_user := Wfa_Sec.GetUser();
4260 end if;
4261 end if;
4262
4263 g_context_user_comment := forwardinternal.forward_comment;
4264 g_context_recipient_role := old_role;
4265 g_context_original_recipient:= old_origrole;
4266 g_context_from_role := l_from_role;
4267 g_context_new_role := new_role;
4268 g_context_more_info_role := l_more_info_role;
4269
4270 -- Call the callback in whatever mode specified if callback is provided
4271 if (cb is not null) then
4272 tvalue := new_role;
4273 nvalue := nid;
4274 -- ### Review Note 2 - cb is from table
4275 -- BINDVAR_SCAN_IGNORE
4276 l_charcheck := wf_notification_util.CheckIllegalChar(cb);
4277 --Throw the Illegal exception when the check fails
4278
4279
4280 sqlbuf := 'begin '||cb||
4281 '(:p1, :p2, :p3, :p4, :p5, :p6, :p7); end;';
4282 execute immediate sqlbuf using
4283 in fmode,
4284 in context,
4285 in l_dummy,
4286 in l_dummy,
4287 in out tvalue,
4288 in out nvalue,
4289 in out dvalue;
4290
4291 end if;
4292
4293 -- Old style comment appending to existing user comment is obsolete
4294 -- Call SetComments with the required values. Also, the audit message
4295 -- is no longer available
4296
4297 -- CTILLEY: Bug 2331070.
4298 if (fmode = 'FORWARD') then
4299 l_from_role := nvl(user,old_role);
4300 else
4301 l_from_role := nvl(user,old_origrole);
4302 end if;
4303
4304 --Bug 2474770
4305 --If the transfer/delegate role is the same as the more_info_role
4306 --the more_info_role is set to null
4307 --Bug 2609352 - if the notification is reassigned the more_info_role
4308 --will be set to null.
4309 if (l_more_info_role is not NULL) then
4310 l_more_info_role := null;
4311 end if;
4312
4313 -- Finally, do the update.
4314 -- Reset the mail flag so mailer will look for it again.
4315 -- BUG 1772490 JWSMITH - added new access_key when fmode is transfer
4316
4317 -- BUG 2331070 CTILLEY - added update to FROM_ROLE
4318 -- Bug 2474770
4319 -- Update the more_info_role aswell
4320 if (fmode = 'TRANSFER') then
4321 update WF_NOTIFICATIONS set
4322 RECIPIENT_ROLE = ForwardInternal.new_role,
4323 ORIGINAL_RECIPIENT = decode(ForwardInternal.fmode,
4324 'TRANSFER', ForwardInternal.new_role,
4325 ORIGINAL_RECIPIENT),
4326 -- USER_COMMENT = ForwardInternal.newcomment,
4327 MAIL_STATUS = decode(ForwardInternal.mailpref,
4328 'QUERY', '',
4329 'SUMMARY', '',
4330 'SUMHTML', '',
4331 'DISABLED', 'FAILED',
4332 null, '', 'MAIL'),
4333 ACCESS_KEY = wf_core.random,
4334 FROM_ROLE = l_from_role,
4335 MORE_INFO_ROLE = l_more_info_role
4336 where NOTIFICATION_ID = nid;
4337
4338 Wf_Notification.SetComments(nid, l_from_role, new_role, 'TRANSFER',
4339 action_source, forward_comment);
4340
4341 else
4342 update WF_NOTIFICATIONS set
4343 RECIPIENT_ROLE = ForwardInternal.new_role,
4344 ORIGINAL_RECIPIENT = decode(ForwardInternal.fmode,
4345 'TRANSFER', ForwardInternal.new_role,
4346 ORIGINAL_RECIPIENT),
4347 -- USER_COMMENT = ForwardInternal.newcomment,
4348 MAIL_STATUS = decode(ForwardInternal.mailpref,
4349 'QUERY', '',
4350 'SUMMARY', '',
4351 'SUMHTML', '',
4352 'DISABLED', 'FAILED',
4353 null, '', 'MAIL'),
4354 FROM_ROLE = l_from_role,
4355 MORE_INFO_ROLE = l_more_info_role
4356 where NOTIFICATION_ID = nid;
4357
4358 Wf_Notification.SetComments(nid, l_from_role, new_role, 'DELEGATE',
4359 action_source, forward_comment);
4360 end if;
4361
4362 -- Pop any messages from then outbound queue
4363
4364 -- GK: 1636402: wf_xml.RemoveNotification is not necessary
4365 -- since the message is likely to be sent by the time the
4366 -- user goes in and does an action from the worklist.
4367 -- wf_xml.RemoveNotification(nid);
4368
4369 -- Check for auto-routing of notification just forwarded
4370 Wf_Notification.Route(nid, cnt);
4371
4372 -- Denormalize after all the routing is done
4373 if (cnt = 0) then
4374 Wf_Notification.Denormalize_Notification(nid);
4375
4376 -- Push the new notification to the queue
4377 -- The call to wf_xml.EnqueueNotification has been moved
4378 -- to an event subscription.
4379 -- wf_xml.EnqueueNotification(nid);
4380 end if;
4381
4382 --Bug 2283697
4383 --To raise an EVENT whenever DML operation is performed on
4384 --WF_NOTIFICATIONS and WF_NOTIFICATION_ATTRIBUTES table.
4385 wf_event.AddParameterToList('NOTIFICATION_ID',nid,l_parameterlist);
4386 wf_event.AddParameterToList('NEW_ROLE',new_role,l_parameterlist);
4387 wf_event.AddParameterToList('MODE',fmode,l_parameterlist);
4388 if (user is not null) then
4389 wf_event.AddParameterToList('USER',user,l_parameterlist);
4390 elsif (fmode = 'FORWARD') then
4391 wf_event.AddParameterToList('USER',old_role,l_parameterlist);
4392 else
4393 wf_event.AddParameterToList('USER',old_origrole,l_parameterlist);
4394 end if;
4395
4396 -- <7641725> we need to include Q_CORRELATION_ID parameter
4397 wf_event.addParameterToList('Q_CORRELATION_ID'
4398 , l_msgType||':'||l_msgName, l_parameterlist);
4399
4400 select WN.RECIPIENT_ROLE
4401 into l_recipient_role
4402 from WF_NOTIFICATIONS WN
4403 where WN.NOTIFICATION_ID = nid;
4404
4405 Wf_Directory.GetRoleInfo2(l_recipient_role, role_info_tbl);
4406 l_language := role_info_tbl(1).language;
4407
4408 select code into l_language from wf_languages where nls_language = l_language;
4409
4410 -- AppSearch
4411 wf_event.AddParameterToList('OBJECT_NAME',
4412 'oracle.apps.fnd.wf.worklist.server.AllNotificationsVO', l_parameterlist);
4413 wf_event.AddParameterToList('CHANGE_TYPE', 'INSERT',l_parameterlist);
4414 wf_event.AddParameterToList('ID_TYPE', 'PK', l_parameterlist);
4415 wf_event.addParameterToList('PK_NAME_1', 'NOTIFICATION_ID',l_parameterlist);
4416 wf_event.addParameterToList('PK_VALUE_1', nid, l_parameterlist);
4417 wf_event.addParameterToList('PK_NAME_2', 'LANGUAGE',l_parameterlist);
4418 wf_event.addParameterToList('PK_VALUE_2', l_language, l_parameterlist);
4419
4420
4421 --Raise the event
4422 wf_event.Raise(p_event_name => 'oracle.apps.wf.notification.reassign',
4423 p_event_key => to_char(nid),
4424 p_parameters => l_parameterlist);
4425
4426 exception
4427 when others then
4428 wf_core.context('Wf_Notification', 'ForwardInternal', to_char(nid),
4429 new_role, fmode, forward_comment);
4430 raise;
4431 end ForwardInternal;
4432
4433 --
4434 -- Forward
4435 -- Forward a notification, identified by NID to another user. Validate
4436 -- the user and Return error messages ...
4437 -- IN:
4438 -- nid - Notification Id
4439 -- new_role - Role to forward notification to
4440 -- forward_comment - comment to append to notification
4441 -- user - role who perform this action if provided
4442 -- cnt - count for recursive purpose
4443 -- action_source - Source from where the action is performed
4444 --
4445 procedure Forward(nid in number,
4446 new_role in varchar2,
4447 forward_comment in varchar2,
4448 user in varchar2,
4449 cnt in number,
4450 action_source in varchar2)
4451 is
4452 begin
4453 ForwardInternal(nid, new_role, 'FORWARD', forward_comment, user, cnt, action_source);
4454 exception
4455 when others then
4456 wf_core.context('Wf_Notification', 'Forward', to_char(nid),
4457 new_role, forward_comment);
4458 -- This call is for enhanced error handling with respect to OAFwk
4459 wf_notification.SetUIErrorMessage;
4460 raise;
4461 end Forward;
4462
4463 --
4464 -- Transfer
4465 -- Transfer a notification, identified by NID to another user. Validate
4466 -- the user and Return error messages ...
4467 -- IN:
4468 -- nid - Notification Id
4469 -- new_role - Role to transfer notification to
4470 -- forward_comment - comment to append to notification
4471 -- user - role who perform this action if provided
4472 -- cnt - count for recursive purpose
4473 -- action_source - Source from where the action is performed
4474 --
4475 procedure Transfer(nid in number,
4476 new_role in varchar2,
4477 forward_comment in varchar2,
4478 user in varchar2,
4479 cnt in number,
4480 action_source in varchar2)
4481 is
4482 begin
4483 ForwardInternal(nid, new_role, 'TRANSFER', forward_comment, user, cnt, action_source);
4484 exception
4485 when others then
4486 wf_core.context('Wf_Notification', 'Transfer', to_char(nid),
4487 new_role, forward_comment);
4488 -- This call is for enhanced error handling with respect to OAFwk
4489 wf_notification.SetUIErrorMessage;
4490 raise;
4491 end Transfer;
4492
4493 --
4494 -- CancelSingle (PRIVATE)
4495 -- Cancel a single notification.
4496 -- Called by Cancel and CancelGroup public functions.
4497 -- Argument error checking should be done by Cancel and CancelGroup before
4498 -- calling this function.
4499 -- IN:
4500 -- nid - Notification Id
4501 -- role - Role notification is sent to
4502 -- cancel_comment - Comment to append to notification
4503 --
4504 procedure CancelSingle(nid in number,
4505 role in varchar2,
4506 cancel_comment in varchar2,
4507 timeout in boolean)
4508 is
4509 mailpref varchar2(8);
4510 newcomment varchar2(4000);
4511 status varchar2(8);
4512 cb varchar2(240);
4513 context varchar2(2000);
4514 dummy pls_integer;
4515
4516 --Bug 2283697
4517 l_parameterlist wf_parameter_list_t := wf_parameter_list_t();
4518
4519 --Bug 2373925
4520 l_mail varchar2(4);
4521 l_from_role varchar2(320);
4522 l_action varchar2(30);
4523 l_send_cancel varchar2(10);
4524 l_msg_type varchar2(8);
4525 l_msg_name varchar2(30);
4526 l_language varchar2(30);
4527 begin
4528
4529 -- Check the notification exists and is open
4530 begin
4531 select WN.STATUS, WN.CALLBACK, WN.CONTEXT, WN.MESSAGE_TYPE, WN.MESSAGE_NAME, WN.LANGUAGE
4532 into status, cb, context, l_msg_type, l_msg_name, l_language
4533 from WF_NOTIFICATIONS WN
4534 where WN.NOTIFICATION_ID = nid
4535 for update nowait;
4536 exception
4537 when no_data_found then
4538 wf_core.token('NID', to_char(nid));
4539 wf_core.raise('WFNTF_NID');
4540 end;
4541 if (status <> 'OPEN') then
4542 wf_core.token('NID', to_char(nid));
4543 wf_core.raise('WFNTF_NID_OPEN');
4544 end if;
4545
4546 -- Check role is valid and get mail preference
4547 mailpref := Wf_Notification.GetMailPreference(role, cb, context);
4548
4549 -- If no responses expected, then do not mail cancel notice
4550 -- regardless of role notification_preference setting.
4551 begin
4552 select 1 into dummy from sys.dual where exists
4553 (select NULL
4554 from WF_NOTIFICATIONS WN, WF_MESSAGE_ATTRIBUTES WMA
4555 where WN.NOTIFICATION_ID = nid
4556 and WN.MESSAGE_NAME = WMA.MESSAGE_NAME
4557 and WN.MESSAGE_TYPE = WMA.MESSAGE_TYPE
4558 and WMA.SUBTYPE = 'RESPOND');
4559
4560 exception
4561 when no_data_found then
4562 -- No responses, set mailpref to not mail cancel notice regardless.
4563 mailpref := 'QUERY';
4564 end;
4565
4566 -- if mailer config parameter SEND_CANCELED_EMAIL is set to N, no e-mails
4567 -- are sent for canceled notifications
4568 l_send_cancel := wf_mailer_parameter.GetValueForCorr(pCorrId => l_msg_type || ':'|| l_msg_name,
4569 pName => 'SEND_CANCELED_EMAIL');
4570 if (l_send_cancel = 'Y') then
4571 l_mail := 'MAIL';
4572 else
4573 l_mail := '';
4574 end if;
4575
4576 update WF_NOTIFICATIONS set
4577 STATUS = 'CANCELED',
4578 END_DATE = sysdate,
4579 -- USER_COMMENT = CancelSingle.newcomment,
4580 MAIL_STATUS = decode(MAIL_STATUS,
4581 'ERROR', 'ERROR',
4582 -- if this was never sent, dont bother sending cancelation
4583 'MAIL', '',
4584 decode(CancelSingle.mailpref,
4585 'QUERY', '',
4586 'SUMMARY', '',
4587 'SUMHTML', '',
4588 'DISABLED', 'FAILED',
4589 null, '', l_mail))
4590 where NOTIFICATION_ID = nid;
4591
4592 if (timeout) then
4593 l_action := 'TIMEOUT';
4594 l_from_role := role;
4595 else
4596 l_action := 'CANCEL';
4597 l_from_role := Wfa_Sec.GetUser();
4598 end if;
4599
4600 if (l_from_role is null) then
4601 l_from_role := 'WF_SYSTEM';
4602 end if;
4603 Wf_Notification.SetComments(nid, l_from_role, 'WF_SYSTEM', l_action, null, newcomment);
4604
4605 -- GK: 1636402: wf_xml.RemoveNotification is not necessary
4606 -- since the message is likely to be sent by the time the
4607 -- user goes in and does an action from the worklist.
4608 -- wf_xml.RemoveNotification(nid);
4609 -- SJM: 2122556 - Cancelled notifications are not being sent out
4610 -- becuase they are not being enqueued.
4611 -- The call to wf_xml.EnqueueNotification has been moved to an
4612 -- event subscription
4613 -- wf_xml.EnqueueNotification(nid);
4614
4615 --Bug 2283697
4616 --To raise an EVENT whenever DML operation is performed on
4617 --WF_NOTIFICATIONS and WF_NOTIFICATION_ATTRIBUTES table.
4618 wf_event.AddParameterToList('NOTIFICATION_ID',nid,l_parameterlist);
4619 wf_event.AddParameterToList('ROLE',role,l_parameterlist);
4620 wf_event.addParameterToList('Q_CORRELATION_ID', l_msg_type || ':'||
4621 l_msg_name, l_parameterlist);
4622
4623
4624 -- AppSearch
4625 wf_event.AddParameterToList('OBJECT_NAME',
4626 'oracle.apps.fnd.wf.worklist.server.AllNotificationsVO', l_parameterlist);
4627 wf_event.AddParameterToList('CHANGE_TYPE', 'INSERT',l_parameterlist);
4628 wf_event.AddParameterToList('ID_TYPE', 'PK', l_parameterlist);
4629 wf_event.addParameterToList('PK_NAME_1', 'NOTIFICATION_ID',l_parameterlist);
4630 wf_event.addParameterToList('PK_VALUE_1', nid, l_parameterlist);
4631 wf_event.addParameterToList('PK_NAME_2', 'LANGUAGE',l_parameterlist);
4632 wf_event.addParameterToList('PK_VALUE_2', l_language, l_parameterlist);
4633
4634 --Raise the event
4635 wf_event.Raise(p_event_name => 'oracle.apps.wf.notification.cancel',
4636 p_event_key => to_char(nid),
4637 p_parameters => l_parameterlist);
4638
4639
4640 exception
4641 when others then
4642 wf_core.context('Wf_Notification', 'CancelSingle', to_char(nid),
4643 role, cancel_comment);
4644 raise;
4645 end CancelSingle;
4646
4647 --
4648 -- Cancel
4649 -- Cancel a single notification.
4650 -- IN:
4651 -- nid - Notification Id
4652 -- cancel_comment - Comment to append to notification
4653 --
4654 procedure Cancel(nid in number,
4655 cancel_comment in varchar2)
4656 is
4657 status varchar2(8);
4658 role varchar2(320);
4659 begin
4660 if (nid is null) then
4661 wf_core.token('NID', to_char(nid));
4662 wf_core.raise('WFSQL_ARGS');
4663 end if;
4664
4665 -- Check the notification exists and is open
4666 begin
4667 select STATUS, RECIPIENT_ROLE
4668 into status, role
4669 from WF_NOTIFICATIONS
4670 where NOTIFICATION_ID = nid
4671 for update nowait;
4672 exception
4673 when no_data_found then
4674 wf_core.token('NID', to_char(nid));
4675 wf_core.raise('WFNTF_NID');
4676 end;
4677
4678 if (status <> 'OPEN') then
4679 wf_core.token('NID', to_char(nid));
4680 wf_core.raise('WFNTF_NID_OPEN');
4681 end if;
4682
4683 -- Call CancelSingle to complete cancellation of single notification
4684 CancelSingle(nid, role, cancel_comment, FALSE);
4685
4686 exception
4687 when others then
4688 wf_core.context('Wf_Notification', 'Cancel', to_char(nid), cancel_comment);
4689 raise;
4690 end Cancel;
4691
4692 --
4693 -- CancelGroup
4694 -- Cancel all notifications belonging to a notification group
4695 -- IN:
4696 -- gid - Notification group id
4697 -- cancel_comment - Comment to append to all notifications
4698 --
4699 procedure CancelGroup(gid in number,
4700 cancel_comment in varchar2,
4701 timeout in boolean)
4702 is
4703 -- Get all still open notifications in the group
4704 cursor group_curs is
4705 select NOTIFICATION_ID, RECIPIENT_ROLE
4706 from WF_NOTIFICATIONS
4707 where GROUP_ID = gid
4708 and status = 'OPEN'
4709 for update nowait;
4710
4711 begin
4712 if (gid is null) then
4713 wf_core.token('NID', to_char(gid));
4714 wf_core.raise('WFSQL_ARGS');
4715 end if;
4716
4717 -- Cancel all open notifications in this group
4718 for notice in group_curs loop
4719 -- Call CancelSingle to complete cancellation of single notification
4720 CancelSingle(notice.notification_id, notice.recipient_role,
4721 cancel_comment, timeout);
4722 end loop;
4723 exception
4724 when others then
4725 wf_core.context('Wf_Notification', 'CancelGroup', to_char(gid),
4726 cancel_comment);
4727 raise;
4728 end CancelGroup;
4729
4730 --
4731 -- Respond
4732 -- Respond to a notification.
4733 -- IN:
4734 -- nid - Notification Id
4735 -- respond_comment - Comment to append to notification
4736 -- responder - User or role responding to notification
4737 -- action_source - Source from where the action is performed
4738 --
4739 procedure Respond(nid in number,
4740 respond_comment in varchar2,
4741 responder in varchar2,
4742 action_source in varchar2)
4743 is
4744 callback varchar2(240);
4745 context varchar2(2000);
4746 status varchar2(8);
4747 newcomment varchar2(4000);
4748
4749 -- Dynamic sql stuff
4750 sqlbuf varchar2(2000);
4751
4752 --Bug 2283697
4753 l_parameterlist wf_parameter_list_t := wf_parameter_list_t();
4754
4755
4756 cursor notification_attrs_cursor(nid number) is
4757 select WNA.NAME, WMA.TYPE, WNA.TEXT_VALUE, WNA.NUMBER_VALUE,
4758 WNA.DATE_VALUE
4759 from WF_NOTIFICATION_ATTRIBUTES WNA,
4760 WF_MESSAGE_ATTRIBUTES WMA,
4761 WF_NOTIFICATIONS WN
4762 where WNA.NOTIFICATION_ID = nid
4763 and WN.NOTIFICATION_ID = WNA.NOTIFICATION_ID
4764 and WN.MESSAGE_TYPE = WMA.MESSAGE_TYPE
4765 and WN.MESSAGE_NAME = WMA.MESSAGE_NAME
4766 and WNA.NAME = WMA.NAME
4767 and WMA.SUBTYPE = 'RESPOND';
4768
4769 aname varchar2(30);
4770 atype varchar2(8);
4771 tvalue varchar2(4000);
4772 nvalue number;
4773 dvalue date;
4774
4775 response_found boolean;
4776
4777 -- kma bug2376058 digital signature support
4778 proxyuser varchar2(4000);
4779
4780 --Bug 3065814
4781 l_recip_role varchar2(320);
4782 l_orig_recip_role varchar2(320);
4783 l_more_info_role varchar2(320);
4784 l_from_role varchar2(320);
4785 l_dispname VARCHAR2(360);
4786 l_responder varchar2(320);
4787 l_found boolean;
4788 l_dummy varchar2(1);
4789
4790 l_language varchar2(30);
4791
4792 --Bug 3827935
4793 l_charcheck boolean;
4794
4795 begin
4796 if (nid is null) then
4797 wf_core.token('NID', to_char(nid));
4798 wf_core.raise('WFSQL_ARGS');
4799 end if;
4800
4801 -- kma bug2376058 digital signature support
4802 begin
4803 proxyuser := Wf_Notification.GetAttrText(nid, '#WF_PROXIED_VIA');
4804 exception
4805 when others then
4806 if (wf_core.error_name = 'WFNTF_ATTR') then
4807 -- Pass null result if no result attribute.
4808 wf_core.clear;
4809 proxyuser := '';
4810 else
4811 raise;
4812 end if;
4813 end;
4814 if ((proxyuser is not null) and (proxyuser <> '') and
4815 ((responder is null) or
4816 ((responder is not null) and (proxyuser <> responder)))) then
4817 wf_core.token('NID', to_char(nid));
4818 wf_core.raise('WFNTF_DIGSIG_USER_MISMATCH');
4819 end if;
4820
4821 -- bug 2698999 Checking if ntf's signature requirements are met
4822 if (NOT Wf_Notification.NtfSignRequirementsMet(nid)) then
4823 wf_core.token('NID', to_char(nid));
4824 wf_core.raise('WFNTF_NOT_SIGNED');
4825 end if;
4826
4827 -- Get callback, check for valid notification id.
4828 begin
4829 select N.CALLBACK, N.CONTEXT, N.STATUS, N.USER_COMMENT,
4830 N.RECIPIENT_ROLE, N.ORIGINAL_RECIPIENT,N.MORE_INFO_ROLE, N.FROM_ROLE, N.LANGUAGE
4831 into respond.callback, respond.context, respond.status, newcomment,
4832 l_recip_role,l_orig_recip_role,l_more_info_role, l_from_role, l_language
4833 from WF_NOTIFICATIONS N
4834 where N.NOTIFICATION_ID = nid
4835 for update nowait;
4836 exception
4837 when no_data_found then
4838 wf_core.token('NID', to_char(nid));
4839 wf_core.raise('WFNTF_NID');
4840 end;
4841
4842 -- Check notification is open
4843 if (status <> 'OPEN') then
4844 wf_core.token('NID', to_char(nid));
4845 wf_core.raise('WFNTF_NID_OPEN');
4846 end if;
4847
4848 -- If we are in a different Fwk session, need to clear Workflow PLSQL state
4849 if (not Wfa_Sec.CheckSession) then
4850 Wf_Global.Init;
4851 end if;
4852
4853 -- Bug 3065814
4854 -- Set the global context variables to appropriate values for this mode
4855 if (respond.responder is not null and
4856 substr(respond.responder, 1, 6) = 'email:') then
4857 -- If responded through mail then get the username from email
4858 GetUserfromEmail(respond.responder, l_responder, l_dispname, l_found);
4859 if (not l_found) then
4860 l_responder := 'email:' || l_responder;
4861 end if;
4862 else
4863 if (action_source = 'WA') then
4864 -- notification is responded by a proxy who is logged in and has a Fwk session
4865 g_context_proxy := Wfa_Sec.GetUser();
4866 l_responder := respond.responder;
4867 elsif (action_source = 'RULE') then
4868 -- notification is responded by Routing Rule, the context user should be the user
4869 -- to whom the rule belongs who is actually responding to the notification
4870 g_context_proxy := null;
4871 l_responder := respond.responder;
4872 else
4873 -- notification is responded by the recipient.
4874 -- responder should be respond.responder
4875 g_context_proxy := null;
4876 l_responder := respond.responder;
4877 end if;
4878 end if;
4879
4880 -- Set the approrpiate responder to context user
4881 g_context_user := l_responder;
4882
4883 if respond.respond_comment is null then
4884 g_context_user_comment := Wf_Notification.GetAttrText(nid,'WF_NOTE',TRUE);
4885 else
4886 g_context_user_comment := respond.respond_comment;
4887 end if;
4888 g_context_recipient_role := l_recip_role;
4889 g_context_original_recipient:= l_orig_recip_role;
4890 g_context_from_role := l_from_role;
4891 g_context_new_role := '';
4892 g_context_more_info_role := l_more_info_role;
4893
4894 -- Call the callback in VALIDATE mode to execute the post notification
4895 -- function to perform some custom validation and reject the response by
4896 -- raising exception. If validation is already done in RESPOND mode, it
4897 -- can stay there... VALIDATE mode can be called back from outside of
4898 -- notification code also before calling the Wf_Notification.Respond API
4899 if (callback is not null) then
4900 tvalue := respond.responder;
4901 nvalue := nid;
4902 -- ### Review Note 2 - callback is from table
4903 -- Check for bug#3827935
4904 l_charcheck := wf_notification_util.CheckIllegalChar(callback);
4905 --Throw the Illegal exception when the check fails
4906
4907
4908 -- BINDVAR_SCAN_IGNORE
4909 sqlbuf := 'begin '||callback||
4910 '(:p1, :p2, :p3, :p4, :p5, :p6, :p7); end;';
4911 execute immediate sqlbuf using
4912 in 'VALIDATE',
4913 in context,
4914 in l_dummy,
4915 in l_dummy,
4916 in out tvalue,
4917 in out nvalue,
4918 in out dvalue;
4919
4920 -- Call the callback in RESPOND mode to perform the post-notification
4921 -- callback if there is one. Note this should be before the response is
4922 -- actually processed to give the callback a chance to reject the response
4923 -- by raising an exception.
4924
4925 -- ### Review Note 2 - callback is from table
4926 -- BINDVAR_SCAN_IGNORE
4927 -- sqlbuf := 'begin '||callback||
4928 -- '(:p1, :p2, :p3, :p4, :p5, :p6, :p7); end;';
4929 execute immediate sqlbuf using
4930 in 'RESPOND',
4931 in context,
4932 in l_dummy,
4933 in l_dummy,
4934 in out tvalue,
4935 in out nvalue,
4936 in out dvalue;
4937
4938 tvalue := '';
4939 nvalue := '';
4940 end if;
4941
4942 -- Append the respond_comment (if any) to the user_comment
4943 -- if (respond_comment is not null) then
4944 -- if (newcomment is not null) then
4945 -- newcomment := substrb(newcomment||wf_core.newline||
4946 -- respond_comment, 1, 4000);
4947 -- else
4948 -- newcomment := respond_comment;
4949 -- end if;
4950 -- end if;
4951
4952 -- Mark notification closed
4953 update WF_NOTIFICATIONS
4954 set STATUS = 'CLOSED',
4955 MAIL_STATUS = NULL,
4956 END_DATE = sysdate,
4957 -- RESPONDER = respond.responder
4958 -- For responses through e-mail, this helps strip off unwanted parts from e-mail like
4959 -- "John Doe" [email protected]> and have only email:[email protected]
4960 RESPONDER = l_responder
4961 -- USER_COMMENT = respond.newcomment
4962 where NOTIFICATION_ID = respond.nid;
4963
4964 -- responder should be the From role that appears in the action history
4965 Wf_Notification.SetComments(nid, l_responder, 'WF_SYSTEM', 'RESPOND',
4966 action_source, respond_comment);
4967
4968 --Bug 2283697
4969 --To raise an EVENT whenever DML operation is performed on
4970 --WF_NOTIFICATIONS and WF_NOTIFICATION_ATTRIBUTES table.
4971 wf_event.AddParameterToList('NOTIFICATION_ID',nid,l_parameterlist);
4972 wf_event.AddParameterToList('RESPONDER',respond.responder,l_parameterlist);
4973
4974 -- AppSearch
4975 wf_event.AddParameterToList('OBJECT_NAME',
4976 'oracle.apps.fnd.wf.worklist.server.AllNotificationsVO', l_parameterlist);
4977 wf_event.AddParameterToList('CHANGE_TYPE', 'INSERT',l_parameterlist);
4978 wf_event.AddParameterToList('ID_TYPE', 'PK', l_parameterlist);
4979 wf_event.addParameterToList('PK_NAME_1', 'NOTIFICATION_ID',l_parameterlist);
4980 wf_event.addParameterToList('PK_VALUE_1', nid, l_parameterlist);
4981 wf_event.addParameterToList('PK_NAME_2', 'LANGUAGE',l_parameterlist);
4982 wf_event.addParameterToList('PK_VALUE_2', l_language, l_parameterlist);
4983
4984 --Raise the event
4985 wf_event.Raise(p_event_name => 'oracle.apps.wf.notification.respond',
4986 p_event_key => to_char(nid),
4987 p_parameters => l_parameterlist);
4988
4989 -- If no callback, there is nothing else to do.
4990 if (callback is null) then
4991 return;
4992 end if;
4993
4994 --
4995 -- Open dynamic sql cursor for SET callback calls.
4996 --
4997 -- ### Review Note 2 - callback is from table
4998 l_charcheck := wf_notification_util.CheckIllegalChar(callback);
4999 --Throw the Illegal exception when the check fails
5000
5001
5002 -- BINDVAR_SCAN_IGNORE
5003 sqlbuf := 'begin '||callback||
5004 '(:p1, :p2, :p3, :p4, :p5, :p6, :p7); end;';
5005 --
5006 -- Call callback to SET all RESPOND attributes for notification.
5007 --
5008 response_found := FALSE;
5009 for response_row in notification_attrs_cursor(nid) loop
5010 response_found := TRUE;
5011
5012 aname := response_row.name;
5013 atype := response_row.type;
5014 tvalue := response_row.text_value;
5015 nvalue := response_row.number_value;
5016 dvalue := response_row.date_value;
5017
5018 execute immediate sqlbuf using
5019 in 'SET',
5020 in context,
5021 in aname,
5022 in atype,
5023 in out tvalue,
5024 in out nvalue,
5025 in out dvalue;
5026 end loop;
5027 --
5028 -- If no response attributes are found, then no response was expected
5029 -- for this notification. Do not call the callback to complete the
5030 -- activity. The activity 'completed' as soon as the notification
5031 -- was sent.
5032 --
5033 if (not response_found) then
5034 return;
5035 end if;
5036
5037 --
5038 -- Call callback one more time to mark activity COMPLETE.
5039 -- Send the result and notification id as the value.
5040 --
5041 begin
5042 tvalue := Wf_Notification.GetAttrText(nid, 'RESULT');
5043
5044 exception
5045 when others then
5046 if (wf_core.error_name = 'WFNTF_ATTR') then
5047 -- Pass null result if no result attribute.
5048 wf_core.clear;
5049 tvalue := '';
5050 else
5051 raise;
5052 end if;
5053 end;
5054 nvalue := nid;
5055 dvalue := '';
5056
5057 -- Reparse and bind dynamic sql for the COMPLETE callback call.
5058 -- ### Review Note 2 - callback is from table
5059 -- Check for bug#3827935
5060 l_charcheck := wf_notification_util.CheckIllegalChar(callback);
5061 --Throw the Illegal exception when the check fails
5062
5063
5064 -- BINDVAR_SCAN_IGNORE[3]
5065 sqlbuf := 'begin '||callback||
5066 '(:p1, :p2, :p3, :p4, :p5, :p6, :p7); end;';
5067 execute immediate sqlbuf using
5068 in 'COMPLETE',
5069 in context,
5070 in 'NID',
5071 in 'NUMBER',
5072 in out tvalue,
5073 in out nvalue,
5074 in out dvalue;
5075
5076 -- Remove any messages from the outbound queue
5077 -- GK: 1636402: wf_xml.RemoveNotification is not necessary
5078 -- since the message is likely to be sent by the time the
5079 -- user goes in and does an action from the worklist.
5080 -- wf_xml.RemoveNotification(nid);
5081 exception
5082 when others then
5083 wf_core.context('Wf_Notification', 'Respond', to_char(nid),
5084 respond_comment, responder);
5085 -- This call is for enhanced error handling with respect to OAFwk
5086 wf_notification.SetUIErrorMessage;
5087 raise;
5088 end Respond;
5089
5090 --
5091 -- TestContext
5092 -- Test if current context is correct
5093 -- IN
5094 -- nid - Notification id
5095 -- RETURNS
5096 -- TRUE if context ok, or context check not implemented
5097 -- FALSE if context check fails
5098 --
5099 function TestContext(
5100 nid in number)
5101 return boolean
5102 is
5103 callback varchar2(240);
5104 context varchar2(2000);
5105
5106 -- Dynamic sql stuff
5107 sqlbuf varchar2(2000);
5108 tvalue varchar2(4000);
5109 nvalue number;
5110 dvalue date;
5111 l_dummy varchar2(1);
5112
5113 l_charcheck boolean;
5114 begin
5115 if (nid is null) then
5116 wf_core.token('NID', to_char(nid));
5117 wf_core.raise('WFSQL_ARGS');
5118 end if;
5119
5120 -- Get callback, check for valid notification id.
5121 begin
5122 select N.CALLBACK, N.CONTEXT
5123 into TestContext.callback, TestContext.context
5124 from WF_NOTIFICATIONS N
5125 where N.NOTIFICATION_ID = nid;
5126 exception
5127 when no_data_found then
5128 wf_core.token('NID', to_char(nid));
5129 wf_core.raise('WFNTF_NID');
5130 end;
5131
5132 -- If no callback, then nothing to check
5133 if (callback is null) then
5134 return(TRUE);
5135 end if;
5136
5137 -- Open dynamic sql cursor for callback call
5138 -- ### Review Note 2 - callback is from table
5139 -- Check for bug#3827935
5140 l_charcheck := wf_notification_util.CheckIllegalChar(callback);
5141 --Throw the Illegal exception when the check fails
5142
5143
5144 -- BINDVAR_SCAN_IGNORE
5145 sqlbuf := 'begin '||callback||
5146 '(:p1, :p2, :p3, :p4, :p5, :p6, :p7); end;';
5147 execute immediate sqlbuf using
5148 in 'TESTCTX',
5149 in context,
5150 in l_dummy,
5151 in l_dummy,
5152 in out tvalue,
5153 in out nvalue,
5154 in out dvalue;
5155
5156 if (tvalue in ('FALSE', 'NOTSET')) then
5157 return(FALSE);
5158 else
5159 -- Any other returned value means TEST_CTX mode is not implemented
5160 return(TRUE);
5161 end if;
5162
5163 exception
5164 when others then
5165 wf_core.context('Wf_Notification', 'TestContext', to_char(nid));
5166 raise;
5167 end TestContext;
5168
5169 --
5170 -- VoteCout
5171 -- Count the number of responses for a result_code
5172 -- IN:
5173 -- Gid - Notification group id
5174 -- ResultCode - Result code to be tallied
5175 -- OUT:
5176 -- ResultCount - Number of responses for ResultCode
5177 -- PercentOfTotalPop - % ResultCode ( As a % of total population )
5178 -- PercentOfVotes - % ResultCode ( As a % of votes cast )
5179 --
5180 procedure VoteCount ( Gid in number,
5181 ResultCode in varchar2,
5182 ResultCount out nocopy number,
5183 PercentOfTotalPop out nocopy number,
5184 PercentOfVotes out nocopy number ) is
5185 --
5186 --
5187 l_code_count pls_integer;
5188 l_total_pop pls_integer;
5189 l_total_voted pls_integer;
5190 begin
5191 --
5192 --
5193 select count(*)
5194 into l_total_pop
5195 from wf_notifications
5196 where group_id = Gid;
5197 --
5198 select count(*)
5199 into l_total_voted
5200 from wf_notifications
5201 where group_id = Gid
5202 and status = 'CLOSED';
5203 --
5204 select count(*)
5205 into l_code_count
5206 from wf_notifications wfn,
5207 wf_notification_attributes wfna
5208 where wfn.group_id = Gid
5209 and wfn.notification_id = wfna.notification_id
5210 and wfn.status = 'CLOSED'
5211 and wfna.name = 'RESULT'
5212 and wfna.text_value = ResultCode;
5213 --
5214
5215 ResultCount := l_code_count;
5216 --
5217 -- Prevent division by zero if group has no notifications
5218 --
5219 if ( l_total_pop = 0 ) then
5220 --
5221 PercentOfTotalPop := 0;
5222 --
5223 else
5224 --
5225 PercentOfTotalPop := l_code_count/l_total_pop*100;
5226 --
5227 end if;
5228 --
5229 -- Prevent division by zero if nobody votes
5230 --
5231 if ( l_total_voted = 0 ) then
5232 --
5233 PercentOfVotes := 0;
5234 --
5235 else
5236 --
5237 PercentOfVotes := l_code_count/l_total_voted*100;
5238 --
5239 end if;
5240 --
5241 exception
5242 when others then
5243 --
5244 wf_core.context('Wf_Notification', 'VoteCount', to_char(gid), ResultCode );
5245 raise;
5246 --
5247 end VoteCount;
5248 --
5249 -- OpenNotifications
5250 -- Determine if any Notifications in the Group are OPEN
5251 --
5252 --IN:
5253 -- Gid - Notification group id
5254 --
5255 --Returns:
5256 -- TRUE - if the Group contains open notifications
5257 -- FALSE - if the group does NOT contain open notifications
5258 --
5259 function OpenNotificationsExist( Gid in Number ) return Boolean is
5260 --
5261 dummy pls_integer;
5262 --
5263 begin
5264 --
5265 select 1
5266 into dummy
5267 from sys.dual
5268 where exists ( select null
5269 from wf_notifications
5270 where group_id = Gid
5271 and status = 'OPEN'
5272 );
5273 --
5274 return(TRUE);
5275 --
5276 exception
5277 when NO_DATA_FOUND then
5278 --
5279 return(FALSE);
5280 --
5281 when others then
5282 --
5283 wf_core.context('Wf_Notification', 'OpenNotifications', to_char(gid) );
5284 raise;
5285 --
5286 end OpenNotificationsExist;
5287
5288 --
5289 -- WorkCount
5290 -- Count number of open notifications for user
5291 -- IN:
5292 -- username - user to check
5293 -- RETURNS:
5294 -- Number of open notifications for that user
5295 --
5296 function WorkCount(
5297 username in varchar2)
5298 return number
5299 is
5300 colon pls_integer;
5301 ncount pls_integer;
5302 begin
5303 colon := instr(username, ':');
5304 if (colon = 0) then
5305 select count(1)
5306 into ncount
5307 from WF_NOTIFICATIONS WN
5308 where WN.RECIPIENT_ROLE in
5309 (select WUR.ROLE_NAME
5310 from WF_USER_ROLES WUR
5311 where WUR.USER_NAME = WorkCount.username)
5312 and WN.STATUS = 'OPEN';
5313 else
5314 select count(1)
5315 into ncount
5316 from WF_NOTIFICATIONS WN
5317 where WN.RECIPIENT_ROLE in
5318 (select WUR.ROLE_NAME
5319 from WF_USER_ROLES WUR
5320 where WUR.USER_ORIG_SYSTEM = substr(WorkCount.username, 1, colon-1)
5321 and WUR.USER_ORIG_SYSTEM_ID = substr(WorkCount.username, colon+1)
5322 and WUR.USER_NAME = WorkCount.username)
5323 and WN.STATUS = 'OPEN';
5324 end if;
5325
5326 return(ncount);
5327 exception
5328 when others then
5329 wf_core.context('Wf_Notification', 'WorkCount', username);
5330 raise;
5331 end WorkCount;
5332
5333 --
5334 -- Close
5335 -- Close a notification.
5336 -- IN:
5337 -- nid - Notification Id
5338 -- resp - Respond Required? 0 - No, 1 - Yes
5339 -- responder - User or role close this notification
5340 --
5341 procedure Close(nid in number,
5342 responder in varchar2)
5343 is
5344 status varchar2(8);
5345
5346 -- Any existence of response attribute constitutes a response required.
5347 cursor attrs(mnid in number) is
5348 select MA.NAME
5349 from WF_NOTIFICATION_ATTRIBUTES NA,
5350 WF_MESSAGE_ATTRIBUTES_VL MA,
5351 WF_NOTIFICATIONS N
5352 where N.NOTIFICATION_ID = mnid
5353 and NA.NOTIFICATION_ID = N.NOTIFICATION_ID
5354 and MA.MESSAGE_NAME = N.MESSAGE_NAME
5355 and MA.MESSAGE_TYPE = N.MESSAGE_TYPE
5356 and MA.NAME = NA.NAME
5357 and MA.SUBTYPE = 'RESPOND';
5358
5359 result attrs%rowtype;
5360
5361 --Bug 2283697
5362 l_parameterlist wf_parameter_list_t := wf_parameter_list_t();
5363
5364 l_language varchar2(30);
5365
5366 begin
5367
5368 if (nid is null) then
5369 wf_core.token('NID', to_char(nid));
5370 wf_core.raise('WFSQL_ARGS');
5371 end if;
5372
5373 -- Get Status
5374 begin
5375
5376 select N.STATUS, N.LANGUAGE
5377 into close.status, l_language
5378 from WF_NOTIFICATIONS N
5379 where N.NOTIFICATION_ID = nid
5380 for update nowait;
5381 exception
5382 when no_data_found then
5383 wf_core.token('NID', Wf_Notification.GetSubject(nid));
5384 wf_core.raise('WFNTF_NID');
5385 end;
5386
5387 -- Check notification is open
5388 if (status <> 'OPEN') then
5389 wf_core.token('NID', Wf_Notification.GetSubject(nid) );
5390 wf_core.raise('WFNTF_NID_OPEN');
5391 end if;
5392
5393
5394 open attrs(nid);
5395 fetch attrs into result;
5396 if (attrs%found) then
5397 -- Check response required?
5398 wf_core.token('NID', Wf_Notification.GetSubject(nid));
5399 wf_core.raise('WFNTF_NID_REQUIRE');
5400 end if;
5401
5402 -- Mark notification closed
5403 update WF_NOTIFICATIONS
5404 set STATUS = 'CLOSED',
5405 END_DATE = sysdate,
5406 RESPONDER = close.responder
5407 where NOTIFICATION_ID = nid;
5408
5409
5410 -- Remove any messages from the outbound queue
5411 -- GK: 1636402: wf_xml.RemoveNotification is not necessary
5412 -- since the message is likely to be sent by the time the
5413 -- user goes in and does an action from the worklist.
5414 -- wf_xml.RemoveNotification(nid);
5415
5416 --Bug 2283697
5417 --To raise an EVENT whenever DML operation is performed on
5418 --WF_NOTIFICATIONS and WF_NOTIFICATION_ATTRIBUTES table.
5419 wf_event.AddParameterToList('NOTIFICATION_ID',nid,l_parameterlist);
5420 wf_event.AddParameterToList('RESPONDER',close.responder,l_parameterlist);
5421
5422 -- AppSearch
5423 wf_event.AddParameterToList('OBJECT_NAME',
5424 'oracle.apps.fnd.wf.worklist.server.AllNotificationsVO', l_parameterlist);
5425 wf_event.AddParameterToList('CHANGE_TYPE', 'INSERT',l_parameterlist);
5426 wf_event.AddParameterToList('ID_TYPE', 'PK', l_parameterlist);
5427 wf_event.addParameterToList('PK_NAME_1', 'NOTIFICATION_ID',l_parameterlist);
5428 wf_event.addParameterToList('PK_VALUE_1', nid, l_parameterlist);
5429 wf_event.addParameterToList('PK_NAME_2', 'LANGUAGE',l_parameterlist);
5430 wf_event.addParameterToList('PK_VALUE_2', l_language, l_parameterlist);
5431
5432 --Raise the event
5433 wf_event.Raise(p_event_name => 'oracle.apps.wf.notification.close',
5434 p_event_key => to_char(nid),
5435 p_parameters => l_parameterlist);
5436
5437 exception
5438 when others then
5439 wf_core.context('Wf_Notification', 'Close', to_char(nid), responder);
5440 raise;
5441 end Close;
5442
5443 --
5444 -- GetSubSubjectDisplay
5445 -- Get the design subject of a notification and Substitute tokens in text
5446 -- with the display name of the attributes in the subject.
5447 -- This is used in routing rule poplists
5448 -- IN:
5449 -- message_type - Item type of the message
5450 -- message_name - Name of the message to substitute
5451 --
5452 function GetSubSubjectDisplay(message_type IN VARCHAR2, message_name IN VARCHAR2)
5453 return varchar2 is
5454
5455 local_text varchar2(2000);
5456
5457 -- Select attr values, formatting numbers and dates as requested.
5458 -- The order-by is to handle cases where one attr name is a substring
5459 -- of another.
5460 cursor message_attrs_cursor(c_message_type VARCHAR2,
5461 c_message_name VARCHAR2) is
5462 select WMA.NAME, WMA.DISPLAY_NAME, WMA.TYPE
5463 from WF_MESSAGE_ATTRIBUTES_VL WMA
5464 where WMA.MESSAGE_TYPE = c_message_type
5465 and WMA.MESSAGE_NAME = c_message_name
5466 order by length(WMA.NAME) desc;
5467
5468 begin
5469
5470 -- Get the message subject
5471 SELECT SUBJECT
5472 INTO local_text
5473 FROM wf_messages_vl
5474 WHERE type = message_type
5475 AND name = message_name;
5476
5477 for msg_attr_row in message_attrs_cursor (message_type, message_name) loop
5478
5479 --
5480 -- Substitute all occurrences of SEND tokens with values.
5481 -- Limit to 1950 chars to avoid value errors if substitution pushes
5482 -- it over the edge.
5483 -- Wanted to use '<' and '>' to denote substituted attribute but these
5484 -- characters are the tag markers in html and cause problems in the
5485 -- poplist
5486 --
5487 if (msg_attr_row.type = 'URL') then
5488
5489 local_text := substrb(replace(local_text, '-&'||msg_attr_row.name||'-',
5490 '['||msg_attr_row.display_name||']'), 1, 1950);
5491 else
5492
5493 local_text := substrb(replace(local_text, '&'||msg_attr_row.name,
5494 '[<I><B>'||msg_attr_row.display_name||'</B></I>]'), 1, 1950);
5495
5496 end if;
5497
5498 end loop;
5499
5500 --
5501 -- Process special '#' internal tokens. Supported tokens are:
5502 -- NID - Notification id
5503 --
5504 local_text := substrb(replace(local_text, '&'||'#NID',
5505 '[<I><B>'||wf_core.translate('WF_NOTIFICATION_ID')||'</B></I>]'), 1, 1950);
5506
5507
5508 return(local_text);
5509
5510 exception
5511 when others then
5512 return(local_text);
5513 end GetSubSubjectDisplay;
5514
5515 --
5516 -- GetSubSubjectDisplayShort
5517 -- Get the design subject of a notification and Substitute tokens in text
5518 -- with ellipsis '...' in the subject.
5519 -- This is used in routing rule poplists for the new web screens
5520 -- IN:
5521 -- message_type - Item type of the message
5522 -- message_name - Name of the message to substitute
5523 --
5524 function GetSubSubjectDisplayShort(message_type IN VARCHAR2, message_name IN VARCHAR2)
5525 return varchar2 is
5526
5527 local_text varchar2(2000);
5528
5529 -- Select attr values, formatting numbers and dates as requested.
5530 -- The order-by is to handle cases where one attr name is a substring
5531 -- of another.
5532 cursor message_attrs_cursor(c_message_type VARCHAR2,
5533 c_message_name VARCHAR2) is
5534 select WMA.NAME, WMA.DISPLAY_NAME, WMA.TYPE
5535 from WF_MESSAGE_ATTRIBUTES_VL WMA
5536 where WMA.MESSAGE_TYPE = c_message_type
5537 and WMA.MESSAGE_NAME = c_message_name
5538 order by length(WMA.NAME) desc;
5539
5540 begin
5541
5542 -- Get the message subject
5543 SELECT SUBJECT
5544 INTO local_text
5545 FROM wf_messages_vl
5546 WHERE type = message_type
5547 AND name = message_name;
5548
5549 for msg_attr_row in message_attrs_cursor (message_type, message_name) loop
5550
5551 --
5552 -- Substitute all occurrences of SEND tokens with values.
5553 -- Limit to 1950 chars to avoid value errors if substitution pushes
5554 -- it over the edge.
5555 -- Wanted to use '<' and '>' to denote substituted attribute but these
5556 -- characters are the tag markers in html and cause problems in the
5557 -- poplist
5558 --
5559 if (msg_attr_row.type = 'URL') then
5560
5561 local_text := substrb(replace(local_text, '-&'||msg_attr_row.name||'-',
5562 '['||msg_attr_row.display_name||']'), 1, 1950);
5563 else
5564
5565 local_text := substrb(replace(local_text, '&'||msg_attr_row.name,
5566 '...'), 1, 1950);
5567
5568 end if;
5569
5570 end loop;
5571
5572 --
5573 -- Process special '#' internal tokens. Supported tokens are:
5574 -- NID - Notification id
5575 --
5576 local_text := substrb(replace(local_text, '&'||'#NID',
5577 '...'), 1, 1950);
5578
5579
5580 return(local_text);
5581
5582 exception
5583 when others then
5584 return(local_text);
5585 end GetSubSubjectDisplayShort;
5586
5587 -- PLSQL-Clob Procssing
5588 -----------------------------------------------------------
5589 --Name : WriteToClob (PUBLIC)
5590 --Desc : appends a string to the end of a clob.
5591 --note : the efficiency of clob manipulation makes is dubious.
5592 -- It is probably best to call this as infrequently as possible
5593 -- by concatenating the string as long as possible before hand.
5594
5595 procedure WriteToClob ( clob_loc in out nocopy clob,
5596 msg_string in varchar2) is
5597 pos integer;
5598 amt number;
5599 begin
5600
5601 pos := dbms_lob.getlength(clob_loc) +1;
5602 amt := length(msg_string);
5603 dbms_lob.write(clob_loc,amt,pos,msg_string);
5604
5605 exception
5606 when others then
5607 wf_core.context('WF_NOTIFICATION','WriteToClob');
5608 raise;
5609 end WriteToClob;
5610
5611 --Name : GetFullBody (PUBLIC)
5612 --Desc : Gets full body of message with all PLSQLCLOB variables transalted.
5613 -- and returns the message in 32K chunks in the msgbody out variable.
5614 -- Call this repeatedly until end_of_body is true.
5615 -- Call syntax is
5616 --while not (end_of_msgbody) loop
5617 -- wf_notification.getfullbody(nid,msgbody,end_of_msgbody);
5618 --end loop;
5619 procedure GetFullBody (nid in number,
5620 msgbody out nocopy varchar2,
5621 end_of_body in out nocopy boolean,
5622 disptype in varchar2) is
5623
5624 buffer varchar2(200);
5625 buff_length number;
5626
5627
5628 msg varchar2(30);
5629 pos number;
5630 amt number;
5631 msg_body varchar2(3000);
5632
5633 strt number;
5634 ampersand number;
5635 attr_name varchar2(30);
5636 begin
5637
5638 -- if this is the same nid as was just used in this session,
5639 -- and the message is stored as a clob (so clob_exists is not null) then
5640 -- retrieve message from the temp clob.
5641
5642 if nid = wf_notification.last_nid
5643 and disptype = wf_notification.last_disptype
5644 and wf_notification.clob_exists is not null then
5645 wf_notification.read_Clob(msgbody, end_of_body);
5646 return;
5647 end if;
5648
5649 wf_notification.clob_exists :=null;
5650 wf_notification.last_nid:=nid;
5651 wf_notification.last_disptype:=disptype;
5652 plsql_clob_exists:=null;
5653
5654 msgbody := wf_notification.getbody(nid,disptype);
5655
5656 if msgbody is null
5657 or instr(msgbody,'&') = 0
5658 or plsql_clob_exists is null then
5659
5660 end_of_body := TRUE;
5661
5662 else
5663
5664 strt:=1;
5665
5666 wf_notification.newclob(wf_notification.temp_clob,null);
5667 wf_notification.clob_exists :=1;
5668
5669 loop
5670
5671 attr_name := null;
5672 ampersand := instr(msgbody,'&',strt);
5673
5674
5675 if ampersand = 0 then
5676 if strt <= length(msgbody) then
5677
5678 wf_notification.WriteToClob(wf_notification.temp_clob,
5679 substr(msgbody,strt,length(msgbody)));
5680 end if;
5681 exit;
5682 end if;
5683
5684 -- If the token starts at the first character of the message body, we
5685 -- would encounter an error in our logic. 1 - 1 is 0 so the call to
5686 -- substr would fail.
5687
5688 if ((ampersand - strt) > 0) then
5689 wf_notification.writeToClob(wf_notification.temp_clob,
5690 substr(msgbody,strt,ampersand-strt));
5691
5692 end if;
5693
5694 -- 2691290 if the '&' is at the end of the body the notification
5695 -- will error when calling GetAttrClob API for "Invalid values for
5696 -- Arguments".
5697 if (substr(msgbody,ampersand+1,30) is not null) then
5698 wf_notification.getattrclob(nid,substr(msgbody,ampersand+1,30),disptype,
5699 wf_notification.temp_clob , attr_name);
5700 end if;
5701
5702
5703 if attr_name is not null then
5704 --it was already written to clob.
5705 strt := ampersand + 1 + length(attr_name);
5706 else
5707 --the string was not a plsqlclob
5708 wf_notification.writeToClob(wf_notification.temp_clob,'&');
5709 strt := ampersand + 1;
5710 end if;
5711
5712 end loop;
5713
5714
5715 --set the clob chunk to zero. then request the next chunk in the msgbody
5716 wf_notification.clob_chunk := 0;
5717 wf_notification.read_Clob(msgbody, end_of_body);
5718
5719 end if;
5720
5721 exception
5722 when others then
5723 wf_core.context('WF_NOTIFICATION','GetFullBody', 'nid => '||to_char(nid),
5724 'disptype => '||disptype);
5725 raise;
5726 end GetFullBody;
5727
5728
5729 --Name: GetFullBodyWrapper (PUBLIC)
5730 --Desc : Gets full body of message with all PLSQLCLOB variables transalted.
5731 -- and returns the message in 32K chunks in the msgbody out variable.
5732 -- Call this repeatedly until end_of_body is "Y". Uses string arg
5733 -- instead of boolean like GetFullBody for end_of_msg_body
5734 -- Call syntax is
5735 --while (end_of_msgbody <> 'Y') loop
5736 -- wf_notification.getfullbody(nid,msgbody,end_of_msgbody);
5737 --end loop;
5738 procedure GetFullBodyWrapper (nid in number,
5739 msgbody out nocopy varchar2,
5740 end_of_body out nocopy varchar2,
5741 disptype in varchar2)
5742 is
5743 end_of_body_b boolean;
5744 begin
5745 end_of_body_b := FALSE;
5746 WF_Notification.GetFullBody(nid, msgbody, end_of_body_b, disptype);
5747 if (end_of_body_b = TRUE) then
5748 end_of_body := 'Y';
5749 else
5750 end_of_body := 'N';
5751 end if;
5752 end GetFullBodyWrapper;
5753
5754 --
5755 -- GetAttrClob
5756 -- Get the displayed value of a PLSQLCLOB DOCUMENT-type attribute.
5757 -- Returns referenced document in format requested.
5758 -- Use GetAttrText to get retrieve the actual attr value (i.e. the
5759 -- document key string instead of the actual document).
5760 -- NOTE:
5761 -- a. Only PLSQL document type is implemented.
5762 -- b. This will be called by old mailers. This is a wrapper to the
5763 -- new implementation which returns the doctype also.
5764 -- IN:
5765 -- nid - Notification id
5766 -- astring - the string to substitute on (ex: '&ATTR1 is your order..')
5767 -- disptype - Requested display type. Valid values:
5768 -- wf_notification.doc_text - 'text/plain'
5769 -- wf_notification.doc_html - 'text/html'
5770 -- document - The clob into which
5771 -- aname - Attribute Name (the first part of the string that matches
5772 -- the attr list)
5773 --
5774 procedure GetAttrClob(
5775 nid in number,
5776 astring in varchar2,
5777 disptype in varchar2,
5778 document in out nocopy clob,
5779 aname out nocopy varchar2)
5780 is
5781 doctype varchar2(500);
5782 begin
5783
5784 Wf_Notification.GetAttrClob(nid, astring, disptype, document, doctype, aname);
5785
5786 exception
5787 when others then
5788 wf_core.context('Wf_Notification', 'oldGetAttrClob', to_char(nid), aname,
5789 disptype);
5790 raise;
5791 end GetAttrClob;
5792
5793 --
5794 -- GetAttrClob
5795 -- Get the displayed value of a PLSQLCLOB DOCUMENT-type attribute.
5796 -- Returns referenced document in format requested.
5797 -- Use GetAttrText to get retrieve the actual attr value (i.e. the
5798 -- document key string instead of the actual document).
5799 -- NOTE:
5800 -- Only PLSQL document type is implemented.
5801 -- IN:
5802 -- nid - Notification id
5803 -- astring - the string to substitute on (ex: '&ATTR1 is your order..')
5804 -- disptype - Requested display type. Valid values:
5805 -- wf_notification.doc_text - 'text/plain'
5806 -- wf_notification.doc_html - 'text/html'
5807 -- document - Th clob into which
5808 -- aname - Attribute Name (the string that matches
5809 -- the attr list)
5810 --
5811 procedure GetAttrClob(
5812 nid in number,
5813 astring in varchar2,
5814 disptype in varchar2,
5815 document in out nocopy clob,
5816 doctype out nocopy varchar2,
5817 aname out nocopy varchar2)
5818 is
5819 key varchar2(4000);
5820 colon pls_integer;
5821 slash pls_integer;
5822 dmstype varchar2(30);
5823 display_name varchar2(80);
5824 procname varchar2(240);
5825 launch_url varchar2(4000);
5826 procarg varchar2(32000);
5827 username varchar2(320);
5828
5829 --curs integer;
5830 sqlbuf varchar2(2000);
5831 --rows integer;
5832
5833 target varchar2(240);
5834 l_charcheck boolean;
5835
5836 begin
5837
5838 -- Check args
5839 if ((nid is null) or (astring is null) or
5840 (disptype not in (wf_notification.doc_text,
5841 wf_notification.doc_html))) then
5842 wf_core.token('NID', to_char(nid));
5843 wf_core.token('ASTRING', aname);
5844 wf_core.token('DISPTYPE', disptype);
5845 wf_core.raise('WFSQL_ARGS');
5846 end if;
5847
5848 -- of all the possible Document type matches,
5849 -- make sure its a PLSQLCLOB
5850 dmstype := '';
5851
5852 -- Bug 6324545: Replaced the cursor with a simple sql to fetch a single row.
5853 begin
5854 -- <7443088> improved query
5855 select NAME into aname from
5856 (select WMA.NAME
5857 from WF_NOTIFICATIONS WN,
5858 WF_MESSAGE_ATTRIBUTES WMA,
5859 WF_NOTIFICATION_ATTRIBUTES NA
5860 where WN.NOTIFICATION_ID = nid
5861 and wn.notification_id = na.notification_id
5862 and wma.name = na.name
5863 and WN.MESSAGE_TYPE = WMA.MESSAGE_TYPE
5864 and WN.MESSAGE_NAME = WMA.MESSAGE_NAME
5865 and WMA.TYPE = 'DOCUMENT'
5866 and instr( upper(astring) ,wma.name) = 1
5867 and upper(na.text_value) like 'PLSQLCLOB:%'
5868 order by length(wma.name) desc)
5869 where rownum=1;
5870 exception
5871 when no_data_found then
5872 aname:=null;
5873 return;
5874 end;
5875
5876 if (aname is not null) then
5877 -- Retrieve key string
5878 key := wf_notification.GetAttrText(nid, aname);
5879
5880 -- If the key is empty then return a null string
5881 if (key is not null) then
5882
5883 -- Parse doc mgmt system type from key
5884 colon := instr(key, ':');
5885 if ((colon <> 0) and (colon < 30)) then
5886 dmstype := upper(substr(key, 1, colon-1));
5887 end if;
5888 end if;
5889 end if;
5890
5891 -- if we didnt find any plsqlclobs then exit now
5892 if dmstype is null or (dmstype <> 'PLSQLCLOB') then
5893 aname:=null;
5894 return;
5895 end if;
5896
5897 -- We must be processing a CLOB PLSQL doc type
5898 slash := instr(key, '/');
5899 if (slash = 0) then
5900 procname := substr(key, colon+1);
5901 procarg := '';
5902 else
5903 procname := substr(key, colon+1, slash-colon-1);
5904 procarg := substr(key, slash+1);
5905 end if;
5906
5907 -- Dynamic sql call to procedure
5908 -- bug 2706082 using execute immediate instead of dbms_sql.execute
5909
5910 if (procarg is null) then
5911 procarg := NULL;
5912 else
5913 procarg := Wf_Notification.GetTextInternal(procarg, nid, target,
5914 FALSE, FALSE, 'text/plain');
5915 end if;
5916
5917 -- ### Review Note 1
5918 l_charcheck := wf_notification_util.CheckIllegalChar(procname);
5919 --Throw the Illegal exception when the check fails
5920
5921 if (wf_log_pkg.level_statement >= fnd_log.g_current_runtime_level) then
5922 wf_log_pkg.string2(wf_log_pkg.level_statement,
5923 'wf.plsql.wf_notification.GetAttrClob.plsqlclob_callout',
5924 'Start executing PLSQLCLOB Doc procedure - '||procname, true);
5925 end if;
5926
5927 sqlbuf := 'begin '||procname||'(:p1, :p2, :p3, :p4); end;';
5928 execute immediate sqlbuf using
5929 in procarg,
5930 in disptype,
5931 in out document,
5932 in out doctype;
5933
5934 if (wf_log_pkg.level_statement >= fnd_log.g_current_runtime_level) then
5935 wf_log_pkg.string2(wf_log_pkg.level_statement,
5936 'wf.plsql.wf_notification.GetAttrClob.plsqlclob_callout',
5937 'End executing PLSQLCLOB Doc procedure - '||procname, false);
5938 end if;
5939
5940 /* curs := dbms_sql.open_cursor;
5941 if (procarg is null) then
5942 --force a null string
5943 sqlbuf := 'begin '||procname||'('''','''||disptype||
5944 ''', :document, :doctype); end;';
5945 else
5946 -- Substitute refs to other attributes in argument
5947 -- NOTE: There is a slight chance of recursive loop here,
5948 -- if the substituted string eventually contains a reference
5949 -- back to this same docattr.
5950
5951 procarg := Wf_Notification.GetTextInternal(procarg, nid, target, FALSE,
5952 FALSE);
5953
5954 sqlbuf := 'begin '||procname||'('''||procarg||''', '''||disptype||
5955 ''',:document, :doctype); end;';
5956 end if;
5957
5958 dbms_sql.parse(curs, sqlbuf, dbms_sql.v7);
5959 dbms_sql.bind_variable(curs, ':document', document);
5960 dbms_sql.bind_variable(curs, ':doctype', doctype, 240);
5961 rows := dbms_sql.execute(curs);
5962 dbms_sql.variable_value(curs, ':document', document);
5963 dbms_sql.variable_value(curs, ':doctype', doctype);
5964 dbms_sql.close_cursor(curs);
5965
5966 -- Translate doc types if needed
5967 Dont do this anymore. The plsql must take care of it.
5968 if ((disptype = wf_notification.doc_html) and
5969 (doctype = wf_notification.doc_text)) then
5970 -- Change plain text to html by wrapping in preformatted tags
5971 return('<PRE>'||document||'</PRE>');
5972 end if;
5973 */
5974
5975 exception
5976 when others then
5977 -- Close cursor just in case
5978 /* if (dbms_sql.is_open(curs)) then
5979 dbms_sql.close_cursor(curs);
5980 end if; */
5981
5982 wf_core.context('Wf_Notification', 'GetAttrClob', to_char(nid), aname,
5983 disptype);
5984 raise;
5985 end GetAttrClob;
5986
5987
5988
5989 -- Name: NewClob
5990 -- Creates a new record in the temp table with a clob
5991 -- this is necessary because clobs cannot reside in plsql
5992 -- but must be part of a table.
5993 Procedure NewClob (clobloc in out nocopy clob,
5994 -- -- clobid in out nocopy number,
5995 msg_string in varchar2) is
5996 pos integer;
5997 amt number;
5998
5999 begin
6000
6001 -- Before allocating TEMP LOb we should check whether existing Locator
6002 -- is pointing to a locator and whether being freed .
6003 -- bug 6511028
6004 begin
6005 if(clobloc is not null) then
6006 dbms_lob.freeTemporary(clobloc);
6007 end if;
6008 exception
6009 WHEN OTHERS THEN
6010 null; -- ignore ORA-22275 and other exceptions
6011 end;
6012
6013 -- make clob temporary. this may impact the speed of the UI
6014 -- such that user has to wait to see the notification.
6015 -- To improve performance make sure buffer cache is well tuned.
6016 dbms_lob.createtemporary(clobloc, TRUE, DBMS_LOB.session);
6017
6018 if msg_string is not null then
6019 pos := 1;
6020 amt := length(msg_string);
6021 dbms_lob.write(clobloc,amt,pos,msg_string);
6022 end if;
6023
6024 exception
6025 when others then
6026 wf_core.context('WF_NOTIFICATION','NewClob');
6027 raise;
6028 end NewClob;
6029
6030 --Name Read_Clob
6031 --reads a specific clob in 8K chunks. Call this repeatedly until
6032 --end_of_clob is true.
6033 procedure read_clob (line out nocopy varchar2 ,
6034 end_of_clob in out nocopy boolean) is
6035
6036 pos number;
6037 buff_length pls_integer:=8000;
6038 begin
6039
6040 --linenbr is always one before the line to print.
6041 --it is incremented afterwards.
6042 pos:=(buff_length * nvl(wf_notification.clob_chunk,0) ) +1;
6043
6044 dbms_lob.read(wf_notification.temp_clob,buff_length,pos,line);
6045
6046 if pos+buff_length > dbms_lob.getlength(wf_notification.temp_clob) then
6047 end_of_clob := TRUE;
6048 wf_notification.clob_chunk := 0;
6049 else
6050 wf_notification.clob_chunk := wf_notification.clob_chunk +1;
6051 end if;
6052
6053 exception
6054 when others then
6055 wf_core.context('Wf_Notification', 'Read_Clob','pos => '||to_char(pos),
6056 'line => {'||line||'}');
6057 raise;
6058 end Read_Clob;
6059
6060 --Name ReadAttrClob (PUBLIC)
6061 --Desc : Gets full text of a PLSQLCLOB variable
6062 -- and returns the 32K chunks in the doctext out variable.
6063 -- Call this repeatedly until end_of_text is true.
6064 --USE : use this to get the value of idividual PLSQLCLOBs such as attachments.
6065 -- to susbtitute a PLSQLSQL clob into a message body, use GetFullBody
6066
6067 procedure ReadAttrClob(nid in number,
6068 aname in varchar2,
6069 doctext in out nocopy varchar2,
6070 end_of_text in out nocopy boolean) is
6071 clob_id number;
6072 attr_name varchar2(30);
6073 begin
6074 if nid = wf_notification.last_nid
6075 and wf_notification.clob_exists is not null then
6076 wf_notification.read_Clob(doctext, end_of_text);
6077 else
6078 --create a clob
6079 wf_notification.newclob(wf_notification.temp_clob,null);
6080 wf_notification.clob_exists :=1;
6081
6082 --set the clob text
6083 wf_notification.getattrclob(nid, aname,
6084 wf_notification.doc_html, wf_notification.temp_clob , attr_name);
6085
6086 --retreive all the clob text in 32K chunks.
6087 if attr_name = aname then
6088 -- the attribute was substituted with something in the clob so print it.
6089 wf_notification.clob_chunk := 0;
6090 wf_notification.read_Clob(doctext, end_of_text);
6091
6092 else
6093 --the aname was not substituted so just print it.
6094 doctext := aname;
6095 end if;
6096
6097 --finally set the global vars
6098 wf_notification.last_nid:=nid;
6099
6100
6101 end if;
6102
6103 exception
6104 when others then
6105 wf_core.context('Wf_Notification', 'ReadAttrClob');
6106 raise;
6107 end ReadAttrClob;
6108
6109 --
6110 -- Denormalization of Notifications
6111 --
6112
6113 --
6114 -- GetSessionLanguage (PRIVATE)
6115 -- Try to return the cached session language value.
6116 -- If it is not cached yet, call the real query function.
6117 --
6118 function GetSessionLanguage
6119 return varchar2
6120 is
6121 l_lang varchar2(64);
6122 l_terr varchar2(64);
6123 l_chrs varchar2(64);
6124 begin
6125 -- <7514495> no cached variable in Wf_Notification now?
6126 -- if (Wf_Notification.nls_language is not null) then
6127 -- return Wf_Notification.nls_language;
6128 -- end if;
6129
6130 GetNLSLanguage(l_lang, l_terr, l_chrs);
6131 return l_lang;
6132
6133 end GetSessionLanguage;
6134
6135 --
6136 -- GetNLSLanguage (PRIVATE)
6137 -- Get the NLS Lanugage setting of current session
6138 -- Try to cached the value for future use.
6139 -- NOTE:
6140 -- Because it tried to use cached values first. The subsequent calls
6141 -- will give you the cached values instead of the current value.
6142 --
6143 procedure GetNLSLanguage(language out nocopy varchar2,
6144 territory out nocopy varchar2,
6145 charset out nocopy varchar2)
6146 is
6147 tmpbuf varchar2(240);
6148 pos1 number; -- position for '_'
6149 pos2 number; -- position for '.'
6150 l_nlsDateFormat varchar2(64);
6151 l_nlsDateLang varchar2(64);
6152 l_nlsNumChars varchar2(64);
6153 l_nlsSort varchar2(64);
6154 l_nlsCalendar varchar2(64);
6155
6156 begin
6157 -- <7514495> now uses centralized api
6158 -- if (Wf_Notification.nls_language is null) then
6159 -- tmpbuf := userenv('LANGUAGE');
6160 -- pos1 := instr(tmpbuf, '_');
6161 -- pos2 := instr(tmpbuf, '.');
6162 --
6163 -- Wf_Notification.nls_language := substr(tmpbuf, 1, pos1-1);
6164 -- Wf_Notification.nls_territory := substr(tmpbuf, pos1+1, pos2-pos1-1);
6165 -- Wf_Notification.nls_charset := substr(tmpbuf, pos2+1);
6166 -- end if;
6167 --
6168 -- GetNLSLanguage.language := Wf_Notification.nls_language;
6169 -- GetNLSLanguage.territory := Wf_Notification.nls_territory;
6170 -- GetNLSLanguage.charset := Wf_Notification.nls_charset;
6171
6172 wf_notification_util.getNLSContext( p_nlsLanguage=> GetNLSLanguage.language,
6173 p_nlsTerritory => GetNLSLanguage.territory,
6174 p_nlsCode => GetNLSLanguage.charset,
6175 p_nlsDateFormat => l_nlsDateFormat,
6176 p_nlsDateLanguage => l_nlsDateLang,
6177 p_nlsNumericCharacters => l_nlsNumChars,
6178 p_nlsSort => l_nlsSort,
6179 p_nlsCalendar => l_nlsCalendar);
6180
6181 end GetNLSLanguage;
6182
6183
6184
6185 --
6186 -- SetNLSLanguage (PRIVATE)
6187 -- Set the NLS Lanugage setting of current session
6188 --
6189 procedure SetNLSLanguage(p_language in varchar2,
6190 p_territory in varchar2)
6191 is
6192 l_language varchar2(30);
6193 l_territory varchar2(30);
6194 begin
6195 -- <7514495> now we use centralized api
6196 -- if (p_language = Wf_Notification.nls_language) then
6197 -- return;
6198 -- end if;
6199 --
6200 -- l_language := ''''||p_language||'''';
6201 -- l_territory := ''''||p_territory||'''';
6202 --
6203 -- DBMS_SESSION.SET_NLS('NLS_LANGUAGE', l_language);
6204 -- DBMS_SESSION.SET_NLS('NLS_TERRITORY', l_territory);
6205 --
6206 -- -- update cache
6207 -- Wf_Notification.nls_language := p_language;
6208 -- Wf_Notification.nls_territory := p_territory;
6209
6210 wf_notification_util.SetNLSContext( -- p_nid ,
6211 p_nlsLanguage => l_language,
6212 p_nlsTerritory => p_territory
6213 -- ok not to pass next parameters
6214 -- as fnd_global.set_nls_context won't set
6215 -- if null
6216 -- p_nlsDateFormat ,
6217 -- p_nlsDateLanguage ,
6218 -- p_nlsNumericCharacters ,
6219 -- p_nlsSort ,
6220 -- p_nlsCalendar
6221 );
6222 exception
6223 when others then
6224 Wf_Core.Context('Wf_Notification', 'SetNLSLanguage', p_language, p_territory);
6225 raise;
6226 end SetNLSLanguage;
6227
6228 --
6229 -- Denormalize_Notification
6230 -- Populate the donormalized value to WF_NOTIFICATIONS table according
6231 -- to the language setting of username provided.
6232 -- IN:
6233 -- nid - Notification id
6234 -- username - optional role name, if not provided, use the
6235 -- recipient_role of the notification.
6236 -- langcode - language code
6237 --
6238 -- NOTE: username has precedence over langcode. Either username or
6239 -- langcode is needed.
6240 --
6241 procedure Denormalize_Notification(nid in number,
6242 username in varchar2,
6243 langcode in varchar2)
6244 is
6245 l_orig_lang varchar2(64);
6246 l_user varchar2(320);
6247 l_from_role varchar2(320);
6248 l_from_user varchar2(360);
6249 l_to_user varchar2(360);
6250 l_subject varchar2(2000);
6251 l_language varchar2(64);
6252 role_info_tbl wf_directory.wf_local_roles_tbl_type;
6253 l_territory varchar2(64);
6254 l_nls_date_format varchar2(64);
6255 l_nls_date_language varchar2(64);
6256 l_nls_calendar varchar2(64);
6257 l_nls_numeric_characters varchar2(64);
6258 l_nls_sort varchar2(64);
6259 l_nls_currency varchar2(64);
6260
6261 l_defer_denormalize boolean;
6262 l_parameterlist wf_parameter_list_t;
6263 l_orig_nlsterritory varchar2(64);
6264 l_orig_nlsCode varchar2(64);
6265 l_orig_nlsDateFormat varchar2(64);
6266 l_orig_nlsDateLang varchar2(64);
6267 l_orig_nlsNumChars varchar2(64);
6268 l_orig_nlsSort varchar2(64);
6269 l_orig_nlsCalendar varchar2(64);
6270 l_logSTMT boolean;
6271 l_logPRCD boolean;
6272 l_sessionUser varchar2(320);
6273 l_sessionUserInfo wf_directory.wf_local_roles_tbl_type;
6274 l_canDefer boolean;
6275 l_module varchar2(100):=g_plsqlName|| 'Denormalize_Notification()';
6276
6277 begin
6278 l_logPRCD := WF_LOG_PKG.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level;
6279 l_logSTMT := WF_LOG_PKG.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level;
6280 if ( l_logPRCD ) then
6281 wf_log_pkg.String(wf_log_pkg.LEVEL_PROCEDURE, l_module
6282 , 'BEGIN, nid='||nid||', username='||username||', langcode='||langcode);
6283 end if;
6284
6285 -- 8286459. Get value, and always reset flag;
6286 l_canDefer := wf_notification_util.g_allowDeferDenormalize;
6287 wf_notification_util.g_allowDeferDenormalize := true;
6288
6289 -- <7720908>
6290 wf_notification_util.getNLSContext( l_orig_lang, l_orig_nlsterritory, l_orig_nlsCode
6291 , l_orig_nlsDateFormat, l_orig_nlsDateLang
6292 , l_orig_nlsNumChars, l_orig_nlsSort, l_orig_nlsCalendar);
6293
6294 if (l_orig_nlsCalendar is null) then
6295 -- when null (typically, online session), get calendar from user prefs
6296 l_sessionUser := Wfa_Sec.GetUser();
6297 if ( l_logSTMT ) then
6298 wf_log_pkg.String(wf_log_pkg.LEVEL_STATEMENT, l_module
6299 , 'l_sessionUser=>'||l_sessionUser||'<');
6300 end if;
6301
6302 if (l_sessionUser is not null) then
6303 Wf_Directory.GetRoleInfo2(l_sessionUser, l_sessionUserInfo);
6304 l_orig_nlsCalendar := l_sessionUserInfo(1).nls_calendar;
6305 if ( l_logSTMT ) then
6306 wf_log_pkg.String(wf_log_pkg.LEVEL_STATEMENT, l_module
6307 , 'l_sessionUser calendar=>'||l_orig_nlsCalendar||'<');
6308 end if;
6309 end if;
6310 end if;
6311
6312 l_defer_denormalize := FALSE;
6313 l_parameterlist := wf_parameter_list_t();
6314
6315 -- if username is supplied, use the language setting of such user
6316 -- default to Recipient's setting if no valid language is found.
6317 begin
6318 if (username is not null) then
6319 Wf_Directory.GetRoleInfo2(username, role_info_tbl);
6320 l_language := role_info_tbl(1).language;
6321
6322 -- <7514495> full NLS support
6323 l_territory := role_info_tbl(1).territory;
6324 l_nls_date_format := role_info_tbl(1).nls_date_format;
6325 l_nls_date_language := role_info_tbl(1).nls_date_language;
6326 l_nls_calendar := role_info_tbl(1).nls_calendar;
6327 l_nls_numeric_characters := role_info_tbl(1).nls_numeric_characters;
6328 l_nls_sort := role_info_tbl(1).nls_sort;
6329 l_nls_currency := role_info_tbl(1).nls_currency; -- </7514495>
6330
6331 role_info_tbl.DELETE;
6332
6333 if ( l_logSTMT ) then
6334 wf_log_pkg.String(wf_log_pkg.LEVEL_STATEMENT, l_module
6335 , 'Got '||username||'''s preferences (passed by caller), lang='||l_language);
6336 end if;
6337
6338 -- check if user language match
6339 if (l_orig_lang <> l_language) then
6340 return;
6341 end if;
6342
6343 elsif (langcode is not null) then
6344 -- check if langcode match
6345 if (langcode <> userenv('LANG')) then
6346 if ( l_logPRCD ) then
6347 wf_log_pkg.String(wf_log_pkg.LEVEL_PROCEDURE, l_module
6348 , 'END - did nothing, langcode <> session lang');
6349 end if;
6350 return;
6351 end if;
6352 select NLS_LANGUAGE
6353 into l_language
6354 from WF_LANGUAGES
6355 where CODE = langcode;
6356 -- ### and INSTALLED_FLAG = 'Y';
6357 -- ### Maybe we do not need to restrict installed flag to be Y
6358 end if;
6359 exception
6360 when OTHERS then
6361 l_language := null;
6362 end;
6363
6364 begin
6365 select RECIPIENT_ROLE, FROM_ROLE
6366 into l_user, l_from_role
6367 from WF_NOTIFICATIONS
6368 where NOTIFICATION_ID = nid;
6369 exception
6370 when NO_DATA_FOUND then
6371 wf_core.token('NID', to_char(nid));
6372 wf_core.raise('WFNTF_NID');
6373 end;
6374
6375 if ( l_logSTMT ) then
6376 wf_log_pkg.String(wf_log_pkg.LEVEL_STATEMENT, l_module
6377 , 'Getting '||l_user||'''s (recipient) preferences');
6378 end if;
6379
6380 Wf_Directory.GetRoleInfo2(l_user, role_info_tbl);
6381 -- in most cases, l_language should be null and we use the language setting
6382 -- of the recipient role.
6383 if (l_language is null) then
6384 l_language := role_info_tbl(1).language;
6385
6386 -- <7514495> full NLS support
6387 l_territory := role_info_tbl(1).territory;
6388 l_nls_date_format := role_info_tbl(1).nls_date_format;
6389 l_nls_date_language := role_info_tbl(1).nls_date_language;
6390 l_nls_calendar := role_info_tbl(1).nls_calendar;
6391 l_nls_numeric_characters := role_info_tbl(1).nls_numeric_characters;
6392 l_nls_sort := role_info_tbl(1).nls_sort;
6393 l_nls_currency := role_info_tbl(1).nls_currency; -- </7514495>
6394
6395 -- 8286459
6396 if l_canDefer and
6397 ((l_orig_lang <> l_language) or
6398 (l_orig_nlsDateFormat <> l_nls_date_format) or
6399 (l_orig_nlsCalendar <> l_nls_calendar)
6400 ) then
6401 -- do not do anything if the NLS settings do not match
6402 l_defer_denormalize := TRUE;
6403
6404 if ( l_logSTMT ) then
6405 wf_log_pkg.String(wf_log_pkg.LEVEL_STATEMENT, l_module
6406 , 'l_orig_lang:>'|| l_orig_lang ||'<, l_language:>'||l_language);
6407 wf_log_pkg.String(wf_log_pkg.LEVEL_STATEMENT, l_module
6408 , 'l_orig_nlsDateFormat:>'||l_orig_nlsDateFormat ||'<, l_nls_date_format:>'||l_nls_date_format);
6409 wf_log_pkg.String(wf_log_pkg.LEVEL_STATEMENT, l_module
6410 , 'l_orig_nlsDateLang:>'||l_orig_nlsDateLang ||'<, l_nls_date_language:>'||l_nls_date_language);
6411 wf_log_pkg.String(wf_log_pkg.LEVEL_STATEMENT, l_module
6412 , 'l_orig_nlsCalendar:>'|| l_orig_nlsCalendar ||'<, l_nls_calendar:>'||l_nls_calendar);
6413 wf_log_pkg.String(wf_log_pkg.LEVEL_STATEMENT, l_module
6414 , 'l_orig_nlsterritory:>'|| l_orig_nlsterritory ||'<, l_territory:>'||l_territory);
6415 wf_log_pkg.String(wf_log_pkg.LEVEL_STATEMENT, l_module
6416 , 'l_orig_nlsSort:>'|| l_orig_nlsSort||'<, l_nls_sort:>'||l_nls_sort);
6417 end if;
6418 end if;
6419 end if;
6420
6421 if (l_defer_denormalize) then
6422
6423 -- To raise an EVENT when l_defer_denormalize is set true
6424 wf_event.AddParameterToList('NOTIFICATION_ID',nid,l_parameterlist);
6425 wf_event.AddParameterToList('ROLE',l_user,l_parameterlist);
6426 wf_event.AddParameterToList('LANGUAGE',l_language,l_parameterlist);
6427 wf_event.AddParameterToList('TERRITORY',l_territory,l_parameterlist);
6428
6429 -- <7514495>
6430 wf_event.AddParameterToList('NLS_DATE_FORMAT', l_nls_date_format,l_parameterlist);
6431 wf_event.AddParameterToList('NLS_DATE_LANGUAGE', l_nls_date_language,l_parameterlist);
6432 wf_event.AddParameterToList('NLS_CALENDAR', l_nls_calendar,l_parameterlist);
6433 wf_event.AddParameterToList('NLS_NUMERIC_CHARACTERS', l_nls_numeric_characters,l_parameterlist);
6434 wf_event.AddParameterToList('NLS_SORT', l_nls_sort,l_parameterlist);
6435 wf_event.AddParameterToList('NLS_CURRENCY', l_nls_currency,l_parameterlist);
6436 -- </7514495>
6437
6438 if ( l_logSTMT ) then
6439 wf_log_pkg.String(wf_log_pkg.LEVEL_STATEMENT, l_module, 'defer parameters: '||
6440 'nid('||nid||'), role('||l_user||'), language('||l_language||')');
6441 wf_log_pkg.String(wf_log_pkg.LEVEL_STATEMENT, l_module, 'territory('||l_territory||
6442 '), date_format('||l_nls_date_format||'), date_language('||
6443 l_nls_date_language||')');
6444 wf_log_pkg.String(wf_log_pkg.LEVEL_STATEMENT, l_module, 'calendar('||l_nls_calendar||
6445 '), numeric_characters('||l_nls_numeric_characters||'), sort('||
6446 l_nls_sort||'), currency('||l_nls_currency||')');
6447 end if;
6448 if ( l_logPRCD ) then
6449 wf_log_pkg.String(wf_log_pkg.LEVEL_PROCEDURE, l_module, 'END - deferring denormalization');
6450 end if;
6451
6452 -- Raise the event
6453 wf_event.Raise(p_event_name => 'oracle.apps.wf.notification.denormalize',
6454 p_event_key => to_char(nid),
6455 p_parameters => l_parameterlist);
6456
6457 return;
6458 end if;
6459
6460 if ( l_logSTMT ) then
6461 wf_log_pkg.String(wf_log_pkg.LEVEL_STATEMENT, l_module,
6462 'Not deferring denormalization, so using session''s settings');
6463 end if;
6464
6465 -- To User
6466 -- N.B.: substrb is used in stead of substr because 320 is a hard byte limit.
6467 -- substr in some characterset may return > 320 bytes.
6468 l_to_user := role_info_tbl(1).display_name;
6469 l_to_user := substrb(l_to_user,1,320);
6470
6471 -- From User
6472 -- If FROM_ROLE has not been defined yet, we tried to draw this from
6473 -- #FROM_ROLE.
6474 if (l_from_role is NULL) then
6475 begin
6476 l_from_role := Wf_Notification.GetAttrText(nid, '#FROM_ROLE');
6477 exception
6478 when OTHERS then
6479 wf_core.clear; -- clear the error stack
6480 l_from_role := NULL;
6481 end;
6482 end if;
6483
6484 -- We need to make l_from_user consistant with l_from_role.
6485 if (l_from_role is not NULL) then
6486 l_from_user := Wf_Directory.GetRoleDisplayName(l_from_role);
6487 l_from_user := substrb(l_from_user, 1,320);
6488 end if;
6489
6490
6491 -- Subject
6492 -- skilaru 08-MAY-03 bug fix 2883247
6493 l_subject := Wf_Notification.GetSubject(nid, 'text/plain');
6494
6495 if ( l_logSTMT ) then
6496 wf_log_pkg.String(wf_log_pkg.LEVEL_STATEMENT, l_module, 'subject('||l_subject||')');
6497 end if;
6498 -- Populate the notification values
6499 --
6500 begin
6501 update WF_NOTIFICATIONS
6502 set FROM_USER = l_from_user,
6503 FROM_ROLE = nvl(l_from_role,FROM_ROLE),
6504 TO_USER = l_to_user,
6505 SUBJECT = l_subject,
6506 LANGUAGE = userenv('LANG')
6507 where NOTIFICATION_ID = nid;
6508 exception
6509 when OTHERS then
6510 wf_core.token('NID', to_char(nid));
6511 wf_core.raise('WFNTF_DENORM_FAILED');
6512 end;
6513
6514 if ( l_logPRCD ) then
6515 wf_log_pkg.String(wf_log_pkg.LEVEL_PROCEDURE, l_module, 'END');
6516 end if;
6517 exception
6518 when OTHERS then
6519 wf_core.context('Wf_Notification', 'Denormalize_Notification',
6520 to_char(nid), username);
6521 raise;
6522 end Denormalize_Notification;
6523
6524 --
6525 -- closeFYI
6526 -- Close FYI notifications that are not associated with an item.
6527 -- IN:
6528 -- itemtype - Item Type the notification belongs to.
6529 -- begindate - Close FYI notifications that were opened on
6530 -- or before this date.
6531 --
6532 procedure closeFYI( itemtype in varchar2,
6533 messageName in varchar2,
6534 begindate in date) is
6535
6536 xitemtype varchar2(8);
6537 xmessageName varchar2(30);
6538
6539 cursor fyiNid is
6540 select WN.NOTIFICATION_ID
6541 from WF_NOTIFICATIONS WN
6542 where MESSAGE_TYPE like xitemtype
6543 and MESSAGE_NAME like xmessageName
6544 and BEGIN_DATE<=begindate
6545 and STATUS = 'OPEN'
6546 and not exists (
6547 select NULL
6548 from WF_MESSAGE_ATTRIBUTES WMA
6549 where WMA.MESSAGE_TYPE = WN.MESSAGE_TYPE
6550 and WMA.MESSAGE_NAME = WN.MESSAGE_NAME
6551 and WMA.SUBTYPE = 'RESPOND');
6552
6553
6554 begin
6555
6556 xitemtype := nvl(itemtype, '%');
6557 xmessageName := nvl(messageName, '%');
6558
6559 for c in fyiNid LOOP
6560
6561 WF_NOTIFICATION.Close( c.NOTIFICATION_ID );
6562
6563 end loop;
6564
6565 end closeFYI;
6566
6567 --
6568 -- NtfSignRequirementsMet (PUBLIC) (Bug 2698999)
6569 -- Checks if the notification's singature requirements are met
6570 -- IN
6571 -- nid - notification id
6572 -- OUT
6573 -- true - if the ntf is signed
6574 -- false - if the ntf is not signed
6575 --
6576 function NtfSignRequirementsMet(nid in number)
6577 return boolean is
6578 sig_policy varchar2(100);
6579 sig_id number;
6580 sig_status number;
6581 creation_date date;
6582 signed_date date;
6583 verified_date date;
6584 lastAttVal_date date;
6585 validated_date date;
6586 ebuf varchar2(4000);
6587 estack varchar2(4000);
6588 l_attr_sigid number;
6589 sig_required Varchar2(1);
6590 fwk_sig_flavor Varchar2(255);
6591 email_sig_flavor Varchar2(255);
6592 render_hint Varchar2(255);
6593
6594 begin
6595 -- get the signature policy for the notification
6596 -- wf_mail.GetSignaturePolicy(nid, sig_policy);
6597 begin
6598 sig_policy := Wf_Notification.GetAttrText(nid, '#WF_SIG_POLICY');
6599 exception
6600 when others then
6601 if (wf_core.error_name = 'WFNTF_ATTR') then
6602 wf_core.clear;
6603 sig_policy := 'DEFAULT';
6604 else
6605 raise;
6606 end if;
6607 end;
6608
6609 /* check if signature is required for this sig_policy*/
6610 GetSignatureRequired(sig_policy, nid,sig_required, fwk_sig_flavor,
6611 email_sig_flavor, render_hint);
6612
6613 If(sig_required = 'N') then
6614 return TRUE;
6615 Elsif(sig_required = 'Y') then
6616
6617 -- bug 2779748: Cancelled Notification does not need to be signed
6618 begin
6619 if (WF_Notification.GetAttrText(nid, 'RESULT') = '#SIG_CANCEL') then
6620 return TRUE;
6621 end if;
6622 exception
6623 when others then
6624 if (wf_core.error_name = 'WFNTF_ATTR') then
6625 wf_core.clear;
6626 else
6627 raise;
6628 end if;
6629 end;
6630
6631 sig_id := WF_DIG_SIG_EVIDENCE_STORE.GetMostRecentSigID('WF_NTF', nid);
6632 begin
6633 -- #WF_SIG_ID may be defined as text or number... Now both will work
6634 -- Eventually should use GetAttrNumber
6635 l_attr_sigid := to_number(Wf_Notification.GetAttrText(nid, '#WF_SIG_ID'));
6636 exception
6637 when others then
6638 if (wf_core.error_name = 'WFNTF_ATTR') then
6639 wf_core.clear;
6640 l_attr_sigid := -1;
6641 else
6642 raise;
6643 end if;
6644 end;
6645 if (sig_id = -1 or sig_id <> l_attr_sigid) then
6646 return FALSE;
6647 end if;
6648
6649 WF_DIG_SIG_EVIDENCE_STORE.GetSigStatusInfo(sig_id, sig_status, creation_date,
6650 signed_date, verified_date, lastAttVal_date, validated_date,
6651 ebuf, estack);
6652 if (sig_status >= WF_DIGITAL_SECURITY_PRIVATE.STAT_AUTHORIZED) then
6653 return TRUE;
6654 end if;
6655 return FALSE;
6656 end if;
6657
6658 -- Currently only two policies supported. For others assume it is signed
6659 return TRUE;
6660
6661 exception
6662 when others then
6663 wf_core.context('Wf_Notification', 'NtfSignRequirementsMet', to_char(nid));
6664 raise;
6665 end NtfSignRequirementsMet;
6666
6667 --
6668 -- Request More Info
6669 --
6670
6671 --
6672 -- UpdateInfo
6673 -- non-null username: Ask this user for more information
6674 -- null username: Reply to the inquery
6675 -- comment could be question or answer
6676 -- NOTE:
6677 -- This is a Framework specific api. Embedded version SHOULD NOT call
6678 -- this api.
6679 -- Because we cannot validate a session inside Framework, calling such
6680 -- api outside of Framework may produce erroneous result.
6681 -- IN
6682 -- nid - Notification Id
6683 -- username - User to whom the comment is intended
6684 -- comment - Comment text
6685 -- wl_user - Worklist user to whom the notfication belongs, in case a proxy is acting
6686 -- action_source - Source from where the call is made. Could be null or 'WA'
6687 --
6688 procedure UpdateInfo(nid in number,
6689 username in varchar2,
6690 comment in varchar2,
6691 wl_user in varchar2,
6692 action_source in varchar2,
6693 cnt in number)
6694 is
6695 resource_busy exception;
6696 pragma exception_init(resource_busy, -00054);
6697
6698 l_from_role varchar2(320);
6699 replyby varchar2(320);
6700 myusername varchar2(320);
6701 mydispname varchar2(360);
6702 l_messageType varchar2(8);
6703 l_messageName varchar2(30);
6704 l_groupId number;
6705 l_parameterlist wf_parameter_list_t := wf_parameter_list_t();
6706 mailpref varchar2 (8);
6707 recipient_role VARCHAR2 (320);
6708 cb varchar2(240);
6709 context varchar2(2000);
6710 sqlbuf varchar2(2000);
6711 tvalue varchar2(4000);
6712 nvalue number ;
6713 dvalue date ;
6714 --Bug 3827935
6715 l_charcheck boolean;
6716
6717 --Bug 3065814
6718 l_recip_role varchar2(320);
6719 l_orig_recip_role varchar2(320);
6720 l_more_info_role varchar2(320);
6721 l_question_role varchar2(320);
6722 l_dummy varchar2(1);
6723
6724 --Bug 5444378
6725 l_ruleAction varchar2(8);
6726 l_newRole varchar2(2000);
6727 l_sysComment varchar2(320);
6728
6729 l_language varchar2(30);
6730 role_info_tbl wf_directory.wf_local_roles_tbl_type;
6731
6732 -- bug 7130745
6733 l_event_name varchar2(240);
6734
6735 CURSOR rulecurs is
6736 select action, action_argument
6737 from wf_routing_rules
6738 where role = username
6739 and nvl(message_type, l_messageType) = l_messageType
6740 and nvl(message_name,l_messageName) = l_messageName
6741 and sysdate between nvl(begin_date, sysdate-1) and
6742 nvl(end_date, sysdate+1);
6743 begin
6744
6745 -- Framework has control of a session.
6746 -- We are not allowed to re-validate a session any more. So we cannot
6747 -- use wfa_sec.GetSession() directly.
6748 myusername := wfa_sec.GetFWKUserName;
6749
6750 -- Bug 3065814
6751 -- Set the global context variables to appropriate values for this mode
6752 if (action_source = 'WA') then
6753 -- Action is performed by a proxy on behalf of wl_user.
6754 g_context_proxy := myusername;
6755 g_context_user := UpdateInfo.wl_user;
6756 -- In Answer mode, wl_user is the more_info_role and Fwk session user is the proxy_role
6757 myusername := UpdateInfo.wl_user;
6758 else
6759 -- Action is performed by the recipient of the notification
6760 g_context_proxy := null;
6761 g_context_user := myusername;
6762 end if;
6763
6764 mydispname := Wf_Directory.GetRoleDisplayName(myusername);
6765 g_context_user_comment := updateinfo.comment;
6766
6767 --Bug 3065814
6768 --Get the callback function
6769 SELECT callback , context ,RECIPIENT_ROLE, ORIGINAL_RECIPIENT,
6770 MORE_INFO_ROLE ,from_role, message_type, message_name
6771 into cb, context,l_recip_role , l_orig_recip_role,
6772 l_more_info_role, l_from_role, l_messageType, l_messageName
6773 FROM wf_notifications
6774 WHERE notification_id = nid;
6775
6776 g_context_recipient_role := l_recip_role;
6777 g_context_original_recipient:= l_orig_recip_role;
6778 g_context_from_role := l_from_role;
6779 --The new role will be different for 'ANSWER mode
6780 --we overwrite it there.
6781 g_context_new_role := username;
6782 g_context_more_info_role := l_more_info_role;
6783
6784 -- If we are in a different Fwk session, need to clear Workflow PLSQL state
6785 if (not Wfa_Sec.CheckSession) then
6786 Wf_Global.Init;
6787 end if;
6788
6789 -- question mode
6790 if (username is not null) then
6791 if (myusername = username) then
6792 --Bug 2474770
6793 --If the current user is the same as the one from
6794 --whom more-info is requested then raise the error
6795 --that you cannot ask for more info from yourself.
6796 wf_core.token('USER',username);
6797 wf_core.raise('WFNTF_INVALID_MOREINFO_REQUEST');
6798 else
6799 open rulecurs;
6800 fetch rulecurs INTO l_ruleAction,l_newRole;
6801 if rulecurs%NOTFOUND then
6802 l_ruleAction := 'NOOP';
6803 end if;
6804 close rulecurs;
6805
6806 if l_ruleAction IN ('FORWARD','TRANSFER') then
6807
6808 if l_newRole = myusername then
6809 wf_core.token('USER',myusername);
6810 wf_core.raise('WFNTF_INVALID_MOREINFO_REQUEST');
6811 elsif l_newRole = l_recip_role then
6812 wf_core.token('USER',l_recip_role);
6813 wf_core.raise('WFNTF_INVALID_MOREINFO_REQUEST');
6814 else
6815 -- Routing rule defined
6816 wf_core.token('ROLE', WF_Directory.GetRoleDisplayName(l_newRole));
6817 l_sysComment := wf_core.translate('WFNTF_AUTO_RESPONSE_TO_ROLE');
6818
6819 if myusername is not null then
6820 wf_notification.SetComments(nid, username, myusername, 'ANSWER', action_source, l_sysComment);
6821 else
6822 wf_notification.SetComments(nid, username, l_recip_role, 'ANSWER', action_source, l_sysComment);
6823 end if;
6824
6825 /* implement the above loop recursively */
6826 if (cnt > wf_notification.max_forward) then
6827 -- it means max_forward must have been exceeded. Treat as a loop error.
6828 wf_core.token('NID', to_char(nid));
6829 wf_core.raise('WFNTF_ROUTE_LOOP');
6830 end if;
6831 UpdateInfo(nid,l_newRole,comment,wl_user,action_source,cnt+1);
6832 end if;
6833 else
6834
6835 if l_ruleAction = 'RESPOND' then
6836 l_sysComment := wf_core.translate('WFNTF_AUTO_RESPONSE');
6837 if myusername is not null then
6838 wf_notification.SetComments(nid, username, myusername, 'ANSWER', action_source, l_sysComment);
6839 else
6840 wf_notification.SetComments(nid, username, l_recip_role, 'ANSWER', action_source, l_sysComment);
6841 end if;
6842 end if;
6843 -- do not want it hung when some one is doing update.
6844 begin
6845 select MORE_INFO_ROLE
6846 into l_from_role
6847 from WF_NOTIFICATIONS
6848 where NOTIFICATION_ID = nid
6849 for update nowait;
6850 exception
6851 when NO_DATA_FOUND then
6852 null;
6853 when resource_busy then
6854 wf_core.raise('WFNTF_BEING_UPDATED');
6855 -- ### This notification is being updated currently, please
6856 -- ### try again in a brief moment.
6857 end;
6858
6859 if (cb is not null) then
6860 tvalue := myusername;
6861 nvalue := nid;
6862 -- ### Review Note 2 - cb is from table
6863 --Check for bug#3827935
6864 l_charcheck := wf_notification_util.CheckIllegalChar(cb);
6865 --Throw the Illegal exception when the check fails
6866
6867
6868 -- BINDVAR_SCAN_IGNORE
6869 sqlbuf := 'begin '||cb||
6870 '(:p1, :p2, :p3, :p4, :p5, :p6, :p7); end;';
6871 execute immediate sqlbuf using
6872 in 'QUESTION',
6873 in context,
6874 in l_dummy,
6875 in l_dummy,
6876 in out tvalue,
6877 in out nvalue,
6878 in out dvalue;
6879
6880 end if;
6881
6882 -- always allow question
6883 -- bug 2474562
6884 -- allows any role, as restriction is done in the pop list level
6885 -- if (IsValidInfoRole(nid,username)) then
6886
6887 -- shanjgik 01-JUL-03 bug 2887130
6888 -- get mail preference of the user who will respond with more information
6889 mailpref := wf_notification.GetMailPreference (username, null, null);
6890
6891 -- if there is a valid session, then we can update the FROM_ROLE
6892 -- and FROM_USER accurately.
6893 if (myusername is not null) then
6894 update WF_NOTIFICATIONS
6895 set MORE_INFO_ROLE = username,
6896 FROM_USER = mydispname,
6897 FROM_ROLE = myusername,
6898 MAIL_STATUS = decode (mailpref, 'QUERY', '',
6899 'SUMMARY', '',
6900 'SUMHTML','',
6901 'DISABLED', 'FAILED',
6902 null, '', 'MAIL')
6903 where NOTIFICATION_ID = nid;
6904
6905 -- otherwise, we default to what it should be. Unfortunately, if it
6906 -- is a group role, we will not be able to identify which member I am.
6907 else
6908 update WF_NOTIFICATIONS
6909 set MORE_INFO_ROLE = username,
6910 FROM_USER = TO_USER,
6911 FROM_ROLE = RECIPIENT_ROLE,
6912 MAIL_STATUS = decode (mailpref, 'QUERY', '',
6913 'SUMMARY', '',
6914 'SUMHTML','',
6915 'DISABLED', 'FAILED',
6916 null, '', 'MAIL')
6917 where NOTIFICATION_ID = nid;
6918 end if;
6919
6920 Wf_Notification.SetComments(nid, myusername, username, 'QUESTION', action_source, substrb(comment,1,4000));
6921
6922 -- LANGUAGE here is for FROM_USER which came from WF_NOTIFICATIONS above
6923 -- insert into WF_COMMENTS (
6924 -- NOTIFICATION_ID,
6925 -- FROM_ROLE,
6926 -- FROM_USER,
6927 -- COMMENT_DATE,
6928 -- ACTION,
6929 -- USER_COMMENT,
6930 -- LANGUAGE
6931 -- )
6932 -- select NOTIFICATION_ID,
6933 -- FROM_ROLE,
6934 -- FROM_USER,
6935 -- sysdate,
6936 -- 'QUESTION',
6937 -- substrb(comment,1,4000),
6938 -- LANGUAGE
6939 -- from WF_NOTIFICATIONS
6940 -- where NOTIFICATION_ID = nid;
6941
6942 -- bug 2474562
6943 -- else
6944 -- wf_core.token('ROLE',username);
6945 -- wf_core.raise('WFNTF_NOT_PARTICIPANTS');
6946 -- end if;
6947 end if;
6948 end if;
6949
6950 -- if we are here, mean we are going to raise
6951 -- oracle.apps.wf.notification.question event.
6952 l_event_name := 'oracle.apps.wf.notification.question';
6953
6954
6955 -- answer mode
6956 -- NOTE: the language here is the language of the MORE_INFO_ROLE,
6957 -- no denormalization is needed here.
6958 else
6959 -- Do not allow reply when a question has not been asked, or it has
6960 -- already been answered. In both cases, MORE_INFO_ROLE is set to null.
6961 -- Also acquire a row lock, so that we do not let multiple people to
6962 -- answer at the same time.
6963 begin
6964 select MORE_INFO_ROLE,Wf_Directory.GetRoleDisplayName(MORE_INFO_ROLE), RECIPIENT_ROLE,FROM_ROLE
6965 into l_from_role, replyby, recipient_role,l_question_role
6966 from WF_NOTIFICATIONS
6967 where NOTIFICATION_ID = nid
6968 and MORE_INFO_ROLE is not null
6969 for update nowait;
6970
6971 exception
6972 when NO_DATA_FOUND then
6973 -- if it has no row, we cannot reply to this notification
6974 -- ### You cannot reply to a question that has not been asked
6975 -- ### or has already been answered.
6976 WF_MAIL.SendMoreInfoResponseWarning(nid);
6977 return;
6978 when resource_busy then
6979 wf_core.raise('WFNTF_BEING_UPDATED');
6980 -- ### This notification is being updated currently, please
6981 -- ### try again in a brief moment.
6982 end;
6983
6984 if (myusername is not null and l_from_role <> myusername) then
6985 if (not Wf_Directory.IsPerformer(myusername, l_from_role)) then
6986 wf_core.token('ROLE',myusername);
6987 wf_core.raise('WFNTF_NOT_PARTICIPANTS');
6988 end if;
6989 l_from_role := myusername;
6990 replyby := mydispname;
6991 end if;
6992
6993 if (cb is not null) then
6994 tvalue := myusername;
6995 nvalue := nid;
6996 g_context_new_role := l_question_role;
6997 -- ### Review Note 2 - cb is from table
6998 -- Check for bug#3827935
6999 l_charcheck := wf_notification_util.CheckIllegalChar(cb);
7000 --Throw the Illegal exception when the check fails
7001
7002 -- BINDVAR_SCAN_IGNORE
7003 sqlbuf := 'begin '||cb||
7004 '(:p1, :p2, :p3, :p4, :p5, :p6, :p7); end;';
7005 execute immediate sqlbuf using
7006 in 'ANSWER',
7007 in context,
7008 in l_dummy,
7009 in l_dummy,
7010 in out tvalue,
7011 in out nvalue,
7012 in out dvalue;
7013
7014 end if;
7015
7016 -- shanjgik 01-JUL-03 bug 2887130
7017 -- get the recipient's(one who requested more information) mail preference
7018 mailpref := wf_notification.GetMailPreference (recipient_role, null, null);
7019
7020 update WF_NOTIFICATIONS
7021 set FROM_USER = replyby,
7022 FROM_ROLE = l_from_role,
7023 MORE_INFO_ROLE = null,
7024 MAIL_STATUS = decode (mailpref, 'QUERY', '',
7025 'SUMMARY', '',
7026 'SUMHTML','',
7027 'DISABLED', 'FAILED',
7028 null, '', 'MAIL')
7029 where NOTIFICATION_ID = nid;
7030
7031 Wf_Notification.SetComments(nid, l_from_role, recipient_role, 'ANSWER', action_source, substrb(comment,1,4000));
7032 Wf_Notification.Route(nid, 0);
7033
7034 -- LANGUAGE here is for FROM_USER which came from GetRoleDisplayName above,
7035 -- so the LANGUAGE should be current userenv('LANG').
7036 -- insert into WF_COMMENTS (
7037 -- NOTIFICATION_ID,
7038 -- FROM_ROLE,
7039 -- FROM_USER,
7040 -- COMMENT_DATE,
7041 -- ACTION,
7042 -- USER_COMMENT,
7043 -- LANGUAGE
7044 -- )
7045 -- select NOTIFICATION_ID,
7046 -- FROM_ROLE,
7047 -- FROM_USER,
7048 -- sysdate,
7049 -- 'ANSWER',
7050 -- substrb(comment,1,4000),
7051 -- userenv('LANG')
7052 -- from WF_NOTIFICATIONS
7053 -- where NOTIFICATION_ID = nid;
7054
7055 -- <<BUG 7130745>>
7056 -- if we are here, mean we are going to raise
7057 -- oracle.apps.wf.notification.answer event.
7058 l_event_name := 'oracle.apps.wf.notification.answer';
7059
7060 end if;
7061
7062 -- Send the notification through email
7063 -- wf_xml.EnqueueNotification(nid);
7064
7065 -- Bug 2283697
7066 -- To raise an EVENT whenever DML operation is performed on
7067 -- WF_NOTIFICATIONS and WF_NOTIFICATION_ATTRIBUTES table.
7068 wf_event.AddParameterToList('NOTIFICATION_ID', nid, l_parameterlist);
7069 wf_event.AddParameterToList('ROLE', username, l_parameterlist);
7070 wf_event.AddParameterToList('GROUP_ID', nvl(l_groupId, nid), l_parameterlist);
7071 wf_event.addParameterToList('Q_CORRELATION_ID', l_messageType||':'||
7072 l_messageName, l_parameterlist);
7073
7074
7075 Wf_Directory.GetRoleInfo2(l_recip_role, role_info_tbl);
7076 l_language := role_info_tbl(1).language;
7077
7078 select code into l_language from wf_languages where nls_language = l_language;
7079
7080 -- AppSearch
7081 wf_event.AddParameterToList('OBJECT_NAME',
7082 'oracle.apps.fnd.wf.worklist.server.AllNotificationsVO', l_parameterlist);
7083 wf_event.AddParameterToList('CHANGE_TYPE', 'INSERT',l_parameterlist);
7084 wf_event.AddParameterToList('ID_TYPE', 'PK', l_parameterlist);
7085 wf_event.addParameterToList('PK_NAME_1', 'NOTIFICATION_ID',l_parameterlist);
7086 wf_event.addParameterToList('PK_VALUE_1', nid, l_parameterlist);
7087 wf_event.addParameterToList('PK_NAME_2', 'LANGUAGE',l_parameterlist);
7088 wf_event.addParameterToList('PK_VALUE_2', l_language, l_parameterlist);
7089
7090
7091 -- Raise the event
7092 -- wf_event.Raise(p_event_name => 'oracle.apps.wf.notification.send',
7093 -- p_event_key => to_char(nid),
7094 -- p_parameters => l_parameterlist);
7095
7096 -- <<sstomar: bug 7130745 : use different event names for question and answer >>
7097 wf_event.Raise(p_event_name => l_event_name,
7098 p_event_key => to_char(nid),
7099 p_parameters => l_parameterlist);
7100
7101 exception
7102 when OTHERS then
7103 Wf_Core.Context('Wf_Notification', 'UpdateInfo', to_char(nid), username, wl_user, action_source);
7104 raise;
7105 end UpdateInfo;
7106
7107 -- bug 2474562
7108 -- deprecate - this api is no longer needed, keeps this for reference only
7109 --
7110 -- IsValidInfoRole
7111 -- Check to see if a role is a participant so far
7112 function IsValidInfoRole(nid in number,
7113 username in varchar2)
7114 return boolean
7115 is
7116 itype varchar2(30);
7117 ikey varchar2(240);
7118 ans number;
7119 begin
7120 begin
7121 -- 99% of the case, it should be found in WIAS
7122 select ITEM_TYPE, ITEM_KEY
7123 into itype, ikey
7124 from WF_ITEM_ACTIVITY_STATUSES
7125 where NOTIFICATION_ID = nid;
7126 exception
7127 when NO_DATA_FOUND then
7128 begin
7129 -- rarely the nid is from WIASH, but just in case
7130 select ITEM_TYPE, ITEM_KEY
7131 into itype, ikey
7132 from WF_ITEM_ACTIVITY_STATUSES_H
7133 where NOTIFICATION_ID = nid;
7134 exception
7135 when NO_DATA_FOUND then
7136 -- Notification only
7137 begin
7138 select NULL, '#SYNCH'
7139 into itype, ikey
7140 from WF_NOTIFICATIONS
7141 where NOTIFICATION_ID = nid;
7142 exception
7143 when OTHERS then
7144 return(FALSE);
7145 end;
7146 when OTHERS then
7147 return(FALSE);
7148 end;
7149 when OTHERS then
7150 return(FALSE);
7151 end;
7152
7153 -- check if this is item owner
7154 begin
7155 select 1 into ans
7156 from WF_ITEMS
7157 where ITEM_TYPE = itype
7158 and ITEM_KEY = ikey
7159 and OWNER_ROLE = IsValidInfoRole.username;
7160
7161 return(TRUE);
7162 exception
7163 when NO_DATA_FOUND then
7164 null;
7165 end;
7166
7167 if (itype is null and ikey = '#SYNCH') then
7168 -- this is notification only
7169 begin
7170 select 1 into ans
7171 from WF_NOTIFICATIONS
7172 where IsValidInfoRole.username in (RECIPIENT_ROLE, ORIGINAL_RECIPIENT)
7173 and NOTIFICATION_ID = nid;
7174 exception
7175 when NO_DATA_FOUND then
7176 return(FALSE);
7177 end;
7178 else
7179 -- this is notification from a flow
7180 begin
7181 -- NOTE
7182 -- The following sql is suggested by Deb in the performance team
7183 -- it uses the index on group_id which is much more selective
7184 -- than those on recipient_role or original_recipient.
7185 -- Even though the explain plan seems to indicate a high cost, the
7186 -- run time performance on volumn database is much better.
7187 select 1 into ans
7188 from (
7189 select /*+ leading(grp_id_view) */
7190 RECIPIENT_ROLE , ORIGINAL_RECIPIENT
7191 from WF_NOTIFICATIONS a ,
7192 ( select notification_id group_id
7193 from WF_ITEM_ACTIVITY_STATUSES
7194 where item_type = itype
7195 and item_key = ikey
7196 union all
7197 select notification_id group_id
7198 from WF_ITEM_ACTIVITY_STATUSES_H
7199 where item_type = itype
7200 and item_key = ikey
7201 ) grp_id_view
7202 where grp_id_view.group_id = a.group_id
7203 ) recipient_view
7204 where (recipient_view.RECIPIENT_ROLE = IsValidInfoRole.username
7205 or recipient_view.ORIGINAL_RECIPIENT = IsValidInfoRole.username)
7206 and rownum < 2;
7207 exception
7208 when NO_DATA_FOUND then
7209 return(FALSE);
7210 end;
7211 end if;
7212 return(TRUE);
7213 exception
7214 when OTHERS then
7215 Wf_Core.Context('Wf_Notification','IsValidInfoRole',to_char(nid),username);
7216 raise;
7217 end IsValidInfoRole;
7218
7219 -- UpdateInfo2 - bug 2282139
7220 -- non-null username - Ask this user for more information
7221 -- null username - Reply to the inquery
7222 -- from email - from email id of responder/requestor
7223 -- comment - could be question or answer
7224 -- NOTE:
7225 -- This is a WF Mailer specific API. Used when a user requests more info
7226 -- or provides info through email response.
7227 --
7228 procedure UpdateInfo2(nid in number,
7229 username in varchar2,
7230 from_email in varchar2,
7231 comment in varchar2)
7232 is
7233 resource_busy exception;
7234 pragma exception_init(resource_busy, -00054);
7235
7236 l_from_role varchar2(320);
7237 replyby varchar2(320);
7238 myusername varchar2(320);
7239 mydispname varchar2(360);
7240 l_messageType varchar2(8);
7241 l_messageName varchar2(30);
7242 l_groupId number;
7243 l_parameterlist wf_parameter_list_t := wf_parameter_list_t();
7244 role_info_tbl wf_directory.wf_local_roles_tbl_type;
7245 l_username varchar2(320);
7246 l_stat varchar2(8);
7247
7248 --Bug 3065814
7249 l_recip_role varchar2(320);
7250 l_orig_recip_role varchar2(320);
7251 l_more_info_role varchar2(320);
7252 cb varchar2(240);
7253 context varchar2(2000);
7254 sqlbuf varchar2(2000);
7255 tvalue varchar2(4000);
7256 nvalue number;
7257 dvalue date;
7258 l_question_role varchar2(320);
7259 l_found boolean;
7260 l_dummy varchar2(1);
7261 -- Bug 3827935
7262 l_charcheck boolean;
7263 l_language varchar2(30);
7264
7265 -- bug 7130745
7266 l_event_name varchar2(240);
7267
7268 begin
7269 if (wf_log_pkg.level_procedure >= fnd_log.g_current_runtime_level) then
7270 wf_log_pkg.string(wf_log_pkg.level_procedure,
7271 'wf.plsql.WF_NOTIFICATION.UpdateInfo2.Begin',
7272 'NID: '||to_char(nid) ||', Username: '||username||
7273 ' From: '||from_email);
7274 end if;
7275
7276 -- Get notification related information
7277 SELECT callback, context, recipient_role, original_recipient,
7278 more_info_role, from_role, status, message_type, message_name
7279 INTO cb, context, l_recip_role, l_orig_recip_role,
7280 l_more_info_role, l_from_role, l_stat, l_messageType, l_messageName
7281 FROM wf_notifications
7282 WHERE notification_id = nid;
7283
7284 -- Donot process the request if the notification is not open.
7285 if (l_stat <> 'OPEN') then
7286 if (wf_log_pkg.level_statement >= fnd_log.g_current_runtime_level) then
7287 wf_log_pkg.string(wf_log_pkg.level_statement,
7288 'wf.plsql.WF_NOTIFICATION.UpdateInfo2.not_open',
7289 'Notification '||to_char(nid)||' is not OPEN. Returning.');
7290 end if;
7291 return;
7292 end if;
7293
7294 -- mailer doesnot have a valid user session. need to get
7295 -- the user name based on the from_email
7296 GetUserfromEmail(from_email, myusername, mydispname, l_found);
7297 if (l_found) then
7298 g_context_user := myusername;
7299 else
7300 g_context_user := 'email:' || myusername;
7301 end if;
7302
7303 if (wf_log_pkg.level_statement >= fnd_log.g_current_runtime_level) then
7304 wf_log_pkg.string(wf_log_pkg.level_statement,
7305 'wf.plsql.WF_NOTIFICATION.UpdateInfo2.got_user',
7306 'Email: '||from_email||' User: '||myusername||' DispName: '||mydispname);
7307 end if;
7308
7309 --Bug 3065814
7310 --Set the global context variables to appropriate values for this mode
7311 g_context_user_comment := updateinfo2.comment;
7312 g_context_recipient_role := l_recip_role;
7313 g_context_original_recipient:= l_orig_recip_role;
7314 g_context_from_role := l_from_role;
7315
7316 -- The new role will be different for 'ANSWER mode we overwrite it there.
7317 g_context_new_role := username;
7318 g_context_more_info_role := l_more_info_role;
7319
7320 -- question mode
7321 if (username is not null) then
7322
7323 -- Check if the question is asked to a valid role
7324 -- i. The role should be valid within WF Directory Service.
7325 -- ii. We might also want to check if the user is a participant of the ntf??
7326
7327 wf_directory.GetRoleInfo2(username, role_info_tbl);
7328 l_username := role_info_tbl(1).name;
7329
7330 -- Check if it is a Display Name
7331 if (l_username is NULL) then
7332 begin
7333 SELECT name
7334 INTO l_username
7335 FROM wf_role_lov_vl
7336 WHERE upper(display_name) = upper(username)
7337 AND rownum = 1;
7338 exception
7339 when NO_DATA_FOUND then
7340 wf_core.token('ROLE', username);
7341 wf_core.raise('WFNTF_ROLE');
7342 end;
7343 end if;
7344
7345 -- If the username was specified as display name, l_username would have the internal name
7346 if (l_username in (myusername, l_recip_role)) then
7347 -- If the current user is the same as the one from whom more-info is requested
7348 -- requested then raise the error that you cannot ask for more info from yourself.
7349 wf_core.token('USER',username);
7350 wf_core.raise('WFNTF_INVALID_MOREINFO_REQUEST');
7351 else
7352 -- do not want it hung when some one is doing update.
7353 begin
7354 select MORE_INFO_ROLE, MESSAGE_TYPE, MESSAGE_NAME, GROUP_ID
7355 into l_from_role, l_messageType, l_messageName, l_groupId
7356 from WF_NOTIFICATIONS
7357 where NOTIFICATION_ID = nid
7358 for update nowait;
7359 exception
7360 when NO_DATA_FOUND then
7361 null;
7362 when resource_busy then
7363 wf_core.raise('WFNTF_BEING_UPDATED');
7364 end;
7365
7366 if (wf_log_pkg.level_statement >= fnd_log.g_current_runtime_level) then
7367 wf_log_pkg.string(wf_log_pkg.level_statement,
7368 'wf.plsql.WF_NOTIFICATION.UpdateInfo2.question',
7369 'Updating QUESTION');
7370 end if;
7371
7372 if (cb is not null) then
7373 tvalue := myusername;
7374 nvalue := nid;
7375 -- ### Review Note 2 - cb is from table
7376 -- Check for bug#3827935
7377 l_charcheck := wf_notification_util.CheckIllegalChar(cb);
7378 --Throw the Illegal exception when the check fails
7379
7380 -- BINDVAR_SCAN_IGNORE
7381 sqlbuf := 'begin '||cb||
7382 '(:p1, :p2, :p3, :p4, :p5, :p6, :p7); end;';
7383 execute immediate sqlbuf using
7384 in 'QUESTION',
7385 in context,
7386 in l_dummy,
7387 in l_dummy,
7388 in out tvalue,
7389 in out nvalue,
7390 in out dvalue;
7391
7392 end if;
7393
7394 -- as we donot have a user session for the mailer, the only way to
7395 -- find FROM_ROLE and FROM_USER are through the from_addr. If the
7396 -- user name and display name are not available, email address is updated
7397 /* if (myusername is not null) then
7398 update WF_NOTIFICATIONS
7399 set MORE_INFO_ROLE = username,
7400 FROM_USER = mydispname,
7401 FROM_ROLE = myusername
7402 where NOTIFICATION_ID = nid;
7403
7404 -- otherwise, we default to what it should be. Unfortunately, if it
7405 -- is a group role, we will not be able to identify which member I am.
7406 else */
7407 update WF_NOTIFICATIONS
7408 set MAIL_STATUS = 'MAIL',
7409 MORE_INFO_ROLE = l_username,
7410 FROM_USER = TO_USER,
7411 FROM_ROLE = RECIPIENT_ROLE
7412 where NOTIFICATION_ID = nid;
7413 /*end if; */
7414
7415 Wf_Notification.SetComments(nid, myusername, l_username, 'QUESTION', null, substrb(comment,1,4000));
7416
7417 -- LANGUAGE here is for FROM_USER which came from WF_NOTIFICATIONS above
7418 -- insert into WF_COMMENTS (
7419 -- NOTIFICATION_ID,
7420 -- FROM_ROLE,
7421 -- FROM_USER,
7422 -- COMMENT_DATE,
7423 -- ACTION,
7424 -- USER_COMMENT,
7425 -- LANGUAGE
7426 -- )
7427 -- select NOTIFICATION_ID,
7428 -- FROM_ROLE,
7429 -- FROM_USER,
7430 -- sysdate,
7431 -- 'QUESTION',
7432 -- substrb(comment,1,4000),
7433 -- LANGUAGE
7434 -- from WF_NOTIFICATIONS
7435 -- where NOTIFICATION_ID = nid;
7436
7437 end if;
7438
7439 -- <<sstomar: bug 7130745>>
7440 -- we are here, mean we are going to raise
7441 -- oracle.apps.wf.notification.question event.
7442 l_event_name := 'oracle.apps.wf.notification.question';
7443
7444 -- answer mode
7445 -- NOTE: the language here is the language of the MORE_INFO_ROLE,
7446 -- no denormalization is needed here.
7447 else
7448 -- Do not allow reply when a question has not been asked, or it has
7449 -- already been answered. In both cases, MORE_INFO_ROLE is set to null.
7450 -- Also acquire a row lock, so that we do not let multiple people to
7451 -- answer at the same time.
7452 begin
7453 select MORE_INFO_ROLE, Wf_Directory.GetRoleDisplayName(MORE_INFO_ROLE),
7454 MESSAGE_TYPE, MESSAGE_NAME, GROUP_ID , from_role
7455 into l_from_role, replyby, l_messageType, l_messageName, l_groupId, l_question_role
7456 from WF_NOTIFICATIONS
7457 where NOTIFICATION_ID = nid
7458 and MORE_INFO_ROLE is not null
7459 for update nowait;
7460
7461 exception
7462 when NO_DATA_FOUND then
7463 -- if it has no row, we cannot reply to this notification
7464 -- ### You cannot reply to a question that has not been asked
7465 -- ### or has already been answered.
7466 WF_MAIL.SendMoreInfoResponseWarning(nid,from_email);
7467 return;
7468 when resource_busy then
7469 wf_core.raise('WFNTF_BEING_UPDATED');
7470 end;
7471
7472 -- we donot validate the role, it may be email address. we donot want
7473 -- FROM_ROLE and FROM_USER to be NULL.
7474 l_from_role := myusername;
7475 replyby := mydispname;
7476 if (cb is not null) then
7477 tvalue := myusername;
7478 nvalue := nid;
7479 g_context_new_role := l_question_role;
7480 -- ### Review Note 2 - cb is from table
7481 -- BINDVAR_SCAN_IGNORE
7482 sqlbuf := 'begin '||cb||
7483 '(:p1, :p2, :p3, :p4, :p5, :p6, :p7); end;';
7484 execute immediate sqlbuf using
7485 in 'ANSWER',
7486 in context,
7487 in l_dummy,
7488 in l_dummy,
7489 in out tvalue,
7490 in out nvalue,
7491 in out dvalue;
7492
7493 end if;
7494
7495 if (wf_log_pkg.level_statement >= fnd_log.g_current_runtime_level) then
7496 wf_log_pkg.string(wf_log_pkg.level_statement,
7497 'wf.plsql.WF_NOTIFICATION.UpdateInfo2.answer',
7498 'Updating ANSWER');
7499 end if;
7500
7501 update WF_NOTIFICATIONS
7502 set MAIL_STATUS = 'MAIL',
7503 FROM_USER = replyby,
7504 FROM_ROLE = l_from_role,
7505 MORE_INFO_ROLE = null
7506 where NOTIFICATION_ID = nid;
7507
7508 Wf_Notification.SetComments(nid, myusername, l_recip_role, 'ANSWER', null, substrb(comment,1,4000));
7509
7510 -- LANGUAGE here is for FROM_USER which came from GetRoleDisplayName above,
7511 -- so the LANGUAGE should be current userenv('LANG').
7512 -- insert into WF_COMMENTS (
7513 -- NOTIFICATION_ID,
7514 -- FROM_ROLE,
7515 -- FROM_USER,
7516 -- COMMENT_DATE,
7517 -- ACTION,
7518 -- USER_COMMENT,
7519 -- LANGUAGE
7520 -- )
7521 -- select NOTIFICATION_ID,
7522 -- FROM_ROLE,
7523 -- FROM_USER,
7524 -- sysdate,
7525 -- 'ANSWER',
7526 -- substrb(comment,1,4000),
7527 -- userenv('LANG')
7528 -- from WF_NOTIFICATIONS
7529 -- where NOTIFICATION_ID = nid;
7530
7531 -- we are here, mean we are going to raise
7532 -- oracle.apps.wf.notification.answer event.
7533 l_event_name := 'oracle.apps.wf.notification.answer';
7534
7535 end if; -- End of Answer mode
7536
7537 -- Send the notification through email
7538 -- Enqueuing has been moved to a subscription for forward
7539 -- compatability. The subscription need only be enabled to use
7540 -- the older mailer. The subscription will call the
7541 -- wf_xml.EnqueueNotification(nid);
7542
7543 -- Bug 2283697
7544 -- To raise an EVENT whenever DML operation is performed on
7545 -- WF_NOTIFICATIONS and WF_NOTIFICATION_ATTRIBUTES table.
7546 wf_event.AddParameterToList('NOTIFICATION_ID', nid, l_parameterlist);
7547
7548 -- username MAY be a display name
7549 wf_event.AddParameterToList('ROLE', nvl(l_username, username), l_parameterlist);
7550 wf_event.AddParameterToList('GROUP_ID', nvl(l_groupId, nid), l_parameterlist);
7551 wf_event.addParameterToList('Q_CORRELATION_ID', l_messageType||':'||
7552 l_messageName, l_parameterlist);
7553
7554 Wf_Directory.GetRoleInfo2(l_recip_role, role_info_tbl);
7555 l_language := role_info_tbl(1).language;
7556
7557 select code into l_language from wf_languages where nls_language = l_language;
7558
7559
7560 -- AppSearch
7561 wf_event.AddParameterToList('OBJECT_NAME',
7562 'oracle.apps.fnd.wf.worklist.server.AllNotificationsVO', l_parameterlist);
7563 wf_event.AddParameterToList('CHANGE_TYPE', 'INSERT',l_parameterlist);
7564 wf_event.AddParameterToList('ID_TYPE', 'PK', l_parameterlist);
7565 wf_event.addParameterToList('PK_NAME_1', 'NOTIFICATION_ID',l_parameterlist);
7566 wf_event.addParameterToList('PK_VALUE_1', nid, l_parameterlist);
7567 wf_event.addParameterToList('PK_NAME_2', 'LANGUAGE',l_parameterlist);
7568 wf_event.addParameterToList('PK_VALUE_2', l_language, l_parameterlist);
7569
7570
7571 -- Raise the event
7572 -- wf_event.Raise(p_event_name => 'oracle.apps.wf.notification.send',
7573 -- p_event_key => to_char(nid),
7574 -- p_parameters => l_parameterlist);
7575
7576 -- <<sstomar: bug 7130745 : use different event names for question and answer >>
7577 wf_event.Raise(p_event_name => l_event_name,
7578 p_event_key => to_char(nid),
7579 p_parameters => l_parameterlist);
7580
7581 exception
7582 when OTHERS then
7583 Wf_Core.Context('Wf_Notification', 'UpdateInfo2', to_char(nid), username, from_email);
7584 raise;
7585 end UpdateInfo2;
7586
7587
7588 -- UpdateInfoGuest:
7589 -- Called for updating more info when access key is present,
7590 -- responder to the request more info role as the user
7591 -- is trying to respond to Request More Info as GUEST user
7592 -- via E-mail without logging in.
7593 -- responder - Responder to the request more info
7594 -- moreinfoanswer - answer to request more information
7595 --
7596 procedure UpdateInfoGuest(nid in number,
7597 moreinforesponder in varchar2 default null,
7598 moreinfoanswer in varchar2 default null)
7599 is
7600 resource_busy exception;
7601 pragma exception_init(resource_busy, -00054);
7602
7603 l_from_role varchar2(320);
7604 l_recipient_role varchar2(320);
7605 replyby varchar2(320);
7606 l_messageType varchar2(8);
7607 l_messageName varchar2(30);
7608 l_groupId number;
7609 l_parameterlist wf_parameter_list_t := wf_parameter_list_t();
7610
7611 l_more_info_role varchar2(320);
7612 cb varchar2(240);
7613 context varchar2(2000);
7614 sqlbuf varchar2(2000);
7615 tvalue varchar2(4000);
7616 nvalue number;
7617 dvalue date;
7618 l_question_role varchar2(320);
7619 l_dummy varchar2(1);
7620 l_orig_recip_role varchar2(320);
7621 l_language varchar2(30);
7622 role_info_tbl wf_directory.wf_local_roles_tbl_type;
7623
7624 begin
7625 wf_log_pkg.string(WF_LOG_PKG.LEVEL_UNEXPECTED, 'WF_NOTIFICATION.UpdateInfoGuest',
7626 'NID: '||to_char(nid));
7627
7628 -- Do not allow reply when a question has not been asked, or it has
7629 -- already been answered. In both cases, MORE_INFO_ROLE is set to null.
7630 -- Also acquire a row lock, so that we do not let multiple people to
7631 -- answer at the same time.
7632 begin
7633 select ORIGINAL_RECIPIENT, RECIPIENT_ROLE, MORE_INFO_ROLE,
7634 Wf_Directory.GetRoleDisplayName(MORE_INFO_ROLE),
7635 MESSAGE_TYPE, MESSAGE_NAME, GROUP_ID , from_role, callback, context
7636 into l_orig_recip_role, l_recipient_role, l_from_role,
7637 replyby, l_messageType, l_messageName, l_groupId, l_question_role, cb, context
7638 from WF_NOTIFICATIONS
7639 where NOTIFICATION_ID = nid
7640 and MORE_INFO_ROLE is not null
7641 for update nowait;
7642
7643 --Bug 3924931
7644 --Set the global context variables to appropriate values for this mode
7645 g_context_user := updateinfoguest.moreinforesponder;
7646 g_context_user_comment := updateinfoguest.moreinfoanswer;
7647 g_context_new_role := l_question_role;
7648 g_context_recipient_role := l_recipient_role;
7649 g_context_original_recipient:= l_orig_recip_role;
7650 g_context_from_role := l_question_role;
7651 g_context_more_info_role := l_from_role;
7652
7653 exception
7654 when NO_DATA_FOUND then
7655 -- if it has no row, we cannot reply to this notification
7656 wf_core.raise('WFNTF_CANNOT_REPLY');
7657 -- ### You cannot reply to a question that has not been asked
7658 -- ### or has already been answered.
7659
7660 when resource_busy then
7661 wf_core.raise('WFNTF_BEING_UPDATED');
7662 end;
7663
7664 if (cb is not null) then
7665 tvalue := moreinforesponder;
7666 nvalue := nid;
7667 -- ### Note 2 - cb is from table
7668 -- BINDVAR_SCAN_IGNORE
7669 sqlbuf := 'begin '||cb||
7670 '(:p1, :p2, :p3, :p4, :p5, :p6, :p7); end;';
7671 execute immediate sqlbuf using
7672 in 'ANSWER',
7673 in context,
7674 in l_dummy,
7675 in l_dummy,
7676 in out tvalue,
7677 in out nvalue,
7678 in out dvalue;
7679
7680 end if;
7681
7682
7683 wf_log_pkg.string(WF_LOG_PKG.LEVEL_UNEXPECTED, 'WF_NOTIFICATION.UpdateInfoGuest',
7684 'Updating ANSWER');
7685
7686 update WF_NOTIFICATIONS
7687 set MAIL_STATUS = 'MAIL',
7688 FROM_USER = moreinforesponder,
7689 FROM_ROLE = moreinforesponder,
7690 MORE_INFO_ROLE = null
7691 where NOTIFICATION_ID = nid;
7692
7693 Wf_Notification.SetComments(nid, moreinforesponder, l_recipient_role, 'ANSWER', null, substrb(moreinfoanswer,1,4000));
7694 Wf_Notification.Route(nid, 0);
7695
7696 -- Send the notification through email
7697 -- Enqueuing has been moved to a subscription for forward
7698 -- compatability. The subscription need only be enabled to use
7699 -- the older mailer. The subscription will call the
7700 -- wf_xml.EnqueueNotification(nid);
7701
7702 -- Bug 2283697
7703 -- To raise an EVENT whenever DML operation is performed on
7704 -- WF_NOTIFICATIONS and WF_NOTIFICATION_ATTRIBUTES table.
7705 wf_event.AddParameterToList('NOTIFICATION_ID', nid, l_parameterlist);
7706 -- skilaru 12-MAR-04 In UpdateInfo2 username would be null in Answer mode
7707 -- to keep the behaviour same just pass null as ROLE..
7708 wf_event.AddParameterToList('ROLE', null, l_parameterlist);
7709 wf_event.AddParameterToList('GROUP_ID', nvl(l_groupId, nid), l_parameterlist);
7710 wf_event.addParameterToList('Q_CORRELATION_ID', l_messageType||':'||
7711 l_messageName, l_parameterlist);
7712
7713 Wf_Directory.GetRoleInfo2(l_recipient_role, role_info_tbl);
7714 l_language := role_info_tbl(1).language;
7715
7716 select code into l_language from wf_languages where nls_language = l_language;
7717
7718 -- AppSearch
7719 wf_event.AddParameterToList('OBJECT_NAME',
7720 'oracle.apps.fnd.wf.worklist.server.AllNotificationsVO', l_parameterlist);
7721 wf_event.AddParameterToList('CHANGE_TYPE', 'INSERT',l_parameterlist);
7722 wf_event.AddParameterToList('ID_TYPE', 'PK', l_parameterlist);
7723 wf_event.addParameterToList('PK_NAME_1', 'NOTIFICATION_ID',l_parameterlist);
7724 wf_event.addParameterToList('PK_VALUE_1', nid, l_parameterlist);
7725 wf_event.addParameterToList('PK_NAME_2', 'LANGUAGE',l_parameterlist);
7726 wf_event.addParameterToList('PK_VALUE_2', l_language, l_parameterlist);
7727
7728
7729 -- Raise the event
7730 -- wf_event.Raise(p_event_name => 'oracle.apps.wf.notification.send',
7731 -- p_event_key => to_char(nid),
7732 -- p_parameters => l_parameterlist);
7733
7734 -- <<sstomar: bug 7130745 : use different event names for question and answer >>
7735 wf_event.Raise(p_event_name => 'oracle.apps.wf.notification.answer',
7736 p_event_key => to_char(nid),
7737 p_parameters => l_parameterlist);
7738
7739 exception
7740 when OTHERS then
7741 Wf_Core.Context('Wf_Notification', 'UpdateInfoGuest', to_char(nid), moreinforesponder);
7742 raise;
7743 end UpdateInfoGuest;
7744
7745
7746
7747 --
7748 -- HideMoreInfo (PUBLIC)
7749 -- Checks the notification attribute #HIDE_MOREINFO to see if the
7750 -- More Information request button is allowed or hidden. Just in case
7751 -- more_info_role becomes not null with direct table update...
7752
7753 function HideMoreInfo(nid in number)
7754 return varchar2
7755 is
7756 l_hide varchar2(1);
7757 begin
7758 -- Get value for #HIDE_MOREINFO attribute for the notification
7759 begin
7760 l_hide := substrb(WF_NOTIFICATION.GetAttrText(nid, '#HIDE_MOREINFO'), 1, 1);
7761 -- Bugfix 2880029 - changed sacsharm - 03/31/03
7762 -- Only if attribute value is explicitly 'Y' hide Request More Info. else
7763 -- if it is null or 'N' or any other character donot hide Request More Info.
7764 l_hide := upper(nvl(l_hide, 'N'));
7765 if (l_hide <> 'Y') then
7766 l_hide := 'N';
7767 end if;
7768 exception
7769 when others then
7770 -- Bugfix 2880029 - changed sacsharm - 03/31/03
7771 -- If attribute not defined, do not hide Request More Info.
7772 if (wf_core.error_name = 'WFNTF_ATTR') then
7773 wf_core.clear;
7774 l_hide := 'N';
7775 else
7776 raise;
7777 end if;
7778 end;
7779 return (l_hide);
7780 exception
7781 when others then
7782 wf_core.context('Wf_Notification', 'HideMoreInfo', to_char(nid));
7783 raise;
7784 end HideMoreInfo;
7785
7786 -- GetComments
7787 -- Consolidates the questions and answers asked for the notification
7788 -- Also returns the last question asked.
7789 -- This is for the mailer to send the history with the email.
7790 -- It assumes that the table has been already opened
7791 -- IN
7792 -- nid - Notification id
7793 -- dislay_type The display type for the history
7794 -- OUT
7795 -- history in either text or html format
7796 -- last asked question
7797 procedure GetComments(nid in number,
7798 display_type in varchar2,
7799 html_history out nocopy varchar2,
7800 last_ques out nocopy varchar2)
7801 is
7802 CURSOR c_ques IS
7803 SELECT user_comment
7804 FROM wf_comments
7805 WHERE notification_id = nid
7806 AND action = 'QUESTION'
7807 ORDER BY comment_date desc;
7808 begin
7809 open c_ques;
7810 fetch c_ques into last_ques;
7811 if (c_ques%notfound) then
7812 last_ques := '';
7813 end if;
7814 close c_ques;
7815
7816 -- Call the GetComments2 procedure to get the Action History for only
7817 -- More Info Requests. This procedure was doing that previously
7818 Wf_Notification.GetComments2(p_nid => nid,
7819 p_display_type => display_type,
7820 p_hide_reassign => 'Y',
7821 p_hide_requestinfo => 'N',
7822 p_action_history => html_history);
7823
7824 exception
7825 when others then
7826 wf_core.context('Wf_Notification', 'GetComments', to_char(nid), display_type);
7827 raise;
7828 end GetComments;
7829
7830 --
7831 -- GetComments2
7832 -- Creates the Action History table for a given a notification id based on
7833 -- different filter criteria.
7834 -- IN
7835 -- p_nid - Notification id
7836 -- p_display_type - Display Type
7837 -- p_action_type - Action Type to look for (REASSIGN, RESPOND, QA,...)
7838 -- p_comment_date - Comment Date
7839 -- p_from_role - Comment provider
7840 -- p_to_role - Comment receiver
7841 -- p_hide_reassign - If Reassign comments be shown or not
7842 -- p_hide_requestinfo - If More Info request be shown or not
7843 -- OUT
7844 -- p_action_history - Action History table
7845 --
7846 procedure GetComments2(p_nid in number,
7847 p_display_type in varchar2,
7848 p_action_type in varchar2,
7849 p_comment_date in date,
7850 p_from_role in varchar2,
7851 p_to_role in varchar2,
7852 p_hide_reassign in varchar2,
7853 p_hide_requestinfo in varchar2,
7854 p_action_history out nocopy varchar2)
7855 is
7856 l_user_comment varchar2(4000);
7857 i pls_integer;
7858 j pls_integer;
7859 l_pos pls_integer;
7860 l_table_dir varchar2(1);
7861 l_dir varchar2(10);
7862 cells tdType;
7863 l_result varchar2(32000);
7864 l_delim varchar2(1);
7865 l_note varchar2(4000);
7866 l_action varchar2(30);
7867 l_item_type varchar2(8);
7868 l_item_key varchar2(240);
7869 l_actid number;
7870 l_result_type varchar2(30);
7871 l_result_code varchar2(30);
7872 l_action_str varchar2(250);
7873 l_wf_system varchar2(360);
7874 l_count number;
7875 l_suppress_hist varchar2(1);
7876 l_title varchar2(250);
7877
7878 CURSOR c_comments IS
7879 select rownum H_SEQUENCE, H_NOTIFICATION_ID, H_FROM_USER, H_TO_USER, H_ACTION_TYPE, H_ACTION,
7880 H_COMMENT, H_ACTION_DATE from
7881 (select H_SEQUENCE, H_NOTIFICATION_ID, H_FROM_USER, H_TO_USER, H_ACTION_TYPE, H_ACTION,
7882 H_COMMENT, H_ACTION_DATE from
7883 (select
7884 99999999 H_SEQUENCE,
7885 IAS.NOTIFICATION_ID H_NOTIFICATION_ID,
7886 IAS.ASSIGNED_USER H_FROM_ROLE,
7887 wf_directory.getRoleDisplayName(IAS.ASSIGNED_USER) H_FROM_USER,
7888 'WF_SYSTEM' H_TO_ROLE,
7889 l_wf_system H_TO_USER,
7890 A.RESULT_TYPE H_ACTION_TYPE,
7891 IAS.ACTIVITY_RESULT_CODE H_ACTION,
7892 '#WF_NOTE#' H_COMMENT,
7893 nvl(IAS.END_DATE, IAS.BEGIN_DATE) H_ACTION_DATE
7894 from WF_ITEM_ACTIVITY_STATUSES IAS,
7895 WF_ACTIVITIES A,
7896 WF_PROCESS_ACTIVITIES PA,
7897 WF_ITEMS I
7898 where IAS.ITEM_TYPE = l_item_type
7899 and IAS.ITEM_KEY = l_item_key
7900 and IAS.PROCESS_ACTIVITY = l_actid
7901 and IAS.ITEM_TYPE = I.ITEM_TYPE
7902 and IAS.ITEM_KEY = I.ITEM_KEY
7903 and IAS.ACTIVITY_RESULT_CODE IS NOT NULL
7904 and IAS.ACTIVITY_RESULT_CODE not in( '#EXCEPTION', '#FORCE', '#MAIL', '#NULL', '#STUCK', '#TIMEOUT')
7905 and I.BEGIN_DATE between A.BEGIN_DATE
7906 and nvl(A.END_DATE, I.BEGIN_DATE)
7907 and IAS.PROCESS_ACTIVITY = PA.INSTANCE_ID
7908 and PA.ACTIVITY_NAME = A.NAME
7909 and PA.ACTIVITY_ITEM_TYPE = A.ITEM_TYPE
7910 union all
7911 select
7912 99999999 H_SEQUENCE,
7913 IAS.NOTIFICATION_ID H_NOTIFICATION_ID,
7914 IAS.ASSIGNED_USER H_FROM_ROLE,
7915 wf_directory.getRoleDisplayName(IAS.ASSIGNED_USER) H_FROM_USER,
7916 'WF_SYSTEM' H_TO_ROLE,
7917 l_wf_system H_TO_USER,
7918 A.RESULT_TYPE H_ACTION_TYPE,
7919 IAS.ACTIVITY_RESULT_CODE H_ACTION,
7920 '#WF_NOTE#' H_COMMENT,
7921 nvl(IAS.END_DATE, IAS.BEGIN_DATE) H_ACTION_DATE
7922 from WF_ITEM_ACTIVITY_STATUSES_H IAS,
7923 WF_ACTIVITIES A,
7924 WF_PROCESS_ACTIVITIES PA,
7925 WF_ITEMS I
7926 where IAS.ITEM_TYPE = l_item_type
7927 and IAS.ITEM_KEY = l_item_key
7928 and IAS.PROCESS_ACTIVITY = l_actid
7929 and IAS.ITEM_TYPE = I.ITEM_TYPE
7930 and IAS.ITEM_KEY = I.ITEM_KEY
7931 and IAS.ACTIVITY_RESULT_CODE IS NOT NULL
7932 and IAS.ACTIVITY_RESULT_CODE not in( '#EXCEPTION', '#FORCE', '#MAIL', '#NULL', '#STUCK', '#TIMEOUT')
7933 and I.BEGIN_DATE between A.BEGIN_DATE
7934 and nvl(A.END_DATE, I.BEGIN_DATE)
7935 and IAS.PROCESS_ACTIVITY = PA.INSTANCE_ID
7936 and PA.ACTIVITY_NAME = A.NAME
7937 and PA.ACTIVITY_ITEM_TYPE = A.ITEM_TYPE
7938 union all
7939 select C.SEQUENCE H_SEQUENCE,
7940 C.NOTIFICATION_ID H_NOTIFICATION_ID,
7941 C.FROM_ROLE H_FROM_ROLE,
7942 C.FROM_USER H_FROM_USER,
7943 C.TO_ROLE H_TO_ROLE,
7944 C.TO_USER H_TO_USER,
7945 '#WF_COMMENTS#' H_ACTION_TYPE,
7946 C.ACTION H_ACTION,
7947 C.USER_COMMENT H_COMMENT,
7948 C.COMMENT_DATE H_ACTION_DATE
7949 from WF_ITEM_ACTIVITY_STATUSES IAS,
7950 WF_COMMENTS C
7951 where IAS.ITEM_TYPE = l_item_type
7952 and IAS.ITEM_KEY = l_item_key
7953 and IAS.PROCESS_ACTIVITY = l_actid
7954 and IAS.NOTIFICATION_ID = C.NOTIFICATION_ID
7955 and C.ACTION not in ('RESPOND', 'RESPOND_WA', 'RESPOND_RULE', 'SEND')
7956 union all
7957 select C.SEQUENCE H_SEQUENCE,
7958 C.NOTIFICATION_ID H_NOTIFICATION_ID,
7959 C.FROM_ROLE H_FROM_ROLE,
7960 C.FROM_USER H_FROM_USER,
7961 C.TO_ROLE H_TO_ROLE,
7962 C.TO_USER H_TO_USER,
7963 '#WF_COMMENTS#' H_ACTION_TYPE,
7964 C.ACTION H_ACTION,
7965 C.USER_COMMENT H_COMMENT,
7966 C.COMMENT_DATE H_ACTION_DATE
7967 from WF_ITEM_ACTIVITY_STATUSES_H IAS,
7968 WF_COMMENTS C
7969 where IAS.ITEM_TYPE = l_item_type
7970 and IAS.ITEM_KEY = l_item_key
7971 and IAS.PROCESS_ACTIVITY = l_actid
7972 and IAS.NOTIFICATION_ID = C.NOTIFICATION_ID
7973 and C.ACTION not in ('RESPOND', 'RESPOND_WA', 'RESPOND_RULE', 'SEND')
7974 )
7975 order by H_NOTIFICATION_ID, H_ACTION_DATE, H_SEQUENCE
7976 );
7977
7978 cursor c_ntf_hist is
7979 select rownum H_SEQUENCE, H_NOTIFICATION_ID, H_FROM_USER, H_TO_USER, H_ACTION_TYPE, H_ACTION,
7980 H_COMMENT, H_ACTION_DATE from
7981 (select H_SEQUENCE, H_NOTIFICATION_ID, H_FROM_USER, H_TO_USER, H_ACTION_TYPE, H_ACTION,
7982 H_COMMENT, H_ACTION_DATE from
7983 (select C.SEQUENCE H_SEQUENCE,
7984 C.NOTIFICATION_ID H_NOTIFICATION_ID,
7985 C.FROM_ROLE H_FROM_ROLE,
7986 C.FROM_USER H_FROM_USER,
7987 C.TO_ROLE H_TO_ROLE,
7988 C.TO_USER H_TO_USER,
7989 C.ACTION_TYPE H_ACTION_TYPE,
7990 C.ACTION H_ACTION,
7991 C.USER_COMMENT H_COMMENT,
7992 C.COMMENT_DATE H_ACTION_DATE
7993 from WF_ITEM_ACTIVITY_STATUSES IAS,
7994 WF_COMMENTS C
7995 where IAS.ITEM_TYPE = l_item_type
7996 and IAS.ITEM_KEY = l_item_key
7997 and IAS.PROCESS_ACTIVITY = l_actid
7998 and IAS.NOTIFICATION_ID = C.NOTIFICATION_ID
7999 and C.ACTION_TYPE in ('REASSIGN', 'QA')
8000 union all
8001 select C.SEQUENCE H_SEQUENCE,
8002 C.NOTIFICATION_ID H_NOTIFICATION_ID,
8003 C.FROM_ROLE H_FROM_ROLE,
8004 C.FROM_USER H_FROM_USER,
8005 C.TO_ROLE H_TO_ROLE,
8006 C.TO_USER H_TO_USER,
8007 C.ACTION_TYPE H_ACTION_TYPE,
8008 C.ACTION H_ACTION,
8009 C.USER_COMMENT H_COMMENT,
8010 C.COMMENT_DATE H_ACTION_DATE
8011 from WF_ITEM_ACTIVITY_STATUSES_H IAS,
8012 WF_COMMENTS C
8013 where IAS.ITEM_TYPE = l_item_type
8014 and IAS.ITEM_KEY = l_item_key
8015 and IAS.PROCESS_ACTIVITY = l_actid
8016 and IAS.NOTIFICATION_ID = C.NOTIFICATION_ID
8017 and C.ACTION_TYPE in ('REASSIGN', 'QA')
8018 )
8019 order by H_NOTIFICATION_ID, H_ACTION_DATE, H_SEQUENCE
8020 );
8021
8022 l_comm_rec c_comments%ROWTYPE;
8023 l_ntf_hist_rec c_ntf_hist%ROWTYPE;
8024 begin
8025
8026
8027 begin
8028 SELECT item_type, item_key, process_activity
8029 INTO l_item_type, l_item_key, l_actid
8030 FROM wf_item_activity_statuses
8031 WHERE notification_id = p_nid;
8032 exception
8033 when NO_DATA_FOUND then
8034 begin
8035 SELECT item_type, item_key, process_activity
8036 INTO l_item_type, l_item_key, l_actid
8037 FROM wf_item_activity_statuses_h
8038 WHERE notification_id = p_nid;
8039 exception
8040 when NO_DATA_FOUND then
8041 -- It is possible that notification is sent outside of a flow,
8042 -- in that case, it will not appear in Workflow runtime tables.
8043 -- Just return here.
8044 return;
8045 end;
8046 end;
8047
8048 if (l_item_type in ('POSCHORD', 'POSUPDNT', 'POSORDNT', 'POSASNNB', 'CREATEPO', 'POAPPRV',
8049 'POPRICAT', 'PORCOTOL', 'PONGRQCH', 'POERROR', 'POWFDS', 'RCVDMEMO',
8050 'APVRMDER', 'POREQCHA', 'PORCPT', 'REQAPPRV', 'PORPOCHA')) then
8051 l_suppress_hist := 'Y';
8052 l_title := Wf_Core.Translate('WFNTF_NTF_HISTORY');
8053 else
8054 l_suppress_hist := 'N';
8055 l_title := Wf_Core.Translate('WFNTF_ACTION_HISTORY');
8056 end if;
8057
8058 l_wf_system := Wf_Core.Translate('WF_SYSTEM');
8059 l_delim := ':';
8060
8061 l_table_dir := table_direction;
8062 if (l_table_dir = 'L') then
8063 l_dir := null;
8064 else
8065 l_dir := 'dir="RTL"';
8066 end if;
8067
8068 j := 1;
8069 -- Action History Title
8070 cells(j) := wf_core.translate('NUM');
8071 if (p_display_type = wf_notification.doc_html) then
8072 cells(j) := 'S5%:'||cells(j);
8073 end if;
8074
8075 j := j+1;
8076 cells(j) := wf_core.translate('ACTION_DATE');
8077 if (p_display_type = wf_notification.doc_html) then
8078 cells(j) := 'S15%:'||cells(j);
8079 end if;
8080
8081 j := j+1;
8082 cells(j) := wf_core.translate('ACTION');
8083 if (p_display_type = wf_notification.doc_html) then
8084 cells(j) := 'S10%:'||cells(j);
8085 end if;
8086
8087 j := j+1;
8088 cells(j) := wf_core.translate('FROM');
8089 if (p_display_type = wf_notification.doc_html) then
8090 cells(j) := 'S15%:'||cells(j);
8091 end if;
8092
8093 j := j+1;
8094 cells(j) := wf_core.translate('TO');
8095 if (p_display_type = wf_notification.doc_html) then
8096 cells(j) := 'S15%:'||cells(j);
8097 end if;
8098
8099 j := j+1;
8100 cells(j) := wf_core.translate('DETAILS');
8101 if (p_display_type = wf_notification.doc_html) then
8102 cells(j) := 'S40%:'||cells(j);
8103 end if;
8104
8105 j := j+1;
8106
8107 -- OPEN l_comments_c FOR l_sql_stmt using p_action_type, p_action_type, p_action_type,
8108 -- l_action_type1, l_action_type2, p_comment_date, p_from_role, p_to_role, p_nid;
8109
8110 if (l_suppress_hist = 'N') then
8111
8112 OPEN c_comments;
8113 -- Construct the action history table with all the matching comments records
8114 loop
8115 fetch c_comments into l_comm_rec;
8116 exit when c_comments%NOTFOUND;
8117
8118 cells(j) := to_char(l_comm_rec.h_sequence);
8119
8120 j := j+1;
8121 if (p_display_type = wf_notification.doc_html) then
8122 -- <bug 7514495>
8123 cells(j) := 'S:' || wf_notification_util.GetCalendarDate(p_nid, l_comm_rec.h_action_date, null, true);
8124 else
8125 cells(j) := wf_notification_util.GetCalendarDate(p_nid, l_comm_rec.h_action_date, null, true);
8126 end if;
8127
8128 j := j+1;
8129
8130 -- If the record is not from WF_COMMENTS, need to resolve the action
8131 if (l_comm_rec.h_action_type <> '#WF_COMMENTS#') then
8132 l_action_str := Wf_Core.Activity_Result(l_comm_rec.h_action_type, l_comm_rec.h_action);
8133 else
8134 l_action := l_comm_rec.h_action;
8135 l_pos := instr(l_action, '_', 1);
8136 if (l_pos > 0) then
8137 l_action := substr(l_action, 1, l_pos-1);
8138 end if;
8139 l_action_str := Wf_Core.Translate(l_action);
8140 end if;
8141
8142 if (p_display_type = wf_notification.doc_html) then
8143 cells(j) := 'S:'||l_action_str;
8144 else
8145 cells(j) := l_action_str;
8146 end if;
8147
8148 j := j+1;
8149 if (p_display_type = wf_notification.doc_html) then
8150 cells(j) := 'S:'||Wf_Notification.SubstituteSpecialChars(l_comm_rec.h_from_user);
8151 else
8152 cells(j) := l_comm_rec.h_from_user;
8153 end if;
8154
8155 j := j+1;
8156 if (p_display_type = wf_notification.doc_html) then
8157 cells(j) := 'S:'||Wf_Notification.SubstituteSpecialChars(l_comm_rec.h_to_user);
8158 else
8159 cells(j) := l_comm_rec.h_to_user;
8160 end if;
8161
8162 j := j+1;
8163 l_note := l_comm_rec.h_comment;
8164 -- WF_NOTE indicates that this is a Respond attribute.
8165 if (l_note = '#WF_NOTE#') then
8166 begin
8167 SELECT text_value
8168 INTO l_note
8169 FROM wf_notification_attributes
8170 WHERE notification_id = l_comm_rec.h_notification_id
8171 AND name = 'WF_NOTE';
8172 exception
8173 when no_data_found then
8174 l_note := '';
8175 end;
8176 end if;
8177 if (p_display_type = wf_notification.doc_html) then
8178 l_note := substrb(Wf_Notification.SubstituteSpecialChars(l_note), 1, 4000);
8179 end if;
8180 cells(j) := l_note;
8181
8182 if (p_display_type = wf_notification.doc_html) then
8183 if (cells(j) is null) then
8184 cells(j) := 'S: ';
8185 else
8186 cells(j) := 'S:'||cells(j);
8187 end if;
8188 end if;
8189 j := j+1;
8190 end loop;
8191
8192 l_count := c_comments%rowcount;
8193 CLOSE c_comments;
8194
8195 elsif (l_suppress_hist = 'Y') then
8196
8197 OPEN c_ntf_hist;
8198
8199 -- Construct the action history table with all the matching comments records
8200 loop
8201 fetch c_ntf_hist into l_ntf_hist_rec;
8202 exit when c_ntf_hist%NOTFOUND;
8203
8204 cells(j) := to_char(l_ntf_hist_rec.h_sequence);
8205
8206 j := j+1;
8207 if (p_display_type = wf_notification.doc_html) then
8208 cells(j) := 'S:'|| wf_notification_util.GetCalendarDate(p_nid, l_ntf_hist_rec.h_action_date, null, true);
8209 else
8210 cells(j) := wf_notification_util.GetCalendarDate(p_nid, l_ntf_hist_rec.h_action_date, null, true);
8211 end if;
8212
8213 j := j+1;
8214
8215 l_action := l_ntf_hist_rec.h_action;
8216 l_pos := instr(l_action, '_', 1);
8217 if (l_pos > 0) then
8218 l_action := substr(l_action, 1, l_pos-1);
8219 end if;
8220 l_action_str := Wf_Core.Translate(l_action);
8221
8222 if (p_display_type = wf_notification.doc_html) then
8223 cells(j) := 'S:'||l_action_str;
8224 else
8225 cells(j) := l_action_str;
8226 end if;
8227
8228 j := j+1;
8229 if (p_display_type = wf_notification.doc_html) then
8230 cells(j) := 'S:'||Wf_Notification.SubstituteSpecialChars(l_ntf_hist_rec.h_from_user);
8231 else
8232 cells(j) := l_ntf_hist_rec.h_from_user;
8233 end if;
8234
8235 j := j+1;
8236 if (p_display_type = wf_notification.doc_html) then
8237 cells(j) := 'S:'||Wf_Notification.SubstituteSpecialChars(l_ntf_hist_rec.h_to_user);
8238 else
8239 cells(j) := l_ntf_hist_rec.h_to_user;
8240 end if;
8241
8242 j := j+1;
8243 l_note := l_ntf_hist_rec.h_comment;
8244 if (p_display_type = wf_notification.doc_html) then
8245 l_note := substrb(Wf_Notification.SubstituteSpecialChars(l_note), 1, 4000);
8246 end if;
8247 cells(j) := l_note;
8248
8249 if (p_display_type = wf_notification.doc_html) then
8250 if (cells(j) is null) then
8251 cells(j) := 'S: ';
8252 else
8253 cells(j) := 'S:'||cells(j);
8254 end if;
8255 end if;
8256 j := j+1;
8257 end loop;
8258
8259 l_count := c_ntf_hist%rowcount;
8260 CLOSE c_ntf_hist;
8261
8262 end if;
8263
8264 -- If there is nothing to display, return a null
8265 if (l_count = 0) then
8266 p_action_history := '';
8267 return;
8268 end if;
8269
8270 -- Sequence is now based on the rownum, not the reverse
8271 -- for k in 0..(i-1) loop
8272 -- if (p_display_type = wf_notification.doc_html) then
8273 -- cells((k+1)*6+1) := 'C:'||to_char(i-k-1);
8274 -- else
8275 -- cells((k+1)*6+1) := to_char(i-k-1);
8276 -- end if;
8277 -- end loop;
8278
8279 -- Construct table from the cells
8280 if (p_display_type = wf_notification.doc_html) then
8281 table_width := '100%';
8282 -- bug 7718246 - set the table border to 1 only for action history
8283 table_border := '1';
8284 NTF_Table(cells=>cells,
8285 col=>6,
8286 type=>'H'||l_table_dir,
8287 rs=>l_result);
8288
8289 -- Display title "Action History"
8290 l_result := '<table width='||table_width||
8291 ' border="0" cellspacing="0" cellpadding="0" '||l_dir||'>' ||
8292 '<tr><td class="x3w">'||
8293 l_title||'</td></tr>'||'<tr><td>'||l_result||'</td></tr></table>';
8294 -- reset table border to default value after generating action history
8295 table_border := '0';
8296 else
8297 for k in 1..cells.LAST loop
8298 if (mod(k, 6) <> 0) then
8299 l_result := l_result||cells(k)||' '||l_delim||' ';
8300 else
8301 l_result := l_result||cells(k)||wf_core.newline;
8302 end if;
8303 end loop;
8304 l_result := wf_core.translate('WFNTF_ACTION_HISTORY')||wf_core.newline||l_result;
8305 end if;
8306
8307 p_action_history := l_result;
8308
8309 exception
8310 when others then
8311 wf_core.context('Wf_Notification', 'GetComments2', to_char(p_nid), p_display_type);
8312 raise;
8313 end GetComments2;
8314
8315 --
8316 -- GetAttrblob
8317 -- Get the displayed value of a PLSQLBLOB DOCUMENT-type attribute.
8318 -- Returns referenced document in format requested.
8319 -- Use GetAttrText to get retrieve the actual attr value (i.e. the
8320 -- document key string instead of the actual document).
8321 -- NOTE:
8322 -- a. Only PLSQL document type is implemented.
8323 -- b. This will be called by old mailers. This is a wrapper to the
8324 -- new implementation which returns the doctype also.
8325 -- IN:
8326 -- nid - Notification id
8327 -- astring - the string to substitute on (ex: '&ATTR1 is your order..')
8328 -- disptype - Requested display type. Valid values:
8329 -- wf_notification.doc_text - 'text/plain'
8330 -- wf_notification.doc_html - 'text/html'
8331 -- document - The blob into which
8332 -- aname - Attribute Name (the first part of the string that matches
8333 -- the attr list)
8334 --
8335 procedure GetAttrblob(
8336 nid in number,
8337 astring in varchar2,
8338 disptype in varchar2,
8339 document in out nocopy blob,
8340 aname out nocopy varchar2)
8341 is
8342 doctype varchar2(500);
8343 begin
8344
8345 Wf_Notification.GetAttrblob(nid, astring, disptype, document, doctype, aname);
8346
8347 exception
8348 when others then
8349 wf_core.context('Wf_Notification', 'oldGetAttrblob', to_char(nid), aname,
8350 disptype);
8351 raise;
8352 end GetAttrblob;
8353
8354 -- GetAttrblob
8355 -- Get the displayed value of a PLSQLBLOB DOCUMENT-type attribute.
8356 -- Returns referenced document in format requested.
8357 -- Use GetAttrText to get retrieve the actual attr value (i.e. the
8358 -- document key string instead of the actual document).
8359 -- NOTE:
8360 -- Only PLSQL document type is implemented.
8361 -- IN:
8362 -- nid - Notification id
8363 -- astring - the string to substitute on (ex: '&ATTR1 is your order..')
8364 -- disptype - Requested display type. Valid values:
8365 -- wf_notification.doc_text - 'text/plain'
8366 -- wf_notification.doc_html - 'text/html'
8367 -- document - The blob into which
8368 -- aname - Attribute Name (the string that matches
8369 -- the attr list)
8370 --
8371 procedure GetAttrblob(
8372 nid in number,
8373 astring in varchar2,
8374 disptype in varchar2,
8375 document in out nocopy blob,
8376 doctype out nocopy varchar2,
8377 aname out nocopy varchar2)
8378 is
8379 key varchar2(4000);
8380 colon pls_integer;
8381 slash pls_integer;
8382 dmstype varchar2(30);
8383 display_name varchar2(80);
8384 procname varchar2(240);
8385 launch_url varchar2(4000) := null;
8386 procarg varchar2(32000);
8387 username varchar2(320);
8388
8389 --curs integer;
8390 sqlbuf varchar2(2000);
8391 --rows integer;
8392
8393 target varchar2(240);
8394 l_charcheck boolean;
8395
8396 begin
8397
8398 -- Check args
8399 if ((nid is null) or (astring is null) or
8400 (disptype not in (wf_notification.doc_text,
8401 wf_notification.doc_html))) then
8402 wf_core.token('NID', to_char(nid));
8403 wf_core.token('ASTRING', aname);
8404 wf_core.token('DISPTYPE', disptype);
8405 wf_core.raise('WFSQL_ARGS');
8406 end if;
8407
8408 -- of all the possible Document type matches,
8409 -- make sure its a PLSQLBLOB
8410 dmstype := '';
8411
8412 -- Bug 6324545: Replaced the cursor with a simple sql to fetch a single row.
8413 begin
8414 -- <7443088> improved query
8415 select NAME into aname from
8416 (select WMA.NAME
8417 from WF_NOTIFICATIONS WN,
8418 WF_MESSAGE_ATTRIBUTES WMA,
8419 WF_NOTIFICATION_ATTRIBUTES NA
8420 where WN.NOTIFICATION_ID = nid
8421 and wn.notification_id = na.notification_id
8422 and wma.name = na.name
8423 and WN.MESSAGE_TYPE = WMA.MESSAGE_TYPE
8424 and WN.MESSAGE_NAME = WMA.MESSAGE_NAME
8425 and WMA.TYPE = 'DOCUMENT'
8426 and instr( upper(astring) ,wma.name) = 1
8427 and upper(na.text_value) like 'PLSQLBLOB:%'
8428 order by length(wma.name) desc)
8429 where rownum=1;
8430 exception
8431 when no_data_found then
8432 aname:=null;
8433 return;
8434 end;
8435
8436 if (aname is not null) then
8437 -- Retrieve key string
8438 key := wf_notification.GetAttrText(nid, aname);
8439
8440 -- If the key is empty then return a null string
8441 if (key is not null) then
8442
8443 -- Parse doc mgmt system type from key
8444 colon := instr(key, ':');
8445 if ((colon <> 0) and (colon < 30)) then
8446 dmstype := upper(substr(key, 1, colon-1));
8447 end if;
8448 end if;
8449 end if;
8450
8451 -- if we didnt find any plsqlblobs then exit now
8452 if dmstype is null or (dmstype <> 'PLSQLBLOB') then
8453 aname:=null;
8454 return;
8455 end if;
8456
8457 -- We must be processing a BLOB PLSQL doc type
8458 slash := instr(key, '/');
8459 if (slash = 0) then
8460 procname := substr(key, colon+1);
8461 procarg := '';
8462 else
8463 procname := substr(key, colon+1, slash-colon-1);
8464 procarg := substr(key, slash+1);
8465 end if;
8466
8467 -- Dynamic sql call to procedure
8468 -- bug 2706082 using execute immediate instead of dbms_sql.execute
8469
8470 if (procarg is null) then
8471 procarg := NULL;
8472 else
8473 procarg := Wf_Notification.GetTextInternal(procarg, nid, target,
8474 FALSE, FALSE);
8475 end if;
8476
8477 -- ### Review Note 1
8478 -- Check for bug#3827935
8479 l_charcheck := wf_notification_util.CheckIllegalChar(procname);
8480 --Throw the Illegal exception when the check fails
8481
8482 if (wf_log_pkg.level_statement >= fnd_log.g_current_runtime_level) then
8483 wf_log_pkg.string2(wf_log_pkg.level_statement,
8484 'wf.plsql.wf_notification.GetAttrBlob.plsqlblob_callout',
8485 'Start executing PLSQLBLOB Doc procedure - '||procname, true);
8486 end if;
8487
8488 sqlbuf := 'begin '||procname||'(:p1, :p2, :p3, :p4); end;';
8489 execute immediate sqlbuf using
8490 in procarg,
8491 in disptype,
8492 in out document,
8493 in out doctype;
8494
8495 if (wf_log_pkg.level_statement >= fnd_log.g_current_runtime_level) then
8496 wf_log_pkg.string2(wf_log_pkg.level_statement,
8497 'wf.plsql.wf_notification.GetAttrBlob.plsqlblob_callout',
8498 'End executing PLSQLBLOB Doc procedure - '||procname, false);
8499 end if;
8500
8501 exception
8502 when others then
8503 -- Close cursor just in case
8504 /* if (dbms_sql.is_open(curs)) then
8505 dbms_sql.close_cursor(curs);
8506 end if; */
8507
8508 wf_core.context('Wf_Notification', 'GetAttrblob', to_char(nid), aname,
8509 disptype);
8510 raise;
8511 end GetAttrblob;
8512
8513 --
8514 -- Set_NTF_Table_Direction
8515 -- Sets the default direction of notification tables
8516 -- generated through wf_notification.wf_ntf_history
8517 -- and wf_notification.wf_msg_attr
8518 procedure Set_NTF_Table_Direction(direction in varchar2)
8519 is
8520 begin
8521 table_direction := direction;
8522 end Set_NTF_Table_direction;
8523
8524 --
8525 -- Set_NTF_Table_Type
8526 -- Sets the default table type for attr tables
8527 -- generated through wf_notification.wf_msg_attr
8528 procedure Set_NTF_Table_Type(tableType in varchar2)
8529 is
8530 begin
8531 table_type := tableType;
8532 end Set_NTF_Table_Type;
8533
8534
8535 -- isFwkRegion
8536 -- verifies whether message for given notification id contains
8537 -- any framework regions.
8538 -- Algorithm: Function returns 'Y' if one of the following condition is met
8539 -- - If header region attribute #HDR_REGION of type 'DOCUMENT'
8540 -- and its value starts with 'JSP:/OA_HTML/OA.jsp?'
8541 -- - If the message body is of type framework region
8542 --
8543
8544 function isFwkRegion(nid in number) return varchar2 is
8545
8546 begin
8547 return isFwkRegion(nid, wf_notification.doc_html);
8548 end isFwkRegion;
8549
8550
8551 -- isFwkRegion
8552 -- verifies whether message for given notification id contains
8553 -- any framework regions.
8554 -- Algorithm: Function returns 'Y' if one of the following condition is met
8555 -- - If header region attribute #HDR_REGION of type 'DOCUMENT'
8556 -- and its value starts with 'JSP:/OA_HTML/OA.jsp?'
8557 -- - If the message body is of type framework region
8558 --
8559 -- Auth : SSTOMAR
8560
8561 function isFwkRegion(nid in number, content_type in varchar2 ) return varchar2 is
8562
8563 lv_body varchar2(32000);
8564 lv_html_body varchar2(32000);
8565 lv_final_body varchar2(32000);
8566 lv_fwk_region varchar2(1);
8567 lv_first_token varchar2(240);
8568 lv_token_start number;
8569
8570 cursor cur_hdr_region is
8571 select WNA.NAME, WNA.TEXT_VALUE
8572 from WF_NOTIFICATION_ATTRIBUTES WNA,
8573 WF_MESSAGE_ATTRIBUTES_VL WMA
8574 where WNA.NOTIFICATION_ID = nid
8575 and WMA.NAME = WNA.NAME
8576 and WMA.TYPE = 'DOCUMENT'
8577 and WNA.NAME = '#HDR_REGION';
8578
8579 begin
8580
8581 lv_fwk_region := 'N';
8582
8583 for attr_row in cur_hdr_region loop
8584 if( instr(attr_row.text_value, fwk_region_start) = 1 ) then
8585 lv_fwk_region := 'Y';
8586 exit;
8587 end if;
8588 end loop;
8589 -- If framework header region exists then return
8590 if( lv_fwk_region = 'Y' ) then
8591 return lv_fwk_region;
8592 -- else check whether message body is of type framework region
8593 else
8594 return isFwkBody( nid, content_type);
8595 end if;
8596 exception
8597 when OTHERS then
8598 wf_core.context('Wf_Notification','isFwkRegion',to_char(nid), content_type);
8599 raise;
8600
8601 End isFwkRegion;
8602
8603 -- isFwkBody
8604 -- verifies whether message body for given notification id contains
8605 -- any framework regions.
8606 -- Algorithm: Function returns 'Y' if one of the following condition is met
8607 -- - If the first attribute referred in the body is of
8608 -- type 'DOCUMENT' and its value starts with 'JSP:/OA_HTML/OA.jsp?'
8609 -- - If the message body does not have any attributes refered except
8610 -- for WF_NOTIFICATION macro and simple text
8611
8612 function isFwkBody(nid in number) return varchar2 is
8613
8614
8615 begin
8616 -- invoke overrided API with default as 'text/html'
8617 return isFwkBody(nid, wf_notification.doc_html);
8618
8619 End isFwkBody;
8620
8621 -- isFwkBody
8622 -- verifies whether message body for given notification id contains
8623 -- any framework regions.
8624 -- Algorithm: Function returns 'Y' if one of the following condition is met
8625 -- - If the first attribute referred in the body is of
8626 -- type 'DOCUMENT' and its value starts with 'JSP:/OA_HTML/OA.jsp?'
8627 -- - If the message body does not have any attributes refered except
8628 -- for WF_NOTIFICATION macro and simple text
8629 -- Auth : SSTOMAR
8630 function isFwkBody(nid in number, content_type in varchar2) return varchar2 is
8631
8632 lv_body varchar2(32000);
8633 lv_html_body varchar2(32000);
8634 lv_final_body varchar2(32000);
8635 lv_fwk_body varchar2(1);
8636 lv_first_token varchar2(240);
8637 lv_token_start number;
8638
8639 begin
8640
8641 lv_fwk_body := 'N';
8642
8643 select nvl(WM.BODY, ''), nvl(WM.HTML_BODY, '')
8644 into lv_body, lv_html_body
8645 from WF_NOTIFICATIONS N, WF_MESSAGES_VL WM
8646 where N.NOTIFICATION_ID = nid
8647 and N.MESSAGE_NAME = WM.NAME
8648 and N.MESSAGE_TYPE = WM.TYPE;
8649
8650 -- bug 5456241 (SSTOMAR)
8651 -- Based on that, we will pick up corresponding message bdoy.
8652 --
8653 -- KNOWN ISSUE: Generally the simple text in message body also considered
8654 -- as Framework based notification but If message body has simple text contains
8655 -- '& ' character without token name
8656 -- then according to below logic it will be plsql based ntf.
8657
8658 if (content_type = wf_notification.doc_html ) then
8659 if (length(trim(lv_html_body)) > 0 ) then
8660
8661 if (fwkTokenExist(nid, lv_html_body) = 'Y'
8662 or instr(lv_html_body, '&') = 0 ) then
8663 lv_fwk_body := 'Y';
8664 end if;
8665 -- HTML body is blank, check text body. And if text body has any TOKEN which
8666 -- is a DOCUMENT type OR it does not has any attribute then assume it as Fwk based Ntf.
8667 elsif (length(trim(lv_body)) > 0
8668 and (fwkTokenExist(nid, lv_body) = 'Y'
8669 or instr(lv_body, '&') = 0)) then
8670 lv_fwk_body := 'Y';
8671 end if;
8672 else -- doc_type is plan/text
8673 -- Check the text body only
8674 if (length(trim(lv_body)) > 0
8675 and (fwkTokenExist(nid, lv_body) = 'Y'
8676 or instr(lv_body, '&') = 0) ) then
8677 lv_fwk_body := 'Y';
8678 end if;
8679 end if;
8680
8681
8682 --lv_token_start := instr( lv_final_body, '&');
8683 -- get the first token in the body
8684 --if( lv_token_start > 0 ) then
8685 -- lv_first_token := substr(lv_final_body, lv_token_start+1, 30);
8686 -- for attr_row in cur_msg_attrs(nid, lv_first_token) loop
8687 -- if( instr(attr_row.text_value, fwk_region_start) = 1 ) then
8688 -- lv_fwk_body := 'Y';
8689 -- exit;
8690 -- end if;
8691 -- end loop;
8692 -- no attributes refered in body so render framework region
8693 --else
8694 -- lv_fwk_body := 'Y';
8695 -- end if;
8696
8697 return lv_fwk_body;
8698 exception
8699 when OTHERS then
8700 wf_core.context('Wf_Notification','isFwkBody',to_char(nid), content_type);
8701 raise;
8702
8703 End isFwkBody;
8704
8705 -- fwkTokenExist
8706 -- This function check whether first TOKEN within the message body exist AND
8707 -- has the value like 'JSP:/OA_HTML/OA.jsp?' (value hold by variable fwk_region_start) .
8708 -- Auther : SSTOMAR
8709 function fwkTokenExist(nid in number, msgbody in varchar2) return varchar2 is
8710 lv_token_exist varchar2(1) ;
8711 lv_token_start number;
8712 lv_first_token varchar2(240);
8713
8714 -- Cursur to check for each message Attribute
8715 cursor cur_msg_attrs(nid number, msgToken varchar2) is
8716 select WNA.NAME, WNA.TEXT_VALUE, WMA.TYPE
8717 from WF_NOTIFICATION_ATTRIBUTES WNA, WF_NOTIFICATIONS WN,
8718 WF_MESSAGE_ATTRIBUTES_VL WMA
8719 where WNA.NOTIFICATION_ID = nid
8720 and WN.NOTIFICATION_ID = WNA.NOTIFICATION_ID
8721 and WN.MESSAGE_TYPE = WMA.MESSAGE_TYPE
8722 and WN.MESSAGE_NAME = WMA.MESSAGE_NAME
8723 and WMA.TYPE = 'DOCUMENT'
8724 and WMA.NAME = WNA.NAME
8725 and instr( msgToken, WMA.NAME ) = 1
8726 order by length(WMA.NAME) desc;
8727
8728 begin
8729
8730 lv_token_exist := 'N';
8731 lv_token_start := instr( msgbody, '&');
8732
8733 -- get the first token in the body
8734 if( lv_token_start > 0 ) then
8735 -- sstomar:
8736 -- Note: No. of characters 30 are hardcoded,
8737 -- I think we should take upto space ' ' mark.
8738 lv_first_token := substr(msgbody, lv_token_start+1, 30);
8739
8740 for attr_row in cur_msg_attrs(nid, lv_first_token) loop
8741 if( instr(attr_row.text_value, fwk_region_start) = 1 ) then
8742 lv_token_exist := 'Y';
8743 exit;
8744 end if;
8745 end loop;
8746 end if;
8747
8748 return lv_token_exist;
8749 exception
8750 when OTHERS then
8751 wf_core.context('Wf_Notification','fwkTokenExist',to_char(nid));
8752 raise;
8753
8754 end fwkTokenExist;
8755
8756 --
8757 -- getNtfActInfo
8758 -- Fetch Notification Activity info of a given notification. It is possible
8759 -- that there will not be an entry in WF_ITEM_ACTIVITY_STATUSES and/or
8760 -- WF_ITEM_ACTIVITY_STATUSES_H in case when the notification is sent using
8761 -- Send API instead of part of a process.
8762 -- IN
8763 -- nid - Notification ID
8764 -- OUT
8765 -- l_itype Itemtype of the notification activity
8766 -- l_itype Itemkey of the process part of which the notification was sent
8767 -- l_actid Activity ID of the Notification Activity in the process
8768
8769 procedure getNtfActInfo (nid in number,
8770 l_itype out nocopy varchar2,
8771 l_ikey out nocopy varchar2,
8772 l_actid out nocopy number)
8773 is
8774 --bug 2276260 skilaru 15-July-03
8775 cursor act_info_statuses_cursor( group_nid number ) is
8776 select ITEM_TYPE, ITEM_KEY, PROCESS_ACTIVITY
8777 from WF_ITEM_ACTIVITY_STATUSES
8778 where notification_id = group_nid;
8779
8780 cursor act_info_statuses_h_cursor( group_nid number ) is
8781 select ITEM_TYPE, ITEM_KEY, PROCESS_ACTIVITY
8782 from WF_ITEM_ACTIVITY_STATUSES_H
8783 where notification_id = group_nid;
8784
8785 l_group_nid number;
8786
8787 begin
8788 --skilaru 16-July-03
8789 --WF_ITEM_ACTIVITY_STATUSES.NOTIFICATION_ID is the foreing key
8790 --mapped to WF_NOTIFICATIONS.GROUP_ID
8791 SELECT group_id
8792 INTO l_group_nid
8793 FROM wf_notifications
8794 WHERE notification_id = nid;
8795
8796 for act_status_row in act_info_statuses_cursor( l_group_nid ) loop
8797 l_itype := act_status_row.ITEM_TYPE;
8798 l_ikey := act_status_row.ITEM_KEY;
8799 l_actid := act_status_row.PROCESS_ACTIVITY;
8800 end loop;
8801
8802 if( l_itype is null and l_ikey is null ) then
8803 for act_status_row in act_info_statuses_h_cursor( l_group_nid ) loop
8804 l_itype := act_status_row.ITEM_TYPE;
8805 l_ikey := act_status_row.ITEM_KEY;
8806 l_actid := act_status_row.PROCESS_ACTIVITY;
8807 end loop;
8808 end if;
8809
8810 exception
8811 when OTHERS then
8812 wf_core.context('Wf_Notification','getNtfActInfo',to_char(nid));
8813 raise;
8814
8815 End getNtfActInfo;
8816
8817 --
8818 -- getFwkBodyURLLang
8819 -- This API returns a URL to access notification body with
8820 -- Framework content embedded.
8821 -- IN:
8822 -- nid - Notification id
8823 -- disptype - Requested display type. Valid values:
8824 -- 'text/plain' - plain text
8825 -- 'text/html' - html
8826 -- RETURNS:
8827 -- Returns the URL to access the notification detail body
8828 --
8829 function getFwkBodyURLLang(nid in number,
8830 contenttype varchar2,
8831 language in varchar2)
8832 return varchar2
8833 is
8834 begin
8835
8836 return getFwkBodyURL2(nid, contenttype, language, null);
8837
8838 end getFwkBodyURLLang;
8839
8840
8841 --
8842 -- getFwkBodyURL
8843 -- This API returns a URL to access notification body with
8844 -- Framework content embedded.
8845 -- IN:
8846 -- p_nid - Notification id
8847 -- p_contenttype - Requested display type. Valid values:
8848 -- 'text/plain' - plain text
8849 -- 'text/html' - html,
8850 -- p_language language value of that notification / user
8851 -- p_nlsCalendar nls calender of that user
8852
8853 -- RETURNS:
8854 -- Returns the URL to access the notification detail body
8855 --
8856
8857 function getFwkBodyURL2( p_nid in number,
8858 p_contentType varchar2,
8859 p_language varchar2,
8860 p_nlsCalendar varchar2) return varchar2
8861 is
8862 url_value varchar2(2000);
8863 lang_code varchar2(4);
8864 l_api varchar2(250) := g_plsqlName ||'getFwkBodyURL2';
8865 begin
8866 if (WF_LOG_PKG.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
8867 wf_log_pkg.String(WF_LOG_PKG.LEVEL_PROCEDURE, l_api,'BEGIN');
8868 end if;
8869
8870 url_value := rtrim(fnd_profile.Value('WF_MAIL_WEB_AGENT'), '/');
8871
8872 if url_value is null then
8873 url_value := rtrim(fnd_profile.Value('APPS_FRAMEWORK_AGENT'), '/');
8874 end if;
8875
8876 url_value := url_value || wf_notification.fwk_mailer_page;
8877 url_value := url_value || '&WFRegion=NtfDetail&NtfId=' || to_char(p_nid);
8878 url_value := url_value || '&dbc=' || fnd_web_config.Database_ID;
8879
8880 if (p_contentType = wf_notification.doc_html) then
8881 -- url_value := url_value || '&OALAF=blaf&OARF=email';
8882 url_value := url_value || '&OARF=email';
8883 elsif (p_contentType = wf_notification.doc_text) then
8884 url_value := url_value || '&OALAF=oaText&OARF=email';
8885 end if;
8886
8887 -- Bug 5170348
8888 -- Append the language_code to the fwk URL to set session p_language
8889 if (p_language is not null) then
8890 begin
8891
8892 select code into lang_code from wf_languages where nls_language=p_language;
8893 url_value := url_value || '&language_code='|| lang_code;
8894
8895 exception
8896 when others then
8897 wf_log_pkg.string(WF_LOG_PKG.LEVEL_UNEXPECTED, 'WF_NOTIFICATION.getFwkBodyURLLang',
8898 'nid: '||to_char(p_nid)||'; language: '|| p_language);
8899 end;
8900 end if;
8901
8902 -- Append the NLS_CALENDAR to the fwk URL only for 12.1.0 or above
8903 if (fnd_release.major_version = 12 and fnd_release.minor_version >= 1
8904 and fnd_release.point_version>=1) or (fnd_release.major_version > 12) then
8905 url_value := url_value || '&nlsCalendar='|| nvl(p_nlsCalendar, wf_core.nls_calendar);
8906 end if;
8907
8908 if (WF_LOG_PKG.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
8909 wf_log_pkg.String(WF_LOG_PKG.LEVEL_PROCEDURE, l_api,'p_nlsCalendar: '||p_nlsCalendar);
8910 wf_log_pkg.String(WF_LOG_PKG.LEVEL_PROCEDURE, l_api,'url: '||url_value);
8911 wf_log_pkg.String(WF_LOG_PKG.LEVEL_PROCEDURE, l_api,'END');
8912 end if;
8913
8914 return url_value;
8915
8916 exception
8917 when OTHERS then
8918 wf_core.context('Wf_Notification','getFwkBodyURL2',
8919 to_char(p_nid), p_contentType,p_language,p_nlsCalendar );
8920 raise;
8921
8922 END getFwkBodyURL2;
8923
8924
8925
8926 --
8927 -- getFwkBodyURL
8928 -- This API returns a URL to access notification body with
8929 -- Framework content embedded
8930 -- IN:
8931 -- nid - Notification id
8932 -- disptype - Requested display type. Valid values:
8933 -- 'text/plain' - plain text
8934 -- 'text/html' - html
8935 -- RETURNS:
8936 -- Returns the URL to returned by call to getFwkBodyURLLang
8937 --
8938 function getFwkBodyURL(nid in number, contenttype varchar2 ) return varchar2
8939 is
8940 begin
8941 return getFwkBodyURLLang(nid, contenttype, null);
8942 end getFwkBodyURL;
8943
8944 --
8945 -- getSummaryURL
8946 -- This API returns a URL to access summary of notiifications
8947 --
8948 -- IN:
8949 -- mailer_role - role for which summary of notifications required
8950 -- disptype - Requested display type. Valid values:
8951 -- 'text/plain' - plain text
8952 -- 'text/html' - html
8953 -- RETURNS:
8954 -- Returns the URL to access the summary of notiification for the role
8955 --
8956
8957 function getSummaryURL(mailer_role varchar2, contenttype varchar2 ) return varchar2
8958 is
8959 begin
8960
8961 RETURN getSummaryURL2(p_mailer_role =>mailer_role,
8962 p_contentType => contenttype,
8963 p_nlsCalendar => null);
8964
8965 end getSummaryURL;
8966
8967 -- getSummaryURL
8968 -- This API returns a URL to access summary of notiifications
8969 --
8970 -- IN:
8971 -- p_mailer_role - role for which summary of notifications required
8972 -- p_contentType - Requested display type. Valid values:
8973 -- 'text/plain' - plain text
8974 -- 'text/html' - html
8975 -- p_nlsCalendar - nls Calender of that role / user
8976 --
8977 -- RETURNS:
8978 -- Returns the URL to access the summary of notiification for the role
8979 --
8980
8981 function getSummaryURL2( p_mailer_role varchar2,
8982 p_contentType varchar2,
8983 p_nlsCalendar varchar2) return varchar2
8984 is
8985 url_value varchar2(2000);
8986 l_api varchar2(250) := g_plsqlName ||'getSummaryURL2';
8987 begin
8988 if (WF_LOG_PKG.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
8989 wf_log_pkg.String(WF_LOG_PKG.LEVEL_PROCEDURE, l_api,'BEGIN');
8990 end if;
8991
8992 url_value := rtrim(fnd_profile.Value('WF_MAIL_WEB_AGENT'), '/');
8993 if url_value is null then
8994 url_value := rtrim(fnd_profile.Value('APPS_FRAMEWORK_AGENT'), '/');
8995 end if;
8996
8997 url_value := url_value || wf_notification.fwk_mailer_page;
8998 url_value := url_value ||'&WFRegion=NtfSummary&mailerRole=' || p_mailer_role;
8999 url_value := url_value || '&dbc=' || fnd_web_config.Database_ID;
9000
9001 if (p_contentType = wf_notification.doc_html) then
9002 -- url_value := url_value || '&OALAF=blaf&OARF=email';
9003 url_value := url_value || '&OARF=email';
9004 elsif (p_contentType = wf_notification.doc_text) then
9005 url_value := url_value || '&OALAF=oaText&OARF=email';
9006 end if;
9007
9008 -- Append the NLS_CALENDAR to the fwk URL only for 12.1.0 or above
9009 if (fnd_release.major_version = 12 and fnd_release.minor_version >= 1
9010 and fnd_release.point_version>=1) or (fnd_release.major_version > 12) then
9011
9012 url_value := url_value || '&nlsCalendar='|| nvl(p_nlsCalendar,
9013 wf_core.nls_calendar);
9014 end if;
9015
9016 if (WF_LOG_PKG.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
9017 wf_log_pkg.String(WF_LOG_PKG.LEVEL_PROCEDURE, l_api,'p_nlsCalendar: '||p_nlsCalendar);
9018 wf_log_pkg.String(WF_LOG_PKG.LEVEL_PROCEDURE, l_api,'url: '||url_value);
9019 wf_log_pkg.String(WF_LOG_PKG.LEVEL_PROCEDURE, l_api,'END');
9020 end if;
9021
9022 return url_value;
9023
9024 exception
9025 when OTHERS then
9026 wf_core.context('Wf_Notification','getSummaryURL2', p_mailer_role, p_contentType, p_nlsCalendar);
9027 raise;
9028
9029 END getSummaryURL2;
9030
9031
9032
9033
9034 -- GetSignatureRequired
9035 -- Determine signing requirements for a policy
9036 -- IN:
9037 -- nid - Notification id - used for error context only
9038 -- p_sig_policy - Policy Name
9039 -- OUT:
9040 -- p_sig_required - Y/N
9041 -- p_fwk_sig_flavor - sigFlavor for browser signing.
9042 -- p_email_sig_flavor - sigFlavor for email
9043 -- p_render_hint - hints like ATTR_ONLY or FULL_TEXT
9044
9045 procedure GetSignatureRequired(p_sig_policy in varchar2,
9046 p_nid in number,
9047 p_sig_required out nocopy varchar2,
9048 p_fwk_sig_flavor out nocopy varchar2,
9049 p_email_sig_flavor out nocopy varchar2,
9050 p_render_hint out nocopy varchar2)
9051
9052 is
9053
9054 v_sig_policy varchar2(50);
9055
9056 begin
9057 -- if the signature policy is null, set it as default
9058 if (p_sig_policy is null) then
9059 v_sig_policy := 'DEFAULT';
9060 else
9061 v_sig_policy := p_sig_policy;
9062 end if;
9063
9064 --select the flavors corresponding to the sig policy
9065 select SIG_REQUIRED,FWK_SIG_FLAVOR,EMAIL_SIG_FLAVOR, RENDER_HINT
9066 into p_sig_required,p_fwk_sig_flavor,p_email_sig_flavor,p_render_hint
9067 from WF_SIGNATURE_POLICIES
9068 where sig_policy=UPPER(TRIM(v_sig_policy));
9069
9070 --when any exception raise the error with the corresponding notification id
9071
9072 exception
9073 when others then
9074 wf_core.context('WF_Notification', 'GetSignatureRequired', to_char(p_nid));
9075 wf_core.token('NID', to_char(p_nid));
9076 wf_core.raise('WFMLR_INVALID_SIG_POLICY');
9077 end;
9078
9079 -- SetUIErrorMessage
9080 -- API for Enhanced error handling for OAFwk UI Bug#2845488 grengara
9081 -- This procedure can be used for handling exceptions gracefully when dynamic SQL is invloved
9082
9083 procedure SetUIErrorMessage
9084 is
9085 begin
9086 if ((wf_core.error_name is null) AND (sqlcode <= -20000) AND (sqlcode >= -20999)) then
9087 -- capture the SQL Error message in this global variable so that it can be propogated
9088 -- back to OAF without the need for an OUT paramter
9089 wf_core.error_message := sqlerrm;
9090 end if;
9091 end SetUIErrorMessage;
9092
9093 --
9094 -- SetComments
9095 -- Private procedure that is used to store a comment record into WF_COMMENTS
9096 -- table with the denormalized information. A record is inserted for every
9097 -- action performed on a notification.
9098 -- IN
9099 -- p_nid - Notification Id
9100 -- p_from_role - Internal Name of the comment provider
9101 -- p_to_role - Internal Name of the comment recipient
9102 -- p_action - Action performed
9103 -- p_action_source - Source from where the action is performed
9104 -- p_user_comment - Comment Text
9105 --
9106 procedure SetComments(p_nid in number,
9107 p_from_role in varchar2,
9108 p_to_role in varchar2,
9109 p_action in varchar2,
9110 p_action_source in varchar2,
9111 p_user_comment in varchar2)
9112 is
9113 l_from_role varchar2(320);
9114 l_from_user varchar2(360);
9115 l_to_user varchar2(360);
9116 l_action_type varchar2(30);
9117 l_proxy_user varchar2(320);
9118 l_action varchar2(30);
9119 l_seq_num number;
9120 begin
9121 -- Just because p_from_role was null due to some reason, there should not be failure.
9122 -- All cases are taken care to make sure from_role is valid. Just in case...
9123 if (p_from_role is null) then
9124 l_from_role := 'WF_SYSTEM';
9125 else
9126 l_from_role := p_from_role;
9127 end if;
9128
9129 -- If p_from_role is an e-mail address with 'email:' prefixed, it is better remove it
9130 -- since it would not appear good on the UI
9131 if (substr(l_from_role, 1, 6) = 'email:') then
9132 l_from_role := substr(l_from_role, 7);
9133 end if;
9134
9135 -- Sometimes p_from_role is email address when answering for more info request
9136 if (l_from_role = 'WF_SYSTEM') then
9137 l_from_user := Wf_Core.Translate(l_from_role);
9138 else
9139 l_from_user := nvl(Wf_Directory.GetRoleDisplayName(l_from_role), l_from_role);
9140 end if;
9141 if (p_to_role = 'WF_SYSTEM') then
9142 l_to_user := Wf_Core.Translate(p_to_role);
9143 else
9144 l_to_user := nvl(Wf_Directory.GetRoleDisplayname(p_to_role), p_to_role);
9145 end if;
9146 l_action := p_action;
9147
9148 if (l_action in ('DELEGATE','TRANSFER')) then
9149 l_action_type := 'REASSIGN';
9150 elsif (l_action in ('QUESTION','ANSWER')) then
9151 l_action_type := 'QA';
9152 else
9153 -- Actions like RESPOND, CANCEL, SEND
9154 l_action_type := p_action;
9155 end if;
9156
9157 -- suffix source to action... DELEGATE_RULE, FORWARD_WA, etc.
9158 if (p_action_source is not null) then
9159 l_action := l_action||'_'||p_action_source;
9160 -- if the action is performed from WA, the user performing the action
9161 -- should be acting as a proxy to another user
9162 if (p_action_source = 'WA') then
9163 l_proxy_user := Wfa_Sec.GetUser();
9164 end if;
9165 end if;
9166
9167 -- Calculate sequence for comments in the same session
9168 l_seq_num := g_comments_seq + 1;
9169 g_comments_seq := g_comments_seq + 1;
9170
9171 INSERT INTO wf_comments (
9172 sequence,
9173 notification_id,
9174 from_role,
9175 from_user,
9176 to_role,
9177 to_user,
9178 comment_date,
9179 action,
9180 action_type,
9181 proxy_role,
9182 user_comment,
9183 language
9184 ) VALUES (
9185 l_seq_num,
9186 p_nid,
9187 l_from_role,
9188 l_from_user,
9189 p_to_role,
9190 l_to_user,
9191 sysdate,
9192 l_action,
9193 l_action_type,
9194 l_proxy_user,
9195 p_user_comment,
9196 userenv('LANG')
9197 );
9198
9199 exception
9200 when others then
9201 wf_core.context('Wf_Notification', 'SetComments', to_char(p_nid), p_from_role,
9202 p_to_role, p_action, p_action_source);
9203 raise;
9204 end SetComments;
9205
9206 --
9207 -- Resend
9208 -- Private procedure to resend a notification given the notification id. This
9209 -- procedure checks the mail status and recipient's notification preference to
9210 -- see if it is eligible to send e-mail.
9211 -- IN
9212 -- p_nid - Notification Id
9213 --
9214 procedure Resend(p_nid in number)
9215 is
9216 l_recipient_role varchar2(320);
9217 l_group_id number;
9218 l_mail_status varchar2(8);
9219 l_status varchar2(8);
9220 l_message_type varchar2(8);
9221 l_message_name varchar2(30);
9222 l_paramlist wf_parameter_list_t := wf_parameter_list_t();
9223
9224 l_display_name varchar2(360);
9225 l_email_address varchar2(320);
9226 l_notification_pref varchar2(8);
9227 l_language varchar2(30);
9228 l_territory varchar2(30);
9229 l_orig_system varchar2(30);
9230 l_orig_system_id number;
9231 l_installed varchar2(1);
9232 role_info_tbl wf_directory.wf_local_roles_tbl_type;
9233
9234 begin
9235
9236 begin
9237 SELECT message_type, message_name, status, mail_status, nvl(more_info_role, recipient_role) recipient_role, group_id
9238 INTO l_message_type, l_message_name, l_status, l_mail_status, l_recipient_role, l_group_id
9239 FROM wf_notifications
9240 WHERE notification_id = p_nid;
9241 exception
9242 when no_data_found then
9243 wf_core.token('NID', to_char(p_nid));
9244 wf_core.raise('WFNTF_NID');
9245 end;
9246
9247 -- Get recipient information using Dir Service API. Select from WF_ROLES
9248 -- may not give the right information
9249 Wf_Directory.GetRoleInfoMail(l_recipient_role, l_display_name, l_email_address,
9250 l_notification_pref, l_language, l_territory,
9251 l_orig_system, l_orig_system_id, l_installed);
9252
9253 -- Check if the notification is eligible to be e-mailed. We throw specific error
9254 -- for the UI to display appropriately to the user
9255 if (l_status <> 'OPEN') then
9256 wf_core.token('NID', to_char(p_nid));
9257 wf_core.raise('WFNTF_NID_OPEN');
9258 end if;
9259
9260 if (l_notification_pref not in ('MAILHTML','MAILTEXT','MAILATTH','MAILHTM2')) then
9261 wf_core.token('NTF_PREF', l_notification_pref);
9262 wf_core.token('RECIPIENT', l_recipient_role);
9263 wf_core.raise('WFNTF_INVALID_PREF');
9264 end if;
9265
9266 if (l_mail_status not in ('SENT', 'ERROR', 'FAILED', 'UNAVAIL')) then
9267 wf_core.token('MAILSTATUS', l_mail_status);
9268 wf_core.raise('WFNTF_EMAIL_NOTSENT');
9269 end if;
9270
9271 -- Raise the event to send an e-mail
9272 UPDATE wf_notifications
9273 SET mail_status = 'MAIL'
9274 WHERE notification_id = p_nid;
9275
9276 Wf_Event.AddParameterToList('NOTIFICATION_ID', p_nid, l_paramlist);
9277 Wf_Event.AddParameterToList('ROLE', l_recipient_role, l_paramlist);
9278 Wf_Event.AddParameterToList('GROUP_ID', l_group_id, l_paramlist);
9279 Wf_Event.AddParameterToList('Q_CORRELATION_ID', l_message_type||':'||
9280 l_message_name, l_paramlist);
9281
9282 Wf_Directory.GetRoleInfo2(l_recipient_role, role_info_tbl);
9283 l_language := role_info_tbl(1).language;
9284
9285 select code into l_language from wf_languages where nls_language = l_language;
9286
9287 -- AppSearch
9288 wf_event.AddParameterToList('OBJECT_NAME',
9289 'oracle.apps.fnd.wf.worklist.server.AllNotificationsVO', l_paramlist);
9290 wf_event.AddParameterToList('CHANGE_TYPE', 'INSERT',l_paramlist);
9291 wf_event.AddParameterToList('ID_TYPE', 'PK', l_paramlist);
9292 wf_event.addParameterToList('PK_NAME_1', 'NOTIFICATION_ID',l_paramlist);
9293 wf_event.addParameterToList('PK_VALUE_1', p_nid, l_paramlist);
9294 wf_event.addParameterToList('PK_NAME_2', 'LANGUAGE',l_paramlist);
9295 wf_event.addParameterToList('PK_VALUE_2', l_language, l_paramlist);
9296
9297 Wf_Event.Raise(p_event_name => 'oracle.apps.wf.notification.send',
9298 p_event_key => to_char(p_nid),
9299 p_parameters => l_paramlist);
9300
9301 exception
9302 when others then
9303 wf_core.context('Wf_Notification', 'Resend', to_char(p_nid));
9304 raise;
9305 end Resend;
9306
9307 --
9308 -- getNtfResponse
9309 -- Fetches result(response) CODE and response display prompt to the notification
9310 -- IN
9311 -- p_nid - Notification ID
9312 -- OUT
9313 -- p_result_code Result code of the notification
9314 -- p_result_display Display value of the result code
9315
9316 procedure getNtfResponse (p_nid in number,
9317 p_result_code out nocopy varchar2,
9318 p_result_display out nocopy varchar2)
9319 is
9320 l_result_type varchar2(250);
9321 begin
9322
9323 begin
9324 select A.RESULT_TYPE, IAS.ACTIVITY_RESULT_CODE
9325 into l_result_type, p_result_code
9326 from WF_ITEM_ACTIVITY_STATUSES_H IAS,
9327 WF_ACTIVITIES A,
9328 WF_PROCESS_ACTIVITIES PA,
9329 WF_ITEMS I
9330 where IAS.NOTIFICATION_ID = p_nid
9331 and IAS.ITEM_TYPE = I.ITEM_TYPE
9332 and IAS.ITEM_KEY = I.ITEM_KEY
9333 and IAS.PROCESS_ACTIVITY = PA.INSTANCE_ID
9334 and I.BEGIN_DATE between A.BEGIN_DATE
9335 and nvl(A.END_DATE, I.BEGIN_DATE)
9336 and PA.ACTIVITY_NAME = A.NAME
9337 and PA.ACTIVITY_ITEM_TYPE = A.ITEM_TYPE;
9338 exception
9339 when NO_DATA_FOUND then
9340 select A.RESULT_TYPE, IAS.ACTIVITY_RESULT_CODE
9341 into l_result_type, p_result_code
9342 from WF_ITEM_ACTIVITY_STATUSES IAS,
9343 WF_ACTIVITIES A,
9344 WF_PROCESS_ACTIVITIES PA,
9345 WF_ITEMS I
9346 where IAS.NOTIFICATION_ID = p_nid
9347 and IAS.ITEM_TYPE = I.ITEM_TYPE
9348 and IAS.ITEM_KEY = I.ITEM_KEY
9349 and IAS.PROCESS_ACTIVITY = PA.INSTANCE_ID
9350 and I.BEGIN_DATE between A.BEGIN_DATE
9351 and nvl(A.END_DATE, I.BEGIN_DATE)
9352 and PA.ACTIVITY_NAME = A.NAME
9353 and PA.ACTIVITY_ITEM_TYPE = A.ITEM_TYPE;
9354 end;
9355
9356 p_result_display := wf_core.activity_result( l_result_type, p_result_code );
9357
9358 exception
9359 when NO_DATA_FOUND then
9360 p_result_code := null;
9361 p_result_display := null;
9362 when others then
9363 wf_core.context('Wf_Notification', 'getNtfResponse', to_char(p_nid));
9364 raise;
9365 end getNtfResponse;
9366
9367 --
9368 -- PropagateHistory (PUBLIC)
9369 -- This API allows Product Teams to publish custom action
9370 -- to WF_COMMENTS table.
9371 --
9372 procedure PropagateHistory(p_item_type in varchar2,
9373 p_item_key in varchar2,
9374 p_document_id in varchar2,
9375 p_from_role in varchar2,
9376 p_to_role in varchar2,
9377 p_action in varchar2,
9378 p_action_source in varchar2,
9379 p_user_comment in varchar2)
9380 is
9381 --Get the nids in curs_nid which have the attribute document_id
9382 cursor curs_nid(l_doc_id varchar2,l_item_type varchar2,l_item_key varchar2) is
9383 select wfn.notification_id
9384 from wf_item_activity_statuses wfas, wf_notifications wfn , wf_notification_attributes wfna
9385 where wfna.name = '#DOCUMENT_ID'
9386 and wfna.text_value = l_doc_id
9387 and wfas.item_type = l_item_type
9388 and wfas.item_key = l_item_key
9389 and wfn.notification_id = wfna.notification_id
9390 and wfas.notification_id = wfn.group_id;
9391 begin
9392 for comment_curs in curs_nid(p_document_id,p_item_type,p_item_key) loop
9393 --Now loop through the cursor and set the comments
9394 wf_notification.SetComments(p_nid => comment_curs.notification_id,
9395 p_from_role => p_from_role,
9396 p_to_role => p_to_role,
9397 p_action => p_action,
9398 p_action_source => p_action_source,
9399 p_user_comment => p_user_comment);
9400 end loop;
9401 exception
9402 when others then
9403 wf_core.context('Wf_Notification', 'propagatehistory', p_item_type,p_item_key, p_document_id);
9404 raise;
9405 end;
9406
9407 --
9408 -- Resend_Failed_Error_Ntfs (CONCURRENT PROGRAM API)
9409 -- API to re-enqueue notifications with mail_status FAILED and/or ERROR
9410 -- in order to re-send them. Mailer had processed these notifications
9411 -- earlier and updated the status since these notifications could not be
9412 -- delivered/processed. Only FYI notifications with ERROR mail status
9413 -- can be resent.
9414 --
9415 -- OUT
9416 -- errbuf - CP error message
9417 -- retcode - CP return code (0 = success, 1 = warning, 2 = error)
9418 -- IN
9419 -- p_mail_status - Mail status that needs to be resent.
9420 -- ERROR - Only for FYI notifications
9421 -- FAILED - All notifications
9422 -- p_msg_type - Message type of the notification
9423 -- p_role - Workflow role whose notifications are to be re-enqueued
9424 -- p_from_date - Notifications sent on or after this date
9425 -- p_to_date - Notifications sent on before this date
9426 -- - Type is varchar2 because CP reports problems with Date type
9427 procedure Resend_Failed_Error_Ntfs(errbuf out nocopy varchar2,
9428 retcode out nocopy varchar2,
9429 p_mail_status in varchar2,
9430 p_msg_type in varchar2,
9431 p_role in varchar2,
9432 p_from_date in varchar2,
9433 p_to_date in varchar2 )
9434 is
9435 l_errname varchar2(30);
9436 l_errmsg varchar2(2000);
9437 l_errstack varchar2(2000);
9438 l_nid number;
9439 l_from_date date;
9440 l_to_date date;
9441
9442 -- Cursor def. for FAILED notification
9443 CURSOR c_failed_ntfs(cp_msg_type varchar2,
9444 cp_role varchar2,
9445 cp_from_date date,
9446 cp_to_date date)
9447 IS
9448 SELECT notification_id
9449 FROM wf_notifications wn
9450 WHERE wn.status = 'OPEN'
9451 AND wn.mail_status = 'FAILED'
9452 AND wn.recipient_role like nvl(cp_role, '%')
9453 AND wn.message_type like nvl(cp_msg_type, '%')
9454 -- No date conversion is required on wn.begin_date
9455 AND (cp_from_date is null or wn.begin_date >= cp_from_date)
9456 AND (cp_to_date is null or wn.begin_date <= cp_to_date ) ;
9457
9458 -- Cussor for FYI errored out notifications
9459 CURSOR c_error_fyi_ntfs(cp_msg_type varchar2,
9460 cp_role varchar2 ,
9461 cp_from_date date,
9462 cp_to_date date)
9463 IS
9464 SELECT notification_id
9465 FROM wf_notifications wn
9466 WHERE wn.status = 'OPEN'
9467 AND wn.mail_status = 'ERROR'
9468 AND wn.recipient_role like nvl(cp_role, '%')
9469 AND wn.message_type like nvl(cp_msg_type, '%')
9470 AND (cp_from_date is null or wn.begin_date >= cp_from_date)
9471 AND (cp_to_date is null or wn.begin_date <= cp_to_date )
9472 AND NOT EXISTS (
9473 SELECT 1
9474 FROM wf_message_attributes wma,
9475 wf_notifications wn2
9476 WHERE wn2.notification_id = wn.notification_id
9477 AND wma.message_type = wn2.message_type
9478 AND wma.message_name = wn2.message_name
9479 AND wma.subtype = 'RESPOND'
9480 AND rownum = 1);
9481
9482 begin
9483
9484 -- Convert from varchar2 to date format
9485 if(p_from_date is not null) then
9486 l_from_date := to_date(p_from_date, wf_core.canonical_date_mask);
9487 end if;
9488
9489 if(p_to_date is not null) then
9490 l_to_date := to_date(p_to_date, wf_core.canonical_date_mask);
9491 end if;
9492
9493 -- if mail status is specified as null, both failed and errored
9494 -- ntfs require to be resent
9495 if (nvl(p_mail_status, 'FAILED') = 'FAILED') then
9496 open c_failed_ntfs(p_msg_type, p_role, l_from_date, l_to_date);
9497 loop
9498 fetch c_failed_ntfs into l_nid;
9499 exit when c_failed_ntfs%NOTFOUND;
9500 begin
9501 -- Raise event
9502 Wf_Notification.Resend(l_nid);
9503
9504 exception
9505 when others then
9506 -- ignore any error while enqueing
9507 Wf_Core.Clear();
9508 end;
9509 commit;
9510 end loop;
9511 close c_failed_ntfs;
9512 end if;
9513
9514 -- only errored FYI notifications are resent. For response required
9515 -- ntfs, the activity would be errored which can be retried
9516 if (nvl(p_mail_status, 'ERROR') = 'ERROR') then
9517 open c_error_fyi_ntfs(p_msg_type, p_role , l_from_date, l_to_date);
9518 loop
9519 fetch c_error_fyi_ntfs into l_nid;
9520 exit when c_error_fyi_ntfs%NOTFOUND;
9521 begin
9522 -- Raise event
9523 Wf_Notification.Resend(l_nid);
9524 exception
9525 when others then
9526 -- ignore any error while enqueing
9527 Wf_Core.Clear();
9528 end;
9529 commit;
9530 end loop;
9531 close c_error_fyi_ntfs;
9532 end if;
9533
9534 -- successful completion
9535 errbuf := '';
9536 retcode := '0';
9537 exception
9538 when others then
9539 -- get error message into errbuf
9540 wf_core.get_error(l_errname, l_errmsg, l_errstack);
9541 if (l_errmsg is not null) then
9542 errbuf := l_errmsg;
9543 else
9544 errbuf := sqlerrm;
9545 end if;
9546
9547 -- return 2 for error
9548 retcode := '2';
9549 end Resend_Failed_Error_Ntfs;
9550
9551 begin
9552 -- Loads the user's nls date mask
9553 g_nls_date_mask := sys_context('USERENV','NLS_DATE_FORMAT');
9554
9555 if (instr(upper(g_nls_date_mask), 'HH') = 0) then
9556 g_nls_date_mask := g_nls_date_mask||' HH24:MI:SS';
9557 end if;
9558
9559 End WF_Notification;
9560