advanced application express
TRANSCRIPT
Advanced Application Express Tips and Techniques
By Bradley D. Brown http://bradleydbrown.blogspot.com
Chairman and Chief Architect, TUSC
www.tusc.com/briefing
Abstract
Oracle Application Express is a powerful and comprehensive tool. Numerous advanced tips and techniques will be covered in this presentation. These topics include: pop-up windows, complex searches, document management, indexing and searching, “tool tip” or hints, email links, page 0, help text, background jobs, “add to my calendar” feature, saving contacts, sending mass emails, and more.
Topics
HTMLFavorite IconPop-ups & pass-backsTool tipsAnchors
ApexPage 0Help text / pageCustom login pageScrolling textDocument ManagementOutlook IntegrationEmail
PL/SQLComplex searchesFunction-based queriesBackground jobs
JavaScriptSetting focusAJAXNetflix FlyupsGoogle Maps
Most everything is in the sample app Download from www.tusc.com
http://www.tusc.com/oracle/technology/briefing_menu.html
HTML Tips
Favorite Icon
Place favicon2.ico file in $ORACLE_HOME\Apache\Apache\htdocs
In Page template <link rel="shortcut icon" href="/favicon2.ico" type="image/x-icon" />
Pop-up windows
Pop-up WindowTarget=“_blank” in “Link Attributes”
The Oracle Experts
Custom Pop Ups Returning Multiple Values
Javascript passes the values back
“Bubble-Help,” “Tool tip” or hints
Title attribute in “Link Attributes”Title="#COMMENTS#"
Anchors
Anchors can provide direct links to places on a pageSet an anchor anywhere on the page
• <a name="Location">Reference an anchor anywhere
• <a href="#Location"> jump to location</a>
Best practice in page template is to include at the top of the body section
<a name="Top">
This way you can place a “return to top” link anywhere on the page
<a href=#Top><img src="/i/themes/theme_8/blue_arrow_up.gif" alt=“Top of page" title=“Top of page"></a>
Long Page in App
Application Express Tips
Page 0
Anything placed on Page 0 will appear on every page by default
Can use “Conditional display” to turn region on or off for specific pages, users, etc.
Help text
Page level help textCreate a table HELP_TEXT with help for each page
• Spell check your help textCreate a new blank page
• Add a new region to the page “Help Text”• Could also have conditional HTML sections
Add an item to your navigation bar (i.e. Help)• Branch to your “help text page” (i.e. 10)• Pass &APP_PAGE_ID to P10_PAGE_ID
Item level help textOpen any page itemEnter help text for the itemSpell check your help text
Highlight Current
Note highlighting of current rowMaster, master, detail
Key Elements
SQL Report RegionsLink to select current master recordDetail SQL Query limited by “selected”master row
Hidden Items to implement details
Custom Templates to highlight current row
Variation on alternating color report templatePL/SQL expression condition for “selected” row
The Oracle Experts
SQL Report Regions
Decode primary key against hidden item for “selected” master row
select decode(customer_id, :P1_CUST_ID, 'CURRENT','Select') sel_label,customer_id, cust_last_name||', '||cust_first_name as cust_name,cust_street_address1||'<br>'||Cust_city||', '|| cust_state||' '||cust_postal_code address
from demo_customers
The Oracle Experts
Custom Template
•Hidden item used in PL/SQL expression to determine “Selected” row•Based off of current themes alternating row report template
Custom Logon Page
You can create your own logon page for any application
You can use Oracle Application Express’s built-in authentication or you can use any existing scheme you have
Good document on creating a custom logon page:http://forums.oracle.com/forums/thread.jsp?forum=137&thread=220034
Document management, indexing and searching
Documents will be stored in Oracle Application Express schema
See Sample Application for great code examples
Create a table to store the link to the documentsCreate table person_document(PERSON_ID number,DOCUMENT_ID numberDESCRIPTION varchar2(4000),DOCUMENT varchar2(4000))
Show a link to the document (if there is one – otherwise, create new)
Link is to p?n=document_numberselect DESCRIPTION, decode(document, null, '<a href="f?p=' ||
:app_id || ':11:::::p11_person_id,p11_document_id:' || person_id || ',' || document_id || '">Upload Supporting Document</a>', '<a href="p?n=' || document || '">Download Document</a>') document
from PERSON_DOCUMENTwhere person_id = :p1_id
Best Practices and Indexing Documents
If you…Might want to port your applicationHave many documentsDesire to index document content
Create your own document table
Create table resumes as select * from apex_application_filesWhere…
Easier to export an application (and data) – otherwise, it’s in Apex schema
Mass Upload of Documents
Create placeholderCREATE TABLE
TEMP_RESUMES
(NAME VARCHAR2(90),
BLOB_CONTENT BLOB)
Start with directoryDir /b >resume.ctl
Add SQL*Loader text above list
Sqlldr user/pass control=docs.ctl
load data
infile *
into table resumes
fields terminated by ',’
(file_name char(50),
blob_content lobfile(file_name)
terminated by eof)
BEGINDATA
a.doc
b.doc…
Load up Resumes from Temp_Resumes
INSERT INTO RESUMES
SELECT ROWNUM ID, 0 flow_id, NAME NAME,
NAME filename, NULL title,
CASE WHEN SUBSTR(NAME,LENGTH(NAME)-
3+1) = 'doc' THEN 'application/msword‘
WHEN SUBSTR(NAME,LENGTH(NAME)-3+1) =
'pdf' THEN 'application/pdf’
WHEN SUBSTR(NAME,LENGTH(NAME)-3+1) IN
('csv','xls') THEN 'application/vnd.ms-excel’
WHEN SUBSTR(NAME,LENGTH(NAME)-3+1) =
'gif' THEN 'image/gif’
WHEN SUBSTR(NAME,LENGTH(NAME)-3+1) =
'png' THEN 'image/png’
WHEN SUBSTR(NAME,LENGTH(NAME)-3+1) =
'jpg' THEN 'image/pjpeg’
ELSE 'application/text’
END mime_type,
LENGTH(blob_content) doc_size, 'ascii'
dad_charset, '[email protected]'
created_by,
SYSDATE created_on, '[email protected]'
updated_by, SYSDATE updated_on,
SYSDATE last_updated, 'BLOB'
content_type, blob_content, NULL
LANGUAGE, 'Loaded by BDB in bulk'
description,
NULL file_type, NULL file_charset
FROM TEMP_RESUMES
Loading Documents
Need a process to move document after upload
See http://download.oracle.com/docs/cd/B32472_01/doc/appdev.300/b32469/up_dn_files.htm
INSERT INTO
resumes(id,flow_id,name,filename,mime_type
,doc_size,dad_charset,created_by,created_o
n,updated_by,updated_on,last_updated,cont
ent_type,blob_content,description) SELECT
id,flow_id,name,filename,mime_type,doc_size
,dad_charset,created_by,created_on,update
d_by,updated_on,last_updated,content_type,
blob_content,:P20_DESCRIPTION FROM
APEX_APPLICATION_FILES WHERE name =
:P1_FILE_NAME;
DELETE from APEX_APPLICATION_FILES
WHERE name = :P1_FILE_NAME;
Need Procedure to Download DocumentCREATE OR REPLACE PROCEDURE download_my_file(p_file in number) AS
v_mime VARCHAR2(48);
v_length NUMBER;
v_file_name VARCHAR2(2000);
Lob_loc BLOB;
BEGIN
SELECT MIME_TYPE, BLOB_CONTENT, name, doc_size
INTO v_mime,lob_loc,v_file_name,v_length
FROM resumes
WHERE id = p_file;
-- set up HTTP header
owa_util.mime_header( nvl(v_mime,'application/octet'), FALSE );
-- set the size so the browser knows how much to download
htp.p('Content-length: ' || v_length);
-- the filename will be used by the browser if the users does a save as
htp.p('Content-Disposition: attachment;
filename="'||replace(replace(substr(v_file_name,instr(v_file_name,'/')+1),chr(10),null),chr(13),null)|| '"');
-- close the headers
owa_util.http_header_close;
-- download the BLOB
wpg_docload.download_file( Lob_loc );
end download_my_file;
Indexing the Documents
Need an Oracle Text indexcreate index doc_ctxidx onresumes(blob_content)indextype is ctxsys.contextparameters ('datastore CTXSYS.DEFAULT_DATASTORE')
You must re-index regularlybegin ctx_ddl.sync_index('doc_ctxidx','2M'); end;
Searching the Index
Create a search field on the page (e.g. p14_search)
Query, joining document to a person
select r.ID, pd.person_ID person_id, NAME, FILENAME, TITLE, MIME_TYPE, DOC_SIZE, CREATED_BY, CREATED_ON, UPDATED_BY, UPDATED_ON, LAST_UPDATED, CONTENT_TYPE, LANGUAGE, r.DESCRIPTION, pd.DESCRIPTION person_description, FILE_TYPE, FILE_CHARSET,
get_person_attributes(pd.person_id, chr(13)) person_attribute_info, get_person_docs(pd.person_id, '<br>') all_docsfrom resumes r, person_document pdwhere contains (BLOB_CONTENT, :p14_search) > 1and r.id = pd.document(+)
The Results
Outlook (or other) Integration
Add to my CalendarIndustry standard (RFC 2445) ICS format
• Mac/Apple iCalendar, Mozilla Calendar, Outlook, etc.
Add contactIndustry standard contact VCF format
Email Links
Mass EmailCampaignsRemindersWorkflow
Email links
Select statementselect '<a href="mailto:' || me.EMAIL_ADDRESS || '">' || first_name || ' ' || last_name || '</a>' Member
LinkReport attributes, pick attribute, linkLink Text = #MEMBER#Target = URLURL = mailto:#EMAIL_ADDRESS#
Sending mass emails
I like my own send_email procedure (email me and I’ll send it)
CREATE OR REPLACE PROCEDURE Send_Email(in_mail_server VARCHAR2 DEFAULT 'exchange.tusc.com',in_sender_email VARCHAR2 DEFAULT '[email protected]',in_sender_name VARCHAR2 DEFAULT 'My Full Name',in_recipient_email VARCHAR2 DEFAULT '[email protected]',in_recipient_name VARCHAR2 DEFAULT 'Your Full Name',in_cc_email VARCHAR2 DEFAULT '[email protected]',in_cc_name VARCHAR2 DEFAULT 'My Full Name',in_html_flg VARCHAR2 DEFAULT 'N',in_subject VARCHAR2 DEFAULT 'No Subject was Provided',in_importance VARCHAR2 DEFAULT 'Normal',in_body VARCHAR2 DEFAULT 'No Body for this Email')
“Add to my calendar” feature
Industry standard (RFC 2445) ICS formatMac/Apple iCalendar, Mozilla Calendar, Outlook, etc.
Branch to a PL/SQL page
Mime-type is key
The Guts of the Code
htp.init; -- Wipe out the buffer
owa_util.MIME_HEADER('text/cal endar; method=request');
-- Get the event information
htp.print('BEGIN:VCALENDAR');
htp.print('PRODID:-//Microsoft Corporation//Outlook 9.0 MIMEDIR//EN');
htp.print('VERSION:2.0');
htp.print('METHOD:PUBLISH');
htp.print('BEGIN:VEVENT');
htp.print('ORGANIZER:MAILTO:br [email protected]');
htp.print('DTSTART;TZNAME=MST: ' || to_char(nvl(to_date(v('P12_ expires_ON'),'mm/dd/yyyy'), l_expires_on), 'yyyymmdd') || 'T080000');
…
htp.print('DESCRIPTION:Reminde r for account expiring');
htp.print('END:VALARM');
htp.print('END:VEVENT');
htp.print('END:VCALENDAR');
end;
“Add Contact” feature
Industry standard VCF formatMac/Apple iCalendar, Mozilla Calendar, Outlook, etc.
Similar to ICS
Branch to a PL/SQL page
Mime-type is key
The guts of the contactsDeclare
cursor user_cur is
select user_name, expires_on, company, phone, …
from demo_users
where user_id = :p14_user_id;
Begin
-- Get the user data
for user_rec in user_cur loop
htp.init; -- Wipe out the buffer
owa_util.MIME_HEADER('text/x-vcard; method=request');
-- Get the user information
htp.print('BEGIN:VCARD');
htp.print('VERSION:3.0');
htp.print('N:' || user_rec.user_name || ';;');
…
htp.print('ADR;HOME:;;');
htp.print('EMAIL;PREF;INTERNET:' || user_rec.email_address);
htp.print('NOTE;ENCODING=QUOTED-PRINTABLE: Expires on ' || user_rec.expires_on || ' Admin user? ' || user_rec.admin_user || ' Quota: ' || user_rec.quota);
htp.print('REV:20050707T234724');
htp.print('END:VCARD');
end loop;
end;
PL/SQL tips
Complex searches
Multiple search fieldsCan’t dynamically build the where clauseMust use instr or like statementsIf large dataset, recommend:
• Indexes on columns• Use like :p1_search || ‘%’• Don’t use like ‘%’ || :p1_search || ‘%’ or instr• Use separate pages / queries / regions for each field queried• Can use Oracle Text (i.e. contains)
Function-based (Complex) Queries
Let’s say you want to knowThe first and last date you received an RSVPThe number of people who RSVPed each day between
No easy way to execute this query
Build a function-based queryFunctionTable and column data typesView based on casted function
The Function RSVP_FUNCTION
CREATE OR REPLACE FUNCTION rsvp_function (in_event_no number)
RETURN rsvp_table
PIPELINED IS
out_rec RSVP_COLUMNS := RSVP_COLUMNS(NULL,NULL,NULL,NULL,NUL L);
TableSet RSVP_TABLE;
cursor min_max_cur is
select min(trunc(rsvp_date)) min_date,
max(trunc(rsvp_date)) max_date,
max(trunc(rsvp_date))- min(trunc(rsvp_date)) days
from rsvp
where event_no = in_event_no;
cursor count_invitees_cur is
select count(*) count
from rsvp
where event_no = in_event_no;
cursor count_as_of_cur (in_date date) is
select sum(decode(yn,'Y',1,0)) y,
sum(decode(yn,'N',1,0)) n
from rsvp
where event_no = in_event_no
and rsvp_date <= in_date + 1;
total number := 0;
RSVP_FUNCTION
BEGIN
-- Figure out the total count of invited people
for count_invitees_rec in count_invitees_cur loop
total := count_invitees_rec.count;
end loop;
for min_max_rec in min_max_cur loop
FOR i IN 1 .. min_max_rec.days+1 LOOP
out_rec.event_no := in_event_no;
out_rec.rsvp_date := min_max_rec.min_date + i - 1;
for count_as_of_rec in count_as_of_cur(out_rec.rsvp_date) loop
out_rec.y := count_as_of_rec.y;
out_rec.n := count_as_of_rec.n;
out_rec.w := total - count_as_of_rec.y - count_as_of_rec.n;
end loop;
PIPE ROW(out_rec);
END LOOP;
END LOOP;
RETURN;
END;
The Data Types RSVP_COLUMNS and RSVP_TABLE
ColumnsCREATE OR REPLACETYPE rsvp_Columns AS OBJECT( event_no number,
rsvp_date date,y number,n number,w number);
TableCREATE OR REPLACETYPE RSVP_Table AS TABLE OF YPO.RSVP_COLUMNS
The View / Query
3 series (yes, no, waiting)select rsvp_date, yfrom table(cast(rsvp_function(:P3_IN_EVENT_NO)
as rsvp_Table))select rsvp_date, nfrom table(cast(rsvp_function(:P3_IN_EVENT_NO)
as rsvp_Table))select rsvp_date, wfrom table(cast(rsvp_function(:P3_IN_EVENT_NO)
as rsvp_Table))
The Results
Apex Add-ons (APIs)
Apex_plssql_job
Apex_collection
Apex_Ldap
Apex_Mail
Apex_Util
…
Background jobs
apex_plsql_job is a wrapper written around dbms_job
APEX_PLSQL_JOBS is a table containing the job information for those submitted
Very helpful for those hourly, daily, weekly, etc. processes
Functions and Procedures
SUBMIT_PROCESS (function)Submits background PL/SQL, returns a unique job number. Job number is a reference point for other procedures and functions in this package.
UPDATE_JOB_STATUSUpdates the status of the currently running job. Most effective when called from the submitted PL/SQL.
TIME_ELAPSEDDetermines how much time has elapsed since the job was submitted.
JOBS_ARE_ENABLEDDetermines whether or not the database is currently in a mode which supports submitting jobs to the APEX_PLSQL_JOB package.
PURGE_PROCESSCleans up submitted jobs. Submitted jobs stay in the APEX_PLSQL_JOBS view until either Oracle Oracle Application Express cleans out those records, or you call PURGE_PROCESS to manually remove them.
JavaScript, AJAX, etc.
JavaScript
JavaScript provides client-side power
Good doc to work from: http://www.oracle.com/technology/products/database/htmldb/howtos/htmldb_javascript_howto2.html#ref
Where to Place JavaScript Functions
Calling JavaScript from a Button
Add Client-Side JavaScript Validations
Enable / Disable Form Elements
Change the Value of Form Elements
Apex JavaScript - Setting Focus
Check out /i/javascript/apex_html_elements.js for a complete list of already developed JavaScript (charCount, setStyle, confirmDelete, hideShow, GetCookie, SetCookie, html_PopUp, etc)
To focus on an item, make sure to change focus property in page properties – first_field is a build-in function:
<script type="text/javascript">first_field('P20_FROM_DATE');</script>
AJAX and Other DHTML Tricks
AJAXAsynchronous JavaScript and XML
RestHttp get commandsReturns XML
Excellent exampleshttp://apex.oracle.com/pls/otn/f?p=11933
The Oracle Experts
Using AJAX
Asynchronous Java Script and XMLNot new technology, just combination of existingInteracts with the server without refreshing the page
Used a lot in APEX SQL workshop
APEX makes implementation simple
Refresh a variety of objectsSingle display itemMulti-value things such as select listEntire report regions
The Oracle Experts
Using AJAX
Select a valuePopulates Job
Select a departmentPopulates select list
The Oracle Experts
AJAX Key Elements
Item with onChange event
JavaScript function called by onChangeCalls Process using htmldb_GetUses results
Application Level ProcessOn Demandreturns value or XML document
Application Level Items, used in process
Page Zero
The Oracle Experts
AJAX using a Query Process
Two page items•Select list with onChange trigger•Text field to accept value
Select list calls JavaScript function when its value is changed
Netflix Flyups
Netflix uses AJAX to retrieve its flyups.
For a great thread on how to implement this, see http://forums.oracle.com/forums/thread.jspa?threadID=318874&tstart=0
Google Map Integration
Easy integration
http://www.google.com/apis/maps/documentation/
Summary
Oracle Application Express is powerful
Download the Sample AppReal examples in thereLots of great code
Forum is helpful and well monitored
Watch for Web 2.0 additions
Where to Get More Information?
Numerous presentations on the TUSC siteFull Day TutorialsNumerous 1-3 hour sessionsLocal Oracle Users Groups
TUSC’s Training Center
Oracle Application Express Handbookby Larry Linnemeyer and Bradley D. BrownDecember 2005
Oracle Application Express Forum, Oracle Application Express Studio, etc.
Want more Information?
Call or email [email protected]
Call or email [email protected]
Visit our sitehttp://www.tusc.comRegister for our newsletter
Questions?
Brad’s Papers and Presentations
Java-based Oracle Web Development
Java Server Pages
JavaMail
Java for the PL/SQL Developer
Web Cache – achieving 150 the performance
9iAS Installation, Configuration, and Tuning
Wireless
Practical Portal Practices
Implementing JSP in Portal
UltraSearch
Search Engines
Utl_smtp and Utl_http
iFS
JavaScript
Top DBA scripts for Web Developers
Security
Special Thanks
Larry Linnemeyer – for his advanced topics and all of his work on the Oracle Application Express book
Other TUSC Presentations and Papers
TuningDatabaseSQLApplications
Security
Migrations
Discoverer & BI
Built-in Packages
PL/SQL
New Features
Forms, Reports
Designer
Team Management
Uncommon Leaders
Workflow
DBA topics
Copyright Information
Neither TUSC nor the author guarantee this document to be error-free. Please provide comments/questions to [email protected].
TUSC © 2007. This document cannot be reproduced without expressed written consent from an officer of TUSC.