DBA Data[Home] [Help]

PACKAGE BODY: APPS.FND_GFM

Source


1 PACKAGE BODY fnd_gfm AS
2 /* $Header: AFGFMB.pls 120.17.12010000.1 2008/07/25 14:15:26 appldev ship $ */
3 
4 -----------------------------------------------------------------------------
5 --global variable to indicate wether we are are in R12 env or 11i
6 --in a broad way we use the 'TYPE' of function 'FND_FNDFLUPL to
7 --obtain the same 11i = TYPE = WWW / R12 = TYPE = JSP respectively
8 --setter and getter methods are used to access the same.
9 
10 g_release_version number := 0;
11 
12 /*--------------------------------------------------------------------------*/
13 /*
14  * file_handle - internal file descriptor
15  *   Files are referenced by a numeric file identifier, which is an index
16  *   into the open_file_handles table.  Due to an odd PL/SQL API for tables,
17  *   we do not actually delete old elements in the table, but simply nullify
18  *   them.  Thus, on new file openings, we first traverse the table looking
19  *   for empty slots.
20  */
21 TYPE file_handle IS RECORD ( fid NUMBER, offset INTEGER );
22 TYPE file_handles IS TABLE OF file_handle;
23 open_file_handles file_handles := file_handles();
24 /*--------------------------------------------------------------------------*/
25 /*
26  * err_msg - (PRIVATE) shortcut for building standard gfm error message
27  */
28 PROCEDURE err_msg(name varchar2) is
29 begin
30   fnd_message.set_name('FND', 'SQL_PLSQL_ERROR');
31   fnd_message.set_token('ROUTINE', 'FND_GFM.'||name);
32   fnd_message.set_token('ERRNO', SQLCODE);
33   fnd_message.set_token('REASON', SQLERRM);
34 end err_msg;
35 /*--------------------------------------------------------------------------*/
36 /*
37  * authenticate - validate the specified access key
38  */
39 FUNCTION authenticate(access_id  number,
40                       file_id    number default -1) RETURN boolean
41 is
42   rowcount number;
43 begin
44   select count(*) into rowcount
45   from   fnd_lob_access
46   where  nvl(file_id,-1) = nvl(authenticate.file_id, -1)
47   and    access_id = authenticate.access_id
48   and    timestamp > sysdate;
49 
50   if (rowcount=1) then
51     return TRUE;
52   end if;
53   return FALSE;
54 exception
55   when others then
56     fnd_gfm.err_msg('authenticate');
57     raise;
58 end;
59 /*--------------------------------------------------------------------------*/
60 /*
61  * authorize - generate an authorized access key, good for one day.
62  */
63 FUNCTION authorize(file_id number) return number is
64 pragma autonomous_transaction;
65   result number;
66 begin
67   insert into fnd_lob_access (access_id, file_id, timestamp)
68   values (fnd_crypto.SmallRandomNumber,
69           file_id, sysdate+1)
70   returning access_id into result;
71   commit;
72 
73   return result;
74 exception
75   when others then
76     fnd_gfm.err_msg('authorize');
77     raise;
78 end;
79 /*--------------------------------------------------------------------------*/
80 /*
81  * construct_download_URL - construct a download URL
82  *   Very little work is done here but the knowledge of the exact
83  *   syntax of the URL is self-contained.  If the purge option is
84  *   indicated, we set the expiration date to the near future, so
85  *   that the file is purged whether or not the URL is issued.
86  */
87 FUNCTION construct_download_url2(gfm_agent     varchar2,
88 				file_id       number,
89 				purge_on_view boolean default FALSE,
90 				modplsql      boolean,
91 			        authenticate  boolean)
92 	 return varchar2 is
93 pragma autonomous_transaction;
94   access_id  number;
95   url        varchar2(2000);
96   file_ext   varchar2(10);
97   file_name  varchar2(300);
98   export_mime varchar2(30);
99   ext_length number;
100 begin
101   access_id := fnd_gfm.authorize(file_id);
102 
103  /*  Changed url structure to be gfmagent/fndgfm/fnd_gfm.get/arg1/arg2/fnd_gfm.ext
104   *  to eliminate the dependancy on the desktop having the content/mime type set
105   *  correctly.  Now the file downloaded will have the correct extension so as long
106   *  as the file ext is associated to an application it will open successfully/correctly.
107   *  Temporarily hardcoding the most common export file types until a lookup can be created.
108  */
109 
110   -- purge_on_view is TRUE then exporting
111 
112  if purge_on_view then
113 
114     export_mime := fnd_profile.value('FND_EXPORT_MIME_TYPE');
115 
116     if export_mime = 'text/tab-separated-values' then
117        file_ext := '.tsv';
118     elsif
119        export_mime = 'text/comma-separated-values' then
120        file_ext := '.csv';
121     elsif
122        export_mime = 'text/plain' then
123        file_ext := '.txt';
124     elsif
125        export_mime like '%excel' then
126        file_ext := '.xls';
127     elsif
128        export_mime = 'text/html' then
129        file_ext := '.htm';
130     elsif
131        export_mime like 'application/%msword' then
132        file_ext := '.doc';
133     else
134        file_ext := '';
135     end if;
136 
137     file_name := 'fnd_gfm'||file_ext;
138     update fnd_lobs set fnd_lobs.expiration_date = sysdate + 0.5
139     where  fnd_lobs.file_id = construct_download_url2.file_id;
140 
141  else
142     select instr(file_name,'.',-1) into ext_length
143     from fnd_lobs
144     where file_id = construct_download_url2.file_id;
145 
146     if ext_length > 0 then
147        select substr(file_name,instr(file_name,'/',-1)+1) into file_name
148        from fnd_lobs
149        where file_id = construct_download_url2.file_id;
150     else
151        file_name := 'fnd_gfm';
152     end if;
153  end if;
154 
155   --check the release
156   if (fnd_gfm.getRelease = 11) then
157      url := fnd_web_config.trail_slash(gfm_agent)||'fndgfm/fnd_gfm.get/'||access_id||'/'||file_id||'/'||file_name;
158   else
159     -- we take all this trouble only if we are on R12
160     --construct the download URL
161     if (modplsql) then
162       url := fnd_web_config.trail_slash(gfm_agent)||'fndgfm/fnd_gfm.get/'||access_id||'/'||file_id||'/'||file_name;
163     else
164       if (authenticate) then
165         url := fnd_web_config.trail_slash(fnd_profile.value('APPS_FRAMEWORK_AGENT'))||'OA_HTML/fndgfm.jsp?mode=download_blob'||'&'||'fid='||file_id||'&'||'accessid='||access_id;
166       else
167         url := fnd_web_config.trail_slash(fnd_profile.value('APPS_FRAMEWORK_AGENT'))||'OA_HTML/fndgfm.jsp?mode=download_blob'||'&'||'fid='||file_id;
168       end if;
169 
170     end if;
171   end if;
172   commit;
173   return url;
174 exception
175   when others then
176     fnd_gfm.err_msg('construct_download_url2');
177     raise;
178 end;
179 
180 /*--------------------------------------------------------------------------*/
181 /*
182  * construct_upload_URL - construct an upload URL
183  *   Very little work is done here but the knowledge of the exact
184  *   syntax of the URL is self-contained.
185  */
186 FUNCTION construct_upload_URL(gfm_agent     VARCHAR2,
187                               proc          VARCHAR2,
188 			      access_id     NUMBER)
189 return varchar2 is
190 --sqlbuf   varchar2(1000);
191 params_passed  varchar2(2000);
192 func_id  number;
193 user_id  number;
194 resp_id  number;
195 resp_appl_id number;
196 sec_grp_id number :=0;
197 begin
198   if (fnd_gfm.getRelease = 12) then
199 
200     user_id := fnd_profile.value('USER_ID');
201     resp_id := fnd_profile.value('RESP_ID');
202     resp_appl_id := fnd_profile.value('RESP_APPL_ID');
203     sec_grp_id := fnd_profile.value('SECURITY_GROUP_ID');
204 
205     params_passed := 'access_id'||access_id;
206 
207     begin
208       select function_id
209       into   func_id
210       from   fnd_form_functions
211       where  function_name = 'FND_FNDFLUPL';
212     exception
213       when no_data_found then
214          return fnd_web_config.trail_slash(gfm_agent)||proc;
215       when too_many_rows then
216          select function_id
217          into   func_id
218          from   fnd_form_functions
219          where  function_name = 'FND_FNDFLUPL'
220          and    upper(type) = 'JSP';
221       when others then
222          fnd_gfm.err_msg('construct_upload_url_r12');
223          raise;
224     end;
225 
226     return FND_RUN_FUNCTION.GET_RUN_FUNCTION_URL(
227                               P_FUNCTION_ID => func_id,
228                               P_RESP_APPL_ID => resp_appl_id,
229                               P_RESP_ID => resp_id,
230                               P_SECURITY_GROUP_ID => sec_grp_id,
231                               P_PARAMETERS => params_passed);
232 
233     /*
234       As of now there seems no way to open from pl/sql block this
235       OA Framework page. If we do we could incorporate the below code
236       for a generic callback
237     */
238 
239     --block the call till we have a submit
240     --fnd_gfm.wait_for_upload('FND_GFM_ALERT' || to_char(access_id));
241     --if we can create a unfirom structure for the callback
242     --we could invoke a callback here things we can provide is
243     --file_id , access_id etc as of now aniticipating a maximum
244     --these params
245     --if (proc is NOT null) then
246      -- sqlbuf := 'begin ' || proc || ' (:v1, :v2, :v3, :v4, :v5); end;';
247       --   execute immediate sqlbuf using
248        --    in access_id,
249         --   in fid,
250          --  in out result;
251       --if (result = true)
252        -- return 'SUCCESS';
253       --else
254         --fnd_gfm.err_msg('construct_upload_url.failed_callback');
255         --raise;
256       --end if;
257     --end if;
258 
259   else
260     return fnd_web_config.trail_slash(gfm_agent)||proc;
261   end if;
262 
263 exception
264   when others then
265     fnd_gfm.err_msg('construct_upload_url');
266     raise;
267 end;
268 /*--------------------------------------------------------------------------*/
269 /*
270  * construct_get_url - construct a get request URL
271  *   Very little work is done here but the knowledge of the exact
272  *   syntax of the URL is self-contained.
273  */
274 FUNCTION construct_get_url(gfm_agent  varchar2,
275 			   proc       varchar2,
276                            path       varchar2) return varchar2 is
277 pragma autonomous_transaction;
278 begin
279   return fnd_web_config.trail_slash(gfm_agent)||
280          fnd_gfm.construct_relative_get(proc,path);
281 exception
282   when others then
283     fnd_gfm.err_msg('construct_get_url');
284     raise;
285 end;
286 /*--------------------------------------------------------------------------*/
287 /*
288  * construct_relative_get - construct a relative get request URL
289  *   fragment without the plsql_agent/dad info.
290  *
291  *   This function constructs a URL that may be presented to the browser
292  *   as a relative url to a document that has already been "gotten".
293  *
294  *   proc   Procedure to run
295  *   path   Argument path
296  */
297 FUNCTION construct_relative_get(proc  varchar2,
298                                 path  varchar2) return varchar2 is
299 pragma autonomous_transaction;
300 begin
301   return 'fndgfm/'||proc||'/'||path;
302 exception
303   when others then
304     fnd_gfm.err_msg('construct_relative_get');
305     raise;
306 end;
307 /*--------------------------------------------------------------------------*/
308 /*
309  * confirm_upload - confirm the completion of an upload
310  *   This procedure must be called from within the user's PL/SQL upload
311  *   procedure to authenticate the upload and move the blob into the
312  *   fnd_lobs table.  The new generated file_id is returned to the caller.
313  *
314  *   access_id        The access identifier that authorized the upload
315  *   file_name        The file_name as stored in the fnd_lobs_document table
316  *                    by WebDB...the value of the file type input field.
317  *   program_name     The application short name to record
318  *   program_tag      The application tag to record
319  *   expiration_date  The expiration date to record; typically, this is null
320  *                    which means that the LOB never expires, but must be
321  *                    explicitly deleted
322  *   language         The document language; defaults to userenv('LANG')
323  *   wakeup           If TRUE, indicates that wait_for_upload is expected to
324  *                    be called on the original authorization key; the GFM
325  *                    can associate the given file identifier to the original
326  *                    authorization key and wake up the identified waiter.
327  *
328  *                    BUG#2461866 Added fn to parse out file_name.
329  */
330 FUNCTION confirm_upload(
331     access_id         number,
332     file_name         varchar2,
333     program_name      varchar2 default null,
334     program_tag       varchar2 default null,
335     expiration_date   date     default null,
336     language          varchar2 default userenv('LANG'),
337     wakeup            boolean  default FALSE)
338 return number is
339   fid number := -1;
340   fn  varchar2(256);
341   mt  varchar2(240);
342   bloblength number;       -- bug 3045375, added variable to set length of blob.
343   ufslim number;
344 begin
345   if (getRelease = 12) then
346      begin
347        --we already through the OA page has inserted the
348        --data into fnd_lobs table
349        --return the file_id corresponding to this access_id
350        select  file_id
351        into    fid
352        from    fnd_lob_access
353        where   access_id = confirm_upload.access_id;
354 
355        --raise the alert back so that the wait is ended
356        if wakeup then
357         dbms_alert.signal('FND_GFM_ALERT'||to_char(access_id), to_char(fid));
358        end if;
359 
360        return fid;
361      exception
362        when others then
363         return -1;
364      end;
365   else
366    if (fnd_gfm.authenticate(confirm_upload.access_id)) then
367     select fnd_lobs_s.nextval into fid from dual;
368 
369     fn := SUBSTR(confirm_upload.file_name, INSTR(confirm_upload.file_name,'/')+1);
370 
371     -- bug 3045375, added select to get length of BLOB.
372     select dbms_lob.getlength(blob_content), mime_type
373     into bloblength, mt
374     from fnd_lobs_document
375     where name = confirm_upload.file_name
376     and rownum=1;
377 
378     -- bug 3045375, added if to check length of blob.
379     -- bug 4279252. added UPLOAD_FILE_SIZE_LIMIT check.
380 
381    if fnd_profile.value('UPLOAD_FILE_SIZE_LIMIT') is null then
382        ufslim := bloblength;
383    else
384      /* The profile is not limited to being a numeric value.  Stripping off any
385         reference to kilobytes. */
386 
387      if (instr(upper(fnd_profile.value('UPLOAD_FILE_SIZE_LIMIT')),'K')>0) then
388         ufslim := substr(fnd_profile.value('UPLOAD_FILE_SIZE_LIMIT'),1,
389                   instr(upper(fnd_profile.value('UPLOAD_FILE_SIZE_LIMIT')),'K')-1);
390      else
391        ufslim := fnd_profile.value('UPLOAD_FILE_SIZE_LIMIT');
392      end if;
393 
394        /* Bug 6490050 - profile is defined to be in KB so we need to convert
395         here.  Consistent with the fwk code.  */
396 
397        ufslim := ufslim * 1000;
398    end if;
399 
400     if bloblength BETWEEN 1 and ufslim then
401       insert into fnd_lobs (file_id,
402                           file_name,
403                           file_content_type,
404                           file_data,
405                           upload_date,
406                           expiration_date,
407 		          program_name,
408                           program_tag,
409                           language,
410                           file_format)
411       (select confirm_upload.fid,
412             fn,
413             ld.mime_type,
414             ld.blob_content,
415             sysdate,
416             confirm_upload.expiration_date,
417             confirm_upload.program_name,
418             confirm_upload.program_tag,
419             confirm_upload.language,
420             fnd_gfm.set_file_format(mt)
421        from   fnd_lobs_document ld
422        where  ld.name = confirm_upload.file_name
423        and    rownum=1);
424 
425       if (sql%rowcount <> 1) then
426         raise no_data_found;
427       end if;
428 
429       update fnd_lob_access set file_id = fid
430       where  access_id = confirm_upload.access_id;
431 
432       if wakeup then
433         dbms_alert.signal('FND_GFM_ALERT'||to_char(access_id), to_char(fid));
434       end if;
435     -- bug 3045375, added else to return fid = -2.
436     else
437        fid := -2;
438     end if;
439     delete from fnd_lobs_document;
440     delete from fnd_lobs_documentpart;
441   end if;
442  end if;
443   return fid;
444 exception
445   when others then
446     delete from fnd_lobs_document;
447     delete from fnd_lobs_documentpart;
448 
449     fnd_gfm.err_msg('confirm_upload');
450     raise;
451 end;
452 /*--------------------------------------------------------------------------*/
453 /*
454  * get_file_id
455  *   This function retrieves the file_id for the corresponding access_id
456  *   from the fnd_lob_access table.
457  *
458  *   access_id     the access id
459  */
460 FUNCTION get_file_id(access_id number) return number is
461   fid number := -1;
462 begin
463   select file_id into fid
464   from   fnd_lob_access
465   where  access_id = get_file_id.access_id;
466 
467   if (sql%rowcount <> 1) then
468     raise no_data_found;
469   end if;
470 
471   return fid;
472 exception
473   when others then
474     fnd_gfm.err_msg('get_file_id');
475     raise;
476 end;
477 /*--------------------------------------------------------------------------*/
478 /*
479  * dispatch
480  *   This procedure takes a single argument in the form of
481  *       <proc>/<arg>
482  *
483  *   and executes the proc, passing along the single arg.
484  *
485  *   p_path     <proc>/<arg>
486  */
487 PROCEDURE dispatch(p_path in varchar2) is
488   proc              varchar2(256);
489   arg               varchar2(1000);
490   slash             number;
491   cmd               varchar2(1300);
492 
493 begin
494   cmd := ltrim(p_path, '/');
495   slash := nvl(instr(cmd, '/'), 0);
496   proc := upper(substr(cmd, 1, slash-1));
497   arg := substr(cmd, slash);
498 
499   if (fnd_web_config.check_enabled(proc) = 'Y') then
500     cmd := 'begin '||proc||'(:1); end;';
501     execute immediate cmd using in arg;
502   else
503     htp.htmlOpen;
504     htp.headOpen;
505     htp.title('Access Denied');
506     htp.headClose;
507     htp.bodyOpen;
508     htp.p('<b>Access Denied</b>');
509     htp.hr;
510 
511     -- If the procedure contains an HTML tag, don't display it.
512     --
513     if (instr(proc,'<') > 0) then
514       htp.p('Not authorized to access procedure.');
515     else
516       htp.p('Not authorized to access '||proc||'.');
517     end if;
518 
519     htp.bodyClose;
520     htp.htmlClose;
521   end if;
522 
523 exception
524   when others then
525     fnd_gfm.err_msg('dispatch');
526     raise;
527 end;
528 /*--------------------------------------------------------------------------*/
529 /*
530  * wait_for_upload - wait for upload confirmation
531  */
532 PROCEDURE wait_for_upload(access_id     number,
533                           timeout       number,
534 			  file_id   out NOCOPY number) is
535 pragma autonomous_transaction;
536   name     varchar2(64);
537   message  varchar2(32);
538   status   integer;
539 begin
540   name := 'FND_GFM_ALERT' || to_char(access_id);
541   dbms_alert.register(name);
542   dbms_alert.waitone(name, message, status, timeout);
543   dbms_alert.remove(name);
544 
545   if status = 1 then
546     file_id := null;
547   else
548     file_id := to_number(message);
549   end if;
550   commit;
551 end;
552 /*--------------------------------------------------------------------------*/
553 /*
554  * purge_expired - purge all expired rows in GFM tables
555  */
556 PROCEDURE purge_expired(program_name varchar2 default null) is
557 pragma autonomous_transaction;
558 begin
559   if purge_expired.program_name is null then
560     delete from fnd_lobs       where sysdate > expiration_date;
561     delete from fnd_lob_access where sysdate > timestamp;
562     commit;
563   else
564     delete from fnd_lobs
565     where fnd_lobs.program_name = purge_expired.program_name
566     and sysdate > expiration_date;
567     commit;
568   end if;
569 exception
570   when others then
571     fnd_gfm.err_msg('purge_expired');
572     raise;
573 end;
574 /*--------------------------------------------------------------------------*/
575 /*
576  * purge_set - purge selected rows from the LOB table
577  */
578 PROCEDURE purge_set(program_name varchar2,
579 		    program_tag  varchar2 default null) is
580 pragma autonomous_transaction;
581 begin
582   if program_tag is null then
583     delete from fnd_lobs where fnd_lobs.program_name = purge_set.program_name;
584   else
585     delete from fnd_lobs
586     where  fnd_lobs.program_name = purge_set.program_name
587     and    fnd_lobs.program_tag  = purge_set.program_tag;
588   end if;
589   commit;
590 exception
591   when others then
592     fnd_gfm.err_msg('purge_set');
593     raise;
594 end;
595 /*--------------------------------------------------------------------------*/
596 /*
597  * purge - CM cover routine for purge_set and purge_expired
598  */
599 PROCEDURE purge(errbuf        out NOCOPY varchar2,
600                 retcode       out NOCOPY varchar2,
601                 expired       in   varchar2 default 'Y',
602                 program_name  in   varchar2 default null,
603 		program_tag   in   varchar2 default null)
604 is
605 begin
606   if (purge.expired <> 'N') then
607     fnd_gfm.purge_expired(purge.program_name);
608 
609   elsif (purge.program_name is not null) then
610     fnd_gfm.purge_set(purge.program_name, purge.program_tag);
611 
612   end if;
613 
614   retcode := '0';                     -- (successful completion)
615   errbuf  := '';
616 
617 exception
618   when others then
619     retcode := '2';                   -- (error)
620     fnd_message.retrieve(errbuf);
621 end;
622 /*--------------------------------------------------------------------------*/
623 /*
624  * file_create - create a new empty file
625  */
626 FUNCTION file_create(file_name     varchar2 default null,
627 		     content_type  varchar2 default 'text/plain',
628 		     program_name  varchar2 default null,
629 		     program_tag   varchar2 default null) return number is
630 pragma autonomous_transaction;
631   fd             integer;
632   fh             file_handle;
633   l_lang         varchar2(4):= userenv('LANG');
634   l_file_format	 varchar2(10) := 'binary';
635   iana_cs        varchar2(150);
636   ocs            varchar2(30);
637   ct             varchar2(100);
638 begin
639   -- get a file handle slot
640   fd := null;
641   for i in 1..open_file_handles.count loop
642     if open_file_handles(i).fid is null then
643       fd := i;
644       exit;
645     end if;
646   end loop;
647 
648   -- Set file format
649   if(upper(substr(content_type,1,4)) = 'TEXT' ) then
650     l_file_format := 'text';
651   end if;
652 
653   -- Determine the IANA Charset and add to content-type, if necessary
654   if (instr(content_type,'charset=') > 0) then
655     iana_cs := substr(content_type, instr(content_type,'=',-1)+1);
656     ct := content_type;
657   else
658     iana_cs := fnd_gfm.get_iso_charset;
659     ct := content_type||'; charset='||iana_cs;
660   end if;
661 
662   if fd is null then
663     open_file_handles.extend;
664     fd := open_file_handles.count;
665   end if;
666 
667   insert into fnd_lobs (file_id, file_name, file_content_type,
668 	file_data, upload_date, expiration_date, program_name, program_tag,
669 	language,oracle_charset,file_format)
670   values (fnd_lobs_s.nextval, file_name, ct,
671 	  EMPTY_BLOB(), sysdate, sysdate + 1, program_name, program_tag,
672 	  l_lang, fnd_gfm.iana_to_oracle(iana_cs), l_file_format)
673   returning file_id into fh.fid;
674 
675   fh.offset := 1;
676   open_file_handles(fd) := fh;
677 
678   commit;
679   return fd;
680 exception
681   when others then
682     fnd_gfm.err_msg('file_create');
683     raise;
684 end;
685 /*--------------------------------------------------------------------------*/
686 /*
687  * file_close - close a file
688  */
689 FUNCTION file_close(fd number) return number is
690 pragma autonomous_transaction;
691   fh file_handle;
692 begin
693   fh := open_file_handles(fd);
694   open_file_handles(fd) := null;
695 
696   update fnd_lobs set expiration_date = null where file_id = fh.fid;
697   commit;
698 
699   return fh.fid;
700 exception
701   when others then
702     fnd_gfm.err_msg('file_close');
703     raise;
704 end;
705 /*--------------------------------------------------------------------------*/
706 /*
707  * file_write - write raw data to a file
708  */
709 PROCEDURE file_write(fd number, amount binary_integer, buffer raw) is
710 pragma autonomous_transaction;
711   fh    file_handle;
712   flob  blob;
713   ocs   varchar2(30);
714   l_len number := utl_raw.length(buffer);
715   l_pos number := 1;
716   l_str raw(6000) := null;
717 begin
718   fh := open_file_handles(fd);
719 
720   -- Re-open blob and get data. We must re-open every time instead of
721   -- caching so that procedures can be made autonomous.
722   select file_data, oracle_charset into flob, ocs
723   from   fnd_lobs
724   where  file_id = fh.fid
725   for update of file_data;
726 
727   -- BUG#1449494, created while loop to bypass convert() limitation.
728   while (l_len > 1000) loop
729     l_str := utl_raw.substr(buffer,l_pos, 1000);
730     dbms_lob.write(flob,1000,fh.offset, convert(l_str,ocs));
731     l_pos := l_pos + 1000;
732     l_len := l_len - 1000;
733     fh.offset := fh.offset + 1000;
734   end loop;
735   l_str := utl_raw.substr(buffer, l_pos);
736   dbms_lob.write(flob, l_len, fh.offset, convert(l_str,ocs));
737   fh.offset := fh.offset + l_len;
738   open_file_handles(fd) := fh;
739   commit;
740 exception
741   when others then
742     fnd_gfm.err_msg('file_write');
743     raise;
744 end;
745 /*--------------------------------------------------------------------------*/
746 /*
747  * file_write - write a line of text to a file
748  */
749 PROCEDURE file_write(fd number, buffer varchar2) IS
750   -- NOTE: This procedure is implicitly Autonomous Transaction,
751   --       because file_write(..<raw>..) is autonomous.
752 begin
753   if buffer is not null then
754     file_write(fd, lengthb(buffer), utl_raw.cast_to_raw(buffer));
755   end if;
756 end;
757 /*--------------------------------------------------------------------------*/
758 /*
759  * file_write_line - write a line of text to a file with a line terminator
760  */
761 PROCEDURE file_write_line(fd number, buffer varchar2) is
762   -- NOTE: This procedure is implicitly Autonomous Transaction,
763   --       because file_write(..<raw>..) is autonomous.
764   --       BUG#1811196
765   --       Combined 2 file_write() calls to 1 for performance gain.
766 begin
767   file_write(fd, (lengthb(buffer)+2),
768   (utl_raw.concat(utl_raw.cast_to_raw(buffer), hextoraw('0D0A'))));
769 end;
770 /*--------------------------------------------------------------------------*/
771 /*
772  * test - testing procedure
773  *   This procedure exists to give the GFM cartridge a test target
774  *   in the database.
775  */
776 PROCEDURE test is
777 begin
778   htp.print('Success');
779 end;
780 /*--------------------------------------------------------------------------*/
781 /*
782  * get_iso_charset
783  *   This procedure retrieves the iso equivalent of the db's character set.
784  */
785 FUNCTION get_iso_charset return varchar2 is
786   charset varchar2(150);
787   charmap constant varchar2(30) := 'FND_ISO_CHARACTER_SET_MAP';
788 begin
789   select tag into charset
790   from   fnd_lookup_values_vl
791   where  lookup_type = charmap
792   and    lookup_code = substr(userenv('LANGUAGE'),
793                             instr(userenv('LANGUAGE'),'.')+1);
794   return charset;
795 exception
796   when others then
797     fnd_gfm.err_msg('get_iso_charset');
798     raise;
799 end;
800 /*--------------------------------------------------------------------------*/
801 /*
802  * iana_to_oracle
803  *   This procedure retrieves the oracle equivalent of the specified iana
804  *   charset.
805  */
806 FUNCTION iana_to_oracle(ics varchar2) return varchar2 is
807   cs varchar2(50);
808   charmap constant varchar2(30) := 'FND_IANA_TO_ORACLE_CHARSET_MAP';
809 begin
810   select tag into cs
811   from   fnd_lookup_values_vl
812   where  lookup_type = charmap
813   and    upper(lookup_code) = upper(ics);
814 
815   if (sql%rowcount <> 1) then
816     raise no_data_found;
817   end if;
818 
819   return cs;
820 exception
821   when others then
822     fnd_gfm.err_msg('iana_to_oracle');
823     raise;
824 end;
825 /*--------------------------------------------------------------------------*/
826 /*
827  * oracle_to_iana
828  *   This procedure retrieves the iana equivalent of the specified oracle
829  *   charset
830  */
831 FUNCTION oracle_to_iana (cs varchar2) return varchar2 is
832   ics varchar2(150);
833   charmap constant varchar2(30) := 'FND_ISO_CHARACTER_SET_MAP';
834 begin
835   select tag into ics
836   from   fnd_lookup_values_vl
837   where  lookup_type = charmap
838   and    upper(lookup_code) = upper(cs)
839   and    rownum = 1;
840 
841   if (sql%rowcount <> 1) then
842     raise no_data_found;
843   end if;
844 
845   return ics;
846 exception
847   when others then
848     fnd_gfm.err_msg('oracle_to_iana');
849     raise;
850 end;
851 /*--------------------------------------------------------------------------*/
852 /*
853  * download -
854  *   This procedure processes a download request
855  *   If purge is specified, then the row is deleted immediately
856  */
857 PROCEDURE download(file_id number,
858                    access  number,
859                    purge   varchar2 default NULL) is
860   doc  blob;
861   ct   varchar2(100);
862 begin
863   if (fnd_gfm.authenticate(access, file_id) = FALSE) then
864     htp.p(Fnd_Message.Get_String('PAY','HR_51401_WEB_NOT_AUTHORIZED'));
865   else
866     fnd_gfm.download_blob(file_id);
867 
868     if (purge = 'yes') then
869  /*  Instead of deleting the data immediatlely we are setting the
870   *  expiration data.
871   *  delete from fnd_lobs where file_id = download.file_id;  */
872     update fnd_lobs
873       set fnd_lobs.expiration_date = sysdate + 0.5
874      where fnd_lobs.file_id = download.file_id;
875     end if;
876   end if;
877 exception
878   when others then
879     fnd_gfm.err_msg('download');
880     raise;
881 end download;
882 /*--------------------------------------------------------------------------*/
883 /*
884 ** download_blob -
885 **   encapsulate the actual downloading of the blob using webdb's procedure
886 */
887 PROCEDURE download_blob(fid number) is
888   ct    varchar2(255);
889   ctype varchar2(255);
890   doc   blob;
891   cs    varchar2(150);
892   semi  number;
893   eq    number;
894   len	number;
895   bb    boolean;
896   buffer	raw(32000);
897   amount	number :=16000;
898   b_amount	number;
899   lenvarb	number;
900   offset 	number :=1;
901   offset2 	number :=1;
902   lob_loc	blob;
903   nce		varchar2(120);
904   ocs		varchar2(120);
905   var		varchar2(32000);
906   pn            varchar2(32);
907 
908 begin
909   select file_content_type, oracle_charset, program_name, file_data
910   into   ct, ocs, pn, doc
911   from   fnd_lobs
912   where  file_id = download_blob.fid;
913 
914   semi := instr(ct, ';');
915   -- Bug 3966022 - looking now specifically for charset value
916   -- eq   := instr(ct, '=');
917   eq := instr(upper(ct), 'CHARSET=');
918 
919   if (semi = 0) then
920     ctype := ct;
921   else
922     ctype := substr(ct, 1, semi-1);
923   end if;
924 
925   if (eq = 0) then  -- no charset so pass null
926     cs := null;
927   else
928     eq := eq + 8; -- Add 8 to account for the length of charset=
929     semi := instr(ct,';', eq);
930     if (semi = 0) then
931       cs := substr(ct, eq);
932     else
933       cs := substr(ct, eq, semi-eq);
934     end if;
935   end if;
936 
937   fnd_profile.get_specific('FND_NATIVE_CLIENT_ENCODING',fnd_global.user_id,NULL,NULL,nce,bb);
938 
939   if (pn = 'export' and nce is not null) then
940     cs	:= fnd_gfm.oracle_to_iana(nce);
941     nce := 'american_america.'||nce;
942     ocs := 'american_america.'||ocs;
943     dbms_lob.createtemporary(lob_loc,TRUE);
944     dbms_lob.open(doc, DBMS_LOB.LOB_READONLY);
945     len := dbms_lob.getlength(doc);
946     loop
947 	if len > amount
948 	then
949     		dbms_lob.read(doc, amount, offset, buffer);
950                 var :=utl_raw.cast_to_varchar2(buffer);
951 		lenvarb :=lengthb(var);
952 		if length(var) > 10
953 		then
954 		var := substr(var, 1, length(var) - 10);
955 		end if;
956 		lenvarb := lenvarb - lengthb(var);
957 		buffer := utl_raw.cast_to_raw(var);
958     		buffer := utl_raw.convert(buffer, nce, ocs);
959     		b_amount := utl_raw.length(buffer);
960     		dbms_lob.write(lob_loc, b_amount, offset2, buffer);
961 		len := len - amount + lenvarb;
962 		offset := offset + amount - lenvarb;
963 		offset2 := offset2 + b_amount;
964 	else
965     		dbms_lob.read(doc, len, offset, buffer);
966     		buffer := utl_raw.convert(buffer, nce, ocs);
967     		amount := utl_raw.length(buffer);
968     		dbms_lob.write(lob_loc, amount, offset2, buffer);
969 	exit;
970 	end if;
971     end loop;
972   end if;
973 
974   owa_util.mime_header(ctype, FALSE, cs);
975   if (pn = 'export' and nce is not null) then
976      htp.p( 'Content-length: ' || dbms_lob.getlength(lob_loc));
977   else
978      htp.p( 'Content-length: ' || dbms_lob.getlength(doc));
979   end if;
980 /* Commenting out the following do resolve the issue with Export
981    failing on Internet Explorer.
982   htp.p( 'Cache-Control: no-cache' ); */
983   owa_util.http_header_close;
984   if (pn = 'export' and nce is not null) then
985      wpg_docload.download_file(lob_loc);
986      dbms_lob.freetemporary(lob_loc);
987      dbms_lob.close(doc);
988   else
989      wpg_docload.download_file(doc);
990   end if;
991 
992 exception
993   when no_data_found then
994     htp.htmlOpen;
995     htp.headOpen; htp.title('404 Not Found'); htp.headClose;
996     htp.bodyOpen; htp.hr; htp.header(nsize=>1, cheader=>'HTTP Error 404');
997     htp.hr;
998     htp.p(Fnd_Message.Get_String('GMD','LM_BAD_FILENAME'));
999     htp.bodyClose; htp.htmlClose;
1000   when others then
1001     fnd_gfm.err_msg('download_blob');
1002     raise;
1003 end download_blob;
1004 
1005 /*
1006  * one_time_use_store
1007  *   Store a value in the FND_LOB_ACCESS table and return a one-time-use
1008  *   ticket that can be used by one_time_use_retrieve() to fetch the value.
1009  */
1010 
1011 FUNCTION one_time_use_store(value number) RETURN number IS
1012 pragma autonomous_transaction;
1013   ticket number;
1014 begin
1015   for i in 1..10 loop
1016     begin
1017       ticket    := fnd_crypto.SmallRandomNumber;
1018       INSERT INTO fnd_lob_access (access_id, file_id, timestamp)
1019         VALUES (ticket, value, sysdate+1);
1020       commit;
1021       return ticket;
1022     exception
1023       when dup_val_on_index then
1024         null;
1025     end;
1026   end loop;
1027   -- More then 10 duplicates return -1 (error)
1028   return -1;
1029 
1030 exception
1031   when others then
1032     rollback;
1033     return -1;
1034 end;
1035 
1036 /*
1037  * one_time_use_store
1038  * A more secure API with a large entropy.
1039  * It returns a string that expires after lifespan
1040  * seconds. Opcode is a verification code used
1041  * when retrieving ticket value.
1042  */
1043 function one_time_use_store( value in varchar2 ,
1044                              lifespan in number default null,
1045                              opcode in varchar2 default null)
1046                      return varchar2 is
1047   l_lifespan number;
1048 begin
1049    if ( lifespan is not null) then
1050         return FND_HTTP_TICKET.CREATE_TICKET_STRING(opcode, value, lifespan);
1051    else
1052         return FND_HTTP_TICKET.CREATE_TICKET_STRING(opcode, value);
1053    end if;
1054 end;
1055 
1056 /*
1057  * one_time_use_retrieve
1058  *   Retrieve a value from the FND_LOB_ACCESS table, given a one-time-use
1059  *   ticket that was generated by one_time_use_store().
1060  */
1061 FUNCTION one_time_use_retrieve(ticket number) return number is
1062 pragma autonomous_transaction;
1063   value number;
1064 begin
1065   select file_id into value from fnd_lob_access
1066     where access_id = ticket for update;
1067   delete from fnd_lob_access where access_id = ticket;
1068   commit;
1069   return value;
1070 exception
1071   when others then
1072     rollback;
1073     return -1;
1074 end;
1075 
1076 /*
1077  * one_time_use_retrieve
1078  *   Retrieves value given a one-time-use
1079  *   ticket that was generated by one_time_use_store().
1080  *   if opcode passed, the API verifies the opcode value
1081  *   against the opcode value passed in one_time_use_store.
1082  */
1083 function one_time_use_retrieve( ticket in varchar2 ,
1084                                 opcode in varchar2 default null)
1085                      return varchar2 is
1086   l_args varchar2(4000);
1087   l_operation varchar2(4000);
1088 begin
1089     -- bug 6772298
1090     if(FND_HTTP_TICKET.CHECK_ONETIME_TICKET_STRING(ticket,
1091                                        l_operation,
1092                                        l_args) )
1093     then
1094           if(l_operation is null AND opcode is null)
1095           then
1096                 return l_args;
1097           end if;
1098 
1099           if(l_operation = opcode)
1100           then
1101                 return l_args;
1102           end if;
1103     end if;
1104 
1105     return null;
1106 end;
1107 /*--------------------------------------------------------------------------*/
1108 /*
1109  * Get -
1110  *   This procedure processes a download request
1111  *   If purge is specified, then the row is deleted immediately
1112  */
1113 PROCEDURE get(p_path varchar2)  is
1114   doc  blob;
1115   file_id   number;
1116   access number;
1117   bool boolean;
1118 begin
1119 
1120   bool := icx_sec.validatesession();
1121   access := substr(p_path,instr(p_path,'/',1)+1,instr(p_path,'/',2)-2);
1122   file_id := substr(p_path,instr(p_path,'/',2)+1,(instr(p_path,'/',-1)-instr(p_path,'/',2)-1));
1123 
1124   if (fnd_gfm.authenticate(access, file_id) = FALSE) then
1125     htp.p(Fnd_Message.Get_String('PAY','HR_51401_WEB_NOT_AUTHORIZED'));
1126   else
1127     fnd_gfm.download_blob(file_id);
1128 
1129   end if;
1130 exception
1131   when others then
1132     fnd_gfm.err_msg('get');
1133     raise;
1134 end get;
1135 /*--------------------------------------------------------------------------*/
1136 /*
1137  *   copy_lob - This function copies one lob to another
1138  *   Returns the file_id of the new lob.
1139  */
1140 FUNCTION copy_lob(fid_from number) return number is
1141   fid_to    number;
1142   fnd_usr_rec fnd_user%ROWTYPE;
1143   fnd_lobs_rec fnd_lobs%ROWTYPE;
1144 begin
1145 
1146   SELECT fnd_lobs_s.nextval
1147   INTO fid_to
1148   FROM dual;
1149 
1150   SELECT file_id,
1151          file_name,
1152          file_content_type,
1153          upload_date,
1154          expiration_date,
1155          program_name,
1156          program_tag,
1157          file_data,
1158          language,
1159          oracle_charset,
1160          file_format
1161   INTO fnd_lobs_rec.file_id,
1162        fnd_lobs_rec.file_name,
1163        fnd_lobs_rec.file_content_type,
1164        fnd_lobs_rec.upload_date,
1165        fnd_lobs_rec.expiration_date,
1166        fnd_lobs_rec.program_name,
1167        fnd_lobs_rec.program_tag,
1168        fnd_lobs_rec.file_data,
1169        fnd_lobs_rec.language,
1170        fnd_lobs_rec.oracle_charset,
1171        fnd_lobs_rec.file_format
1172   FROM fnd_lobs
1173   WHERE file_id = fid_from;
1174 
1175   INSERT INTO fnd_lobs (file_id,
1176                         file_name,
1177                         file_content_type,
1178                         upload_date,
1179                         expiration_date,
1180                         program_name,
1181                         program_tag,
1182                         file_data,
1183                         language,
1184                         oracle_charset,
1185                         file_format)
1186   VALUES  (fid_to,
1187            fnd_lobs_rec.file_name,
1188            fnd_lobs_rec.file_content_type,
1189            fnd_lobs_rec.upload_date,
1190            fnd_lobs_rec.expiration_date,
1191            fnd_lobs_rec.program_name,
1192            fnd_lobs_rec.program_tag,
1193            fnd_lobs_rec.file_data,
1194            fnd_lobs_rec.language,
1195            fnd_lobs_rec.oracle_charset,
1196            fnd_lobs_rec.file_format);
1197 
1198   return fid_to;
1199 
1200 exception
1201   when others then
1202     fnd_gfm.err_msg('copy_lob');
1203     raise;
1204 end copy_lob;
1205 /*--------------------------------------------------------------------------*/
1206 /*
1207  *   set_file_format - This function sets the file_format for fnd_lobs CTX
1208  *   Returns file_format derived from file_content_type in fnd_lobs.
1209  */
1210 FUNCTION set_file_format(l_file_content_type VARCHAR2) RETURN VARCHAR2 IS
1211 l_semicol_exists    number;
1212 l_mime_type         varchar2(256);
1213 l_file_format       varchar2(10);
1214 
1215 BEGIN
1216  -- Check l_file_content_type for a ;
1217  l_semicol_exists := instrb(l_file_content_type, ';', 1, 1);
1218 
1219  IF substr(l_file_content_type, 1, 5) = 'text/' THEN
1220    return('TEXT');
1221  ELSIF l_semicol_exists > 0 THEN
1222          l_mime_type := substr(l_file_content_type, 1, l_semicol_exists-1);
1223  ELSIF l_semicol_exists = 0 THEN
1224    l_mime_type := l_file_content_type;
1225  ELSE
1226    return('IGNORE');
1227  END IF;
1228 
1229  SELECT ctx_format_code
1230  INTO l_file_format
1231  FROM fnd_mime_types
1232  WHERE mime_type = l_mime_type;
1233 
1234  return(l_file_format);
1235 
1236  exception
1237  when others then
1238    return('IGNORE');
1239 
1240 END set_file_format;
1241 /*--------------------------------------------------------------------------*/
1242 /*
1243  *   clob_to_blob This function takes my_clob which can be a varchar2, long,
1244  *   or clob and creates a file in fnd_lobs.  It will create a file id if none
1245  *   is passed in.  file_name will need an extension.
1246  */
1247 
1248 PROCEDURE CLOB_TO_BLOB (
1249     my_clob           clob,
1250     file_name         varchar2,
1251     fid               in out nocopy number,
1252     file_content_type varchar2 default null,
1253     language          varchar2 default null,
1254     x_return_status OUT   NOCOPY Varchar2,
1255     x_msg_count     OUT   NOCOPY Number,
1256     x_msg_data      OUT   NOCOPY Varchar2
1257                            ) IS
1258 v_InputOffset      BINARY_INTEGER;
1259 v_LOBLength        BINARY_INTEGER;
1260 v_CurrentChunkSize BINARY_INTEGER;
1261 v_ChunkSize        NUMBER := 10000;
1262 varbuf             VARCHAR2(32767);
1263 l_blob_loc         blob;
1264 v_content_type     varchar2(100);
1265 v_language         varchar2(20);
1266 retval             INTEGER;
1267 G_EXC_ERROR        EXCEPTION;
1268 
1269 BEGIN
1270 
1271   x_return_status := FND_API.G_RET_STS_SUCCESS;
1272   if file_name is null then
1273     fnd_message.set_name('FND','GR_FILE_DATA_NULL');
1274     raise G_EXC_ERROR;
1275   end if;
1276 
1277   if fid is null then
1278     select fnd_lobs_s.nextval into fid from dual;
1279   end if;
1280 
1281   if file_content_type is null then
1282     v_content_type := 'text/plain';
1283   else
1284     v_content_type := file_content_type;
1285   end if;
1286 
1287   if language is null then
1288      v_language := userenv('LANG');
1289   else
1290      v_language := language;
1291   end if;
1292 
1293     INSERT INTO fnd_lobs (
1294        file_id,
1295        file_name,
1296        file_content_type,
1297        upload_date,
1298        expiration_date,
1299        program_name,
1300        program_tag,
1301        file_data,
1302        language,
1303        oracle_charset,
1304        file_format )
1305      VALUES (
1306        fid,
1307        file_name,
1308        v_content_type,
1309        sysdate,
1310        null,
1311        'FNDAPI',
1312        null,
1313        empty_blob(),
1314        v_language,
1315        fnd_gfm.iana_to_oracle(fnd_gfm.get_iso_charset),
1316        fnd_gfm.set_file_format(v_content_type));
1317 
1318 select file_data into l_blob_loc from fnd_lobs
1319 where file_id = fid;
1320 
1321 
1322 DBMS_LOB.OPEN (l_blob_loc, DBMS_LOB.LOB_READWRITE);
1323 
1324 -- First determine the input LOB length.
1325 v_LOBLength := DBMS_LOB.GETLENGTH(my_clob);
1326 
1327 -- Set up the initial offsets.  The input offset starts at the
1328 -- beginning, the output offset at the end.
1329 v_InputOffset := 1;
1330 
1331   -- Loop through the input LOB, and write each chunk to the output
1332   -- LOB.
1333   LOOP
1334     -- Exit the loop when we've done all the chunks, indicated by
1335     -- v_InputOffset passing v_LOBLength.
1336     EXIT WHEN v_InputOffset > v_LOBLength;
1337     -- If at least v_ChunkSize remains in the input LOB, copy that
1338     -- much.  Otherwise, copy only however much remains.
1339       IF (v_LOBLength - v_InputOffset + 1) > v_ChunkSize THEN
1340            v_CurrentChunkSize := v_ChunkSize;
1341       ELSE
1342            v_CurrentChunkSize := v_LOBLength - v_InputOffset + 1;
1343       END IF;
1344 
1345       dbms_lob.read(my_clob, v_CurrentChunkSize, v_InputOffset, varbuf);
1346 
1347       -- Write the current chunk.
1348       DBMS_LOB.writeappend(l_blob_loc, lengthb(varbuf), UTL_RAW.cast_to_raw(varbuf));
1349 
1350       -- Increment the input offset by the current chunk size.
1351       v_InputOffset := v_InputOffset + v_CurrentChunkSize;
1352   END LOOP;
1353 
1354 DBMS_LOB.CLOSE(l_blob_loc);
1355 --return x_return_status;
1356 
1357 exception
1358    when G_EXC_ERROR  then
1359     x_return_status := FND_API.G_RET_STS_ERROR;
1360     fnd_msg_pub.count_and_get(p_count => x_msg_count,
1361                               p_data => x_msg_data);
1362   when others then
1363     x_return_status := FND_API.G_RET_STS_UNEXP_ERROR ;
1364     fnd_msg_pub.count_and_get(p_count => x_msg_count,
1365                               p_data => x_msg_data);
1366 
1367     END clob_to_blob;
1368 /*--------------------------------------------------------------------------*/
1369 
1370 /*
1371  * delete_lob - this api deletes a lob in fnd_lobs using file_id.
1372  *
1373 */
1374 
1375 PROCEDURE DELETE_LOB (fid number) IS
1376 BEGIN
1377 delete from fnd_lobs where file_id = fid;
1378 commit;
1379 exception
1380   when others then
1381     fnd_gfm.err_msg('delete_lob');
1382     raise;
1383 END delete_lob;
1384 /*--------------------------------------------------------------------------*/
1385 
1386 FUNCTION construct_download_url(gfm_agent     varchar2,
1387 				file_id       number,
1388 				purge_on_view boolean default FALSE)
1389 	 return varchar2 is
1390 begin
1391    return construct_download_url2(gfm_agent,file_id,purge_on_view,false,true);
1392 exception
1393   when others then
1394     fnd_gfm.err_msg('construct_download_url');
1395     raise;
1396 end;
1397 
1398 ------------------------------------------------------------------------------
1399 
1400 /*
1401  *  setRelease - returnes the current major release
1402  *
1403  */
1404 FUNCTION setRelease
1405 return number is
1406 l_type    varchar2(30);
1407 begin
1408    SELECT  upper(type)
1409    INTO    l_type
1410    FROM    fnd_form_functions
1411    WHERE   function_name  = 'FND_FNDFLUPL' ;
1412 
1413    if (l_type = 'JSP') then
1414        --if JSP we are on R12
1415        g_release_version := 12 ;
1416    else
1417        g_release_version := 11;
1418    end if;
1419 
1420    return g_release_version;
1421 exception
1422   when no_data_found then
1423     --for sure R12 has it so we take it as release 11
1424     --if we cannot find a row
1425     g_release_version := 11;
1426     return g_release_version;
1427   when others then
1428     --we give in to the R12 release because
1429     --we will do more work on R12 from here than 11i
1430     g_release_version := 12;
1431     return g_release_version;
1432 end;
1433 
1434 
1435 ------------------------------------------------------------------------------
1436 
1437 /*
1438  *  getRelease - returns the current major release
1439  *
1440  */
1441 FUNCTION getRelease
1442 return number is
1443 begin
1444    if (g_release_version = 0) then
1445       --call the SetRelease for the first time
1446       g_release_version := setRelease;
1447    end if;
1448 
1449    return g_release_version;
1450 exception
1451   when others then
1452     fnd_gfm.err_msg('getRelease');
1453     raise;
1454 end;
1455 
1456 
1457 ------------------------------------------------------------------------------
1458 
1459 
1460 end FND_GFM;