1 package body FND_FUNCTION as
2 /* $Header: AFSCFNSB.pls 120.11 2011/12/05 12:28:21 srinnakk ship $ */
3
4 C_PKG_NAME CONSTANT VARCHAR2(30) := 'FND_FUNCTION';
5 C_LOG_HEAD CONSTANT VARCHAR2(30) := 'fnd.plsql.FND_FUNCTION.';
6
7 /* These column values mark a menu as uncompiled. */
8 /* These will be stored in the FUNCTION_ID and GRANT_FLAG to indicate */
9 /* that a particular menu has been modified and needs to be recompiled */
10 C_INVALID_MENU_VAL CONSTANT NUMBER := -99999;
11 C_INVALID_GRANT_VAL CONSTANT VARCHAR2(1) := 'U'; /*U stands for Uncompiled */
12
13 /* These column values mark a menu as blank (yielding no compilation). */
14 /* These will be stored in the FUNCTION_ID and GRANT_FLAG to indicate */
15 /* that the corresponding FND_MENU row doesn't produce any information */
16 /* when compiled. */
17 /* Some possible reasons: The menu isn't used by any menu entries, or */
18 /* the menu doesn't contain any functions */
19 C_BLANK_MENU_VAL CONSTANT NUMBER := -99990;
20 C_BLANK_GRANT_VAL CONSTANT VARCHAR2(1) := 'B'; /* B stands for Blank */
21
22 /*
23 ** Lock ID for coordinating compilations (2004/10/22)
24 */
25 C_MENU_LOCK_ID constant NUMBER := 20041022;
26
27 TYPE NUMBER_TABLE_TYPE is table of NUMBER INDEX BY BINARY_INTEGER;
28
29 /* Bulk collects are a feature that will have the benefit of increasing */
30 /* performance and hopefully reducing I/O reads on dev115 */
31 /* But the problem is they cause random failures in 8.1.6.1 databases */
32 /* so we can't use them before 8.1.7.1. (due to database bug 1688232).*/
33 /* So we will detect the database version and set this flag to one of: */
34 /* 'UNKNOWN'- Flag needs to be initialized by call to init routine */
35 /* 'TRUE' - supported */
36 /* 'FALSE'- unsupported */
37 /* Once 8.1.7.1+ is required for all Apps customers then this hack */
38 /* Can be gotten rid of, and this can be hardcoded to 'TRUE' */
39 /* 2/03- TM- That time is now... we now require 8.1.7.1+.*/
40 G_BULK_COLLECTS_SUPPORTED VARCHAR2(30) := 'TRUE';
41
42 /* This table stores marks while being called from row level db triggers,*/
43 /* because those row level db triggers can't actually mark the menu */
44 /* That involves reading the FND_MENU_ENTRIES table, which is considered */
45 /* a mutating table. So we store the marks here and then process them in*/
46 /* the after statement trigger. */
47 TBL_QUEUED_MENU_ID NUMBER_TABLE_TYPE;
48 TBL_QUEUED_MENU_ID_MAX NUMBER := 0;
49
50
51 /* This table stores the list of menus being visited as we */
52 /* Compile menus down recursively. */
53 /* This is basically a list that has the parents of a particular menu */
54 /* and it is maintained by pushing and popping items on it as we go down */
55 /* and up the menu hierarchy. If the current menu is also on its list */
56 /* of parents, then we know we've found an infinite recursion. */
57 TBL_RECURS_DETEC_MENU_ID NUMBER_TABLE_TYPE;
58 TBL_RECURS_DETEC_MENU_ID_MAX NUMBER := 0;
59
60 /* This constant is used for recursion detection in the fallback */
61 /* runtime menu scan. We keep track of how many items are on the menu,
62 /* and assume if the number of entries on the current */
63 /* menu is too high then it's caused by recursion. */
64 C_MAX_MENU_ENTRIES CONSTANT pls_integer := 10000;
65
66 /* This simple cache will avoid the need to find which menu is on */
67 /* the current responsibility with SQL every time. We just store */
68 /* the menu around after we get it for the current resp. */
69 P_LAST_RESP_ID NUMBER := -1;
70 P_LAST_RESP_APPL_ID NUMBER := -1;
71 P_LAST_MENU_ID NUMBER := -1;
72
73 g_func_id_cache NUMBER := NULL;
74 -- modified for bug#5395351
75 g_func_name_cache fnd_form_functions.function_name%type := NULL;
76
77
78 /* AVAILABILITY - This function compares the MAINTENANCE_MODE_SUPPORT
79 ** of a particular function to the APPS_MAINTENANCE_MODE
80 ** profile value and determines whether the function is
81 ** available during that maintenance phase.
82 **
83 ** in: MAINTENANCE_MODE_SUPPORT- the value from the database column
84 **
85 ** out: 'Y'= available, 'N'= not available
86 */
87 function AVAILABILITY(MAINTENANCE_MODE_SUPPORT in varchar2) return varchar2 is
88 apps_maintenance_mode varchar2(255);
89 retval boolean;
90 l_api_name CONSTANT VARCHAR2(30) := 'AVAILABILITY';
91 begin
92 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
93 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
94 c_log_head || l_api_name || '.begin',
95 c_pkg_name || '.' ||l_api_name|| '(' ||
96 'MAINTENANCE_MODE_SUPPORT =>'|| MAINTENANCE_MODE_SUPPORT
97 ||');');
98 end if;
99
100 apps_maintenance_mode := FND_PROFILE.VALUE('APPS_MAINTENANCE_MODE');
101
102 if(fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
103 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
104 c_log_head || l_api_name || '.got_mode',
105 'profile APPS_MAINTENANCE_MODE is set to value:'
106 || apps_maintenance_mode);
107 end if;
108
109 -- This following if statement is added to support the new function
110 -- mantenance_mode_support 'OFFLINE'
111 -- Note that we will always preventing function access if is OFFLINE.
112 -- RSheh : I did not want to rewrite the whole thing below so I just
113 -- added the following if statement to support OFFLINE
114 if (MAINTENANCE_MODE_SUPPORT = 'OFFLINE') then
115 retval := FALSE;
116 else
117 -- Original code before adding support for OFFLINE
118 retval := TRUE;
119 if (apps_maintenance_mode is NULL) then
120 retval := TRUE; /* Feature not yet supported, so ignore. */
121 elsif (apps_maintenance_mode = 'NORMAL') then
122 retval := TRUE; /* In normal mode everything is supported */
123 elsif (apps_maintenance_mode = 'DISABLED') then
124 retval := FALSE; /* In normal mode nothing is supported */
125 elsif (apps_maintenance_mode = 'MAINT') then
126 if ( (MAINTENANCE_MODE_SUPPORT = 'MAINT')
127 OR (MAINTENANCE_MODE_SUPPORT = 'QUERY')
128 OR (MAINTENANCE_MODE_SUPPORT = 'FUZZY')) then
129 retval := TRUE;
130 elsif (MAINTENANCE_MODE_SUPPORT = 'NONE') then
131 retval := FALSE;
132 else
133 retval := TRUE; /* Mode not supported so ignore */
134 end if;
135 elsif (apps_maintenance_mode = 'FUZZY') then
136 if (MAINTENANCE_MODE_SUPPORT = 'FUZZY') then
137 retval := TRUE;
138 elsif ( (MAINTENANCE_MODE_SUPPORT = 'NONE')
139 OR (MAINTENANCE_MODE_SUPPORT = 'QUERY')
140 OR (MAINTENANCE_MODE_SUPPORT = 'MAINT')) then
141 retval := FALSE;
142 else
143 retval := TRUE;
144 end if;
145 else
146 /* Unrecognized value for APPS_MAINTENANCE_MODE profile... */
147 /* assume the best and allow access. */
148 if (fnd_log.LEVEL_EXCEPTION >= fnd_log.g_current_runtime_level) then
149 fnd_log.string(FND_LOG.LEVEL_EXCEPTION,
150 c_log_head || l_api_name || '.unrecognized_mode',
151 'Unrecognized value for profile APPS_MAINTENANCE_MODE:'
152 || apps_maintenance_mode);
153 end if;
154 retval := TRUE;
155 end if;
156 end if; /* If statement for supporting OFFLINE */
157
158 if (retval) then
159 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
160 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
161 c_log_head || l_api_name || '.end_true',
162 'returning Y');
163 end if;
164 return 'Y';
165 else
166 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
167 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
168 c_log_head || l_api_name || '.end_false',
169 'returning N');
170 end if;
171 return 'N';
172 end if;
173
174 end;
175
176
177 -- BULK_COLLECTS_SUPPORTED
178 -- This temporary routine determines whether bulk collects are supported.
179 -- See comments around G_BULK_COLLECTS_SUPPORTED above
180 --
181 function BULK_COLLECTS_SUPPORTED return boolean is
182 ver varchar2(255);
183 begin
184 begin
185 if (G_BULK_COLLECTS_SUPPORTED = 'TRUE') then
186 return TRUE;
187 elsif (G_BULK_COLLECTS_SUPPORTED = 'FALSE') then
188 return FALSE;
189 elsif (G_BULK_COLLECTS_SUPPORTED = 'UNKNOWN') then
190 select version
191 into ver
192 from v$instance
193 where rownum = 1;
194
195 if(ver >= '8.1.7.1.0') then
196 G_BULK_COLLECTS_SUPPORTED := 'TRUE';
197 return (TRUE);
198 else
199 G_BULK_COLLECTS_SUPPORTED := 'FALSE';
200 return (FALSE);
201 end if;
202 else
203 return (FALSE); /* Should never happen */
204 end if;
205 exception
206 when others then
207 return FALSE; /* Should never happen */
208 end;
209 end BULK_COLLECTS_SUPPORTED;
210
211
212 -- PROCESS_MENU_TREE_DOWN_FOR_MN
213 -- Plow through the menu tree, processing exclusions and figuring
214 -- out which functions are accessible.
215 -- IN
216 -- p_menu_id - menu_id
217 -- p_function_id- function to check for
218 -- Don't pass values for the following two params if you don't want
219 -- exclusions processed.
220 -- p_appl_id - application id of resp
221 -- p_resp_id - responsibility id of current user
222 -- p_ignore_exclusions - passing 'Y' will ignore the exclusions.
223 --
224 -- RETURNS
225 -- TRUE if function is accessible
226 --
227 function PROCESS_MENU_TREE_DOWN_FOR_MN(
228 p_menu_id in number,
229 p_function_id in number,
230 p_appl_id in number,
231 p_resp_id in number) return boolean is
232
233 l_api_name CONSTANT VARCHAR2(30) := 'PROCESS_MENU_TREE_DOWN_FOR_MN';
234
235 l_sub_menu_id number;
236
237 /* Table to store the list of submenus that we are looking for */
238 TYPE MENULIST_TYPE is table of NUMBER INDEX BY BINARY_INTEGER;
239 MENULIST MENULIST_TYPE;
240
241 TYPE NUMBER_TABLE_TYPE is table of NUMBER INDEX BY BINARY_INTEGER;
242 TYPE VARCHAR2_TABLE_TYPE is table of VARCHAR2(1) INDEX BY BINARY_INTEGER;
243
244 /* The table of exclusions. The index in is the action_id, and the */
245 /* value stored in each element is the rule_type.*/
246 EXCLUSIONS VARCHAR2_TABLE_TYPE;
247
248 /* Returns from the bulk collect (fetches) */
249 TBL_MENU_ID NUMBER_TABLE_TYPE;
250 TBL_ENT_SEQ NUMBER_TABLE_TYPE;
251 TBL_FUNC_ID NUMBER_TABLE_TYPE;
252 TBL_SUBMNU_ID NUMBER_TABLE_TYPE;
253 TBL_GNT_FLG VARCHAR2_TABLE_TYPE;
254
255
256 /* Cursor to get exclusions */
257 cursor excl_c is
258 SELECT RULE_TYPE, ACTION_ID from fnd_resp_functions
259 where application_id = p_appl_id
260 and responsibility_id = p_resp_id;
261
262 /* Cursor to get menu entries on a particular menu.*/
263 cursor get_mnes_c is
264 SELECT MENU_ID, ENTRY_SEQUENCE, FUNCTION_ID, SUB_MENU_ID, GRANT_FLAG
265 from fnd_menu_entries
266 where MENU_ID = l_sub_menu_id;
267
268 menulist_cur pls_integer;
269 menulist_size pls_integer;
270
271 entry_excluded boolean;
272 last_index pls_integer;
273 i number;
274 z number;
275
276 begin
277 --
278 -- This routine processes the menu hierarchy and exclusion rules in PL/SQL
279 -- rather than in the database.
280 -- The basic algorithm of this routine is:
281 -- Populate the list of exclusions by selecting from FND_RESP_FUNCTIONS
282 -- menulist(1) = p_menu_id
283 -- while (elements on menulist)
284 -- {
285 -- Remove first element off menulist
286 -- if this menu is not excluded with a menu exclusion rule
287 -- {
288 -- Query all menu entry children of current menu
289 -- for (each child) loop
290 -- {
291 -- If it's excluded by a func exclusion rule, go on to the next one.
292 -- If we've got the function we're looking for,
293 -- and grant_flag = Y, we're done- return TRUE;
294 -- If it's got a sub_menu_id, add it to the end of menulist
295 -- to be processed
296 -- }
297 -- Move to next element on menulist
298 -- }
299 -- }
300 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
301 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
302 c_log_head || l_api_name || '.begin',
303 c_pkg_name || '.' ||l_api_name|| '(' ||
304 'p_menu_id =>'|| to_char(p_menu_id) ||
305 'p_function_id =>'|| to_char(p_function_id) ||
306 'p_appl_id =>'|| to_char(p_appl_id) ||
307 'p_resp_id =>'|| to_char(p_resp_id) ||');');
308 end if;
309
310 if(p_appl_id is not NULL) then
311 /* Select the list of exclusion rules into our cache */
312 for excl_rec in excl_c loop
313 EXCLUSIONS(excl_rec.action_id) := excl_rec.rule_type;
314 end loop;
315 end if;
316
317
318 -- Initialize menulist working list to parent menu
319 menulist_cur := 0;
320 menulist_size := 1;
321 menulist(0) := p_menu_id;
322
323 -- Continue processing until reach the end of list
327 c_log_head || l_api_name || '.proc_menulist',
324 while (menulist_cur < menulist_size) loop
325 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
326 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
328 'menulist_cur: ' || to_char(menulist_cur));
329 end if;
330
331 -- Check if recursion limit exceeded
332 if (menulist_cur > C_MAX_MENU_ENTRIES) then
333 fnd_message.set_name('FND', 'MENU-MENU LOOP');
334 fnd_message.set_token('MENU_ID', p_menu_id);
335 if (fnd_log.LEVEL_ERROR >= fnd_log.g_current_runtime_level) then
336 fnd_log.message(FND_LOG.LEVEL_ERROR,
337 c_log_head || l_api_name || '.recursion');
338 end if;
339
340 /* If the function were accessible from this menu, then we should */
341 /* have found it before getting to this point, so we are confident */
342 /* that the function is not on this menu. */
343 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
344 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
345 c_log_head || l_api_name || '.end_found_recur',
346 'returning FALSE');
347 end if;
348
349 return FALSE;
350 end if;
351
352 l_sub_menu_id := menulist(menulist_cur);
353
354 -- See whether the current menu is excluded or not.
355 entry_excluded := FALSE;
356 begin
357 if( (l_sub_menu_id is not NULL)
358 and (exclusions(l_sub_menu_id) = 'M')) then
359 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
360 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
361 c_log_head || l_api_name || '.menu_excl',
362 'l_sub_menu_id:' || l_sub_menu_id );
363 end if;
364 entry_excluded := TRUE;
365 end if;
366 exception
367 when no_data_found then
368 null;
369 end;
370
371 if (entry_excluded) then
372 last_index := 0; /* Indicate that no rows were returned */
373 else
374 /* This menu isn't excluded, so find out whats entries are on it. */
375
376 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
377 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
378 c_log_head || l_api_name || '.not_excl',
379 ' Following was not excluded: l_sub_menu_id: ' ||
380 to_char(l_sub_menu_id));
381 end if;
382
383 if (BULK_COLLECTS_SUPPORTED) then
384 open get_mnes_c;
385 fetch get_mnes_c bulk collect into tbl_menu_id, tbl_ent_seq,
386 tbl_func_id, tbl_submnu_id, tbl_gnt_flg;
387 close get_mnes_c;
388 -- See if we found any rows. If not set last_index to zero.
389 begin
390 if((tbl_menu_id.FIRST is NULL) or (tbl_menu_id.FIRST <> 1)) then
391 last_index := 0;
392 else
393 if (tbl_menu_id.FIRST is not NULL) then
394 last_index := tbl_menu_id.LAST;
395 else
396 last_index := 0;
397 end if;
398 end if;
399 exception
400 when others then
401 last_index := 0;
402 end;
403 else
404 z:= 0;
405 for rec in get_mnes_c loop
406 z := z + 1;
407 tbl_menu_id(z) := rec.MENU_ID;
408 tbl_ent_seq(z) := rec.ENTRY_SEQUENCE;
409 tbl_func_id(z) := rec.FUNCTION_ID;
410 tbl_submnu_id (z):= rec.SUB_MENU_ID;
411 tbl_gnt_flg(z) := rec.GRANT_FLAG;
412 end loop;
413 last_index := z;
414 end if;
415
416
417 end if; /* entry_excluded */
418
419 -- Process each of the child entries fetched
420 for i in 1 .. last_index loop
421 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
422 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
423 c_log_head || l_api_name || '.proc_child',
424 'Processing child of current menu. tbl_func_id(i):' ||
425 tbl_func_id(i) || ' tbl_submenu_id(i):'||
426 tbl_submnu_id(i));
427 end if;
428
429 -- Check if there is an exclusion rule for this entry
430 entry_excluded := FALSE;
431 begin
432 if( (tbl_func_id(i) is not NULL)
433 and (exclusions(tbl_func_id(i)) = 'F')) then
434 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
435 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
436 c_log_head || l_api_name || '.func_excl',
437 'tbl_func_id(i):' || tbl_func_id(i) );
438 end if;
439 entry_excluded := TRUE;
440 end if;
441 exception
442 when no_data_found then
443 null;
444 end;
445
446 -- Skip this entry if it's excluded
447 if (not entry_excluded) then
448 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
449 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
450 c_log_head || l_api_name || '.not_excl',
451 'Entry not excluded. Checking function:'
452 ||to_char(tbl_func_id(i))|| ' against p_function_id:'
453 ||to_char(p_function_id)||' where grant_flag='||tbl_gnt_flg(i));
454 end if;
455 -- Check if this is a matching function. If so, return success.
456 if( (tbl_func_id(i) = p_function_id)
457 and (tbl_gnt_flg(i) = 'Y'))
458 then
459 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
460 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
461 c_log_head || l_api_name || '.end_found',
462 'returning TRUE');
466
463 end if;
464 return TRUE;
465 end if;
467 -- If this is a submenu, then add it to the end of the
468 -- working list for processing.
469 if (tbl_submnu_id(i) is not NULL) then
470 menulist(menulist_size) := tbl_submnu_id(i);
471 menulist_size := menulist_size + 1;
472 end if;
473 end if; -- End if not excluded
474 end loop; -- For loop processing child entries
475
476 -- Advance to next menu on working list
477 menulist_cur := menulist_cur + 1;
478 end loop;
479
480 -- We couldn't find the function anywhere, so it's not available
481 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
482 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
483 c_log_head || l_api_name || '.end_not_found',
484 'returning FALSE');
485 end if;
486 return FALSE;
487 end PROCESS_MENU_TREE_DOWN_FOR_MN;
488
489
490 -- PROCESS_MENU_TREE_DOWN
491 -- Plow through the menu tree, processing exclusions and figuring
492 -- out which functions are accessible.
493 -- IN
494 -- p_appl_id - application id of resp
495 -- p_resp_id - responsibility id of current user
496 -- p_function_id- function to check for
497 --
498 -- RETURNS
499 -- TRUE if function is accessible
500 --
501 function PROCESS_MENU_TREE_DOWN(
502 p_appl_id in number,
503 p_resp_id in number,
504 p_function_id in number
505 ) return boolean is
506 l_menu_id NUMBER;
507 l_api_name CONSTANT VARCHAR2(30) := 'PROCESS_MENU_TREE_DOWN';
508 begin
509 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
510 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
511 c_log_head || l_api_name || '.begin',
512 c_pkg_name || '.' ||l_api_name|| '(' ||
513 'p_appl_id=>'|| to_char(p_appl_id) ||
514 ', p_resp_id=>'|| p_resp_id ||
515 ', p_function_id=>'|| p_function_id||
516 ')');
517 end if;
518
519 if ( (P_LAST_RESP_ID = p_resp_id)
520 and (P_LAST_RESP_APPL_ID = p_appl_id)) then
521 /* If the cache is valid just use the cache */
522 l_menu_id := P_LAST_MENU_ID;
523 else
524 /* Find the root menu for this responsibility */
525 begin
526 select menu_id
527 into l_menu_id
528 from fnd_responsibility
529 where responsibility_id = p_resp_id
530 and application_id = p_appl_id;
531 /* Store the new value in the cache */
532 P_LAST_RESP_ID := p_resp_id;
533 P_LAST_RESP_APPL_ID := p_appl_id;
534 P_LAST_MENU_ID := l_menu_id;
535 exception
536 when no_data_found then
537 /* No menu for this resp, so there can't be any functions */
538
539 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
540 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
541 c_log_head || l_api_name || '.end_no_menu_resp',
542 'returning FALSE');
543 end if;
544 return FALSE;
545 end;
546 end if;
547 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
548 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
549 c_log_head || l_api_name || '.end',
550 'return call to PROCESS_MENU_TREE_DOWN_FOR_MN '||
551 ' l_menu_id = '||l_menu_id);
552 end if;
553 return PROCESS_MENU_TREE_DOWN_FOR_MN(l_menu_id, p_function_id,
554 p_appl_id, p_resp_id);
555 end PROCESS_MENU_TREE_DOWN;
556
557
558 -- MARK_MENU
559 -- Plow through the menu tree upwards, marking menus as we go up, so that
560 -- all the menus that include this menu will be marked. Stop when we reach
561 -- menus that have already been marked.
562 -- IN
563 -- menu_id - menu to mark
564 -- RETURNS
565 -- TRUE if success
566 --
567 procedure MARK_MENU_I(p_menu_id in number) is
568 pragma autonomous_transaction;
569 /*
570 ** Normally it would be bad to pragma this as autonomous since it gets called
571 ** from db triggers in transactions that can get rolled back. The author
572 ** claims it's harmless for this routine to have marked rows even if the
573 ** mark is for a rolled back change, but another problem with this scheme
574 ** is that this code won't see the changes that the surrounding transaction
575 ** is trying to commit. The author claims that the pragma should help
576 ** prevent deadlocks (change added for 3229888).
577 ** ### Is this still needed now that locking code has been added?
578 ** ### George thinks that it's best to have marks written and visible
579 ** ### as quickly as possible for situations such as data loading, so
580 ** ### for now the code is left as-is. In theory, though, this pragma
581 ** ### should be moved to MARK_MENU, the locking surround for this routine.
582 */
583 l_api_name CONSTANT VARCHAR2(30) := 'MARK_MENU';
584 l_menu_id number;
585 l_sub_menu_id number;
586 mark_existed boolean;
587
588 /* Menu Entry table record type */
589 TYPE MNE_REC_TYPE IS RECORD
590 (MENU_ID NUMBER,
591 ENTRY_SEQ NUMBER,
592 FUNCTION_ID NUMBER,
593 SUB_MENU_ID NUMBER,
594 GRANT_FLAG VARCHAR2(1));
595
596 /* Define the menu entry table type */
597 TYPE MNE_TYPE is table of MNE_REC_TYPE INDEX BY BINARY_INTEGER;
598
599 /* The actual menu entry tables */
600 CUR_MNES MNE_TYPE;
601 CUR_MNES_SIZE BINARY_INTEGER := 0;
602
603
604
605 /* Table to store the list of submenus that we are looking for */
606 TYPE MENULIST_TYPE is table of NUMBER INDEX BY BINARY_INTEGER;
607 MENULIST MENULIST_TYPE;
608
612 /* Returns from the bulk collect (fetches) */
609 TYPE NUMBER_TABLE_TYPE is table of NUMBER INDEX BY BINARY_INTEGER;
610 TYPE VARCHAR2_TABLE_TYPE is table of VARCHAR2(1) INDEX BY BINARY_INTEGER;
611
613 TBL_MENU_ID NUMBER_TABLE_TYPE;
614 TBL_ENT_SEQ NUMBER_TABLE_TYPE;
615 TBL_FUNC_ID NUMBER_TABLE_TYPE;
616 TBL_SUBMNU_ID NUMBER_TABLE_TYPE;
617 TBL_GNT_FLG VARCHAR2_TABLE_TYPE;
618
619
620 /* Cursor to get menu entries that have a submenu*/
621 cursor get_mnes_w_sm_c is
622 SELECT MENU_ID, ENTRY_SEQUENCE, FUNCTION_ID, SUB_MENU_ID, GRANT_FLAG
623 from fnd_menu_entries
624 where SUB_MENU_ID = l_sub_menu_id;
625
626 entry_excluded boolean;
627 already_in_list boolean;
628 i number;
629 j number;
630 k number;
631 m number;
632 p number;
633 z number;
634 last_index pls_integer;
635
636
637 begin
638 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
639 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
640 c_log_head || l_api_name || '.begin',
641 c_pkg_name || '.' ||l_api_name || '(' ||
642 'p_menu_id=>'|| p_menu_id||');');
643 end if;
644 --
645 -- This routine processes the menu hierarchy upwards, marking on the way.
646 -- The basic algorithm of this routine is:
647 -- cur_mne(1).menu_id = menu passed in
648 -- loop
649 -- {
650 -- for each element of cur_mne
651 -- {
652 -- Mark this menu as uncompiled.
653 -- If it was already marked, go on to another one.
654 -- Select and add all the menu entries under its menu_id onto cur_mne
655 -- }
656 -- } until there are no more elements in cur_mne
657
658 /* We are going to add all the menu entries that have this function */
659 /* to the list, processing exclusion rules and checking them as we go. */
660 CUR_MNES_SIZE := 0;
661
662 CUR_MNES(CUR_MNES_SIZE).MENU_ID := p_menu_id;
663 CUR_MNES(CUR_MNES_SIZE).ENTRY_SEQ := 0;
664 CUR_MNES(CUR_MNES_SIZE).SUB_MENU_ID := 0;
665 CUR_MNES(CUR_MNES_SIZE).FUNCTION_ID := 0;
666 CUR_MNES(CUR_MNES_SIZE).GRANT_FLAG := 0;
667 CUR_MNES_SIZE := CUR_MNES_SIZE + 1;
668
669 /* Keep processing until there are no more menu entries in the list */
670 /* (or until we break out of this loop upon finding the menu) */
671 while (CUR_MNES_SIZE > 0) loop /* For each level */
672 i := 0;
673 m := 0;
674
675 /* Loop through all the menu entries on the current list */
676 while (i < CUR_MNES_SIZE) loop /* For each entry at the level */
677 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
678 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
679 c_log_head || l_api_name || '.proc_ent',
680 'Processing entry. i:' || to_char(i)||
681 ' CUR_MNES(i).MENU_ID:'|| to_char(CUR_MNES(i).MENU_ID) ||
682 ' CUR_MNES(i).ENTRY_SEQ:'|| to_char(CUR_MNES(i).ENTRY_SEQ) ||
683 ' CUR_MNES(i).SUB_MENU_ID:'|| to_char(CUR_MNES(i).SUB_MENU_ID) ||
684 ' CUR_MNES(i).FUNCTION_ID:'|| to_char(CUR_MNES(i).FUNCTION_ID) ||
685 ' CUR_MNES(i).GRANT_FLAG:'|| CUR_MNES(i).GRANT_FLAG);
686 end if;
687 begin
688 insert into fnd_compiled_menu_functions
689 (menu_id, function_id, grant_flag)
690 values
691 (CUR_MNES(i).MENU_ID, C_INVALID_MENU_VAL, C_INVALID_GRANT_VAL);
692 commit;
693 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
694 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
695 c_log_head || l_api_name || '.marked',
696 'Mark inserted.');
697 end if;
698 mark_existed := FALSE;
699 exception
700 when dup_val_on_index then
701 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
702 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
703 c_log_head || l_api_name || '.marked_existed',
704 'Mark was already there. ');
705 end if;
706 mark_existed := TRUE;
707 end;
708
709 if (not mark_existed) then
710 /* Put this on the list of menus we will get */
711 if(CUR_MNES(i).MENU_ID is not NULL) then
712 MENULIST(m) := CUR_MNES(i).MENU_ID;
713 m := m + 1;
714 end if;
715 end if;
716
717 i := i + 1;
718 end loop;
719
720 CUR_MNES_SIZE := 0;
721
722 /* Process the list of parent menuids */
723 p := 0;
724 while (p < m) loop
725 /* Get the list of menu entries above a particular submenu */
726 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
727 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
728 c_log_head || l_api_name || '.handle_menulist',
729 'Handling Menulist. p:' || to_char(p)||
730 ' MENULIST(p):'|| to_char(MENULIST(p)));
731 end if;
732 l_sub_menu_id := MENULIST(p);
733
734 if (BULK_COLLECTS_SUPPORTED) then
735 open get_mnes_w_sm_c;
736 fetch get_mnes_w_sm_c bulk collect into tbl_menu_id, tbl_ent_seq,
737 tbl_func_id, tbl_submnu_id, tbl_gnt_flg;
738 close get_mnes_w_sm_c;
739 -- See if we found any rows. If not set last_index to zero.
740 begin
741 if((tbl_menu_id.FIRST is NULL) or (tbl_menu_id.FIRST <> 1)) then
742 last_index := 0;
743 else
744 if (tbl_menu_id.FIRST is not NULL) then
745 last_index := tbl_menu_id.LAST;
746 else
747 last_index := 0;
748 end if;
749 end if;
750 exception
751 when others then
755 z:= 0;
752 last_index := 0;
753 end;
754 else
756 for rec in get_mnes_w_sm_c loop
757 z := z + 1;
758 tbl_menu_id(z) := rec.MENU_ID;
759 tbl_ent_seq(z) := rec.ENTRY_SEQUENCE;
760 tbl_func_id(z) := rec.FUNCTION_ID;
761 tbl_submnu_id (z):= rec.SUB_MENU_ID;
762 tbl_gnt_flg(z) := rec.GRANT_FLAG;
763 end loop;
764 last_index := z;
765 end if;
766
767
768 /* put those menu entries into the list for next time */
769 for q in 1..last_index loop
770 CUR_MNES(CUR_MNES_SIZE).MENU_ID := tbl_menu_id(q);
771 CUR_MNES(CUR_MNES_SIZE).ENTRY_SEQ := tbl_ent_seq(q);
772 CUR_MNES(CUR_MNES_SIZE).SUB_MENU_ID := tbl_submnu_id(q);
773 CUR_MNES(CUR_MNES_SIZE).FUNCTION_ID := tbl_func_id(q);
774 CUR_MNES(CUR_MNES_SIZE).GRANT_FLAG := tbl_gnt_flg(q);
775 CUR_MNES_SIZE := CUR_MNES_SIZE + 1;
776 end loop; /* for q in...*/
777 p := p + 1;
778 end loop; /* while (j < EXCLUSIONS_SIZE) loop */
779
780 end loop; /* while (i < CUR_MNES_SIZE) loop */
781
782 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
783 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
784 c_log_head || l_api_name || '.end',
785 'end');
786 end if;
787 end MARK_MENU_I;
788
789
790 -- ADD_QUEUED_MARKS-
791 -- Calls MARK_MENUS for all menus that we called QUEUE_MARK on.
792 --
793 procedure ADD_QUEUED_MARKS
794 is
795 l_api_name CONSTANT VARCHAR2(30) := 'ADD_QUEUED_MARKS';
796 RSTATUS number;
797 begin
798 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
799 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
800 c_log_head || l_api_name || '.begin',
801 c_pkg_name || '.' ||l_api_name);
802 end if;
803 if (DBMS_LOCK.REQUEST(C_MENU_LOCK_ID, DBMS_LOCK.S_MODE) = 0) then
804 begin
805 for i in 1..TBL_QUEUED_MENU_ID_MAX loop
806 MARK_MENU_I(TBL_QUEUED_MENU_ID(TBL_QUEUED_MENU_ID_MAX));
807 end loop;
808 -- Commit;
809 TBL_QUEUED_MENU_ID_MAX := 0;
810 exception when OTHERS then
811 null;
812 end;
813 RSTATUS := DBMS_LOCK.RELEASE(C_MENU_LOCK_ID);
814 end if;
815 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
816 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
817 c_log_head || l_api_name || '.end',
818 'return');
819 end if;
820 end;
821
822
823
824 -- QUEUE_MARK- store up a mark so it can later be processed. This is
825 -- normally only called from db triggers; other code can and should
826 -- just call mark_menu directly.
827 -- IN
828 -- p_menu_id - menu to mark
829 --
830 procedure QUEUE_MARK(
831 p_menu_id in number) is
832 l_api_name CONSTANT VARCHAR2(30) := 'QUEUE_MARK';
833 begin
834 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
835 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
836 c_log_head || l_api_name || '.begin',
837 c_pkg_name || '.' ||l_api_name ||'(' ||
838 'p_menu_id=>'|| p_menu_id||');');
839 end if;
840 if (TBL_QUEUED_MENU_ID_MAX <> 0)
841 AND (TBL_QUEUED_MENU_ID(TBL_QUEUED_MENU_ID_MAX) = p_menu_id) then
842 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
843 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
844 c_log_head || l_api_name || '.end_dupfound',
845 'return');
846 end if;
847 return; /* Dont make duplicate marks */
848 end if;
849
850 /* Store the mark away. */
851 TBL_QUEUED_MENU_ID_MAX := TBL_QUEUED_MENU_ID_MAX + 1;
852 TBL_QUEUED_MENU_ID(TBL_QUEUED_MENU_ID_MAX) := p_menu_id;
853
854 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
855 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
856 c_log_head || l_api_name || '.end',
857 'mark stored as TBL_QUEUED_MENU_ID_MAX: '||TBL_QUEUED_MENU_ID_MAX);
858 end if;
859 end;
860
861
862
863 -- MARK_MENU
864 -- Plow through the menu tree upwards, marking menus as we go up, so that
865 -- all the menus that include this menu will be marked. Stop when we reach
866 -- menus that have already been marked.
867 -- IN
868 -- menu_id - menu to mark
869 -- RETURNS
870 -- TRUE if success
871 --
872 procedure MARK_MENU(p_menu_id in number) is
873 RSTATUS number;
874 begin
875 if (DBMS_LOCK.REQUEST(C_MENU_LOCK_ID, DBMS_LOCK.S_MODE) = 0) then
876 begin
877 MARK_MENU_I(p_menu_id);
878 -- Commit;
879 exception when OTHERS then
880 null;
881 end;
882 RSTATUS := DBMS_LOCK.RELEASE(C_MENU_LOCK_ID);
883 end if;
884 end MARK_MENU;
885
886
887
888
889 -- TEST_ID_NO_GRANTS
890 -- Test if function id is accessible under current responsibility.
891 -- Looks only at the menus on current resp, not any grants.
892 -- IN
893 -- function_id - function id to test
894 -- MAINTENANCE_MODE_SUPPORT- the value from the column in fnd_form_functions
895 -- CONTEXT_DEPENDENCE- the value from the column in fnd_form_functions
896 -- TEST_MAINT_AVAILABILTY- 'Y' (default) means check if available for
897 -- current value of profile APPS_MAINTENANCE_MODE
898 -- 'N' means the caller is checking so it's
899 -- unnecessary to check.
900 -- RETURNS
901 -- TRUE if function is accessible
902 --
903 function TEST_ID_NO_GRANTS(function_id in number,
907 return boolean
904 MAINTENANCE_MODE_SUPPORT in varchar2,
905 CONTEXT_DEPENDENCE in varchar2,
906 TEST_MAINT_AVAILABILITY in varchar2)
908 is
909 l_api_name CONSTANT VARCHAR2(30) := 'TEST_ID_NO_GRANTS(4_ARGS)';
910 l_function_id number;
911 l_menu_id number;
912 dummy number;
913 l_resp_id number;
914 l_resp_appl_id number;
915 result boolean;
916 L_TEST_MAINT_AVAILABILITY boolean;
917 begin
918 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
919 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
920 c_log_head || l_api_name || '.begin',
921 c_pkg_name || '.' ||l_api_name|| '(' ||
922 'function_id =>'|| function_id ||
923 'MAINTENANCE_MODE_SUPPORT =>'|| MAINTENANCE_MODE_SUPPORT ||
924 'CONTEXT_DEPENDENCE =>'|| CONTEXT_DEPENDENCE ||
925 'TEST_MAINT_AVAILABILITY =>'|| TEST_MAINT_AVAILABILITY ||
926 ');');
927 end if;
928
929 l_function_id := function_id;
930 l_resp_id := Fnd_Global.Resp_Id;
931 l_resp_appl_id := Fnd_Global.Resp_Appl_Id;
932
933 if ( (TEST_MAINT_AVAILABILITY = 'Y')
934 OR (TEST_MAINT_AVAILABILITY is NULL)) then
935 L_TEST_MAINT_AVAILABILITY := TRUE;
936 else
937 L_TEST_MAINT_AVAILABILITY := FALSE;
938 end if;
939
940 begin
941 /* See if there are any exclusions */
942 select 1
943 into dummy
944 from fnd_resp_functions
945 where responsibility_id = l_resp_id
946 and application_id = l_resp_appl_id
947 and rownum = 1;
948
949 /* If we got here then there are exclusions, so don't use compiled */
950 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
951 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
952 c_log_head || l_api_name || '.end_excl',
953 'Because of exclusions, falling back, calling process_menu_tree_down.');
954 end if;
955
956 result := process_menu_tree_down(Fnd_Global.Resp_Appl_Id,
957 Fnd_Global.Resp_Id,
958 l_function_id);
959 if(result = FALSE) then
960 return FALSE;
961 else
962 if(L_TEST_MAINT_AVAILABILITY) then
963 if(AVAILABILITY(MAINTENANCE_MODE_SUPPORT) = 'Y') then
964 return TRUE;
965 else
966 return FALSE;
967 end if;
968 else
969 return TRUE;
970 end if;
971 end if;
972
973 exception
974 when no_data_found then
975 /* If we got here, there are no exclusions. */
976 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
977 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
978 c_log_head || l_api_name || '.not_excl',
979 'No exclusions, so finding menu');
980 end if;
981 if ( (P_LAST_RESP_ID = l_resp_id)
982 and (P_LAST_RESP_APPL_ID = l_resp_appl_id)) then
983 /* If the cache is valid just use the cache */
984 l_menu_id := P_LAST_MENU_ID;
985 else
986 /* Find the root menu for this responsibility */
987 begin
988 select menu_id
989 into l_menu_id
990 from fnd_responsibility
991 where responsibility_id = l_resp_id
992 and application_id = l_resp_appl_id;
993 /* Store the new value in the cache */
994 P_LAST_RESP_ID := l_resp_id;
995 P_LAST_RESP_APPL_ID := l_resp_appl_id;
996 P_LAST_MENU_ID := l_menu_id;
997 exception
998 when no_data_found then
999 /* No menu for this resp, so there can't be any functions */
1000 if (fnd_log.LEVEL_PROCEDURE >=
1001 fnd_log.g_current_runtime_level) then
1002 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1003 c_log_head || l_api_name || '.end_no_menu',
1004 'Couldnt find root menu for resp. returning FALSE ');
1005 end if;
1006 return FALSE;
1007 end;
1008 end if;
1009 end;
1010
1011 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1012 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1013 c_log_head || l_api_name || '.end',
1014 'Returning with call to is_function_on_menu; ');
1015 end if;
1016
1017 /* This is that call to actually do the test */
1018 result := IS_FUNCTION_ON_MENU(l_menu_id, l_function_id, TRUE);
1019
1020 if(result = FALSE) then
1021 return FALSE;
1022 else
1023 if(L_TEST_MAINT_AVAILABILITY) then
1024 if(AVAILABILITY(MAINTENANCE_MODE_SUPPORT) = 'Y') then
1025 return TRUE;
1026 else
1027 return FALSE;
1028 end if;
1029 else
1030 return TRUE;
1031 end if;
1032 end if;
1033
1034 end TEST_ID_NO_GRANTS;
1035
1036
1037 /* TEST_ID_SLOW- used for testing the security system. Note that this */
1038 /* code is no longer maintained and is actually out of date */
1039 /* because it doesn't consider MAINTENANCE_MODE_SUPPORT */
1040 function TEST_ID_SLOW(function_id in number) return boolean
1041 is
1042 l_api_name CONSTANT VARCHAR2(30) := 'TEST_ID';
1043 l_function_id number;
1044 begin
1045 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1046 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1047 c_log_head || l_api_name || '.begin',
1048 'function_id: '||function_id);
1049 end if;
1050
1051 l_function_id := function_id;
1052
1056 'Returning with call to process_menu_tree_down; ');
1053 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1054 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1055 c_log_head || l_api_name || '.end',
1057 end if;
1058 return process_menu_tree_down(Fnd_Global.Resp_Appl_Id,
1059 Fnd_Global.Resp_Id,
1060 l_function_id);
1061 end TEST_ID_SLOW;
1062
1063
1064 function IS_FUNCTION_ON_MENU(p_menu_id IN NUMBER,
1065 p_function_id IN NUMBER,
1066 p_check_grant_flag IN BOOLEAN)
1067 return boolean is
1068 l_api_name CONSTANT VARCHAR2(30) := 'IS_FUNCTION_ON_MENU';
1069
1070 dummy number;
1071 marked_as_not_compiled boolean;
1072 some_compiled_menus boolean;
1073 p_chk_gnt_as_vc varchar2(1);
1074 begin
1075 if (p_check_grant_flag) then
1076 p_chk_gnt_as_vc := 'Y';
1077 else
1078 p_chk_gnt_as_vc := 'N';
1079 end if;
1080
1081 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1082 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1083 c_log_head || l_api_name || '.begin',
1084 c_pkg_name || '.' ||l_api_name|| '(' ||
1085 'p_menu_id =>'|| to_char(p_menu_id) ||
1086 'p_check_grant_flag (as vc) =>'|| p_chk_gnt_as_vc || ');');
1087 end if;
1088
1089 if (FND_FUNCTION.G_ALREADY_FAST_COMPILED <> 'T') then
1090 FND_FUNCTION.FAST_COMPILE;
1091 end if;
1092
1093 -- Check first if there are any compiled rows at all for the menu
1094 some_compiled_menus := FALSE;
1095 begin
1096 select 1
1097 into dummy
1098 from fnd_compiled_menu_functions
1099 where menu_id = p_menu_id
1100 and rownum = 1;
1101 /* If we got here, there are compiled rows */
1102 some_compiled_menus := TRUE;
1103
1104 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
1105 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
1106 c_log_head || l_api_name || '.compiled_rows',
1107 'There are compiled rows.');
1108 end if;
1109 exception
1110 when no_data_found then
1111 some_compiled_menus := FALSE;
1112 end;
1113
1114 -- If any compiled menus at all, see if any of those are invalid
1115 marked_as_not_compiled := FALSE;
1116 if (some_compiled_menus) then
1117 begin
1118 select 1
1119 into dummy
1120 from fnd_compiled_menu_functions
1121 where menu_id = p_menu_id
1122 and grant_flag = C_INVALID_GRANT_VAL
1123 and rownum = 1;
1124 /* If we got here we know this is marked as not compiled. */
1125 marked_as_not_compiled := TRUE;
1126 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
1127 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
1128 c_log_head || l_api_name || '.marked_as_not_comp',
1129 'Menu Marked as not compiled.');
1130 end if;
1131 exception
1132 when no_data_found then
1133 marked_as_not_compiled := FALSE;
1134 end;
1135 end if;
1136
1137 -- If there are any rows, AND none of the rows are invalid, then
1138 -- assume the menu must be compiled.
1139 if (some_compiled_menus and (not marked_as_not_compiled)) then
1140 begin
1141 if (p_check_grant_flag) then
1142 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
1143 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
1144 c_log_head || l_api_name || '.check_w_gflg',
1145 'Checking with grantflag for p_menu_id: '|| to_char(p_menu_id)
1146 ||' and p_function_id:'||to_char(p_function_id));
1147 end if;
1148
1149 select 1
1150 into dummy
1151 from fnd_compiled_menu_functions
1152 where menu_id = p_menu_id
1153 and function_id = p_function_id
1154 and grant_flag = 'Y' ;
1155 else
1156 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
1157 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
1158 c_log_head || l_api_name || '.check_wo_gflg',
1159 'Checking without grantflag for p_menu_id: '|| to_char(p_menu_id)
1160 ||' and p_function_id:'||to_char(p_function_id));
1161 end if;
1162
1163 select 1
1164 into dummy
1165 from fnd_compiled_menu_functions
1166 where menu_id = p_menu_id
1167 and function_id = p_function_id;
1168 end if;
1169
1170 /* If we got here that means we found the compiled row */
1171 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1172 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1173 c_log_head || l_api_name || '.end_comp_found',
1174 'found compiled row. returning TRUE');
1175 end if;
1176 return TRUE;
1177 exception
1178 when no_data_found then
1179 /* Not in compilation, so this function is not in the menu */
1180 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1181 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1182 c_log_head || l_api_name || '.end_comp_nfound',
1183 'found no compiled row. returning FALSE');
1184 end if;
1185 return FALSE;
1186 end;
1187 else
1188 /* Submit a concurrent request to compile the marked menus. */
1189 begin
1190 FND_JOBS_PKG.SUBMIT_MENU_COMPILE;
1191 exception
1192 when others then /* Don't error out if we can't submit the request.*/
1193 if (fnd_log.LEVEL_EXCEPTION >= fnd_log.g_current_runtime_level) then
1194 fnd_log.string(FND_LOG.LEVEL_EXCEPTION,
1195 c_log_head || l_api_name || '.req_submit_fail',
1199
1196 'Could not submit concurrent request FNDSCMPI to recompile menus');
1197 end if;
1198 end;
1200 /* The menu is uncompiled so fall back to the full tree search */
1201 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1202 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1203 c_log_head || l_api_name || '.end',
1204 'uncompiled, fall back. return process_menu_tree_down_for_mn()');
1205 end if;
1206 return process_menu_tree_down_for_mn(p_menu_id, p_function_id,
1207 NULL, NULL);
1208 end if;
1209 end IS_FUNCTION_ON_MENU;
1210
1211
1212 function TEST_INSTANCE_ID_MAINTMODE(function_id in varchar2,
1213 object_name IN VARCHAR2,
1214 instance_pk1_value IN VARCHAR2,
1215 instance_pk2_value IN VARCHAR2,
1216 instance_pk3_value IN VARCHAR2,
1217 instance_pk4_value IN VARCHAR2,
1218 instance_pk5_value IN VARCHAR2,
1219 user_name IN VARCHAR2,
1220 MAINTENANCE_MODE_SUPPORT in varchar2,
1221 CONTEXT_DEPENDENCE in varchar2,
1222 TEST_MAINT_AVAILABILITY in varchar2
1223 ) return boolean is
1224 l_api_name CONSTANT VARCHAR2(30) := 'TEST_INSTANCE_ID_MAINTMODE';
1225 ret_val varchar2(1) := 'F';
1226 ret_bool boolean := FALSE;
1227 L_MAINTENANCE_MODE_SUPPORT varchar2(8) := NULL;
1228 L_CONTEXT_DEPENDENCE varchar2(8) := NULL;
1229 -- modified for bug#5395351
1230 l_function_name fnd_form_functions.function_name%type := NULL;
1231 begin
1232 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1233 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1234 c_log_head || l_api_name || '.begin',
1235 c_pkg_name || '.' ||l_api_name || '(' ||
1236 'function_id=>'|| function_id||
1237 ', object_name=>'|| object_name||
1238 ', instance_pk1_value=>'|| instance_pk1_value||
1239 ', instance_pk2_value=>'|| instance_pk2_value||
1240 ', instance_pk3_value=>'|| instance_pk3_value||
1241 ', instance_pk4_value=>'|| instance_pk4_value||
1242 ', instance_pk5_value=>'|| instance_pk5_value||
1243 ', user_name=>'|| nvl(user_name, '[NULL]')||
1244 ');');
1245 end if;
1246
1247
1248 if ( (fnd_data_security.DISALLOW_DEPRECATED = 'Y')
1249 and ( (user_name <> SYS_CONTEXT('FND','USER_NAME'))
1250 or ( (user_name is not null)
1251 and (SYS_CONTEXT('FND','USER_NAME') is null)))) then
1252 /* In R12 we do not allow passing values other than */
1253 /* the current user name (which is the default), */
1254 /* so we raise a runtime exception if that deprecated */
1255 /* kind of call is made to this routine. */
1256 fnd_message.set_name('FND', 'GENERIC-INTERNAL ERROR');
1257 fnd_message.set_token('ROUTINE',
1258 c_pkg_name || '.'|| l_api_name);
1259 fnd_message.set_token('REASON',
1260 'Invalid API call. Parameter user_name: '||user_name||
1261 ' was passed to API '||c_pkg_name || '.TEST_INSTANCE' ||
1262 '. object_name: '||object_name||'. '||
1263 ' In Release 12 and beyond the user_name parameter '||
1264 'is unsupported, and any product team that passes it '||
1265 'must correct their code because it does not work '||
1266 'correctly. Please see the deprecated API document at '||
1267 'http://files.oraclecorp.com/content/AllPublic/'||
1268 'SharedFolders/ATG%20Requirements-Public/R12/'||
1269 'Requirements%20Definition%20Document/'||
1270 'Application%20Object%20Library/DeprecatedApiRDD.doc '||
1271 'Oracle employees who encounter this error should log '||
1272 'a bug against the product that owns the call to this '||
1273 'routine, which is likely the owner of the object that '||
1274 'was passed to this routine: '||
1275 object_name);
1276 if (fnd_log.LEVEL_EXCEPTION >=
1277 fnd_log.g_current_runtime_level) then
1278 fnd_log.message(FND_LOG.LEVEL_EXCEPTION,
1279 c_log_head || l_api_name || '.end_depr_param',
1280 FALSE);
1281 end if;
1282 fnd_message.raise_error;
1283 end if;
1284
1285
1286 -- Change function name to id
1287 begin
1288 select F.FUNCTION_NAME, F.MAINTENANCE_MODE_SUPPORT, F.CONTEXT_DEPENDENCE
1289 into l_function_name, L_MAINTENANCE_MODE_SUPPORT, L_CONTEXT_DEPENDENCE
1290 from FND_FORM_FUNCTIONS F
1291 where F.FUNCTION_ID = test_instance_id_maintmode.function_id;
1292 exception
1293 when no_data_found then
1294 -- Invalid function name
1295 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1296 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1297 c_log_head || l_api_name || '.end_notfound',
1298 'returning FALSE');
1299 end if;
1300 return(FALSE);
1301 end;
1302
1303
1304 if (TEST_ID_NO_GRANTS(function_id,
1305 L_MAINTENANCE_MODE_SUPPORT, L_CONTEXT_DEPENDENCE, 'N')) then
1306 ret_bool := TRUE;
1307 goto check_avail;
1308 end if;
1309
1310 if (object_name is not NULL) then
1311 ret_val := FND_DATA_SECURITY.CHECK_FUNCTION(p_api_version => 1.0,
1312 p_function => l_function_name,
1316 p_instance_pk3_value => instance_pk3_value,
1313 p_object_name => object_name,
1314 p_instance_pk1_value => instance_pk1_value,
1315 p_instance_pk2_value => instance_pk2_value,
1317 p_instance_pk4_value => instance_pk4_value,
1318 p_instance_pk5_value => instance_pk5_value,
1319 p_user_name => user_name);
1320 if (ret_val = 'T') then
1321 ret_bool := TRUE;
1322 goto check_avail;
1323 else
1324 if (ret_val = 'E') or (ret_val = 'U') then
1325 FND_MESSAGE.CLEAR;
1326 end if;
1327 ret_bool := FALSE;
1328 end if;
1329 end if;
1330
1331 if (ret_bool = FALSE) then /* Check global object type grant */
1332 ret_val := FND_DATA_SECURITY.check_global_object_type_grant
1333 (p_api_version => 1.0,
1334 p_function => l_function_name,
1335 p_user_name => user_name);
1336 if (ret_val = 'T') then
1337 ret_bool := TRUE;
1338 goto check_avail;
1339 else
1340 ret_bool := FALSE;
1341 goto all_done;
1342 end if;
1343 end if;
1344
1345
1346 <<check_avail>>
1347 if (ret_bool = TRUE) then
1348 if(AVAILABILITY(L_MAINTENANCE_MODE_SUPPORT) = 'Y') then
1349 ret_bool := TRUE;
1350 else
1351 ret_bool := FALSE;
1352 end if;
1353 end if;
1354
1355 <<all_done>>
1356
1357 if (ret_bool) then
1358 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1359 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1360 c_log_head || l_api_name || '.end_available',
1361 'returning TRUE;');
1362 end if;
1363 return TRUE;
1364 else
1365 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1366 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1367 c_log_head || l_api_name || '.end_false',
1368 'returning FALSE;');
1369 end if;
1370 return FALSE;
1371 end if;
1372
1373 end TEST_INSTANCE_ID_MAINTMODE;
1374
1375
1376 function TEST_INSTANCE_ID(function_id in varchar2,
1377 object_name IN VARCHAR2,
1378 instance_pk1_value IN VARCHAR2,
1379 instance_pk2_value IN VARCHAR2,
1380 instance_pk3_value IN VARCHAR2,
1381 instance_pk4_value IN VARCHAR2,
1382 instance_pk5_value IN VARCHAR2,
1383 user_name IN VARCHAR2
1384 ) return boolean is
1385 l_api_name CONSTANT VARCHAR2(30) := 'TEST_INSTANCE_ID';
1386 ret_val boolean := FALSE;
1387 L_MAINTENANCE_MODE_SUPPORT varchar2(8) := NULL;
1388 L_CONTEXT_DEPENDENCE varchar2(8) := NULL;
1389 begin
1390 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1391 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1392 c_log_head || l_api_name || '.begin',
1393 c_pkg_name || '.' ||l_api_name|| '(' ||
1394 'function_id =>'|| function_id ||
1395 ');');
1396 end if;
1397
1398 -- Change function name to id
1399 begin
1400 -- Bug 5059644. Modified bind variable name from function_id to
1401 -- TEST_INSTANCE_ID.function_id. This change is to avoid FTS.
1402 select F.MAINTENANCE_MODE_SUPPORT, F.CONTEXT_DEPENDENCE
1403 into L_MAINTENANCE_MODE_SUPPORT, L_CONTEXT_DEPENDENCE
1404 from FND_FORM_FUNCTIONS F
1405 where F.function_id = test_instance_id.function_id;
1406 exception
1407 when no_data_found then
1408 -- Invalid function name
1409 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1410 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1411 c_log_head || l_api_name || '.end_notfound',
1412 'returning FALSE');
1413 end if;
1414 return(FALSE);
1415 end;
1416
1417 ret_val := TEST_INSTANCE_ID_MAINTMODE(
1418 function_id,
1419 object_name,
1420 instance_pk1_value,
1421 instance_pk2_value,
1422 instance_pk3_value,
1423 instance_pk4_value,
1424 instance_pk5_value,
1425 user_name,
1426 L_MAINTENANCE_MODE_SUPPORT,
1427 L_CONTEXT_DEPENDENCE,
1428 'Y');
1429
1430 if (ret_val) then
1431 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1432 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1433 c_log_head || l_api_name || '.end_available',
1434 'returning TRUE;');
1435 end if;
1436 return TRUE;
1437 else
1438 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1439 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1440 c_log_head || l_api_name || '.end_false',
1441 'returning FALSE;');
1442 end if;
1443 return FALSE;
1444 end if;
1445
1446 end TEST_INSTANCE_ID;
1447
1448
1449 --
1450 -- TEST_INSTANCE
1451 -- Test if function is accessible under current resp and user, for
1452 -- the object instance (database row) which is the current instance.
1453 -- This actually checks both the function security
1454 -- and data security system as described at the oracle internal link:
1455 -- http://www-apps.us.oracle.com/atg/plans/r115x/datasec.txt
1456 -- Note that this takes account of global object type grants (grants
1460 -- Generally the user should pass the object_name and whichever
1457 -- that apply to all object types), as well as global object instance
1458 -- grants (which apply to all instances of a particular object type).
1459 --
1461 -- of the instance_pkX_values apply to that object.
1462 -- If the user does not pass the object_name param, then only global
1463 -- object type grants will get picked up from fnd_grants.
1464 -- If the user does not pass the instance_pkXvalues, but does pass
1465 -- object_name, only instance type grants will get picked up.
1466 --
1467 -- IN
1468 -- function_name - function to test
1469 -- object_name and pk values- object and primary key values of the current
1470 -- object.
1471 -- user_name- Normally the caller leaves this blank so it will test
1472 -- with the current FND user. But folks who populate their
1473 -- grants with special "compound" usernames might need
1474 -- to pass the grantee_key (user_name) of the current user.
1475 -- RETURNS
1476 -- TRUE if function is accessible
1477 -- FALSE if function is not accessible or if there was an error.
1478 --
1479 function TEST_INSTANCE(function_name in varchar2,
1480 object_name IN VARCHAR2,
1481 instance_pk1_value IN VARCHAR2,
1482 instance_pk2_value IN VARCHAR2,
1483 instance_pk3_value IN VARCHAR2,
1484 instance_pk4_value IN VARCHAR2,
1485 instance_pk5_value IN VARCHAR2,
1486 user_name IN VARCHAR2
1487 ) return boolean is
1488 l_api_name CONSTANT VARCHAR2(30) := 'TEST_INSTANCE';
1489 ret_val boolean := FALSE;
1490 function_id number;
1491 L_MAINTENANCE_MODE_SUPPORT varchar2(8);
1492 L_CONTEXT_DEPENDENCE varchar2(8);
1493 begin
1494 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1495 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1496 c_log_head || l_api_name || '.begin',
1497 c_pkg_name || '.' ||l_api_name|| '(' ||
1498 'function_name =>'|| function_name ||
1499 'object_name =>'|| object_name ||
1500 'instance_pk1_value =>'|| instance_pk1_value ||
1501 'instance_pk2_value =>'|| instance_pk2_value ||
1502 'instance_pk3_value =>'|| instance_pk3_value ||
1503 'instance_pk4_value =>'|| instance_pk4_value ||
1504 'instance_pk5_value =>'|| instance_pk5_value ||
1505 'user_name =>'|| user_name ||
1506 ');');
1507 end if;
1508
1509
1510 -- Change function name to id
1511 begin
1512 select F.FUNCTION_ID, F.MAINTENANCE_MODE_SUPPORT, F.CONTEXT_DEPENDENCE
1513 into function_id, L_MAINTENANCE_MODE_SUPPORT, L_CONTEXT_DEPENDENCE
1514 from FND_FORM_FUNCTIONS F
1515 where F.FUNCTION_NAME = test_instance.function_name;
1516 exception
1517 when no_data_found then
1518 -- Invalid function name
1519 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1520 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1521 c_log_head || l_api_name || '.end_notfound',
1522 'returning FALSE');
1523 end if;
1524 return(FALSE);
1525 end;
1526
1527 ret_val := TEST_INSTANCE_ID_MAINTMODE(
1528 function_id,
1529 object_name,
1530 instance_pk1_value,
1531 instance_pk2_value,
1532 instance_pk3_value,
1533 instance_pk4_value,
1534 instance_pk5_value,
1535 user_name,
1536 L_MAINTENANCE_MODE_SUPPORT,
1537 L_CONTEXT_DEPENDENCE,
1538 'Y');
1539
1540 if (ret_val) then
1541 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1542 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1543 c_log_head || l_api_name || '.end_available',
1544 'returning TRUE;');
1545 end if;
1546 return TRUE;
1547 else
1548 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1549 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1550 c_log_head || l_api_name || '.end_false',
1551 'returning FALSE;');
1552 end if;
1553 return FALSE;
1554 end if;
1555
1556 end TEST_INSTANCE;
1557
1558
1559 -- TEST_ID
1560 -- Test if function id is accessible under current responsibility.
1561 -- IN
1562 -- function_id - function id to test
1563 -- MAINTENANCE_MODE_SUPPORT- the value from the column in fnd_form_functions
1564 -- CONTEXT_DEPENDENCE- the value from the column in fnd_form_functions
1565 -- TEST_MAINT_AVAILABILTY- 'Y' (default) means check if available for
1566 -- current value of profile APPS_MAINTENANCE_MODE
1567 -- 'N' means the caller is checking so it's
1568 -- unnecessary to check.
1569 -- RETURNS
1570 -- TRUE if function is accessible
1571 --
1572 function TEST_ID(function_id in number,
1573 MAINTENANCE_MODE_SUPPORT in varchar2,
1574 CONTEXT_DEPENDENCE in varchar2,
1575 TEST_MAINT_AVAILABILITY in varchar2)
1576 return boolean
1577 is
1578 l_api_name CONSTANT VARCHAR2(30) := 'TEST_ID(4_ARGS)';
1579 result boolean;
1580 begin
1581 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1582 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1583 c_log_head || l_api_name || '.begin',
1584 c_pkg_name || '.' ||l_api_name|| '(' ||
1585 'function_id =>'|| function_id ||
1586 'MAINTENANCE_MODE_SUPPORT =>'|| MAINTENANCE_MODE_SUPPORT ||
1587 'CONTEXT_DEPENDENCE =>'|| CONTEXT_DEPENDENCE ||
1591
1588 'TEST_MAINT_AVAILABILITY =>'|| TEST_MAINT_AVAILABILITY ||
1589 ');');
1590 end if;
1592 result := TEST_INSTANCE_ID_MAINTMODE(
1593 function_id => function_id,
1594 object_name => NULL,
1595 instance_pk1_value => NULL,
1596 instance_pk2_value => NULL,
1597 instance_pk3_value => NULL,
1598 instance_pk4_value => NULL,
1599 instance_pk5_value => NULL,
1600 user_name => NULL,
1601 MAINTENANCE_MODE_SUPPORT =>MAINTENANCE_MODE_SUPPORT,
1602 CONTEXT_DEPENDENCE => CONTEXT_DEPENDENCE,
1603 TEST_MAINT_AVAILABILITY => TEST_MAINT_AVAILABILITY);
1604
1605 if (result) then
1606 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1607 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1608 c_log_head || l_api_name || '.end_available',
1609 'returning TRUE;');
1610 end if;
1611 return TRUE;
1612 else
1613 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1614 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1615 c_log_head || l_api_name || '.end_false',
1616 'returning FALSE;');
1617 end if;
1618 return FALSE;
1619 end if;
1620
1621 end TEST_ID;
1622
1623
1624
1625
1626
1627 -- TEST_ID
1628 -- Test if function id is accessible under current responsibility.
1629 -- IN
1630 -- function_id - function id to test
1631 -- RETURNS
1632 -- TRUE if function is accessible
1633 --
1634 function TEST_ID(function_id in number) return boolean
1635 is
1636 L_MAINTENANCE_MODE_SUPPORT varchar2(8);
1637 L_CONTEXT_DEPENDENCE varchar2(8);
1638 l_function_id number;
1639 l_api_name CONSTANT VARCHAR2(30) := 'TEST_ID(1_ARG)';
1640 begin
1641 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1642 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1643 c_log_head || l_api_name || '.begin',
1644 c_pkg_name || '.' ||l_api_name|| '(' ||
1645 'function_id =>'|| function_id || ');');
1646 end if;
1647
1648 L_MAINTENANCE_MODE_SUPPORT := NULL;
1649 L_CONTEXT_DEPENDENCE := NULL;
1650 l_function_id := function_id;
1651 begin
1652 /* Get the extra columns */
1653 select MAINTENANCE_MODE_SUPPORT, CONTEXT_DEPENDENCE
1654 into L_MAINTENANCE_MODE_SUPPORT, L_CONTEXT_DEPENDENCE
1655 from fnd_form_functions
1656 where function_id = l_function_id;
1657
1658 exception
1659 when no_data_found then
1660 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1661 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1662 c_log_head || l_api_name || '.end_bad_fnid',
1663 'function_id passed to test_id cant be found in fnd_form_functions.');
1664 end if;
1665 return FALSE; /* Bad function id passed */
1666 end;
1667
1668 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1669 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1670 c_log_head || l_api_name || '.end_call_3_arg',
1671 'Passing MAINTENANCE_MODE_SUPPORT and CONTEXT_DEPENDENCE to test_id().');
1672 end if;
1673
1674 return test_id(l_function_id,
1675 L_MAINTENANCE_MODE_SUPPORT, L_CONTEXT_DEPENDENCE, 'Y');
1676 end TEST_ID;
1677
1678
1679
1680 --
1681 -- TEST
1682 -- Test if function is accessible under current responsibility.
1683 -- IN
1684 -- function_name - function to test
1685 -- TEST_MAINT_AVAILABILTY- 'Y' (default) means check if available for
1686 -- current value of profile APPS_MAINTENANCE_MODE
1687 -- 'N' means the caller is checking so it's
1688 -- unnecessary to check.
1689 -- RETURNS
1690 -- TRUE if function is accessible
1691 --
1692 function TEST(function_name in varchar2,
1693 TEST_MAINT_AVAILABILITY in varchar2) return boolean
1694 is
1695 l_api_name CONSTANT VARCHAR2(30) := 'TEST';
1696 function_id number;
1697 L_MAINTENANCE_MODE_SUPPORT varchar2(8);
1698 L_CONTEXT_DEPENDENCE varchar2(8);
1699 begin
1700 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1701 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1702 c_log_head || l_api_name || '.begin',
1703 c_pkg_name || '.' ||l_api_name|| '(' ||
1704 'function_name =>'|| function_name || ');');
1705 end if;
1706
1707 -- Change function name to id
1708 begin
1709 select F.FUNCTION_ID, F.MAINTENANCE_MODE_SUPPORT, F.CONTEXT_DEPENDENCE
1710 into function_id, L_MAINTENANCE_MODE_SUPPORT, L_CONTEXT_DEPENDENCE
1711 from FND_FORM_FUNCTIONS F
1712 where F.FUNCTION_NAME = test.function_name;
1713 exception
1714 when no_data_found then
1715 -- Invalid function name
1716 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1717 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1718 c_log_head || l_api_name || '.end_notfound',
1719 'returning FALSE');
1720 end if;
1721 return(FALSE);
1722 end;
1723
1724 -- Call test_id to complete
1725 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1726 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1727 c_log_head || l_api_name || '.end',
1728 'returning call to test_id of function_id:'||function_id
1729 ||' L_MAINTENANCE_MODE_SUPPORT:'||L_MAINTENANCE_MODE_SUPPORT
1730 ||' L_CONTEXT_DEPENDENCE:'||L_CONTEXT_DEPENDENCE);
1731 end if;
1732 return(Test_Id(function_id,
1736
1733 L_MAINTENANCE_MODE_SUPPORT, L_CONTEXT_DEPENDENCE,
1734 TEST_MAINT_AVAILABILITY));
1735 end TEST;
1737 /* Note: The reason for having separate _I (internal) routines is so that*/
1738 /* the non- _I routines will always be top level routines and will never */
1739 /* call other top level routines. That way those top level routines can */
1740 /* be pragma autonomous_transaction. */
1741 /* If we didn't have it this way, we would have the situation where */
1742 /* COMPILE_ALL_FROM_SCRATCH calls COMPILE_ALL_MARKED, which calls */
1743 /* COMPILE_MENU_MARKED, and all those routines are */
1744 /* pragma autononous_transaction because they can be called independently.*/
1745 /* That would yield three nested autononous transactions which would at */
1746 /* minimum waste rollback segments and at maximum would cause weird behavior */
1747
1748 function COMPILE_MENU_MARKED_I(p_menu_id NUMBER,
1749 p_force in varchar2) return NUMBER;
1750 function COMPILE_ALL_FROM_SCRATCH_I return NUMBER;
1751 function COMPILE_ALL_MARKED_I(compile_missing in VARCHAR2)
1752 return NUMBER;
1753 procedure MARK_ALL_I;
1754
1755 /* COMPILE_I- This API is invoked by the concurrent program */
1756 procedure COMPILE_I( errbuf out NOCOPY varchar2,
1757 retcode out NOCOPY varchar2,
1758 everything in varchar2 /* 'Y'= from scratch*/) is
1759 l_api_name CONSTANT VARCHAR2(30) := 'COMPILE';
1760 numrows NUMBER;
1761 msgbuf varchar2(2000);
1762 begin
1763 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1764 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1765 c_log_head || l_api_name || '.begin',
1766 c_pkg_name || '.' ||l_api_name ||'(' ||
1767 'everything=>'|| everything||');');
1768 end if;
1769
1770 if(everything = 'Y') then
1771 numrows := COMPILE_ALL_FROM_SCRATCH_I;
1772 fnd_message.set_name('FND', 'SECURITY_COMPILED_FROM_SCRATCH');
1773 msgbuf := fnd_message.get;
1774 FND_FILE.put_line(FND_FILE.log, msgbuf);
1775 else
1776 numrows := COMPILE_ALL_MARKED_I('Y');
1777 fnd_message.set_name('FND', 'SECURITY_COMPILED_MARKED_MENUS');
1778 msgbuf := fnd_message.get;
1779 FND_FILE.put_line(FND_FILE.log, msgbuf);
1780 end if;
1781
1782 fnd_message.set_name('FND', 'GENERIC_ROWS_PROCESSED');
1783 fnd_message.set_token('ROWS', numrows);
1784 msgbuf := fnd_message.get;
1785 FND_FILE.put_line(FND_FILE.log, msgbuf);
1786
1787 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1788 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1789 c_log_head || l_api_name || '.end',
1790 'returning from compile. numrows='||numrows);
1791 end if;
1792 exception
1793 when others then
1794 errbuf := sqlerrm;
1795 retcode := '2';
1796 FND_FILE.put_line(FND_FILE.log,errbuf);
1797 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1798 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1799 c_log_head || l_api_name || '.end_exception',
1800 'returning from compile with exception. numrows='||numrows);
1801 end if;
1802 raise;
1803 end compile_I;
1804
1805 /* COMPILE- This API is invoked by the concurrent program */
1806 procedure COMPILE( errbuf out NOCOPY varchar2,
1807 retcode out NOCOPY varchar2,
1808 everything in varchar2 /* 'Y'= from scratch*/) is
1809 pragma autonomous_transaction;
1810 RSTATUS number;
1811 begin
1812 if (DBMS_LOCK.REQUEST(C_MENU_LOCK_ID, DBMS_LOCK.X_MODE) = 0) then
1813 begin
1814 COMPILE_I(errbuf, retcode, everything);
1815 exception when OTHERS then
1816 null;
1817 end;
1818 RSTATUS := DBMS_LOCK.RELEASE(C_MENU_LOCK_ID);
1819 end if;
1820 end;
1821
1822
1823 /* COMPILE_CHANGES - This API is invoked by the DBMS_SCHEDULER */
1824 procedure COMPILE_CHANGES is
1825 pragma autonomous_transaction;
1826 l_api_name CONSTANT VARCHAR2(30) := 'COMPILE_CHANGES';
1827 numrows NUMBER;
1828 RSTATUS number;
1829 begin
1830 FND_JOBS_PKG.APPS_INITIALIZE_SYSADMIN;
1831
1832 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1833 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1834 c_log_head || l_api_name || '.begin',
1835 c_pkg_name || '.' ||l_api_name ||';');
1836 end if;
1837
1838 if (DBMS_LOCK.REQUEST(C_MENU_LOCK_ID, DBMS_LOCK.X_MODE) = 0) then
1839 begin
1840 numrows := COMPILE_ALL_MARKED_I('Y');
1841 exception when OTHERS then
1842 null;
1843 end;
1844 RSTATUS := DBMS_LOCK.RELEASE(C_MENU_LOCK_ID);
1845 end if;
1846
1847 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1848 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1849 c_log_head || l_api_name || '.end',
1850 'returning from compile. numrows='||numrows);
1851 end if;
1852
1853 exception
1854 when others then
1855 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1856 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1857 c_log_head || l_api_name || '.end_exception',
1858 'returning from compile with exception. numrows='||numrows);
1859 end if;
1860 -- ### Should log return code = 2 and sqlerrm buffer
1861 raise;
1862 end;
1863
1864
1865 -- COMPILE_ALL_FROM_SCRATCH_I-
1866 -- Recompiles everything from scratch.
1867 function COMPILE_ALL_FROM_SCRATCH_I return NUMBER is
1868 l_api_name CONSTANT VARCHAR2(30) := 'COMPILE_ALL_FROM_SCRATCH';
1869 begin
1870 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1871 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1875 MARK_ALL_I;
1872 c_log_head || l_api_name || '.begin',
1873 c_pkg_name || '.' ||l_api_name );
1874 end if;
1876 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1877 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1878 c_log_head || l_api_name || '.end',
1879 'returning COMPILE_ALL_MARKED');
1880 end if;
1881 return COMPILE_ALL_MARKED_I('Y');
1882 end compile_all_from_scratch_I;
1883
1884 -- COMPILE_ALL_FROM_SCRATCH-
1885 -- Recompiles everything from scratch.
1886 function COMPILE_ALL_FROM_SCRATCH return NUMBER is
1887 pragma autonomous_transaction;
1888 RESULT_COUNT number;
1889 RSTATUS number;
1890 begin
1891 if (DBMS_LOCK.REQUEST(C_MENU_LOCK_ID, DBMS_LOCK.X_MODE) = 0) then
1892 begin
1893 RESULT_COUNT := COMPILE_ALL_FROM_SCRATCH_I;
1894 exception when OTHERS then
1895 null;
1896 end;
1897 RSTATUS := DBMS_LOCK.RELEASE(C_MENU_LOCK_ID);
1898 end if;
1899 return RESULT_COUNT;
1900 end;
1901
1902
1903
1904 -- MARK_MISSING-
1905 --
1906 -- Marks as uncompiled all the menus that don't have any rows
1907 -- in the compiled table.
1908 -- No locking needed because it's only called internally from
1909 -- a procedure that already has a lock.
1910 --
1911 procedure MARK_MISSING is
1912 l_api_name CONSTANT VARCHAR2(30) := 'MARK_MISSING';
1913
1914 TYPE NUMBER_TABLE_TYPE is table of NUMBER INDEX BY BINARY_INTEGER;
1915 TBL_MENU_ID NUMBER_TABLE_TYPE;
1916
1917 last_index pls_integer;
1918
1919 /* Bug 5196541. Added sql hints to improve the performance */
1920 cursor get_missing_menus_c is
1921 select /*+ INDEX_FFS(menus) */ menu_id from fnd_menus menus
1922 where not exists
1923 (select 'X'
1924 from fnd_compiled_menu_functions cmf
1925 where menus.menu_id = cmf.menu_id);
1926 z number;
1927 begin
1928 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1929 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1930 c_log_head || l_api_name || '.begin',
1931 c_pkg_name || '.' ||l_api_name );
1932 end if;
1933
1934 /* Mark as uncompiled any menus that don't have a representation */
1935 /* in the compiled table */
1936
1937 if (BULK_COLLECTS_SUPPORTED) then
1938 open get_missing_menus_c;
1939 fetch get_missing_menus_c bulk collect into tbl_menu_id;
1940 close get_missing_menus_c;
1941
1942 -- See if we found any rows. If not set last_index to zero.
1943 begin
1944 if((tbl_menu_id.FIRST is NULL) or (tbl_menu_id.FIRST <> 1)) then
1945 last_index := 0;
1946 else
1947 if (tbl_menu_id.FIRST is not NULL) then
1948 last_index := tbl_menu_id.LAST;
1949 else
1950 last_index := 0;
1951 end if;
1952 end if;
1953 exception
1954 when others then
1955 last_index := 0;
1956 end;
1957 else
1958 z:= 0;
1959 for rec in get_missing_menus_c loop
1960 z := z + 1;
1961 tbl_menu_id(z) := rec.MENU_ID;
1962 end loop;
1963 last_index := z;
1964 end if;
1965
1966 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
1967 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
1968 c_log_head || l_api_name || '.got_list',
1969 'Got list of menus. Number of elements- last_index:' ||
1970 last_index);
1971 end if;
1972
1973 /* mark that menu as uncompiled */
1974 for q in 1..last_index loop
1975 begin
1976 insert into fnd_compiled_menu_functions
1977 (menu_id, function_id, grant_flag)
1978 values
1979 (tbl_menu_id(q), C_INVALID_MENU_VAL, C_INVALID_GRANT_VAL);
1980 -- We commit the menu mark in order to keep the rollback segment
1981 -- from getting too big as we compile lots of menus.
1982 commit;
1983 exception
1984 when dup_val_on_index then
1985 if (fnd_log.LEVEL_EXCEPTION >= fnd_log.g_current_runtime_level) then
1986 fnd_log.string(FND_LOG.LEVEL_EXCEPTION,
1987 c_log_head || l_api_name || '.exception',
1988 'Duplicate Value on index. Should never happen.');
1989 end if;
1990 null; /* Should never happen but better safe than sorry */
1991 end;
1992 end loop;
1993 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
1994 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
1995 c_log_head || l_api_name || '.end',
1996 'end');
1997 end if;
1998 end MARK_MISSING;
1999
2000
2001 -- COMPILE_ALL_MARKED_I-
2002 --
2003 -- recompiles all the marked menus.
2004 --
2005 -- for (each menu_id marked)
2006 -- FND_FUNCTION.COMPILE_MENU_MARKED(menu_id)
2007 --
2008 -- Returns number of compiled rows changed.
2009 --
2010 function COMPILE_ALL_MARKED_I(compile_missing in VARCHAR2)
2011 return number is
2012 l_api_name CONSTANT VARCHAR2(30) := 'COMPILE_ALL_MARKED';
2013 l_menu_id number;
2014 l_rows_processed number;
2015
2016 TYPE NUMBER_TABLE_TYPE is table of NUMBER INDEX BY BINARY_INTEGER;
2017 TBL_MENU_ID NUMBER_TABLE_TYPE;
2018
2019 begin
2020 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
2021 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
2022 c_log_head || l_api_name || '.begin',
2023 c_pkg_name || '.' ||l_api_name ||'(' ||
2024 'compile_missing=>'|| compile_missing||');');
2025 end if;
2026
2027 /* Mark any menus that aren't in the compiled representation, */
2028 /* so they will get recompiled */
2032
2029 if (compile_missing = 'Y') then
2030 mark_missing;
2031 end if;
2033 l_rows_processed := 0;
2034 while (TRUE) loop
2035 /* Find a menu that needs compilation */
2036 begin /* The hint below was suggested to avoid FTS in bug 2078561 */
2037 select
2038 /*+ INDEX (fnd_compiled_menu_functions fnd_compiled_menu_functions_n3) */
2039 menu_id
2040 into l_menu_id
2041 from fnd_compiled_menu_functions
2042 where grant_flag = C_INVALID_GRANT_VAL
2043 and rownum = 1;
2044 exception
2045 when no_data_found then
2046 /* We've gotten to all the marked rows, so we are done */
2047 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
2048 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
2049 c_log_head || l_api_name || '.end_none_marked',
2050 'returning l_rows_processed:' || l_rows_processed );
2051 end if;
2052
2053 -- Set the alreadt fast compiled flag since we are done bug5184601
2054
2055 fnd_function.g_already_fast_compiled := 'T';
2056 return l_rows_processed;
2057 end;
2058
2059 /* Reset recursion detector and recompile that menu */
2060 TBL_RECURS_DETEC_MENU_ID_MAX := 0;
2061
2062 l_rows_processed := l_rows_processed +
2063 COMPILE_MENU_MARKED_I(l_menu_id, 'N');
2064
2065 end loop;
2066
2067 fnd_function.g_already_fast_compiled := 'T';
2068 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
2069 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
2070 c_log_head || l_api_name || '.end',
2071 'returning l_rows_processed:' || l_rows_processed );
2072 end if;
2073 return l_rows_processed;
2074
2075 end compile_all_marked_I;
2076
2077
2078 -- COMPILE_ALL_MARKED-
2079 --
2080 -- recompiles all the marked menus.
2081 --
2082 -- for (each menu_id marked)
2083 -- FND_FUNCTION.COMPILE_MENU_MARKED(menu_id)
2084 --
2085 -- Returns number of compiled rows changed.
2086 --
2087 function COMPILE_ALL_MARKED(compile_missing in VARCHAR2)
2088 return number is
2089 pragma autonomous_transaction;
2090 RESULT_COUNT number;
2091 RSTATUS number;
2092 begin
2093 if (DBMS_LOCK.REQUEST(C_MENU_LOCK_ID, DBMS_LOCK.X_MODE) = 0) then
2094 begin
2095 RESULT_COUNT := COMPILE_ALL_MARKED_I(compile_missing);
2096 exception when OTHERS then
2097 null;
2098 end;
2099 RSTATUS := DBMS_LOCK.RELEASE(C_MENU_LOCK_ID);
2100 end if;
2101 return RESULT_COUNT;
2102 end;
2103
2104
2105
2106 -- FAST_COMPILE-
2107 --
2108 -- Recompiles all the marked menus if and only if they haven't yet
2109 -- been already compiled in this session.
2110 --
2111 -- Other packages that reference FND_COMPILED_MENU_FUNCTIONS should
2112 -- call it like this in their package initialization block:
2113 --
2114 -- if (FND_FUNCTION.G_ALREADY_FAST_COMPILED <> 'T') then
2115 -- FND_FUNCTION.FAST_COMPILE;
2116 -- end if;
2117 --
2118 -- Administrators can also call it from SQL*Plus in order to compile
2119 -- the FND_COMPILED_MENU_FUNCTIONS table, like this:
2120 --
2121 -- execute FND_FUNCTION.FAST_COMPILE;
2122 --
2123 procedure FAST_COMPILE is
2124 pragma autonomous_transaction;
2125 RESULT_COUNT number;
2126 RSTATUS number;
2127 L_MENU_ID number;
2128 begin
2129 if (G_ALREADY_FAST_COMPILED <> 'T') then
2130 -- Check if any menus need compilation bug5184601
2131 begin
2132
2133 select
2134 /*+ INDEX (fnd_compiled_menu_functions fnd_compiled_menu_functions_n3) */
2135 MENU_ID into L_MENU_ID
2136 from FND_COMPILED_MENU_FUNCTIONS
2137 where GRANT_FLAG = C_INVALID_GRANT_VAL
2138 and ROWNUM = 1;
2139 exception
2140 when NO_DATA_FOUND then
2141 G_ALREADY_FAST_COMPILED:= 'T';
2142 return;
2143 when OTHERS then
2144 null;
2145 end;
2146 if (DBMS_LOCK.REQUEST(C_MENU_LOCK_ID, DBMS_LOCK.X_MODE) = 0) then
2147 begin
2148 RESULT_COUNT := COMPILE_ALL_MARKED_I('N');
2149 exception when OTHERS then
2150 null;
2151 end;
2152 RSTATUS := DBMS_LOCK.RELEASE(C_MENU_LOCK_ID);
2153 end if;
2154 end if;
2155 end;
2156
2157
2158 --
2159 -- COMPILE_MENU_MARKED_I
2160 -- if (not force mode) and (this menu is not marked)
2161 -- return; we're done
2162 -- Delete all rows from fnd_compiled_menu_functions for this menu_id
2163 -- for each menu_entry on this menu
2164 -- if this is a function
2165 -- add it to the compiled table
2166 -- if this is a menu
2167 -- Call FND_FUNCTION.COMPILE_MENU_MARKED() to compile this submenu
2168 -- Copy all the submenu elements from the compiled table
2169 -- delete the marker row for this menu_id
2170 --
2171 -- IN:
2172 -- p_menu_id- menu to compile.
2173 -- p_force- 'Y' means compile even if already marked compiled.
2174 -- 'N' is default, meaning only compile if not marked as compiled.
2175 -- RETURNS - a count of how many rows needed to be processed.
2176 --
2177 function COMPILE_MENU_MARKED_I(p_menu_id NUMBER,
2178 p_force in varchar2)
2179 return NUMBER is
2180
2181 l_api_name CONSTANT VARCHAR2(30) := 'COMPILE_MENU_MARKED';
2182 result boolean;
2183 dummy number;
2184 cursor get_mnes_down_c is
2185 SELECT MENU_ID, ENTRY_SEQUENCE, FUNCTION_ID, SUB_MENU_ID, GRANT_FLAG
2186 from fnd_menu_entries
2187 where MENU_ID = p_menu_id;
2188
2189 TYPE NUMBER_TABLE_TYPE is table of NUMBER INDEX BY BINARY_INTEGER;
2193 TBL_MENU_ID NUMBER_TABLE_TYPE;
2190 TYPE VARCHAR2_TABLE_TYPE is table of VARCHAR2(1) INDEX BY BINARY_INTEGER;
2191
2192 /* Returns from the bulk collect (fetches) */
2194 TBL_ENT_SEQ NUMBER_TABLE_TYPE;
2195 TBL_FUNC_ID NUMBER_TABLE_TYPE;
2196 TBL_SUBMNU_ID NUMBER_TABLE_TYPE;
2197 TBL_GNT_FLG VARCHAR2_TABLE_TYPE;
2198
2199 l_submnu_id NUMBER;
2200 cursor get_compiled_menu_fns_c is
2201 select function_id, grant_flag
2202 from fnd_compiled_menu_functions
2203 where menu_id = l_submnu_id
2204 and grant_flag <> C_INVALID_GRANT_VAL
2205 and grant_flag <> C_BLANK_GRANT_VAL;
2206 TBL_COPY_FUNC_ID NUMBER_TABLE_TYPE;
2207 TBL_COPY_GNT_FLG VARCHAR2_TABLE_TYPE;
2208
2209 l_rows_processed NUMBER;
2210 l_sub_rows_processed NUMBER;
2211 last_index pls_integer;
2212 comp_fail exception;
2213 i number;
2214 j number;
2215 begin
2216 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
2217 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
2218 c_log_head || l_api_name || '.begin',
2219 c_pkg_name || '.' ||l_api_name|| '(' ||
2220 'p_menu_id =>'|| p_menu_id ||
2221 'p_force =>'|| p_force || ');');
2222 end if;
2223
2224 l_rows_processed := 0;
2225
2226 /* Check to see if this menu is marked and bail if not marked. */
2227 if((p_force is NULL) OR (p_force <> 'Y')) then
2228 begin
2229 select 1
2230 into dummy
2231 from fnd_compiled_menu_functions
2232 where menu_id = p_menu_id
2233 and grant_flag = C_INVALID_GRANT_VAL
2234 and rownum = 1;
2235 exception
2236 when no_data_found then
2237 return 0; /* If we got here that means this menu is not marked; bail*/
2238 end;
2239 end if;
2240
2241 /* Check the recursion stack to make sure this menu isn't also a */
2242 /* parent of this menu. */
2243 for i in 1..TBL_RECURS_DETEC_MENU_ID_MAX loop
2244 if ( TBL_RECURS_DETEC_MENU_ID(i) = p_menu_id ) then
2245 if (fnd_log.LEVEL_ERROR >= fnd_log.g_current_runtime_level) then
2246 fnd_log.string(FND_LOG.LEVEL_ERROR,
2247 c_log_head || l_api_name || '.recursion',
2248 'A looping menu has been detected in menu_id:'||to_char(p_menu_id));
2249 end if;
2250
2251 /* Don't go into an infinite recursion; this menu is already */
2252 /* being compiled so we don't need to do it again. */
2253 return 0;
2254 end if;
2255 end loop;
2256
2257 /* Push this menu_id onto the recursion detection stack */
2258 TBL_RECURS_DETEC_MENU_ID_MAX := TBL_RECURS_DETEC_MENU_ID_MAX + 1;
2259 TBL_RECURS_DETEC_MENU_ID(TBL_RECURS_DETEC_MENU_ID_MAX) := p_menu_id;
2260
2261 /* If we havent already deleted the data for this menu, delete it now. */
2262 begin
2263 delete from fnd_compiled_menu_functions
2264 where menu_id = p_menu_id
2265 and grant_flag <> C_INVALID_GRANT_VAL;
2266 exception when no_data_found then
2267 null;
2268 end;
2269
2270 /* Get the list of menu entries below this submenu. */
2271 if(BULK_COLLECTS_SUPPORTED) then
2272 open get_mnes_down_c;
2273 fetch get_mnes_down_c bulk collect into tbl_menu_id, tbl_ent_seq,
2274 tbl_func_id, tbl_submnu_id, tbl_gnt_flg;
2275 close get_mnes_down_c;
2276
2277 -- See if we found any rows. If not set last_index to zero.
2278 begin
2279 if((tbl_menu_id.FIRST is NULL) or (tbl_menu_id.FIRST <> 1)) then
2280 last_index := 0;
2281 else
2282 if (tbl_menu_id.FIRST is not NULL) then
2283 last_index := tbl_menu_id.LAST;
2284 else
2285 last_index := 0;
2286 end if;
2287 end if;
2288 exception
2289 when others then
2290 last_index := 0;
2291 end;
2292 else
2293 i:= 0;
2294 for below in get_mnes_down_c loop
2295 i := i + 1;
2296 tbl_menu_id(i) := below.MENU_ID;
2297 tbl_ent_seq(i) := below.ENTRY_SEQUENCE;
2298 tbl_func_id(i) := below.FUNCTION_ID;
2299 tbl_submnu_id (i):= below.SUB_MENU_ID;
2300 tbl_gnt_flg(i) := below.GRANT_FLAG;
2301 end loop;
2302 last_index := i;
2303 end if;
2304
2305
2306
2307 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
2308 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
2309 c_log_head || l_api_name || '.got_mnulist',
2310 'Got list of mnes below p_menu_id '||
2311 to_char(p_menu_id)||'. Number of elements- last_index:'
2312 ||to_char(last_index));
2313 end if;
2314
2315 /* put those menu entries into the list for next time */
2316 for q in 1..last_index loop
2317 /* If this is a function, Put this function into the cache */
2318 if (tbl_func_id(q) is not NULL) then
2319 begin
2320 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
2321 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
2322 c_log_head || l_api_name || '.insert1',
2323 'Inserting into compiled table menu_id: '|| to_char(p_menu_id)
2324 ||' func_id:'||to_char(tbl_func_id(q) )
2325 ||' gnt_flag:'||tbl_gnt_flg(q) );
2326 end if;
2327 l_rows_processed := l_rows_processed + 1;
2328 insert into fnd_compiled_menu_functions
2329 (MENU_ID, FUNCTION_ID, GRANT_FLAG)
2330 values
2331 (p_menu_id, tbl_func_id(q), tbl_gnt_flg(q));
2332 exception
2333 when dup_val_on_index then
2334 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
2335 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
2336 c_log_head || l_api_name || '.dup_val_index',
2340 if (fnd_log.LEVEL_STATEMENT >=
2337 'Insert failed with dup val on index. No Problem.');
2338 end if;
2339 if (tbl_gnt_flg(q) <> 'N') then /* don't overwrite 'Y' with 'N'*/
2341 fnd_log.g_current_runtime_level) then
2342 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
2343 c_log_head || l_api_name || '.upd_gnt_flag',
2344 'About to update grant_flag.');
2345 end if;
2346 /* If the grant flag isn't right, update it. */
2347 update fnd_compiled_menu_functions
2348 set grant_flag = tbl_gnt_flg(q)
2349 where menu_id = p_menu_id
2350 and function_id = tbl_func_id(q)
2351 and grant_flag = 'N';
2352 end if;
2353 end;
2354 end if;
2355
2356 /* Compile the sub menu, recursively, and then copy the compiled */
2357 /* rows from the submenu into this menu. */
2358 if (tbl_submnu_id(q) is not NULL) then
2359 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
2360 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
2361 c_log_head || l_api_name || '.call_recursive',
2362 'Calling recursively for tbl_submnu_id(q): '||
2363 tbl_submnu_id(q));
2364 end if;
2365
2366 -- ### What if the submenu has never been compiled?
2367 l_sub_rows_processed :=
2368 COMPILE_MENU_MARKED_I(tbl_submnu_id(q), p_force);
2369
2370 /* for all the entries in this just-compiled menu */
2371 l_submnu_id := tbl_submnu_id(q);
2372
2373 if (BULK_COLLECTS_SUPPORTED) then
2374 open get_compiled_menu_fns_c;
2375 fetch get_compiled_menu_fns_c bulk collect
2376 into tbl_copy_func_id, tbl_copy_gnt_flg;
2377 close get_compiled_menu_fns_c;
2378
2379 -- See if we found any rows. If not set last_index to zero.
2380 begin
2381 if( (tbl_copy_func_id.FIRST is NULL)
2382 or (tbl_copy_func_id.FIRST <> 1)) then
2383 last_index := 0;
2384 else
2385 if (tbl_copy_func_id.FIRST is not NULL) then
2386 last_index := tbl_copy_func_id.LAST;
2387 else
2388 last_index := 0;
2389 end if;
2390 end if;
2391 exception
2392 when others then
2393 last_index := 0;
2394 end;
2395 else
2396 j:= 0;
2397 for rec in get_compiled_menu_fns_c loop
2398 j := j + 1;
2399 tbl_copy_func_id(j) := rec.FUNCTION_ID;
2400 tbl_copy_gnt_flg(j) := rec.GRANT_FLAG;
2401 end loop;
2402 last_index := j;
2403 end if;
2404
2405 -- ### Use the correct count for sub-menus
2406 l_rows_processed := l_rows_processed + last_index;
2407
2408 /* put those entries into the compilation for the menu worked on. */
2409 for z in 1..last_index loop
2410 begin
2411
2412 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
2413 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
2414 c_log_head || l_api_name || '.insert2',
2415 'Inserting into compiled table p_menu_id: '|| to_char(p_menu_id)
2416 ||' tbl_copy_func_id(z):'||tbl_copy_func_id(z)
2417 ||' tbl_copy_gnt_flg(ze):'||tbl_copy_gnt_flg(z) );
2418 end if;
2419 insert into fnd_compiled_menu_functions
2420 (menu_id, function_id, grant_flag)
2421 values
2422 (p_menu_id, tbl_copy_func_id(z), tbl_copy_gnt_flg(z));
2423 exception
2424 when dup_val_on_index then
2425 if (l_rows_processed > 1) then
2426 l_rows_processed := l_rows_processed - 1;
2427 end if;
2428 if (fnd_log.LEVEL_STATEMENT >=
2429 fnd_log.g_current_runtime_level) then
2430 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
2431 c_log_head || l_api_name || '.dup_val_index2',
2432 'Insert failed with dup val on index. No Problem.');
2433 end if;
2434 if (tbl_copy_gnt_flg(z) <> 'N') then/*don't overwrite 'Y' w/ 'N'*/
2435 if (fnd_log.LEVEL_STATEMENT >=
2436 fnd_log.g_current_runtime_level) then
2437 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
2438 c_log_head || l_api_name || '.upd_gnt_flag2',
2439 'About to update grant_flag.');
2440 end if;
2441 /* If the grant flag isn't right, update it. */
2442 update fnd_compiled_menu_functions
2443 set grant_flag = tbl_copy_gnt_flg(z)
2444 where menu_id = p_menu_id
2445 and function_id = tbl_copy_func_id(z)
2446 and grant_flag = 'N';
2447 end if;
2448 end;
2449 end loop;
2450 end if;
2451 end loop;
2452
2453 /* If there were no functions in this menu, then insert a "blank" mark */
2454 /* to indicate that we've compiled this menu but there are no fns. */
2455 if (l_rows_processed = 0) then
2456 begin
2457 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
2458 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
2459 c_log_head || l_api_name || '.insert_blank',
2460 'Inserting blank row into compiled table p_menu_id: '||
2461 to_char(p_menu_id) );
2462 end if;
2463 insert into fnd_compiled_menu_functions
2464 (menu_id, function_id, grant_flag)
2465 values
2466 (p_menu_id, C_BLANK_MENU_VAL, C_BLANK_GRANT_VAL);
2467 exception
2468 when dup_val_on_index then
2469 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
2473 end if;
2470 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
2471 c_log_head || l_api_name || '.dup_val_index3',
2472 'Insert failed with dup val on index. No Problem.');
2474 null; /* Only put one copy of this row */
2475 end;
2476 end if;
2477
2478 /* now that this menu has been compiled, remove the "uncompiled mark" */
2479 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
2480 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
2481 c_log_head || l_api_name || '.del_mark',
2482 'Removing uncompiled mark from p_menu_id:'||p_menu_id);
2483 end if;
2484 begin
2485 delete from fnd_compiled_menu_functions
2486 where menu_id = p_menu_id
2487 and grant_flag = C_INVALID_GRANT_VAL;
2488 exception when no_data_found then
2489 null; /* We should never get here, but dont bomb if we do */
2490 end;
2491
2492 -- We commit the menu compilation in order to keep the rollback segment
2493 -- from getting too big as we compile lots of menus.
2494 commit;
2495
2496 -- Pop the recursion detection stack
2497 if(TBL_RECURS_DETEC_MENU_ID_MAX <= 1) then
2498 TBL_RECURS_DETEC_MENU_ID_MAX := 0;
2499 else
2500 TBL_RECURS_DETEC_MENU_ID_MAX := TBL_RECURS_DETEC_MENU_ID_MAX - 1;
2501 end if;
2502
2503 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
2504 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
2505 c_log_head || l_api_name || '.end',
2506 'returning l_rows_processed:' || l_rows_processed );
2507 end if;
2508 return l_rows_processed;
2509 end compile_menu_marked_I;
2510
2511
2512
2513 --
2514 -- COMPILE_MENU_MARKED
2515 -- if (not force mode) and (this menu is not marked)
2516 -- return; we're done
2517 -- Delete all rows from fnd_compiled_menu_functions for this menu_id
2518 -- for each menu_entry on this menu
2519 -- if this is a function
2520 -- add it to the compiled table
2521 -- if this is a menu
2522 -- Call FND_FUNCTION.COMPILE_MENU_MARKED() to compile this submenu
2523 -- Copy all the submenu elements from the compiled table
2524 -- delete the marker row for this menu_id
2525 --
2526 -- RETURNS - a count of how many rows needed to be processed.
2527 --
2528 function COMPILE_MENU_MARKED(p_menu_id NUMBER,
2529 p_force in varchar2) return NUMBER is
2530 pragma autonomous_transaction;
2531 RESULT_COUNT number;
2532 RSTATUS number;
2533 begin
2534 if (DBMS_LOCK.REQUEST(C_MENU_LOCK_ID, DBMS_LOCK.X_MODE) = 0) then
2535 begin
2536 RESULT_COUNT := COMPILE_MENU_MARKED_I(p_menu_id, p_force);
2537 exception when OTHERS then
2538 null;
2539 end;
2540 RSTATUS := DBMS_LOCK.RELEASE(C_MENU_LOCK_ID);
2541 end if;
2542 return RESULT_COUNT;
2543 end;
2544
2545
2546
2547 -- MARK_ALL_I-
2548 --
2549 -- truncate compiled table.
2550 -- for each menu_id in fnd_menus
2551 -- Mark that menu_id in the compiled table
2552 --
2553 --
2554 procedure MARK_ALL_I is
2555 l_api_name CONSTANT VARCHAR2(30) := 'MARK_ALL';
2556 l_menu_id number;
2557
2558 cursor get_all_menus_c is
2559 select menu_id from fnd_menus;
2560
2561 last_index pls_integer;
2562
2563 TYPE NUMBER_TABLE_TYPE is table of NUMBER INDEX BY BINARY_INTEGER;
2564 TBL_MENU_ID NUMBER_TABLE_TYPE;
2565 more_to_delete boolean;
2566
2567 z number;
2568 begin
2569 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
2570 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
2571 c_log_head || l_api_name || '.begin',
2572 c_pkg_name || '.' ||l_api_name );
2573 end if;
2574
2575 more_to_delete := TRUE;
2576 while (more_to_delete) loop
2577 begin
2578 more_to_delete := FALSE;
2579 -- Delete menus one menu at a time.
2580 delete from fnd_compiled_menu_functions
2581 where menu_id = (select menu_id
2582 from fnd_compiled_menu_functions
2583 where rownum = 1);
2584 -- Commit to keep the rollback small
2585 commit;
2586 if (sql%rowcount > 0) then
2587 more_to_delete := TRUE;
2588 end if;
2589 exception
2590 when no_data_found then
2591 more_to_delete := FALSE;
2592 end;
2593 end loop;
2594
2595 /* Mark all menus as uncompiled */
2596
2597 -- We commit the menu mark in order to keep the rollback segment
2598 -- from getting too big as we compile lots of menus.
2599 commit;
2600 if (BULK_COLLECTS_SUPPORTED) then
2601 open get_all_menus_c;
2602 fetch get_all_menus_c bulk collect into tbl_menu_id;
2603 close get_all_menus_c;
2604 -- See if we found any rows. If not set last_index to zero.
2605 begin
2606 if((tbl_menu_id.FIRST is NULL) or (tbl_menu_id.FIRST <> 1)) then
2607 last_index := 0;
2608 else
2609 if (tbl_menu_id.FIRST is not NULL) then
2610 last_index := tbl_menu_id.LAST;
2611 else
2612 last_index := 0;
2613 end if;
2614 end if;
2615 exception
2616 when others then
2617 last_index := 0;
2618 end;
2619 else
2620 z:= 0;
2621 for rec in get_all_menus_c loop
2622 z := z + 1;
2623 tbl_menu_id(z) := rec.MENU_ID;
2624 end loop;
2625 last_index := z;
2626 end if;
2627
2628 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
2632 last_index);
2629 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
2630 c_log_head || l_api_name || '.got_mnulist',
2631 'Got list of menus. Number of elements- last_index:'||
2633 end if;
2634
2635 /* mark that menu as uncompiled */
2636 for q in 1..last_index loop
2637 begin
2638 insert into fnd_compiled_menu_functions
2639 (menu_id, function_id, grant_flag)
2640 values
2641 (tbl_menu_id(q), C_INVALID_MENU_VAL, C_INVALID_GRANT_VAL);
2642 -- We commit the menu mark in order to keep the rollback segment
2643 -- from getting too big as we compile lots of menus.
2644 commit;
2645 exception
2646 when dup_val_on_index then
2647 if (fnd_log.LEVEL_STATEMENT >= fnd_log.g_current_runtime_level) then
2648 fnd_log.string(FND_LOG.LEVEL_STATEMENT,
2649 c_log_head || l_api_name || '.dup_val_index',
2650 'Mark failed because of dup val on index. Shouldnt happen.');
2651 end if;
2652 null; /* Should never happen but better safe than sorry */
2653 end;
2654 end loop;
2655
2656 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
2657 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
2658 c_log_head || l_api_name || '.end',
2659 'end');
2660 end if;
2661 end mark_all_I;
2662
2663 -- MARK_ALL-
2664 --
2665 -- truncate compiled table.
2666 -- for each menu_id in fnd_menus
2667 -- Mark that menu_id in the compiled table
2668 --
2669 --
2670 procedure MARK_ALL is
2671 pragma autonomous_transaction;
2672 RSTATUS number;
2673 begin
2674 if (DBMS_LOCK.REQUEST(C_MENU_LOCK_ID, DBMS_LOCK.S_MODE) = 0) then
2675 begin
2676 MARK_ALL_I;
2677 exception when OTHERS then
2678 null;
2679 end;
2680 RSTATUS := DBMS_LOCK.RELEASE(C_MENU_LOCK_ID);
2681 end if;
2682 end;
2683
2684
2685 ---Function get_function_id
2686 ------------------------------
2687 Function get_function_id(p_function_name in varchar2
2688 ) return number is
2689 v_function_id number;
2690 l_api_name CONSTANT VARCHAR2(30) := 'GET_FUNCTION_ID';
2691 Begin
2692 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
2693 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
2694 c_log_head || l_api_name || '.begin',
2695 c_pkg_name || '.' ||l_api_name|| '(' ||
2696 'p_function_name =>'|| p_function_name ||');');
2697 end if;
2698
2699 if (p_function_name = g_func_name_cache) then
2700 v_function_id := g_func_id_cache; /* If we have it cached, use value */
2701 else /* not cached, hit db */
2702 select function_id
2703 into v_function_id
2704 from fnd_form_functions
2705 where function_name=p_function_name;
2706
2707 /* Store in cache */
2708 g_func_id_cache := v_function_id;
2709 g_func_name_cache := p_function_name;
2710 end if;
2711
2712
2713 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
2714 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
2715 c_log_head || l_api_name || '.end',
2716 'returning v_function_id:' || v_function_id);
2717 end if;
2718 return v_function_id;
2719 exception
2720 when no_data_found then
2721 if (fnd_log.LEVEL_PROCEDURE >= fnd_log.g_current_runtime_level) then
2722 fnd_log.string(FND_LOG.LEVEL_PROCEDURE,
2723 c_log_head || l_api_name || '.end_null',
2724 'returning null');
2725 end if;
2726 return null;
2727 end;
2728
2729 -- COMPILE_MENU
2730 --
2731 -- Called as a Concurrent Program to compile menu when entries are added
2732 --
2733 --
2734 procedure COMPILE_MENU(errbuf out NOCOPY varchar2, retcode out NOCOPY number, p_menu_id in number, p_force in varchar2) is
2735
2736 numrows number;
2737 begin
2738 numrows := COMPILE_MENU_MARKED(p_menu_id, p_force);
2739 errbuf := ' Compile successful. ';
2740 retcode :=0;
2741
2742 exception
2743 when others then
2744 retcode := 2;
2745 errbuf := 'ERROR: '|| sqlerrm;
2746 end;
2747
2748 end FND_FUNCTION;