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