<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss'><id>tag:blogger.com,1999:blog-5551779552143571989</id><updated>2010-03-20T13:17:49.892Z</updated><title type='text'>SHUNT'S Blog</title><subtitle type='html'>Mostly ORACLE APEX and Windsurfing stuff</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://simonhunt.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default'/><link rel='alternate' type='text/html' href='http://simonhunt.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>shunt</name><uri>http://www.blogger.com/profile/08156724954690406968</uri><email>schunt@tiscali.co.uk</email></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>15</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5551779552143571989.post-4726993932828228102</id><published>2009-12-30T14:26:00.003Z</published><updated>2009-12-30T14:33:20.504Z</updated><title type='text'>Building SAAS Applications</title><content type='html'>I recently read this interesting article by Steve Bobrowski on creating a SAAS (software as a service) system.  Of particular note is the process of creating apex item labels for flexi-fields using JS to seperate a colon seperated list.  Throw VPD into the mix and you have an elegant solution.   Enjoy:&lt;br /&gt;&lt;a href="http://www.oracle.com/technology/pub/articles/bobrowski-saas.html"&gt;http://www.oracle.com/technology/pub/articles/bobrowski-saas.html&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5551779552143571989-4726993932828228102?l=simonhunt.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simonhunt.blogspot.com/feeds/4726993932828228102/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=5551779552143571989&amp;postID=4726993932828228102' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/4726993932828228102'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/4726993932828228102'/><link rel='alternate' type='text/html' href='http://simonhunt.blogspot.com/2009/12/building-saas-applications.html' title='Building SAAS Applications'/><author><name>shunt</name><uri>http://www.blogger.com/profile/08156724954690406968</uri><email>schunt@tiscali.co.uk</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='03458542062488405701'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5551779552143571989.post-3870300742757347173</id><published>2009-12-07T13:37:00.010Z</published><updated>2010-02-13T13:54:27.501Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Apex'/><title type='text'>Next and Prev from an Interactive Report 2</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_XIuspQmbd70/Sx0FcLspbKI/AAAAAAAAAtE/w7j_DsIry_I/s1600-h/CM+Capture+2.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 197px; height: 320px;" src="http://1.bp.blogspot.com/_XIuspQmbd70/Sx0FcLspbKI/AAAAAAAAAtE/w7j_DsIry_I/s320/CM+Capture+2.png" alt="" id="BLOGGER_PHOTO_ID_5412488308903734434" border="0" /&gt;&lt;/a&gt;I have found a bit of time to re-look at how to retrieve the sql being generated in an Interactive Report with a view to using it to provide a Next and Previous button in an interactive report.  From my previous work, Stew Stryker rightly pointed out that I didn't cater for global searches or saved interactive reports.  I have incorporated his suggestions into the solution and I think it's just about cracked.  If you notice any problems, please let me know.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://apex.oracle.com/pls/otn/f?p=34492:16"&gt;Demo&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;How to do it&lt;br /&gt;&lt;br /&gt;1.  Create the following function and procedure:&lt;br /&gt;&lt;br /&gt;&lt;span style=";font-family:courier new;font-size:85%;"&gt;create or replace FUNCTION split_columns&lt;br /&gt;(&lt;br /&gt;p_cond_expr VARCHAR2,&lt;br /&gt;p_column    VARCHAR2,&lt;br /&gt;p_date_cols VARCHAR2)&lt;br /&gt;RETURN VARCHAR2&lt;br /&gt;IS&lt;br /&gt;l_sql VARCHAR2(4000);&lt;br /&gt;l_vc_arr2 apex_application_global.vc_arr2;&lt;br /&gt;l_vc_arr1 apex_application_global.vc_arr2;&lt;br /&gt;l_date_y VARCHAR2(1);&lt;br /&gt;BEGIN&lt;br /&gt;l_vc_arr2 := apex_util.string_to_table(p_column);&lt;br /&gt;l_vc_arr1 := apex_util.string_to_table(p_date_cols, ',');&lt;br /&gt;FOR z     IN 1 .. l_vc_arr2.COUNT&lt;br /&gt;LOOP&lt;br /&gt;l_date_y := NULL;&lt;br /&gt;FOR Y    IN 1 .. l_vc_arr1.COUNT&lt;br /&gt;LOOP&lt;br /&gt;IF l_vc_arr2(z) = l_vc_arr1(Y) THEN&lt;br /&gt;l_date_y     := 'Y';&lt;br /&gt;END IF;&lt;br /&gt;END LOOP;&lt;br /&gt;IF l_date_y is null THEN&lt;br /&gt;l_sql    := l_sql || 'OR INSTR(UPPER("' || l_vc_arr2(z) || '"),UPPER(''' || p_cond_expr ||&lt;br /&gt;'''))&gt;0 ';&lt;br /&gt;END IF;&lt;br /&gt;END LOOP;&lt;br /&gt;l_sql := 'WHERE (' || ltrim(l_sql, 'OR')||')';&lt;br /&gt;RETURN(l_sql);&lt;br /&gt;END;&lt;br /&gt;/&lt;br /&gt;create or replace PROCEDURE next_prev_values&lt;br /&gt;(&lt;br /&gt;p_column_id IN VARCHAR2,&lt;br /&gt;p_value     IN NUMBER,-- The id value of the selected record&lt;br /&gt;p_page_id IN NUMBER,-- Page number of the interactive report&lt;br /&gt;p_report_id IN NUMBER,&lt;br /&gt;p_bvar1     IN VARCHAR2,-- Bind variable value1&lt;br /&gt;p_bvar2 IN VARCHAR2,-- Bind variable value2&lt;br /&gt;p_bvar3 IN VARCHAR2,-- Bind variable value3&lt;br /&gt;p_bvar4 IN VARCHAR2,-- Bind variable value4&lt;br /&gt;p_next OUT NUMBER,&lt;br /&gt;p_prev OUT NUMBER,&lt;br /&gt;p_top OUT NUMBER,&lt;br /&gt;p_bot OUT NUMBER,&lt;br /&gt;p_cur_tot OUT VARCHAR2,&lt;br /&gt;p_debug OUT VARCHAR2&lt;br /&gt;-- Returns the report query&lt;br /&gt;)&lt;br /&gt;AS&lt;br /&gt;lv_sql           VARCHAR2(32767);&lt;br /&gt;lv_predicate     VARCHAR2(4000);&lt;br /&gt;lv_sql_sort      VARCHAR2(4000);&lt;br /&gt;lv_comb          VARCHAR2(32767);&lt;br /&gt;lv_condtion_type VARCHAR2(30);&lt;br /&gt;lv_search        VARCHAR2(4000);&lt;br /&gt;BEGIN&lt;br /&gt;/*set the deliminator to be used in the string_agg funtion*/&lt;br /&gt;string_agg_control.g_delim := ' and ';&lt;br /&gt;/*Return the report query and any addition sort and filters from the Interactive Report*/&lt;br /&gt;SELECT&lt;br /&gt;z.sql_query,&lt;br /&gt;nvl2(a.predicate, ' where '&lt;br /&gt;|| a.predicate, NULL) sql_predicate,&lt;br /&gt;nvl2(x.sort_column_1, x.sort_column_1&lt;br /&gt;|| ' '&lt;br /&gt;|| x.sort_direction_1&lt;br /&gt;|| nvl2(x.sort_column_2, ', '&lt;br /&gt;|| x.sort_column_2&lt;br /&gt;|| ' '&lt;br /&gt;|| x.sort_direction_2, NULL)&lt;br /&gt;|| nvl2(x.sort_column_3, ', '&lt;br /&gt;|| x.sort_column_2&lt;br /&gt;|| ' '&lt;br /&gt;|| x.sort_direction_3, NULL)&lt;br /&gt;|| nvl2(x.sort_column_4, ', '&lt;br /&gt;|| x.sort_column_2&lt;br /&gt;|| ' '&lt;br /&gt;|| x.sort_direction_4, NULL)&lt;br /&gt;|| nvl2(x.sort_column_5, ', '&lt;br /&gt;|| x.sort_column_2&lt;br /&gt;|| ' '&lt;br /&gt;|| x.sort_direction_5, NULL)&lt;br /&gt;|| nvl2(x.sort_column_6, ', '&lt;br /&gt;|| x.sort_column_2&lt;br /&gt;|| ' '&lt;br /&gt;|| x.sort_direction_6, NULL)||', rownum ', ' rownum ') sql_sort,&lt;br /&gt;nvl2(a.search, split_columns(a.search, x.report_columns, b.date_cols), NULL) search&lt;br /&gt;INTO&lt;br /&gt;lv_sql,&lt;br /&gt;lv_predicate,&lt;br /&gt;lv_sql_sort,&lt;br /&gt;lv_search&lt;br /&gt;FROM&lt;br /&gt;/*This inline view aggregates any filters added to the Interactive Report*/&lt;br /&gt;(&lt;br /&gt;SELECT&lt;br /&gt;  report_id,&lt;br /&gt;  string_agg( DECODE(condition_type, 'Filter', REPLACE(REPLACE(y.condition_sql,&lt;br /&gt;  '#APXWS_EXPR2#', ''''&lt;br /&gt;  || condition_expression2&lt;br /&gt;  || ''''), '#APXWS_EXPR#', DECODE(SUBSTR(condition_operator, 1, 9), 'is in the',&lt;br /&gt;  condition_expression, ''''&lt;br /&gt;  || condition_expression&lt;br /&gt;  || '''')))) predicate,&lt;br /&gt;  MAX(DECODE(condition_type, 'Search',y.condition_expression)) search&lt;br /&gt;FROM&lt;br /&gt;  apex_application_page_ir_cond y&lt;br /&gt;WHERE&lt;br /&gt;  y.condition_type IN ('Filter', 'Search')&lt;/span&gt;&lt;div&gt;&lt;span style=";font-family:courier new;font-size:85%;"&gt;AND y.condition_enabled = 'Yes'&lt;br /&gt;GROUP BY&lt;br /&gt;  report_id) a,&lt;br /&gt;/*Returns any date columns*/&lt;br /&gt;(&lt;br /&gt;SELECT&lt;br /&gt;  interactive_report_id,&lt;br /&gt;  STRING_AGG(COLUMN_ALIAS) date_cols&lt;br /&gt;FROM&lt;br /&gt;  APEX_APPLICATION_PAGE_IR_COL&lt;br /&gt;WHERE&lt;br /&gt;  column_type = 'DATE'&lt;br /&gt;GROUP BY&lt;br /&gt;  interactive_report_id) b,&lt;br /&gt;/* The following view contains the report original report query*/&lt;br /&gt;apex_application_page_ir z,&lt;br /&gt;/*The following view provides any column sorting*/&lt;br /&gt;apex_application_page_ir_rpt x&lt;br /&gt;WHERE&lt;br /&gt;x.report_id                 = a.report_id(+)&lt;br /&gt;AND x.INTERACTIVE_REPORT_ID = b.INTERACTIVE_REPORT_ID&lt;br /&gt;AND x.interactive_report_id = z.interactive_report_id(+)&lt;br /&gt;AND x.session_id            = nv('SESSION')&lt;br /&gt;AND x.application_id        = nv('APP_ID')&lt;br /&gt;AND x.base_report_id        = p_report_id&lt;br /&gt;AND x.page_id = p_page_id;&lt;br /&gt;/* Build the query used in the Interactive Report */&lt;br /&gt;lv_comb := 'select prev, next, top, bot, cur||'' of '' ||tot from (&lt;br /&gt;select&lt;br /&gt;' ||p_column_id || '&lt;br /&gt;,lag (' || p_column_id || ') over (order by ' || lv_sql_sort ||') prev&lt;br /&gt;,lead (' || p_column_id || ') over (order by ' || lv_sql_sort ||') next&lt;br /&gt;,first_value (' || p_column_id || ') over (order by ' || lv_sql_sort ||') top&lt;br /&gt;,last_value (' || p_column_id || ') over (order by ' || lv_sql_sort ||&lt;br /&gt;' RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) bot&lt;/span&gt;&lt;div&gt;&lt;span style=";font-family:courier new;font-size:85%;"&gt;,count (' || p_column_id ||') over (order by ' || lv_sql_sort || ') cur&lt;br /&gt;,count (' || p_column_id ||') over () tot    &lt;br /&gt;from (select * from (' || lv_sql || ')'||lv_search||')' ||&lt;br /&gt;lv_predicate || ') where ' || p_column_id || ' = ' || p_value;&lt;br /&gt;IF p_bvar4 IS NOT NULL THEN&lt;br /&gt;EXECUTE IMMEDIATE lv_comb INTO p_prev,&lt;br /&gt;p_next,&lt;br /&gt;p_top,&lt;br /&gt;p_bot,&lt;br /&gt;p_cur_tot USING p_bvar1,&lt;br /&gt;p_bvar2,&lt;br /&gt;p_bvar3,&lt;br /&gt;p_bvar4;&lt;br /&gt;ELSIF p_bvar3 IS NOT NULL THEN&lt;br /&gt;EXECUTE IMMEDIATE lv_comb INTO p_prev,&lt;br /&gt;p_next,&lt;br /&gt;p_top,&lt;br /&gt;p_bot,&lt;br /&gt;p_cur_tot USING p_bvar1,&lt;br /&gt;p_bvar2,&lt;br /&gt;p_bvar3;&lt;br /&gt;ELSIF p_bvar2 IS NOT NULL THEN&lt;br /&gt;EXECUTE IMMEDIATE lv_comb INTO p_prev,&lt;br /&gt;p_next,&lt;br /&gt;p_top,&lt;br /&gt;p_bot,&lt;br /&gt;p_cur_tot USING p_bvar1,&lt;br /&gt;p_bvar2;&lt;br /&gt;ELSIF p_bvar1 IS NOT NULL THEN&lt;br /&gt;EXECUTE IMMEDIATE lv_comb INTO p_prev,&lt;br /&gt;p_next,&lt;br /&gt;p_top,&lt;br /&gt;p_bot,&lt;br /&gt;p_cur_tot USING p_bvar1;&lt;br /&gt;ELSE&lt;br /&gt;EXECUTE IMMEDIATE lv_comb INTO p_prev,&lt;br /&gt;p_next,&lt;br /&gt;p_top,&lt;br /&gt;p_bot,&lt;br /&gt;p_cur_tot;&lt;br /&gt;END IF;&lt;br /&gt;p_debug := lv_comb;&lt;br /&gt;END next_prev_values;&lt;br /&gt;/&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;2.  Add this Javascript function into you JS file or between script tags in the page HTML Header&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;function goto(url) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;var newUrl = url.replace("$report_id$", $v('apexir_REPORT_ID'));&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;location.href = newUrl;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;3. Use this link for the IR column link&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;javascript:goto('f?p=&amp;amp;APP_ID.:17:&amp;amp;SESSION.::&amp;amp;DEBUG.:17:P17_EMPNO,P17_IR_REPORT_ID:#EMPNO#,$report_id$');&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;4. Create an  'On Load before Header' process as follows:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;vnext_prev_values(&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  p_column_id     =&gt; 'EMPNO'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; ,p_value       =&gt; :P17_EMPNO    &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; ,p_page_id        =&gt; 16&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; ,p_report_id      =&gt; :P17_IR_REPORT_ID&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; ,p_next          =&gt; :P17_NEXT&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; ,p_prev         =&gt; :P17_PREVIOUS&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; ,p_debug     =&gt; :P17_DEBUG&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; ,p_top              =&gt; :P17_FIRST&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; ,p_bot            =&gt; :P17_LAST&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; ,p_cur_tot          =&gt; :P17_CURRENT&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; ,p_bvar1            =&gt; :P16_JOB&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; ,p_bvar2           =&gt; NULL&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; ,p_bvar3            =&gt; NULL&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; ,p_bvar4            =&gt; NULL&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;*Where p_bvar are items that are used in the main report sql.&lt;br /&gt;&lt;br /&gt;5.  Create items that correspond to the process created above.  The Next, Prev, First &amp;amp; Last are buttons; the rest are hidden items.  The debug will contain the sql being generated and is useful for debugging.&lt;br /&gt;&lt;br /&gt;6.  The above requires the StringAgg function to be installed.  If you don't already have it, then download it from &lt;a href="http://floretli.blogspot.com/2006/06/string-aggregate-in-oracle.html"&gt;here&lt;/a&gt; and install.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5551779552143571989-3870300742757347173?l=simonhunt.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simonhunt.blogspot.com/feeds/3870300742757347173/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=5551779552143571989&amp;postID=3870300742757347173' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/3870300742757347173'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/3870300742757347173'/><link rel='alternate' type='text/html' href='http://simonhunt.blogspot.com/2009/12/next-and-prev-from-interactive-report-2.html' title='Next and Prev from an Interactive Report 2'/><author><name>shunt</name><uri>http://www.blogger.com/profile/08156724954690406968</uri><email>schunt@tiscali.co.uk</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='03458542062488405701'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_XIuspQmbd70/Sx0FcLspbKI/AAAAAAAAAtE/w7j_DsIry_I/s72-c/CM+Capture+2.png' height='72' width='72'/><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5551779552143571989.post-4468549441375361426</id><published>2009-12-03T16:45:00.007Z</published><updated>2009-12-03T17:01:54.236Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Apex'/><title type='text'>Theatre Booking System</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_XIuspQmbd70/SxfraMZTByI/AAAAAAAAAs8/mjGJrLro4Uk/s1600-h/CM+Capture+1.png"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px; height: 241px;" src="http://1.bp.blogspot.com/_XIuspQmbd70/SxfraMZTByI/AAAAAAAAAs8/mjGJrLro4Uk/s320/CM+Capture+1.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5411052312545396514" /&gt;&lt;/a&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica"&gt;&lt;/p&gt;This was an interesting little exercise that cropped up on the Apex forum:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://forums.oracle.com/forums/thread.jspa?threadID=993242&amp;amp;start=0&amp;amp;tstart=0"&gt;Forum Post&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The challenge was to create a theatre seat booking report that showed the booked and available seating.  The report was to lay the seats out in the same configuration as in the theatre with the twist that the seats were staggered.   A solution using a conventional HTML table layout would not work to well.  Instead my solution used a background image as a container and then relative position for the seats.  Rather than a report, my suggestion was to place a checkbox in each seat position so the user could easily select an available seat as part of a booking wizard.  This solution could also be used for a restaurant, hotel, hospital etc booking system.&lt;br /&gt;&lt;br /&gt;Lessons learnt: &lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica"&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;You can format checkboxes with CSS but Safari and Firefox ignore most of the settings.&lt;/li&gt;&lt;li&gt;The X Y positions for the checkboxes would be better stored in the database.  this would give you much more flexibility for complex layouts.&lt;/li&gt;&lt;li&gt;The background image should contain most of the details the user would need for orientation such as the Stage, Isles, stalls, balcony and exits.&lt;/li&gt;&lt;li&gt;The function could be adapted to use images or checkboxes depending on what it was being used for.&lt;/li&gt;&lt;li&gt;It would be clearer for booked seats to be replaced by an image rather than a checked, disabled checkbox.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;    &lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica; min-height: 14.0px"&gt;&lt;br /&gt;&lt;/p&gt; &lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica"&gt;&lt;/p&gt;&lt;a href="http://apex.oracle.com/pls/otn/f?p=579:31"&gt;Example and Code&lt;/a&gt;&lt;p&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5551779552143571989-4468549441375361426?l=simonhunt.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simonhunt.blogspot.com/feeds/4468549441375361426/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=5551779552143571989&amp;postID=4468549441375361426' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/4468549441375361426'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/4468549441375361426'/><link rel='alternate' type='text/html' href='http://simonhunt.blogspot.com/2009/12/theatre-booking-system.html' title='Theatre Booking System'/><author><name>shunt</name><uri>http://www.blogger.com/profile/08156724954690406968</uri><email>schunt@tiscali.co.uk</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='03458542062488405701'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_XIuspQmbd70/SxfraMZTByI/AAAAAAAAAs8/mjGJrLro4Uk/s72-c/CM+Capture+1.png' height='72' width='72'/><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5551779552143571989.post-3415936824985469945</id><published>2009-11-28T18:19:00.006Z</published><updated>2009-12-11T08:06:22.429Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Apex'/><title type='text'>Convert a colon separated function into a table</title><content type='html'>The is a useful function that converts the output from the page items below into a table, so that you can manipulate the data using SQL.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;List Manger&lt;/li&gt;&lt;li&gt;Multiple Select&lt;/li&gt;&lt;li&gt;Checkboxes&lt;/li&gt;&lt;li&gt;Shuttle&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-family:courier new;"&gt;CREATE OR REPLACE PACKAGE useful_functions IS&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  -- Author  : SHUNT&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  -- Created : 25/11/2009&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  -- Purpose : Lots of useful function and procedures&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  -- Public type declarations&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  TYPE ret_val_t IS TABLE OF VARCHAR(4000);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  -- Public function and procedure declarations&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  FUNCTION checkbox_table(p_checkbox VARCHAR2) RETURN ret_val_t&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    PIPELINED;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;END useful_functions;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;/ &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;CREATE OR REPLACE PACKAGE BODY useful_functions IS&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  FUNCTION checkbox_table(p_checkbox VARCHAR2) RETURN ret_val_t&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    PIPELINED IS&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    l_vc_arr2 apex_application_global.vc_arr2;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  BEGIN&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    l_vc_arr2 := apex_util.string_to_table(p_checkbox);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    FOR z IN 1 .. l_vc_arr2.COUNT LOOP&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;      PIPE ROW(l_vc_arr2(z));&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    END LOOP;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  END;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;END useful_functions;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;/&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;To convert the other way i.e. from a table into a colon separated string I would use the &lt;a href="http://floretli.blogspot.com/2006/06/string-aggregate-in-oracle.html"&gt;StringAgg&lt;/a&gt; function.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;To return values from a table function use the following syntax:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;SELECT &lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  *&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;FROM&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;  TABLE(useful_function.checkbox_table('a:b:c:d:e'))&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://forums.oracle.com/forums/thread.jspa?messageID=3927815"&gt;Forum Post&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://articles.techrepublic.com.com/5100-10878_11-5259821.html"&gt;Related Post&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5551779552143571989-3415936824985469945?l=simonhunt.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simonhunt.blogspot.com/feeds/3415936824985469945/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=5551779552143571989&amp;postID=3415936824985469945' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/3415936824985469945'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/3415936824985469945'/><link rel='alternate' type='text/html' href='http://simonhunt.blogspot.com/2009/11/convert-colon-separated-function-into.html' title='Convert a colon separated function into a table'/><author><name>shunt</name><uri>http://www.blogger.com/profile/08156724954690406968</uri><email>schunt@tiscali.co.uk</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='03458542062488405701'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5551779552143571989.post-5809373515247990498</id><published>2009-11-27T10:47:00.004Z</published><updated>2009-12-11T11:07:09.326Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Apex'/><title type='text'>Copying Values</title><content type='html'>I am still a novice with JavaScript so this will appear elementary to most readers.  Following are two JavaScript functions that can be used in Apex to copy, firstly an address and secondly a column in a tabular form.&lt;br /&gt;&lt;br /&gt;1.  &lt;span style="font-weight: bold;"&gt;Addresses&lt;/span&gt;.  It is often the case in a Contact Management database that the same address may need to be entered more than once on the same page.  E.g the main address maybe followed by a postal address, work address, next of kin address etc, which may all be the same.   An easy way to save the user having to retype the addresses is to use JavaScript to copy the fields, as follows:&lt;br /&gt;&lt;br /&gt;Add the following code to the HTML Header section of the page:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;function copyField (fromField, toField){&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    $x(toField).value = $x(fromField).value;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;function copyAddress(fromAddress1, toAddress1, fromAddress2, toAddress2, fromAddress3, toAddress3, fromCounty, toCounty,fromPostcode, toPostcode){&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    copyField(fromAddress1, toAddress1);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    copyField(fromAddress2, toAddress2);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    copyField(fromAddress3, toAddress3);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    copyField(fromCounty, toCounty);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    copyField(fromPostcode, toPostcode);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;add a checkbox with the following in the HTML Form Element Attributes:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;onclick="copyAddress('P1_ADD1','P1_POSTAL_ADD1',&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;'P1_ADD2','P1_POSTAL_ADD2',etc&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;)"&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;OR add this to the href of an image&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;href="javascript:copyAddress(&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;'P1_ADD1','P1_POSTAL_ADD1',&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;'P1_ADD2','P1_POSTAL_ADD2',etc&lt;/span&gt;);"&lt;br /&gt;&lt;br /&gt;Where main address fields are:&lt;br /&gt;P1_ADDRESS_1&lt;br /&gt;P1_ADDRESS_2&lt;br /&gt;P1_ADDRESS_3&lt;br /&gt;P1_COUNTY&lt;br /&gt;P1_POSTCODE&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;and Postal address fields are:&lt;br /&gt;P1_POSTAL_ADD1&lt;br /&gt;P1_POSTAL_ADD2&lt;br /&gt;P1_POSTAL_ADD3&lt;br /&gt;P1_POSTAL_COUNTY&lt;br /&gt;P1_POSTAL_POSTCODE&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;2. &lt;span style="font-weight: bold;"&gt;Copy the column of a tabular form&lt;/span&gt;.  I recently had to do this in a financial system that had ledger sheets where the values to be submitted in a column were often, but not always, the values in one of the other columns.  Using the same principle as above, the trick is to ensure that each field in the tabular form has a unique ID.  This can be done by adding the ROWNUM to the array number in the ID value of the APEX_ITEM API.  e.g.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;apex_item.text(3, NULL, 4,'','style="text-align:right"','f03_#ROWNUM#')&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The copy function then changes to loop through the rows of the tabular form as follows:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;function copyFields(fromField1,toField1) {&lt;br /&gt;var cnt=1;&lt;br /&gt;while (document.getElementById(fromField1+cnt) != null){&lt;br /&gt;copyField(fromField1+cnt,toField1+cnt);&lt;br /&gt;cnt+=1;}&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Then use an 'on event' or url to run the function:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;event&gt;="copyFields('f05_','f03_')" &lt;/event&gt;&lt;/span&gt;&lt;br /&gt;or&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;href="javascript:copyFields('f05_','f03_')"&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5551779552143571989-5809373515247990498?l=simonhunt.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simonhunt.blogspot.com/feeds/5809373515247990498/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=5551779552143571989&amp;postID=5809373515247990498' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/5809373515247990498'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/5809373515247990498'/><link rel='alternate' type='text/html' href='http://simonhunt.blogspot.com/2009/11/copying-values.html' title='Copying Values'/><author><name>shunt</name><uri>http://www.blogger.com/profile/08156724954690406968</uri><email>schunt@tiscali.co.uk</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='03458542062488405701'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5551779552143571989.post-7248172118723321353</id><published>2009-02-22T20:28:00.001Z</published><updated>2009-03-06T09:11:35.055Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Apex'/><title type='text'>Auditing Downloads from Interactive Reports</title><content type='html'>&lt;p&gt;Since the Data Protection Act (DPA) came into force auditing of the viewing and downloading of personnal information has become an issue.  The DPA allows for the use of personal information so long as there is a valid buisiness reason and that appropriate measures are taken to protect that information.  Auditing downloads is one way for an organisation to monitor who has taken information off-line and therefore out of their control.  These are the basic steps:&lt;br /&gt;&lt;br /&gt;1.  Create an audit log table:&lt;br /&gt;&lt;br /&gt; &lt;span style="font-family:Courier"&gt;ID (NUMBER)&lt;br /&gt; APP_USER (VARCHAR2(60))&lt;br /&gt; DOWNLOAD_DATE (DATE)&lt;br /&gt; QUERY (VARCHAR2(4000))&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;2.  Create an Hidden and Protected item :P1_IR_REPORT_ID&lt;br /&gt;&lt;br /&gt;3.  Install the apex_ir_query package by Stew Styker at&lt;br /&gt;&lt;br /&gt;       &lt;a href="http://stewstools.wordpress.com/apex_ir_query-package/"&gt;http://stewstools.wordpress.com/apex_ir_query-package/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;4.  Create a Page Precess&lt;br /&gt;&lt;br /&gt;  Process point - Onload Before Header&lt;br /&gt;  Condition Type - Request is Contained within Expression 1&lt;br /&gt;  Expression 1 - CSV,RTF,XLS,PDF&lt;br /&gt;  Source:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Courier"&gt;INSERT INTO DOANLOAD_AUDIT&lt;br /&gt;    (APP_USER&lt;br /&gt;    ,DOWNLOAD_DATE&lt;br /&gt;    ,QUERY)&lt;br /&gt;   VALUES&lt;br /&gt;     (:APP_USER&lt;br /&gt;     ,SYSDATE&lt;br /&gt;    ,apex_ir_query.ir_query_where(:APP_ID&lt;br /&gt;    ,&amp;lt;IR report page&amp;gt;&lt;br /&gt;    ,:SESSION&lt;br /&gt;    ,:P1_IR_REPORT_ID)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5551779552143571989-7248172118723321353?l=simonhunt.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simonhunt.blogspot.com/feeds/7248172118723321353/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=5551779552143571989&amp;postID=7248172118723321353' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/7248172118723321353'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/7248172118723321353'/><link rel='alternate' type='text/html' href='http://simonhunt.blogspot.com/2009/02/auditing-downloads-from-interactive.html' title='Auditing Downloads from Interactive Reports'/><author><name>shunt</name><uri>http://www.blogger.com/profile/08156724954690406968</uri><email>schunt@tiscali.co.uk</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='03458542062488405701'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5551779552143571989.post-4155647919833294414</id><published>2009-02-17T14:17:00.010Z</published><updated>2009-08-13T14:08:19.769+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Apex'/><title type='text'>How to cope with Lists Of Values (LOVs)</title><content type='html'>Another problem that has been perplexing me is what is the best way to manage LOVs. Apex provides a variety of mechanisms for managing LOVs including static or dynamic LOV which can be specified centrally or locally to an item. Best Practice dictates that centrally managing LOVs is preferred where possible as it provides a single point of update.&lt;br /&gt;&lt;br /&gt;I have found a few issues with the Static LOVs as follows:&lt;br /&gt;• Discoverer Reporting. Viewing LOV values in Discoverer is tricky as you have to query the flows table. This can be done with the following view but you have to remember to change the names of the flows tables after an upgrade of Apex.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Courier;"&gt;SELECT&lt;br /&gt;DISTINCT lov.lov_name&lt;br /&gt;,lovd.lov_disp_value&lt;br /&gt;,lovd.lov_return_value&lt;br /&gt;,lovd.lov_disp_sequence disp_sequence&lt;br /&gt;FROM flows_030100.WWV_FLOW_LISTS_OF_VALUES$ lov&lt;br /&gt;,flows_030100.WWV_FLOW_LIST_OF_VALUES_DATA lovd&lt;br /&gt;WHERE lov.id = lovd.lov_id&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;• Conditions. It is possible to add conditions to each static LOV item, which in effect writes nested sub-queries that are difficult to manage and probably not very performant.&lt;br /&gt;• Maintenance. Changing the values of the LOVs can only be done from the workspace, so a minor change to an LOV values can only be done as part of a release.&lt;br /&gt;• Deleting an LOV item causes the ID from the Flows table to be returned (if display additional items is checked) or a null value is returned (If display nulls is checked) or return the first value in the select list. None of these options are ideal.&lt;br /&gt;&lt;br /&gt;My preferred method of managing LOVs is to create my own custom table along the following lines:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Courier;"&gt;LOV_ID (NUMBER)&lt;br /&gt;DISPLAY_VALUE (VARCHAR2)&lt;br /&gt;ELEMENT_NAME(VARCHAR2)&lt;br /&gt;LOV_NAME (VARCHAR2)&lt;br /&gt;SORT_ORDER (NUMBER)&lt;br /&gt;VALID_FROM (DATE)&lt;br /&gt;VALID_TO (DATE)&lt;br /&gt;NOTES (VARCHAR)&lt;br /&gt;EDITABLE (VARCHAR2)&lt;br /&gt;ATTRIBUTE1 (VARCHAR2)&lt;br /&gt;ATTRIBUTE2 (VARCHAR2)&lt;br /&gt;ATTRIBUTE3 (VARCHAR2)&lt;br /&gt;ATTRIBUTE4 (VARCHAR2)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I have added a few additional attributes columns, which are useful for storing values for reporting or conditional displays. Each LOV item has a validity period, which can be used to expire values so they do not appear as a selectable items in an LOV. However, as they still exist in the table they can be displayed in reports and as additional items. The element name is useful for writing scripts to update attribute values, as the ID could differ from your development environment and the display value may change. The notes field is used for storing information about the values set in the additional attributes.&lt;br /&gt;&lt;br /&gt;To get invalid items to display as additional item in a Select List or popup lov use the following for your LOV query.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Courier;"&gt;SELECT display_value&lt;br /&gt;,lov_id&lt;br /&gt;FROM lov&lt;br /&gt;WHERE SYSDATE BETWEEN valid_from AND&lt;br /&gt;              nvl(valid_to ,SYSDATE)&lt;br /&gt;UNION&lt;br /&gt;SELECT display_value&lt;br /&gt;,lov_id&lt;br /&gt;FROM lov&lt;br /&gt;WHERE lov_id = :p1_colour_id&lt;br /&gt;AND lov_name = 'COLOUR'&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Its quite nice to let the user know that the value has expired and needs changing. I like to do this by appending an asterisk to the display value with the following lov query.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Courier;"&gt;SELECT nvl2(lov2.lov_id&lt;br /&gt;,lov1.display_value&lt;br /&gt;,'(' lov1.display_value ')') display_value&lt;br /&gt;,lov1.lov_id&lt;br /&gt;FROM (SELECT display_value&lt;br /&gt;,lov_id&lt;br /&gt;FROM lov&lt;br /&gt;WHERE SYSDATE BETWEEN valid_from AND&lt;br /&gt;                 nvl(valid_to,SYSDATE)&lt;br /&gt;UNION&lt;br /&gt;SELECT display_value&lt;br /&gt;,lov_id&lt;br /&gt;FROM lov&lt;br /&gt;WHERE lov_id = :p1_colour_id&lt;br /&gt;AND lov_name = 'COLOUR') lov1&lt;br /&gt;FULL OUTER JOIN (SELECT display_value&lt;br /&gt; ,lov_id&lt;br /&gt;FROM lov&lt;br /&gt;WHERE SYSDATE BETWEEN valid_from AND&lt;br /&gt;                 nvl(valid_to,SYSDATE))lov2&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:Courier;"&gt;ON lov1.lov_id = lov2.lov_id&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://apex.oracle.com/pls/otn/f?p=579:20"&gt;Example.&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5551779552143571989-4155647919833294414?l=simonhunt.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simonhunt.blogspot.com/feeds/4155647919833294414/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=5551779552143571989&amp;postID=4155647919833294414' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/4155647919833294414'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/4155647919833294414'/><link rel='alternate' type='text/html' href='http://simonhunt.blogspot.com/2009/02/how-to-cope-with-list-of-values-lovs.html' title='How to cope with Lists Of Values (LOVs)'/><author><name>shunt</name><uri>http://www.blogger.com/profile/08156724954690406968</uri><email>schunt@tiscali.co.uk</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='03458542062488405701'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5551779552143571989.post-7232443179001560349</id><published>2009-02-16T14:04:00.001Z</published><updated>2009-02-22T09:20:03.110Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Apex'/><title type='text'>Next and Previous from an Interactive Report</title><content type='html'>&lt;a href="http://2.bp.blogspot.com/_XIuspQmbd70/SZly1HQ6NmI/AAAAAAAAAm0/kJgHwyHMvCc/s1600-h/ScreenHunter_01+Feb.+16+13.24.gif" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img border="0" style="FLOAT: right; MARGIN: 0pt 0pt 10px 10px; WIDTH: 310px; CURSOR: pointer; HEIGHT: 233px" alt="" src="http://2.bp.blogspot.com/_XIuspQmbd70/SZly1HQ6NmI/AAAAAAAAAm0/kJgHwyHMvCc/s320/ScreenHunter_01+Feb.+16+13.24.gif" id="BLOGGER_PHOTO_ID_5303396293014074978" /&gt;&lt;/a&gt;&lt;br /&gt;The new feature of Interactive Reports (IR) in Apex provides the user with the ability to search, sort and filter reports easily, create their own versions of reports and save them for future use. One limitation with IRs is that I haven’t been able to find a satisfactory way of producing Next and Previous buttons on a custom details screen. The issue being how to best capture the Filters and Sorts that have been set in the IR. I have seen some very slick solutions using JS and AJAX techniques whereby the IDs of the records displayed in the IR are saved to an array and then referenced from the detail screen. Ajax is then used to re-query the main IR to get the next pagination set if required. The only issue I have with these techniques is that they are a bit involved to incorporate and require hidden items to be concatenated to existing fields. If those fields are then removed from the report by the user then that breaks Next Previous function.&lt;br /&gt;&lt;br /&gt;I have written a procedure that reconstructs the query being run in the IR from the APEX views and then uses Analytics to return the Next, Previous, First, Last and Count values. It works pretty well and should be quick and easy to implement. The only failing I have found is when no sort is applied to the IR and the base query has no order by; in this case the order of the next and previous can be different to the order on the IR.&lt;br /&gt;&lt;br /&gt;The complete solution can be viewed from the link below, which includes a demonstration and a copy of the procedure. The procedure references the string_agg function, a must for any Apex developer. I have added a link to a Blog which offers a good explanation of the function and downloads of the code.&lt;br /&gt;&lt;br /&gt;Implementation should be self explanatory, but drop me a line if you need anymore help. I do not profess to being a PLSQL expert so I would be very interested in any ideas of a better way to do it.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://forums.oracle.com/forums/thread.jspa?messageID=3164599&amp;amp;#3164599"&gt;Forum Post&lt;/a&gt;&lt;br /&gt;&lt;a href="http://floretli.blogspot.com/2006/06/string-aggregate-in-oracle.html"&gt;String_agg function&lt;/a&gt;&lt;br /&gt;&lt;a href="http://apex.oracle.com/pls/otn/f?p=579:16"&gt;SHUNTs solution&lt;/a&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5551779552143571989-7232443179001560349?l=simonhunt.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simonhunt.blogspot.com/feeds/7232443179001560349/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=5551779552143571989&amp;postID=7232443179001560349' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/7232443179001560349'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/7232443179001560349'/><link rel='alternate' type='text/html' href='http://simonhunt.blogspot.com/2009/02/next-and-previous-from-interactive.html' title='Next and Previous from an Interactive Report'/><author><name>shunt</name><uri>http://www.blogger.com/profile/08156724954690406968</uri><email>schunt@tiscali.co.uk</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='03458542062488405701'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_XIuspQmbd70/SZly1HQ6NmI/AAAAAAAAAm0/kJgHwyHMvCc/s72-c/ScreenHunter_01+Feb.+16+13.24.gif' height='72' width='72'/><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5551779552143571989.post-2800544429188114815</id><published>2009-02-12T18:23:00.000Z</published><updated>2009-02-22T09:18:18.560Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Apex'/><title type='text'>Merge into Duncs Tabular Form</title><content type='html'>Duncs has created an excellent JS based method for generating tabular forms in Apex. The nice part is that additional rows can be added without the need to refresh the page. I try to avoid using separate insert, update and delete statements and prefer the more performant Merge statement.&lt;br /&gt;&lt;a href="http://djmein.blogspot.com/2007/12/add-delete-row-from-sql-based-tabular.html"&gt;&lt;br /&gt;Duncs method&lt;/a&gt;&lt;br /&gt;&lt;a href="http://apex.oracle.com/pls/otn/f?p=35606:1:3936275200005971:::::"&gt;Demo&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The issue is identifying the rows to be deleted in the UPDATE part of the MERGE as they are deleted from the collection and not just flagged for delete. The answer is to use an outer join to join your update table with your tabular form collection. My first few attempts resulted in syntax errors, so after trying various combinations of inline views, I came up with the following:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Courier"&gt;MERGE INTO emp e&lt;br /&gt;USING (SELECT *&lt;br /&gt; FROM (SELECT to_number(c001) c001&lt;br /&gt;     ,c002 ename&lt;br /&gt;     ,c003 job&lt;br /&gt;     ,c004 mgr&lt;br /&gt;     ,c005 hiredate&lt;br /&gt;     ,f.empno&lt;br /&gt;     ,f.deptno&lt;br /&gt;   FROM (SELECT *&lt;br /&gt; FROM apex_collections&lt;br /&gt; WHERE collection_name = 'EMP')&lt;br /&gt; FULL OUTER JOIN emp f ON to_number(c001) = f.empno)&lt;br /&gt; WHERE deptno = :p1_deptno) c&lt;br /&gt;ON (e.empno = c.empno)&lt;br /&gt;WHEN MATCHED THEN&lt;br /&gt; UPDATE&lt;br /&gt; SET e.ename = c.ename&lt;br /&gt;   ,e.job = c.job&lt;br /&gt;   ,e.mgr = c.mgr&lt;br /&gt;   ,e.hiredate = c.heirdate DELETE&lt;br /&gt; WHERE c.c001 IS NULL&lt;br /&gt;WHEN NOT MATCHED THEN&lt;br /&gt; INSERT&lt;br /&gt;   (e.ename&lt;br /&gt;   ,e.job&lt;br /&gt;   ,e.mgr&lt;br /&gt;   ,e.heirdate&lt;br /&gt;   ,e.deptno)&lt;br /&gt; VALUES&lt;br /&gt;   (c.ename&lt;br /&gt;   ,c.job&lt;br /&gt;   ,c.mgr&lt;br /&gt;   ,c.hiredate&lt;br /&gt;   ,:p1_deptno);&lt;/span&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5551779552143571989-2800544429188114815?l=simonhunt.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simonhunt.blogspot.com/feeds/2800544429188114815/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=5551779552143571989&amp;postID=2800544429188114815' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/2800544429188114815'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/2800544429188114815'/><link rel='alternate' type='text/html' href='http://simonhunt.blogspot.com/2009/02/merge-into-duncs-tabular-form.html' title='Merge into Duncs Tabular Form'/><author><name>shunt</name><uri>http://www.blogger.com/profile/08156724954690406968</uri><email>schunt@tiscali.co.uk</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='03458542062488405701'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5551779552143571989.post-9149666190393258706</id><published>2009-01-19T10:32:00.000Z</published><updated>2009-02-22T09:21:08.578Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Apex'/><title type='text'>Hiding Columns In Interactive Reports</title><content type='html'>This problem has been bugging me for ages, how to hide columns when downloading with interactive reports. The answer is simple; for the column you wish to hide set the Conditional Display as follows:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;    &lt;li&gt;Condition Type: Request Is NOT Contained within Expression 1&lt;/li&gt;&lt;br /&gt;    &lt;li&gt;Expression1: CSV,PDF,RTF,XLS&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5551779552143571989-9149666190393258706?l=simonhunt.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simonhunt.blogspot.com/feeds/9149666190393258706/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=5551779552143571989&amp;postID=9149666190393258706' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/9149666190393258706'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/9149666190393258706'/><link rel='alternate' type='text/html' href='http://simonhunt.blogspot.com/2009/01/hiding-columns-in-interactive-reports.html' title='Hiding Columns In Interactive Reports'/><author><name>shunt</name><uri>http://www.blogger.com/profile/08156724954690406968</uri><email>schunt@tiscali.co.uk</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='03458542062488405701'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5551779552143571989.post-1650666125022067690</id><published>2008-11-25T23:15:00.001Z</published><updated>2009-02-22T09:21:28.432Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Junk'/><title type='text'>Cally</title><content type='html'>&lt;a href="http://1.bp.blogspot.com/_XIuspQmbd70/SSyIYwsLbiI/AAAAAAAAAls/ol-B9J-CFGY/s1600-h/bful.jpg"&gt;&lt;img border="0" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 320px; HEIGHT: 250px" alt="" src="http://1.bp.blogspot.com/_XIuspQmbd70/SSyIYwsLbiI/AAAAAAAAAls/ol-B9J-CFGY/s320/bful.jpg" id="BLOGGER_PHOTO_ID_5272739222712446498" /&gt;&lt;/a&gt; Cally is Chinchilla Persian, Blue Persian cross with a bit of tabby thrown in for good grace. I bought her from a lady in Portsmouth who was the girlfriend of a bloke I used to get a lift to work from. Cally was born as the second in a litter of three, the first one out was Kevin, a big cuddly lump of a cat; next was Cally and then came Jazmine a kind of ratty looking thing, not at all like the other two. I had been looking for a pet for some time and although I would consider myself a dog man these kittens looked gorgeous. I liked Cally and Nikki liked Kevin, and rather than argue about it we decided to take both. To cut a long story short I split with Nikki and was lucky enough to end up with both Kevin and Cally.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_XIuspQmbd70/SSyJn2zdKXI/AAAAAAAAAl8/ekv2TIKYjVY/s1600-h/cats.jpg"&gt;&lt;img border="0" style="FLOAT: left; MARGIN: 0px 10px 10px 0px; WIDTH: 195px; HEIGHT: 191px" alt="" src="http://3.bp.blogspot.com/_XIuspQmbd70/SSyJn2zdKXI/AAAAAAAAAl8/ekv2TIKYjVY/s320/cats.jpg" id="BLOGGER_PHOTO_ID_5272740581563246962" /&gt;&lt;/a&gt;Kevin and Cally have been my companions for the last 16 years and during that time they have been constant source of joy and love for me. They usually lay on the road outside the house or sit on the driveway waiting for me to come home from work. Each year as winter approached, I have a constant fight with the dreaded knots between their legs caused by their long winter coats. No matter what I’m going though they are always there; always understanding and always have lots of love to give. Even when I slammed Cally’s head in a door, she just looked at me to say ‘what did you do that for’ and then continued on her way.&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_XIuspQmbd70/SSyMRwXgIBI/AAAAAAAAAmc/_X_sQJBk15I/s1600-h/DSCF1210.JPG"&gt;&lt;img border="0" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 320px; HEIGHT: 240px" alt="" src="http://2.bp.blogspot.com/_XIuspQmbd70/SSyMRwXgIBI/AAAAAAAAAmc/_X_sQJBk15I/s320/DSCF1210.JPG" id="BLOGGER_PHOTO_ID_5272743500413149202" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Cally particularly has always been a good bed friend, especially if she knows I’m down. Just a few days ago, after I fell out with the RAF at work, I awoke to find her curled up on my pillow, snoring away. Interestingly I have never had any problem sleeping with her snoring, but at boarding school and in army barracks it drives me up the wall. My 'happy thought' is to think of Cally curled up in my arms, both of us asleep. That thought has made some pretty miserable situations very bearable. Particularly useful at Sandhurst where it was pretty miserable the whole time.&lt;br /&gt;&lt;br /&gt;Cally died in my arms at 6 o’clock tonight after severe kidney failure. The sense of loss I feel is indescribable and I have no idea how to move on with my life. I have had wonderful love and support from my friends, family and Gaily, which is very much appreciated, but at the end of the day I have lost the best friend a man could ever have. Bye bye baby I love you and will miss you terribly.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;img border="0" style="DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 225px; HEIGHT: 320px; TEXT-ALIGN: center" alt="" src="http://1.bp.blogspot.com/_XIuspQmbd70/SSyLqfjheTI/AAAAAAAAAmU/EgwygaJdLeA/s320/cally.jpg" id="BLOGGER_PHOTO_ID_5272742825885268274" /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5551779552143571989-1650666125022067690?l=simonhunt.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simonhunt.blogspot.com/feeds/1650666125022067690/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=5551779552143571989&amp;postID=1650666125022067690' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/1650666125022067690'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/1650666125022067690'/><link rel='alternate' type='text/html' href='http://simonhunt.blogspot.com/2008/11/cally.html' title='Cally'/><author><name>shunt</name><uri>http://www.blogger.com/profile/08156724954690406968</uri><email>schunt@tiscali.co.uk</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='03458542062488405701'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_XIuspQmbd70/SSyIYwsLbiI/AAAAAAAAAls/ol-B9J-CFGY/s72-c/bful.jpg' height='72' width='72'/><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5551779552143571989.post-5671389992712440644</id><published>2008-11-09T23:20:00.000Z</published><updated>2009-02-22T09:21:55.582Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Junk'/><title type='text'>Drumming Hunt</title><content type='html'>Wow, no blog for 2 years and then 2 in one day. Perhaps this is a sign of my commitment to spend less time working and more time having fun. Anyway, this is the best clip of my all-time favourite drummer, Mr Johnny Rabb, the free-hand master.&lt;br /&gt;&lt;br /&gt;&lt;object height="344" width="425"&gt;&lt;param name="movie" value="http://www.youtube.com/v/T_eBSmVvTG4&amp;amp;hl=en&amp;amp;fs=1" /&gt;&lt;br /&gt;&lt;param name="allowFullScreen" value="true" /&gt;&lt;br /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;br /&gt;&lt;embed src="http://www.youtube.com/v/T_eBSmVvTG4&amp;amp;hl=en&amp;amp;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" width="425" height="344" /&gt;&lt;/object&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5551779552143571989-5671389992712440644?l=simonhunt.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simonhunt.blogspot.com/feeds/5671389992712440644/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=5551779552143571989&amp;postID=5671389992712440644' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/5671389992712440644'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/5671389992712440644'/><link rel='alternate' type='text/html' href='http://simonhunt.blogspot.com/2008/11/drumming-hunt.html' title='Drumming Hunt'/><author><name>shunt</name><uri>http://www.blogger.com/profile/08156724954690406968</uri><email>schunt@tiscali.co.uk</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='03458542062488405701'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5551779552143571989.post-3061144519176607024</id><published>2008-11-08T22:37:00.001Z</published><updated>2010-01-07T17:08:01.180Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Apex'/><title type='text'>Editable Help</title><content type='html'>I have now delivered a number of Apex applications into production and have to say that as much as I like the item help functionality in Apex, I have never actually taken the time to fill each item with anything particularly useful. The Apex team are as bad with their item help which usually re-words the the label text. A better solution would be to have the ability to edit the item help whilst the application is in production, thus allowing users to add their own help information when required. &lt;a href="http://forums.oracle.com/forums/message.jspa?messageID=2826881#2826881"&gt;Forum question&lt;/a&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;Solution&lt;/span&gt;&lt;br /&gt;This &lt;a href="http://apex.oracle.com/pls/otn/f?p=55349"&gt;demo application&lt;/a&gt; has 2 users: a standard user and an admin users. The standard user is able to view the item help whereas the admin user can view and edit the help. This was achieved with the following steps:&lt;br /&gt;&lt;br /&gt;1. Create an Optional label Template called 'Optional Label with Editable Help'.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Courier;"&gt;&amp;lt;label for="#CURRENT_ITEM_NAME#"&amp;gt;&amp;lt;a class="t13OptionalLabelwithHelp" href="javascript:popUp2('fp=&amp;amp;APP_ID.:506:&amp;amp;SESSION.:::506:P506_ITEM:#CURRENT_ITEM_NAME#',630,600)" tabindex="999"&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;2. Create an Required label Template called 'Required Label with Editable Help.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Courier;"&gt;&amp;lt;label for="#CURRENT_ITEM_NAME#"&amp;gt;&amp;lt;a class="t13RequiredLabelwithHelp" href="javascript:popUp2('fp=&amp;amp;APP_ID.:506:&amp;amp;SESSION.:::506:P506_ITEM_ID:#CURRENT_ITEM_NAME#',630,600)" tabindex="999"&amp;gt;&amp;lt;img src="#IMAGE_PREFIX#requiredicon_status2.gif" alt="" /&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;These label templates can be used for any item help that you wish to allow admin users to edit. If you want all item help to be editable then overwrite the existing templates with the above.&lt;br /&gt;&lt;br /&gt;3. Create a Table in your target Schema to store the help information.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Courier;"&gt;"ITEM_ID" NUMBER PK&lt;br /&gt;"ITEM_NAME" VARCHAR2(60)&lt;br /&gt;"PAGE_ID” NUMBER&lt;br /&gt;"HELP" VARCHAR2(4000)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;4. Create a help page in your application with a 'popup' template (e.g. 506).&lt;br /&gt;&lt;br /&gt;5. Create an Insert/Update form Region based on the Help table. Set the help item to be of type HTML. Create an item :P506_EDlT Hidden and Protected and set the Form Region to conditionally display when item:P506_EDit=' Y'.&lt;br /&gt;&lt;br /&gt;6. Add another HTML region with an item of type Display Only to conditionally display when :P506_EDIT is null source of type SQL&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Courier;"&gt;SELECT help FROM help WHERE item =:P506_ITEM&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;7. Add an edit button to the HTML region with a branch to change the &lt;span style="font-family:Courier;"&gt;:P506_EDit=' Y'.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;8. Migration Script&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Courier;"&gt;INSERT INTO help&lt;br /&gt;  (item_id, item_name, page_id, help)&lt;br /&gt;(SELECT&lt;br /&gt;  item_id, item_name, page_id, item_help_text&lt;br /&gt;FROM&lt;br /&gt;  apex_application_page_items&lt;br /&gt;WHERE application_id = 55349&lt;br /&gt;AND item_label_template LIKE '%Editable%')&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5551779552143571989-3061144519176607024?l=simonhunt.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simonhunt.blogspot.com/feeds/3061144519176607024/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=5551779552143571989&amp;postID=3061144519176607024' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/3061144519176607024'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/3061144519176607024'/><link rel='alternate' type='text/html' href='http://simonhunt.blogspot.com/2008/11/editable-help.html' title='Editable Help'/><author><name>shunt</name><uri>http://www.blogger.com/profile/08156724954690406968</uri><email>schunt@tiscali.co.uk</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='03458542062488405701'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5551779552143571989.post-8056005983183177342</id><published>2007-02-22T00:08:00.000Z</published><updated>2009-02-22T09:25:19.244Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Apex'/><title type='text'>Postcode Plotting</title><content type='html'>&lt;table style="width:auto;"&gt;&lt;br /&gt;    &lt;tr&gt;&lt;br /&gt;        &lt;td&gt;&lt;a href="http://picasaweb.google.co.uk/simonchunt/MikeSImages/photo#5034143146116643522"&gt;&lt;img alt="" src="http://lh4.google.co.uk/image/simonchunt/RdzehPS_-sI/AAAAAAAAARI/vjvrGVezwsc/s288/Plotting.JPG" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;br /&gt;        &lt;td&gt;I have used the postcode to plot locations in a number of Apex applications. Although it’s not as function rich as other products like Google Earth/Maps or Spatial, it’s very quick and easy to do.&lt;br /&gt;Example: &lt;a href="http://apex.oracle.com/pls/otn/f?p=33715"&gt;http://apex.oracle.com/pls/otn/f?p=33715&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It works by comparing the first half of the UK postcode and retrieving the X Y coordinates from a postcode database. The XY coordinates are applied to a scale and then Map Pin images are plotted onto a UK Map image using a CSS position function. I have added a feature that will ‘jog’ map pins for different items that have the same postcode. The calculations are based on &lt;em&gt;Similar Triangles&lt;/em&gt; and the jogging feature uses &lt;em&gt;Trigonometry&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;Instructions can be found at:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://apex.oracle.com/pls/otn/f?p=33715:4"&gt;http://apex.oracle.com/pls/otn/f?p=33715:4&lt;/a&gt;&lt;br /&gt;&lt;/td&gt;&lt;br /&gt;        &lt;td&gt;&lt;/td&gt;&lt;br /&gt;    &lt;/tr&gt;&lt;br /&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;There are a few configurations that need to be made to the procedure for it to work. &lt;br /&gt;1. Work out the Offsets and Scale. You can see the affects of the offset and scale in the above example. It is possible to calculate the scale by cutting the scale out of you map image (if it has one) using an image editor. The size of the image in pixels can then be used to calculate the scale. I prefer to do the whole thing by trial and error.&lt;br /&gt;2. Next add you Map and Map Pin images. I have used this procedure with over 40 different Map Pins named Symbol_1 …… Symbol_40, which are then dynamically assign in the procedure. A more simple method to change the Map Pin is using a DECODE or CASE in the cursor. For example:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Courier"&gt;SELECT decode(dname, ' ACCOUNTING', 'green_flag.gif', ' (RESEARCH)', 'red_flag.gif', ' SALES', 'white_flag.gif', 'g_can.gif') image_file,&lt;br /&gt; location,&lt;br /&gt; deptno id,&lt;br /&gt; ba.post_code postcode, &lt;br /&gt; (pc.x + left_offset) / left_scale x,&lt;br /&gt; (top_offset -pc.y) / top_scale y&lt;br /&gt;FROM dept,&lt;br /&gt; postcode pc&lt;br /&gt;WHERE SUBSTR(ba.post_code, 1, (instr(ba.post_code, ' ', 1, 1) -1)) = pc.postcode(+)&lt;br /&gt;ORDER BY pc.postcode;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;3. Configure the Cursor. Adjust the cursor so that it includes the locations that you wish to display. Above is a simple example that shows how to link your locations to the postcode table. Note the &lt;strong&gt;order by&lt;/strong&gt; at the end. This is important if you want to jog Map Pins that have the same postcode.&lt;br /&gt;4. Add a link. This is optional and can be removed, but it is a nice feature to link to details for that particular location.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5551779552143571989-8056005983183177342?l=simonhunt.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simonhunt.blogspot.com/feeds/8056005983183177342/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=5551779552143571989&amp;postID=8056005983183177342' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/8056005983183177342'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/8056005983183177342'/><link rel='alternate' type='text/html' href='http://simonhunt.blogspot.com/2007/02/postcode-plotting.html' title='Postcode Plotting'/><author><name>shunt</name><uri>http://www.blogger.com/profile/08156724954690406968</uri><email>schunt@tiscali.co.uk</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='03458542062488405701'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5551779552143571989.post-895529904020354266</id><published>2006-12-23T13:03:00.000Z</published><updated>2009-02-22T09:25:41.977Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Junk'/><title type='text'>Vassaliki 2006</title><content type='html'>&lt;div style="CLEAR: both; TEXT-ALIGN: left" align="right"&gt;&lt;a href="http://bp1.blogger.com/_XIuspQmbd70/RY0pH5vXW_I/AAAAAAAAAAU/IYy97412uO8/s1600-h/IMG_6702.JPG"&gt;&lt;img border="0" style="CLEAR: both; FLOAT: left" alt="" src="http://bp1.blogger.com/_XIuspQmbd70/RY0pH5vXW_I/AAAAAAAAAAU/IYy97412uO8/s320/IMG_6702.JPG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;p align="left"&gt;&lt;span style="color:#330099;"&gt;Hunt doing it!&lt;/span&gt;&lt;/p&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;    &lt;li&gt;&lt;br /&gt;        &lt;div align="left"&gt;&lt;span style="color:#330099;"&gt;Pasty white legs and balding head are not a good look.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;    &lt;/li&gt;&lt;br /&gt;    &lt;li&gt;&lt;br /&gt;        &lt;div align="left"&gt;&lt;span style="color:#330099;"&gt;Good example of &lt;em&gt;Super 7&lt;/em&gt; stance.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;    &lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;div style="CLEAR: both; TEXT-ALIGN: left" align="right"&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5551779552143571989-895529904020354266?l=simonhunt.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://simonhunt.blogspot.com/feeds/895529904020354266/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=5551779552143571989&amp;postID=895529904020354266' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/895529904020354266'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5551779552143571989/posts/default/895529904020354266'/><link rel='alternate' type='text/html' href='http://simonhunt.blogspot.com/2006/12/blog-post.html' title='Vassaliki 2006'/><author><name>shunt</name><uri>http://www.blogger.com/profile/08156724954690406968</uri><email>schunt@tiscali.co.uk</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='03458542062488405701'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp1.blogger.com/_XIuspQmbd70/RY0pH5vXW_I/AAAAAAAAAAU/IYy97412uO8/s72-c/IMG_6702.JPG' height='72' width='72'/><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>1</thr:total></entry></feed>