<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>APEX Development</title>
	<atom:link href="http://application-express-blog.e-dba.com/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://application-express-blog.e-dba.com</link>
	<description>Increasing Development Productivity through Integration!</description>
	<lastBuildDate>Wed, 24 Nov 2010 16:14:15 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.4</generator>
		<item>
		<title>Customizing Interactive Report Charts</title>
		<link>http://application-express-blog.e-dba.com/?p=1733</link>
		<comments>http://application-express-blog.e-dba.com/?p=1733#comments</comments>
		<pubDate>Wed, 24 Nov 2010 10:56:39 +0000</pubDate>
		<dc:creator>mnolan</dc:creator>
				<category><![CDATA[APEX Hacks]]></category>
		<category><![CDATA[Interactiv Reports]]></category>
		<category><![CDATA[apex interactive report]]></category>
		<category><![CDATA[apex ir chart]]></category>
		<category><![CDATA[customize ir chart]]></category>
		<category><![CDATA[interactive report chart]]></category>

		<guid isPermaLink="false">http://application-express-blog.e-dba.com/?p=1733</guid>
		<description><![CDATA[I&#8217;m in the process of developing a customized reporting solution using interactive reports for an existing customers application developed in APEX 3.2. Whilst they were pleased with the functionality offered by IR, they required the ability to grant access on certain reports to selected users. Now this is available in 4.0 but they are not [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m in the process of developing a customized reporting solution using interactive reports for an existing customers application developed in APEX 3.2. Whilst they were pleased with the functionality offered by IR, they required the ability to grant access on certain reports to selected users. Now this is available in 4.0 but they are not in a position to upgrade so my hands are basically tied. To facilitate this I&#8217;ve followed the details in the following <a href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2ZvcnVtcy5vcmFjbGUuY29tL2ZvcnVtcy90aHJlYWQuanNwYT90aHJlYWRJRD04NjEzNjE=" target=\"_blank\"> APEX forum thread</a> and there&#8217;s also a <a href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwZXguZGJlLnBsLzIwMDkvMTIva29waW93YW5pZS16YXBpc2FuZWdvLXJhcG9ydHUuaHRtbA==" target=\"_blank\">blog post</a> which you can reference.</p>
<p>In addition to the granting of reports they also require a few changes to the IR charting feature. (this also applies to 4.0)</p>
<ul>
<li>Adding a title to the chart</li>
<li>Adding the point values to the chart</li>
<li>Changing the label rotation for the vertical bar chart</li>
<li>Changing colours</li>
<li>Adding in drill own links to the data</li>
</ul>
<p>So to devise a workaround, back in February (this is when the customer first requested the change, approval to start came in November, they move quickly <img src='http://application-express-blog.e-dba.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />  I wrote a <a href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwcGxpY2F0aW9uLWV4cHJlc3MtYmxvZy5lLWRiYS5jb20vP3A9MTA2OQ==">theoretical approach</a> to try and override the XML chart output.</p>
<p>Putting this theory into practice this week I came up with the following code prototype (Unfortunately I&#8217;m not able to provide the final solution for commercial reasons). It should give you a good idea of what needs to occur, I&#8217;d recommend making a few changes to the XML_CHART_API package as it&#8217;s only written to give you an idea of what to do&#8230;.</p>
<pre class="brush: sql;">
create or replace
PROCEDURE FLASH3
( p                     IN VARCHAR2
, preloaderLoadingText  IN VARCHAR2 DEFAULT NULL
, swffile               IN VARCHAR2 DEFAULT NULL
, preloaderInitText     IN VARCHAR2 DEFAULT NULL
, XMLCallDate           IN VARCHAR2 DEFAULT NULL
)
--
-- URL rewriting See - http://www.yourhtmlsource.com/sitemanagement/urlrewriting.html
--
-- RewriteEngine on
-- RewriteRule ^pls/apex/apex_util.flash2(.*) /pls/apex/flash3$1
--
-- grant execute on #OWNER#.flash3 to apex_public_user;
-- create public synonym flash3 for #OWNER#.flash3;
--
-- update wwv_flow_epg_include_mod_local
--
AS
  v_out_buf         htp.htbuf_arr;
  v_out_row_count   NUMBER := 99999999;
  v_clob            CLOB := empty_clob();
  v_xml_start_found BOOLEAN DEFAULT FALSE;

  PROCEDURE htp_buf_prn
  ( p_text              IN CLOB
  ) AS
    l_buflen  INTEGER := 256;
  BEGIN
    FOR i IN 0 .. FLOOR(LENGTH(p_text)/l_buflen) LOOP
      sys.htp.prn(substr(p_text, i * l_buflen + 1, l_buflen));
    END LOOP;
  END htp_buf_prn;

BEGIN
  dbms_lob.createtemporary(lob_loc =&gt; v_clob, cache =&gt; true, dur =&gt; dbms_lob.call);

  apex_util.flash2( p                     =&gt; p
                  , preloaderLoadingText  =&gt; preloaderLoadingText
                  , swffile               =&gt; swffile
                  , preloaderInitText     =&gt; preloaderInitText
                  , XMLCallDate           =&gt; XMLCallDate
                  );
  owa.get_page(v_out_buf, v_out_row_count);
  htp.init;

  FOR i IN 1..v_out_row_count LOOP
    --
    -- Lets look for the start of our XML document and ignore any of the mime header
    -- X-ORACLE-IGNORE etc.
    --
    IF instr(v_out_buf(i), '&lt;?xml') &gt; 0 OR v_xml_start_found THEN
      dbms_lob.writeappend(lob_loc =&gt; v_clob, amount =&gt; length(v_out_buf(i)), buffer =&gt; v_out_buf(i));
      v_xml_start_found := TRUE;
    END IF;
  END LOOP;
  xml_chart_api.chart_mods
  ( p_in_out_chart_xml =&gt; v_clob
  );
  htp_buf_prn(v_clob);

END FLASH3;
/
create or replace
PACKAGE XML_CHART_API AS 

  PROCEDURE chart_mods
  ( p_in_out_chart_xml IN OUT NOCOPY CLOB
  );

END XML_CHART_API;
/
create or replace
PACKAGE BODY XML_CHART_API AS

  PROCEDURE chart_mods
  ( p_in_out_chart_xml IN OUT NOCOPY CLOB
  )
  AS
    l_xml     XMLType;
    doc       dbms_xmldom.DOMDocument;
    nl        DBMS_XMLDOM.DOMNodeList;
    n         DBMS_XMLDOM.DOMNode;
    n2        DBMS_XMLDOM.DOMNode;
    l_node    DBMS_XMLDOM.DOMNode;
    l_attr    DBMS_XMLDOM.DOMAttr;
    e         DBMS_XMLDOM.DOMElement;
    e2        DBMS_XMLDOM.DOMElement;

    len       NUMBER;
    l_chart   VARCHAR2(100);

  BEGIN
    -------------------------------
    -- Turn our clob into XML doc
    -------------------------------

    l_xml := XMLType.createXML(p_in_out_chart_xml);
    doc   := dbms_xmldom.newDOMDocument(l_xml);

    -------------------------------
    -- Get the report type
    -------------------------------
    nl   := DBMS_XMLDOM.getElementsByTagName(doc, 'chart');
    len  := DBMS_XMLDOM.getLength(nl);

    -- loop through elements (should only be 1
    FOR i IN 1 .. len LOOP
        n := DBMS_XMLDOM.item(nl, i-1);
        e := DBMS_XMLDOM.makeelement(n);
        --
        -- We need to know what type of chart, as label rotation is only
        -- required for vertical chart
        --
        l_chart := DBMS_XMLDOM.getAttribute(e, 'plot_type');
    END LOOP;

    -------------------------------
    -- Add a report drill down link
    -------------------------------
    nl   := DBMS_XMLDOM.getElementsByTagName(doc, 'point');
    len  := DBMS_XMLDOM.getLength(nl);

    -- loop through elements
    FOR i IN 1 .. len LOOP
        n := DBMS_XMLDOM.item(nl, i-1);
        e2 := DBMS_XMLDOM.makeelement(n);
        e:= DBMS_XMLDOM.createElement( doc, 'actions' );
        --sys.htp.p('Debug: adding label');
        n2 := DBMS_XMLDOM.makeNode(e);
        e:= DBMS_XMLDOM.createElement( doc, 'action' );
        DBMS_XMLDOM.setAttribute(e, 'type', 'navigateToURL');
        --
        -- Basic example of how to build up the URL for report drill down
        -- this is where I would hand off to another function with the page number
        -- and value to work out what items need setting in the URL
        --
        DBMS_XMLDOM.setAttribute(e, 'url', 'f?p='||v('APP_ID')||':1:'||v('APP_SESSION')||':'||DBMS_XMLDOM.getAttribute(e2,'name')||'_'||DBMS_XMLDOM.getAttribute(e2,'y'));
        --
        -- Lets open the drill down in a new window
        --
        DBMS_XMLDOM.setAttribute(e, 'target', '_blank');
        l_node := DBMS_XMLDOM.appendChild(n2,dbms_xmldom.makeNode(e));
        l_node := DBMS_XMLDOM.appendChild(n,n2);

    END LOOP;
    -------------------------------
    -- Label Rotation &amp; alignment
    -------------------------------
    nl   := DBMS_XMLDOM.getElementsByTagName(doc, 'labels');
    len  := DBMS_XMLDOM.getLength(nl);

    -- loop through elements
    FOR i IN 1 .. len LOOP
        n := DBMS_XMLDOM.item(nl, i-1);
        -- cast our node into an element which we can manipulate
        e := DBMS_XMLDOM.makeelement(n);
        --DBMS_XMLDOM.setAttribute(e, 'display_mode', 'rotated');
        CASE l_chart
          WHEN 'CategorizedHorizontal' THEN
            DBMS_XMLDOM.setAttribute(e, 'rotation', '0');
          ELSE
            DBMS_XMLDOM.setAttribute(e, 'rotation', '270');
        END CASE;
        DBMS_XMLDOM.setAttribute(e, 'align', 'inside');

    END LOOP;

    -------------------------------
    -- Add point values
    -------------------------------
    nl   := DBMS_XMLDOM.getElementsByTagName(doc, 'series');
    len  := DBMS_XMLDOM.getLength(nl);

    -- loop through elements
    FOR i IN 1 .. len LOOP
        n := DBMS_XMLDOM.item(nl, i-1);
        e:= dbms_xmldom.createElement( doc, 'label' );
        DBMS_XMLDOM.setAttribute (e, 'enabled', 'true');

        l_node := dbms_xmldom.appendChild(n,dbms_xmldom.makeNode(e));
    END LOOP;
    DBMS_XMLDOM.writeToClob(doc,p_in_out_chart_xml);
    DBMS_XMLDOM.freeDocument(doc);
  EXCEPTION
    WHEN OTHERS THEN
      DBMS_XMLDOM.freeDocument(doc);
      sys.htp.p(SQLERRM);
  END chart_mods;

END XML_CHART_API;
/
</pre>
<h3>Additional Setup</h3>
<ol>
<li>Apache mod_rewrite rule: <br />
#<br />
# change the PLS handler to the one defined for your DB<br />
#<br />
RewriteEngine on<br />
RewriteRule ^pls/apex/apex_util.flash2(.*) /pls/apex/flash3$1
</li>
<li>grant execute on #OWNER#.flash3 to apex_public_user</li>
<li>create public synonym flash3 for #OWNER#.flash3;</li>
<li>update #APEX_SCHEMA#.wwv_flow_epg_include_mod_local function to include FLASH3 <br />
e.g.<br />
    if upper(procedure_name) in (<br />
          &#8216;#OWNER#.FLASH3&#8242;,&#8217;FLASH3&#8242;) then<br />
        return TRUE;
</li>
</ol>
 <img src="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?view=1&post_id=1733" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://application-express-blog.e-dba.com/?feed=rss2&#038;p=1733</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Processing CSV Uploaded Files</title>
		<link>http://application-express-blog.e-dba.com/?p=1728</link>
		<comments>http://application-express-blog.e-dba.com/?p=1728#comments</comments>
		<pubDate>Thu, 04 Nov 2010 15:45:31 +0000</pubDate>
		<dc:creator>mnolan</dc:creator>
				<category><![CDATA[APEX Hacks]]></category>
		<category><![CDATA[PLSQL]]></category>
		<category><![CDATA[apex csv process]]></category>
		<category><![CDATA[apex csv upload]]></category>

		<guid isPermaLink="false">http://application-express-blog.e-dba.com/?p=1728</guid>
		<description><![CDATA[I have a requirement to process uploaded CSV files into a number of application tables and provide a number of screens to efficiently manage this process. I&#8217;ve followed the following post by &#8220;Advait Deo&#8221; however I have a requirement to handle columns wrapped in double quotes or some other encapsulator. http://avdeo.com/2008/05/21/uploading-excel-sheet-using-oracle-application-express-apex/ So before I can [...]]]></description>
			<content:encoded><![CDATA[<p>I have a requirement to process uploaded CSV files into a number of application tables and provide a number of screens to efficiently manage this process. I&#8217;ve followed the following post by &#8220;Advait Deo&#8221; however I have a requirement to handle columns wrapped in double quotes or some other encapsulator. </p>
<p><a href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2F2ZGVvLmNvbS8yMDA4LzA1LzIxL3VwbG9hZGluZy1leGNlbC1zaGVldC11c2luZy1vcmFjbGUtYXBwbGljYXRpb24tZXhwcmVzcy1hcGV4Lw==" target=\"_blank\">http://avdeo.com/2008/05/21/uploading-excel-sheet-using-oracle-application-express-apex/</a></p>
<p>So before I can really get started I needed a function to do this. I did see an example in the comments but I was looking for something that could be reused for other purposes.</p>
<p>I looked at dbms_utility.comma_to_table but it had too many limitations and then at regex but unfortunately Oracle doesn&#8217;t support look ahead assertion, so after 30mins of frustration I jumped back into PLSQL and came up with the following after getting some inspiration from the following <a href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL3d3dy5vcmF0ZWNoaW5mby5jby51ay9kZWxpbWl0ZWRfbGlzdHNfdG9fY29sbGVjdGlvbnMuaHRtbA==" target=\"_blank\">http://www.oratechinfo.co.uk/delimited_lists_to_collections.html</a>:</p>
<pre class="brush: sql;">
create or replace
FUNCTION f_csv_convert_to_table
( p_in_string       IN VARCHAR2
, p_in_encapsulator IN VARCHAR2 DEFAULT '&quot;'
)
RETURN wwv_flow_global.vc_arr2
  AS
    l_string              VARCHAR2(32767) := p_in_string || ',';
    l_quote_start_index   PLS_INTEGER := 0;
    l_quote_end_index     PLS_INTEGER := 0;
    l_comma_index         PLS_INTEGER;
    l_index               PLS_INTEGER := 1;
    l_tab                 wwv_flow_global.vc_arr2;
    i                     PLS_INTEGER := 1;
  BEGIN
    LOOP
      l_comma_index := REGEXP_INSTR(l_string, '[,'||p_in_encapsulator||']', l_index);
      EXIT WHEN l_comma_index = 0;

      CASE
        WHEN SUBSTR(l_string,l_comma_index,1) = p_in_encapsulator AND l_quote_start_index = 0 THEN
          l_quote_start_index := l_comma_index;
        WHEN SUBSTR(l_string,l_comma_index,1) = p_in_encapsulator AND l_quote_start_index &lt;&gt; 0 THEN
          l_quote_end_index   := l_comma_index;
        ELSE
          NULL;
      END CASE;

      IF l_quote_start_index = 0 THEN

        l_tab(i) := SUBSTR(l_string, l_index, l_comma_index - l_index);
        i := i+1;

      ELSIF l_quote_start_index &lt;&gt; 0 AND l_quote_end_index &lt;&gt; 0 THEN

        l_tab(i) := SUBSTR(l_string, l_quote_start_index+1, (l_quote_end_index - l_quote_start_index)-1);
        i := i+1;
        --
        -- Lets reset our quote check
        --
        l_quote_start_index := 0;
        l_quote_end_index   := 0;
        --
        -- We need to discard our end comma
        --
        l_comma_index := INSTR(l_string, ',', l_index);
      END IF;
      l_index := l_comma_index + 1;
    END LOOP;
    RETURN l_tab;
END f_csv_convert_to_table;
</pre>
<p>Now the above only works correctly if the encapsulator is matched i.e. start and end around fields. It will also continue to work if there are line breaks between your encapsulated fields. I&#8217;m sure someone can come up with a better solution, but this is all I need at the moment.</p>
<p>In Advait&#8217;s post I plan to swap the line</p>
<pre class="brush: sql;">
v_data_array := wwv_flow_utilities.string_to_table (v_line);
</pre>
<p>with</p>
<pre class="brush: sql;">
v_data_array := f_csv_convert_to_table (v_line);
</pre>
<p>Just to keep things simple. </p>
<p>Now back to the task at hand and changing it be completely generic using collections. I&#8217;ll follow up with another post with the entire solution in the coming week or so, when time permits.</p>
 <img src="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?view=1&post_id=1728" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://application-express-blog.e-dba.com/?feed=rss2&#038;p=1728</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>BRANCH_TO_PAGE_ACCEPT and wwv_flow.g_request</title>
		<link>http://application-express-blog.e-dba.com/?p=1726</link>
		<comments>http://application-express-blog.e-dba.com/?p=1726#comments</comments>
		<pubDate>Wed, 27 Oct 2010 16:00:35 +0000</pubDate>
		<dc:creator>mnolan</dc:creator>
				<category><![CDATA[APEX Hacks]]></category>
		<category><![CDATA[apex request value]]></category>
		<category><![CDATA[BRANCH_TO_PAGE_ACCEPT]]></category>

		<guid isPermaLink="false">http://application-express-blog.e-dba.com/?p=1726</guid>
		<description><![CDATA[I had a requirement today for an existing customer to add in some functionality to display a report listing all the users sharing the same email address when creating a new user in the application. An additional requirement was that they also wanted the user to be able to continue to create the user but [...]]]></description>
			<content:encoded><![CDATA[<p>I had a requirement today for an existing customer to add in some functionality to display a report listing all the users sharing the same email address when creating a new user in the application. An additional requirement was that they also wanted the user to be able to continue to create the user but be prompted to do so to confirm that they are recording a user with a duplicate email address. </p>
<p>I thought about popups, using a modal dialog etc. this is in APEX 3.2 btw. but none of this was simple and straight forward with minimal effort. So whilst I was thinking of possibilities I remembered previously using the <strong>&#8220;BRANCH_TO_PAGE_ACCEPT&#8221;</strong> request value which gave me the idea for the following simple solution.</p>
<p>On submit of the page I would have a branch that executes <strong>&#8220;On Submit: Before Processing: (After Computation, Validation and Before Processing)&#8221;</strong> with a SQL exists condition which checks for the duplicate email address and if so redirects the user to this newly created report page listing all the users with duplicate email addresses. On this page I have button labelled &#8220;Create User&#8221; which allows them to continue creating the user but first redirects to some Javascript similar to confirmDelete which prompts the user to confirm that they do indeed wish to create the user. On click of Ok they are then redirected back to the create user page with the following <strong>REQUEST </strong>value in the branch <strong>&#8220;BRANCH_TO_PAGE_ACCEPT&#8221;</strong> which automatically tells APEX to ignore the rendering phase and simply process the page. This means that I was able to reuse the entire functionality of the create users page to perform all the processing as all the values were still recorded in session state from the initial page submit when the user clicked <strong>&#8220;CREATE&#8221;</strong>. </p>
<p>However setting this request value is simply not enough since the Automatic DML row processing (and my PLSQL processes) requires a valid REQUEST value like <strong>&#8220;INSERT, CREATE, CREATE_AGAIN, CREATEAGAIN&#8221;</strong>. So to work around this issue I used the following PLSQL process (which runs before every other process) to switch the request value.</p>
<pre class="brush: sql;">
wwv_flow.g_request := 'CREATE';
</pre>
<p>I simply set a condition of &#8220;Request = Expression 1&#8243; with a value of &#8220;BRANCH_TO_PAGE_ACCEPT&#8221; and my page is successfully processed and the user added. The thing I like about this sort of approach is that we can conditionally change the REQUEST value on the fly which means that we can attribute some logic to multiple independent PSLQL processes. We can update the REQUEST value in flight to either execute the next process or skip it. Please Note: this is not best practice, and will open up debugging issues, but it does have its uses here and there. It&#8217;s all about solving problems, and this one today was solved rather painlessly with a neat little trick.</p>
 <img src="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?view=1&post_id=1726" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://application-express-blog.e-dba.com/?feed=rss2&#038;p=1726</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Beautifying login screens, how you can cheat</title>
		<link>http://application-express-blog.e-dba.com/?p=1681</link>
		<comments>http://application-express-blog.e-dba.com/?p=1681#comments</comments>
		<pubDate>Fri, 17 Sep 2010 09:13:12 +0000</pubDate>
		<dc:creator>mnolan</dc:creator>
				<category><![CDATA[APEX Templates]]></category>
		<category><![CDATA[login screen]]></category>

		<guid isPermaLink="false">http://application-express-blog.e-dba.com/?p=1681</guid>
		<description><![CDATA[In my job I&#8217;m expected to wear a lot of hats: team leader, project manager, web &#038; plsql developer, business analyst, tester, designer, graphic designer, DBA, webmaster, sales etc. etc. there&#8217;s probably even a few I don&#8217;t know about. If you&#8217;re in a growing company I&#8217;m sure you&#8217;re familiar with this requirement. Whilst I can [...]]]></description>
			<content:encoded><![CDATA[<p>In my job I&#8217;m expected to wear a lot of hats: team leader, project manager, web &#038; plsql developer, business analyst, tester, designer, graphic designer, DBA, webmaster, sales etc. etc. there&#8217;s probably even a few I don&#8217;t know about. If you&#8217;re in a growing company I&#8217;m sure you&#8217;re familiar with this requirement. Whilst I can be out of my depth at times and quite stressed, it&#8217;s a great learning experience and comes with a lot of freedom.</p>
<p>Now I&#8217;m no expert in any of these roles, well I&#8217;d like to think I&#8217;m a good DBA and developer&#8230;. the others though, I do the best I can. I landed the graphic design role simply because I know how to spell Photoshop (I recently had to create some images for our company business cards believe it or not&#8230; I can&#8217;t really myself, considering that I was doing just DBA duties 2 years ago). </p>
<p>I&#8217;m required to beautify and custom theme the applications we build. Now I really don&#8217;t have much artistic flair, my talent is copying and modifying. My first port of call was to either purchase a template from a site like template monster, if the app is a website style, or consider purchasing something from <a href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL3d3dy5hcGV4LXRoZW1lcy5jb20vc3RvY2stdGhlbWVzL2NoYXJsaWUtc2VyaWVzLw==" target=\"_blank\">APEX Themes</a>. The new charlie/delta theme series allows you to custom theme them with jQuery UI. Very smart on their side to put the onus back on the developer to skin the theme to their own requirements using Theme Roller. </p>
<p>However these days I don&#8217;t really have that requirement since we use Ext JS. There&#8217;s no real requirement to theme the applications as most customers are happy with just using their company logo and sticking with either the blue or grey theme&#8230;. well except for login pages. Hence the title of this post&#8230;</p>
<p>Now the cheating part about designing login pages is that: if you don&#8217;t require anything but a username/password with a couple of additional links, then I&#8217;d recommend the following: Create a background image that gives you the cool look that you&#8217;re striving for and simply center your form on the background image. Here&#8217;s an example for the login page for our Sales team&#8217;s licensing application, <strong>Note:</strong> the positioning of the background and form always stay in place on page resizing.</p>
<p>Here&#8217;s the CSS to position your login form and background image (I normally set &#8220;No Template&#8221; on my page 101 login form).</p>
<pre class="brush: css;">
html, body {
	font:normal 12px verdana;
	padding:0;
	border:0 none;
	overflow:hidden;
	height:100%;
}
body {
	padding: 0px;
	background-image:url(&amp;LOGIN_BACKGROUND_IMAGE.);
	background-repeat:repeat;
	background-position:center;
	background-attachment: fixed;
	text-align:center;
	margin:0 auto;
	vertical-align:middle;
}
#Messages {
        width:345px;
        margin-left:-180px;
        position:absolute;
        top:50%;
        left:50%;
        margin-top:-35px;
}
#BoxBody {
        width:345px;
        margin-left:-170px;
        position:absolute;
        top:50%;
        left:50%;
        margin-top:30px;
}
</pre>
<p>And here&#8217;s the page template</p>
<pre class="brush: xml;">
&lt;div id=&quot;Messages&quot;&gt;#GLOBAL_NOTIFICATION##SUCCESS_MESSAGE##NOTIFICATION_MESSAGE#&lt;/div&gt;
&lt;div id=&quot;BoxBody&quot;&gt;
  &lt;div align=&quot;center&quot;&gt;#REGION_POSITION_01##BOX_BODY##REGION_POSITION_02##REGION_POSITION_04#&lt;/div&gt;
&lt;/div&gt;
</pre>
<p>You can download the .psd file <a href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwcGxpY2F0aW9uLWV4cHJlc3MtYmxvZy5lLWRiYS5jb20vd3AtY29udGVudC9ibG9nL2xvZ2luLXNjcmVlbi1iYWNrZ3JvdW5kLWRvd25sb2FkLnppcA==">here</a>. For non-photoshop people you can download the .png <a href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwcGxpY2F0aW9uLWV4cHJlc3MtYmxvZy5lLWRiYS5jb20vd3AtY29udGVudC9ibG9nL2xvZ2luLXNjcmVlbi1iYWNrZ3JvdW5kLWRvd25sb2FkLnBuZw==">here</a>.</p>

<a href='http://application-express-blog.e-dba.com/?attachment_id=1687' title='login-screen-full-screen'><img width="150" height="150" src="http://application-express-blog.e-dba.com/wp-content/uploads/2010/09/login-screen-full-screen-150x150.png" class="attachment-thumbnail" alt="login-screen-full-screen" title="login-screen-full-screen" /></a>
<a href='http://application-express-blog.e-dba.com/?attachment_id=1688' title='login-screen-loading'><img width="150" height="150" src="http://application-express-blog.e-dba.com/wp-content/uploads/2010/09/login-screen-loading-150x150.png" class="attachment-thumbnail" alt="login-screen-loading" title="login-screen-loading" /></a>
<a href='http://application-express-blog.e-dba.com/?attachment_id=1695' title='login-screen-small-screen'><img width="150" height="150" src="http://application-express-blog.e-dba.com/wp-content/uploads/2010/09/login-screen-small-screen-150x150.png" class="attachment-thumbnail" alt="login-screen-small-screen" title="login-screen-small-screen" /></a>
<a href='http://application-express-blog.e-dba.com/?attachment_id=1696' title='login-screen-half-screen'><img width="150" height="150" src="http://application-express-blog.e-dba.com/wp-content/uploads/2010/09/login-screen-half-screen-150x150.png" class="attachment-thumbnail" alt="login-screen-half-screen" title="login-screen-half-screen" /></a>

 <img src="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?view=1&post_id=1681" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://application-express-blog.e-dba.com/?feed=rss2&#038;p=1681</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Making your APEX app changes using SQL</title>
		<link>http://application-express-blog.e-dba.com/?p=1667</link>
		<comments>http://application-express-blog.e-dba.com/?p=1667#comments</comments>
		<pubDate>Wed, 15 Sep 2010 14:08:48 +0000</pubDate>
		<dc:creator>mnolan</dc:creator>
				<category><![CDATA[APEX Hacks]]></category>
		<category><![CDATA[apex backend]]></category>
		<category><![CDATA[apex dml]]></category>

		<guid isPermaLink="false">http://application-express-blog.e-dba.com/?p=1667</guid>
		<description><![CDATA[The best part about APEX, compared with most other development products, is that our applications are just metadata. This gives us the benefit of managing and building our applications using SQL and using the APEX PLSQL API. This can really benefit you on larger applications. You can either update the definition of existing item(s)/region(s)/validation(s) etc. [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://application-express-blog.e-dba.com/wp-content/uploads/2010/09/sql.png" alt="" title="sql" width="111" height="123" class="alignright size-full wp-image-1669" />The best part about APEX, compared with most other development products, is that our applications are just metadata. This gives us the benefit of managing and building our applications using SQL and using the APEX PLSQL API. This can really benefit you on larger applications. You can either update the definition of existing item(s)/region(s)/validation(s) etc. using a SQL DML update statement, or you could add a new region or item to every page in the application using the APEX PLSQL API (The easiest approach for this would be to create a dummy APEX page and create a PLSQL process on this page to issue the APEX PLSQL API calls. This way you&#8217;ll avoid all the setup requirement of running APEX code outside of the APEX engine. Every less line of code helps <img src='http://application-express-blog.e-dba.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  ).</p>
<p>Currently I have a customer who requires a security change to their application, which affects most of the LOV definitions defined on most of the pages. The task of editing these through the APEX IDE does not excite me at all. Being the lazy developer that I am, it&#8217;s much much simpler making these changes at the backend. (As long as you have access to update data in the APEX schema). Now I&#8217;m sure most experienced APEX developers make changes through the backend rather than the frontend, especially if you need to make a consistent change across a number of pages. It&#8217;s less error prone to do this in SQL than it is to click click click paste and click. </p>
<p>A point I&#8217;d like to highlight with making backend updates is that using consistent naming convention can allow you to quickly update more items in a single UPDATE statement. I&#8217;m not saying that you couldn&#8217;t update an irregular naming convention with the use of regex in your update statement, it&#8217;s just going to take you longer and would require a few more tests to get it right. That is unless you&#8217;re a regex master&#8230;. me? no!</p>
<p><strong>Note:</strong> before you manipulate your apps using SQL, all the obvious apply e.g. backup your applications, db, enable flashback, issue a rollback before you exit SQL*Plus after an unsuccessful change etc. etc. Just in case you screw something up, we&#8217;ve all been there before&#8230;. myself included!</p>
<p>So one of the tasks at hand is to update a district LOV definition (it has a foreign key which references a company table). On each of the pages the district appears three times for a &#8220;submitter&#8221;, &#8220;assignee&#8221; and &#8220;location&#8221;. the standard naming convention used was to have DISTRICT_ID in each of these three different page items (the same goes for COMPANY_ID). Therefore it made it quick and easy to write the following UPDATE statement to change the LOV definition.</p>
<p>e.g.</p>
<pre class="brush: sql;">
UPDATE apex_030200.wwv_flow_step_items
SET    lov = 'SELECT district_name, district_id
FROM   district
WHERE  company_id = :'||replace(name,'DISTRICT','COMPANY')||'
AND    (soft_deleted = ''N'' OR (:P'||flow_step_id||'_REQUEST = ''EDIT'' AND :'||name||' = district_id))
ORDER BY 1'
,      lov_display_null = 'YES'
,      lov_null_text = 'Please Select ...'
WHERE  name like '%_DISTRICT_ID%'
AND    flow_id =702 and flow_step_id IN (2,26,29,39,47,55,61,137)
</pre>
<p>Now this is just a simple test case for one type of LOV, I have roughly about 200+ items to change in the application as they have page specific binds defined in them. So rather than using the APEX IDE I can complete this task in minutes as opposed to hours (which I don&#8217;t think my brain could take)&#8230;. </p>
<p>Now if only I could find a quick way to test these mass changes as I&#8217;m yet to find a similar productivity boost? Any advice would be most welcome&#8230;..</p>
 <img src="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?view=1&post_id=1667" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://application-express-blog.e-dba.com/?feed=rss2&#038;p=1667</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>A couple of things I came across today&#8230;..</title>
		<link>http://application-express-blog.e-dba.com/?p=1636</link>
		<comments>http://application-express-blog.e-dba.com/?p=1636#comments</comments>
		<pubDate>Thu, 09 Sep 2010 16:43:50 +0000</pubDate>
		<dc:creator>mnolan</dc:creator>
				<category><![CDATA[apex 4.0]]></category>

		<guid isPermaLink="false">http://application-express-blog.e-dba.com/?p=1636</guid>
		<description><![CDATA[I have to admit that I enjoy blogging, more so because Google Analytics gives you a lot of feedback/insight as to who visits your blog and how much time they spend. The downside though is when you&#8217;ve made mistakes in posts you&#8217;ve published, it can feel quite embarrassing especially if they have had a high [...]]]></description>
			<content:encoded><![CDATA[<p>I have to admit that I enjoy blogging, more so because Google Analytics gives you a lot of feedback/insight as to who visits your blog and how much time they spend. The downside though is when you&#8217;ve made mistakes in posts you&#8217;ve published, it can feel quite embarrassing especially if they have had a high number of visits to them. When I look back over the last couple of years there have been quite a few&#8230;. I&#8217;m only going to name and shame the latest one which I discovered today.  It was the following <a href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwcGxpY2F0aW9uLWV4cHJlc3MtYmxvZy5lLWRiYS5jb20vP3A9MTI3OQ==">post</a> on adding CLOB support for PLJSON. There was a small procedure that I wrote as part of of the solution, well it turns out that it only supports VARCHAR2&#8230;.</p>
<p>e.g. original code:</p>
<pre class="brush: sql;">
  procedure add_to_clob2(buf_lob in out nocopy clob, buf_str in out nocopy varchar2, str CLOB) as
  begin
    dbms_lob.append(buf_lob, buf_str);
    dbms_lob.writeappend(buf_lob,length(str),str);
    buf_str := '';
  end add_to_clob2;
</pre>
<p>and here&#8217;s the correct version, which I just ammended today (including the original blog post)&#8230;.</p>
<pre class="brush: sql;">
  procedure add_to_clob2(buf_lob in out nocopy clob, buf_str in out nocopy varchar2, str CLOB) as
  begin
    dbms_lob.append(buf_lob, buf_str);
    dbms_lob.copy(buf_lob,str,dbms_lob.getlength(str),dbms_lob.getlength(buf_lob)+1,1);
    buf_str := '';
  end add_to_clob2;
</pre>
<p>The procedure dbms_lob.writeappend only supports appending VARCHARS, so it was implicitly converting the CLOB to VARCHAR2. Rookie mistake, I should check the documentation more closely. It did lift the restriction in PLJSON from 4K to 32K which is why it took me some time to stumble across the mistake&#8230;. </p>
<p>The other thing I came across today were a few quirks with APEX 4.0 which I&#8217;ve found slightly annoying (this probably affects me more so than others as I access my APEX dev environments over SSH in another country) is that when you have a little time lag and the page is rendering, you can start interacting on the page before it fully loads. This becomes a problem when you&#8217;re editing a page and using the hide show toolbar. You may start editing a field on the page only to find that when the page finally loads it jumps to your show only region. Sometimes I just forget that I have that hide/show toolbar enabled (usually when I jump between runtime and development modes), hence a little annoyance. </p>
<p><a href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwcGxpY2F0aW9uLWV4cHJlc3MtYmxvZy5lLWRiYS5jb20vP2F0dGFjaG1lbnRfaWQ9MTYzOQ==" rel=\"attachment wp-att-1639\"><img src="http://application-express-blog.e-dba.com/wp-content/uploads/2010/09/bits-and-pieces-toolbar-585x163.png" alt="" title="bits-and-pieces-toolbar" width="585" height="163" class="aligncenter size-medium wp-image-1639" /></a></p>
<p>Another little problem is that you can also get checksum errors if you submit a page too early before it &#8216;s fully loaded (happens when I&#8217;m clicking through the next and previous items e.g. editing columns in a report). A page loading mask would probably resolve these issues whilst all the items are rendered and the jQuery page load operations are performed, but then again that may annoy more people than it pleases.</p>
<p><a href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwcGxpY2F0aW9uLWV4cHJlc3MtYmxvZy5lLWRiYS5jb20vP2F0dGFjaG1lbnRfaWQ9MTY0MA==" rel=\"attachment wp-att-1640\"><img src="http://application-express-blog.e-dba.com/wp-content/uploads/2010/09/bits-and-pieces-error-585x185.png" alt="" title="bits-and-pieces-error" width="585" height="185" class="aligncenter size-medium wp-image-1640" /></a></p>
<p>There&#8217;s also some tighter controls in page templates.  There&#8217;s a new validation for the &#8220;Page Template Header&#8221; section which ensures you use the #HEAD# substitution tag and also include the &#8220;HEAD&#8221; tags. </p>
<p><a href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwcGxpY2F0aW9uLWV4cHJlc3MtYmxvZy5lLWRiYS5jb20vP2F0dGFjaG1lbnRfaWQ9MTY0MQ==" rel=\"attachment wp-att-1641\"><img src="http://application-express-blog.e-dba.com/wp-content/uploads/2010/09/bits-and-pieces-template-errors-585x117.png" alt="" title="bits-and-pieces-template-errors" width="585" height="117" class="aligncenter size-medium wp-image-1641" /></a></p>
<p>This was not the case in the previous version(s). We have created quite a few non-APEX pages in our applications which have various uses, including some that are simply content wrapped in a DIV rather than using the html, head, and body tags. Some of these are for AJAX operations, others are for static content etc. We&#8217;ve upgraded the applications with these templates to APEX 4.0 and they still work fine, the validation problem only arises when we need to edit them. The workaround is to hack the backend table and make the required changes using sqlplus/sqldeveloper. I can understand why the dev team included this but a nicer option might have been to prompt the developer to confirm that they know they have omitted the HEAD tag and allow them to continue to submit/save the changes.</p>
<p>Anyway just a few minor suggestions for the APEX team (if they ever stumble across this post). I&#8217;m bombarded day after day by our own customers with minor changes and design suggestions, so this is my only outlet to get some back&#8230; well at the expense of the APEX team <img src='http://application-express-blog.e-dba.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  </p>
 <img src="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?view=1&post_id=1636" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://application-express-blog.e-dba.com/?feed=rss2&#038;p=1636</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Dynamic Actions, Ext Style&#8230;..</title>
		<link>http://application-express-blog.e-dba.com/?p=1601</link>
		<comments>http://application-express-blog.e-dba.com/?p=1601#comments</comments>
		<pubDate>Fri, 03 Sep 2010 16:53:35 +0000</pubDate>
		<dc:creator>mnolan</dc:creator>
				<category><![CDATA[APEX Hacks]]></category>
		<category><![CDATA[ExtJS Integration]]></category>
		<category><![CDATA[apex ext dynamic actions]]></category>

		<guid isPermaLink="false">http://application-express-blog.e-dba.com/?p=1601</guid>
		<description><![CDATA[I have to say that I love APEX 4.0, plugins and dynamic actions make life a lot more simpler as we&#8217;re able to avoid getting bogged down in javascript or installing/configuring/modifying framework type code. That is unless you have decided to use Ext JS with APEX&#8230;. which is the position I find myself in. We [...]]]></description>
			<content:encoded><![CDATA[<p>I have to say that I love APEX 4.0, plugins and dynamic actions make life a lot more simpler as we&#8217;re able to avoid getting bogged down in javascript or installing/configuring/modifying framework type code. That is unless you have decided to use Ext JS with APEX&#8230;. which is the position I find myself in.</p>
<p><a href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwcGxpY2F0aW9uLWV4cHJlc3MtYmxvZy5lLWRiYS5jb20vP2F0dGFjaG1lbnRfaWQ9MTYwMg==" rel=\"attachment wp-att-1602\"><img src="http://application-express-blog.e-dba.com/wp-content/uploads/2010/09/dynamic-actions-ext-style-585x347.png" alt="" title="dynamic-actions-ext-style" width="585" height="347" class="aligncenter size-medium wp-image-1602" /></a></p>
<p>We transform our APEX forms into Ext equivalent ones, either on the fly after the page has loaded or pre-built in the page header and slotted into viewport code. Now for the transformed elements on the page I was hoping we could still use APEX&#8217;s builtin dynamic actions (not dynamic action plugins) and mixing in jQuery. In theory I thought this may work given that we reuse the same DOM ID for our transformed Ext input items. I was sadly mistaken. <img src='http://application-express-blog.e-dba.com/wp-includes/images/smilies/icon_sad.gif' alt=':(' class='wp-smiley' /> </p>
<p>Did I want to miss out on what the inbuilt dynamic actions provides me from a development productivity point of view? No way!! So what to do&#8230;&#8230;. Well there were two options that came to mind (taking into consideration that I don&#8217;t want to write a dynamic action plugin for a dynamic action that is available in the APEX IDE. I have the long term vision of transforming existing APEX pages into Ext equivalent ones with minimal changes)</p>
<ol>
<li>I could override the existing APEX dynamic actions javascript with an Ext equivalent version (this would take some time)</li>
<li>Or I could simply generate my dynamic action code on the fly by using the data dictionary.</li>
</ol>
<p>I chose the second option for now as it was the easier of the two, as I&#8217;m really time constrained at the moment, however the first option is the more strategic path which I intend to go down in the future. That aside, in the space of about 90 minutes I came up with the following solution for hide and show support on our Ext select lists/combos. I&#8217;ve only started with this one as it was my immediate requirement and it&#8217;s not exactly elegant, but it does work. </p>
<p><a href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwcGxpY2F0aW9uLWV4cHJlc3MtYmxvZy5lLWRiYS5jb20vP2F0dGFjaG1lbnRfaWQ9MTYwNw==" rel=\"attachment wp-att-1607\"><img src="http://application-express-blog.e-dba.com/wp-content/uploads/2010/09/ext-dynamic-action-demo.png" alt="" title="ext-dynamic-action-demo" width="405" height="475" class="aligncenter size-full wp-image-1607" /></a></p>
<p>Here&#8217;s the working prototype code which you put into a PLSQL process that runs before &#8220;Page Footer&#8221;&#8230;</p>
<pre class="brush: sql;">
CREATE OR REPLACE PROCEDURE ext_hide_show_dynamic_action
AS

  l_app_id         VARCHAR2(100) := v('APP_ID');
  l_page_id        VARCHAR2(100) := v('APP_PAGE_ID');
  l_function       VARCHAR2(10);
  l_opacity        VARCHAR2(10);
  l_duration       VARCHAR2(10);

  --
  -- Internal procedure to print out our hide/show function, rather than copy and pasting
  -- this into two almost identical calls in the main section
  --
  FUNCTION print_js_function
  ( p_in_affected_elements IN VARCHAR2
  , p_in_function          IN VARCHAR2
  , p_in_duration          IN VARCHAR2
  , p_in_opacity           IN VARCHAR2
  ) RETURN VARCHAR2
  AS

  BEGIN
    --
    -- We need to check for any icons e.g. date picker, spinner etc.
    -- We need to also hide the label and any resizeable text elements
    --
    RETURN 'Ext.select(&quot;label[for='||p_in_affected_elements||'],fieldset[id*='||p_in_affected_elements||'],'||
                          '[id='||p_in_affected_elements||'],[id='||p_in_affected_elements||'] + img[class*=x-form-trigger]&quot;).'||p_in_function||
              '({'||
                'duration: '||p_in_duration||','||
                'endOpacity: '||p_in_opacity||','||
                'remove: false,'||
                'useDisplay: true'||
              '});';

  END print_js_function;    

BEGIN
  sys.htp.p(wwv_flow_utilities.open_javascript);
  sys.htp.p('Ext.onReady(function() {');
  --
  -- Lets loop through each of our dynamic actions (we are looking for hide operations)
  --
  FOR c IN
  ( SELECT da.when_element                 when_element
    ,      CASE da.when_condition
             WHEN 'equal to' THEN '=='
             WHEN 'is null'  THEN
               --
               -- We need to check for LOV null value settings
               --
               CASE pi.display_as
                 WHEN 'Select List' THEN '== &quot;'||pi.lov_null_value||'&quot;'
                 ELSE '== &quot;undefined&quot;'
               END
             ELSE NULL
           END                             when_condition
    ,      da.when_expression              when_expression
    ,      lower(act.action_name)          action_name
    ,      act.affected_elements           affected_elements
    ,      act.affected_elements_type      affected_elements_type
           --
           -- We have to work out the hide/show action to perform as we may
           -- want to show an item based on a value, or do the opposite and hide
           -- the item. The event result dictates what to do,
           -- i.e. the one with &quot;True&quot; set
           --
    ,      CASE act.dynamic_action_event_result
             WHEN 'False' THEN
               CASE action_code
                 WHEN 'NATIVE_HIDE' THEN 'NATIVE_SHOW'
                 ELSE 'NATIVE_HIDE'
               END
             ELSE act.action_code
           END                             action_code
           --
           -- Ext has a different event for a combo, select is for list item
           -- selection, whereas change is the field changes onblur
           --
    ,      CASE da.when_event_internal_name
             WHEN 'change' THEN
               CASE pi.display_as
                 WHEN 'Select List' THEN 'select'
                 ELSE 'change'
               END
             ElSE lower(act.action_name)
           END                          when_event_internal_name
    ,      rownum                       rn
    FROM   apex_application_page_da      da
    ,      apex_application_page_da_acts act
    ,      apex_application_page_items   pi
    WHERE  da.dynamic_action_id     = act.dynamic_action_id
    AND    da.application_id        = l_app_id
    AND    da.page_id               = l_page_id
    AND    pi.item_name             = da.when_element
    AND    pi.application_id        = da.application_id
    AND    pi.page_id               = da.page_id
    AND    act.execute_on_page_init = 'Yes'
    AND    lower(act.action_name)   = 'hide'
    /*
    --
    -- Lets make sure that the dynamic action condition and authorisations are met
    --
    AND    extjs_utils.auth_condition_check
           ( da.condition_type
           , da.condition_expression1
           , da.condition_expression2
           , da.authorization_scheme_id
           ) = 0

    */
  ) LOOP

    --
    -- We are going to build a standalone function so we can also call it on page load
    --
    sys.htp.p('Ext.app.dynAct'||c.when_element||c.rn|| ' = function() {'||
                 'if (Ext.getCmp(&quot;'||c.when_element||'&quot;).getValue()'||c.when_condition||c.when_expression||') {'||
                     --
                     -- Our dynamic action tells us whetehr to show or hide
                     -- if our condition is met
                     --
                     print_js_function
                     ( p_in_affected_elements =&gt; c.affected_elements
                     , p_in_function          =&gt; CASE c.action_code WHEN 'NATIVE_HIDE' THEN 'fadeOut' ELSE 'fadeIn' END
                     , p_in_duration          =&gt; '.75'
                     , p_in_opacity           =&gt; CASE c.action_code WHEN 'NATIVE_HIDE' THEN '0' ELSE '1' END
                     ) ||
                  '} else {'||
                     --
                     -- We do the opposite action of the above Hide or Show
                     --
                     print_js_function
                     ( p_in_affected_elements =&gt; c.affected_elements
                     , p_in_function          =&gt; CASE c.action_code WHEN 'NATIVE_HIDE' THEN 'fadeIn' ELSE 'fadeOut' END
                     , p_in_duration          =&gt; '.75'
                     , p_in_opacity           =&gt; CASE c.action_code WHEN 'NATIVE_HIDE' THEN '1' ELSE '0' END
                     ) ||

                  '}'||
              '}'
    );
    --
    -- Lets add our event handler
    --
    sys.htp.p('Ext.getCmp(&quot;'||c.when_element||'&quot;).on(&quot;'||c.when_event_internal_name||'&quot;, Ext.app.dynAct'||c.when_element||c.rn||');');
    --
    -- Lets run our function on Page Load, to worrk out what the display should be
    --
    sys.htp.p('Ext.app.dynAct'||c.when_element||c.rn||'();');

  END LOOP;
  sys.htp.p('});');
  sys.htp.p(wwv_flow_utilities.close_javascript);
--
-- Exception Handling Routine
--
EXCEPTION
  WHEN OTHERS
  THEN
    -- Put your excpetion handling code here
    RAISE;
END ext_hide_show_dynamic_action;
/
</pre>
<p>Backing out the Ext JS functionality is easy, we simply change our region template (which is responsible for the Ext JS page item transforms) back to a standard apex template and disbale the above process and we still have the exact same functionality of a hide show item, just using the standard APEX features. </p>
<p>If you&#8217;re not happy with adding custom PLSQL to either page zero or your pages then maybe creating a new dynamic action plugin is for you. It&#8217;s great that we have several choices, it&#8217;s just up to you <strong>&#8220;the developer</strong>&#8221; to decide which method suits your style/direction.</p>
<p>The one thing I&#8217;ve failed to mention about building JSON meta data objects and printing out dynamic javascript, is that we use additional resources by querying the data dictionary all the time (over and above building traditional APEX apps). This of course has performance impact when you start to scale your application(s). The simple solution (if you&#8217;re headed down this path) is to cache the outputted objects into custom tables and store these either, session, user, or application based depending upon your application requirement with expiration/invalidation policies. It will be then as simple as doing a check for a cached item and reusing it if it exists rather than querying the data dictionary for every page load.</p>
<p>Finally if you&#8217;re looking at developing your first region plugin, then download the following plugin written by <a href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL3d3dy5hcGV4LXBsdWdpbi5jb20vb3JhY2xlLWFwZXgtcGx1Z2lucy9yZWdpb24tcGx1Z2luL3N1bW5ldmEtZHJhZy1hbmQtZHJvcC1yZXBvcnQuaHRtbA==" target=\"_blank\">Doug Gualt from Sumneva</a> and install it. As there&#8217;s not much documentation or anything really useful that out there that walks you through how to do it, so it&#8217;s easiest just to edit Doug&#8217;s plugin to see how it hangs together. I coded my first region plugin last night (sad I know) and used this as my way to learn. I had my first region plugin (based on the <a href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL3d3dy5zZW5jaGEuY29tL3Byb2R1Y3RzL2NvcmUvbWFudWFsL2luZGV4LnBocA==" target=\"_blank\">Ext.manual</a> documentation)  completed in under 2 hours, it saved me hours! Thanks Doug!</p>
<p><strong>P.S.</strong> this is a great way to <a href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL3d3dy5uZXR3b3JrLXNjaWVuY2UuZGUvYXNjaWkv" target=\"_blank\">generate a logo in ascii text</a>, which sits nicely in your plugin header code.</p>
 <img src="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?view=1&post_id=1601" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://application-express-blog.e-dba.com/?feed=rss2&#038;p=1601</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Forgotten Password?</title>
		<link>http://application-express-blog.e-dba.com/?p=1545</link>
		<comments>http://application-express-blog.e-dba.com/?p=1545#comments</comments>
		<pubDate>Mon, 30 Aug 2010 09:54:55 +0000</pubDate>
		<dc:creator>mnolan</dc:creator>
				<category><![CDATA[ExtJS Integration]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[apex extjs]]></category>
		<category><![CDATA[forgot password design]]></category>

		<guid isPermaLink="false">http://application-express-blog.e-dba.com/?p=1545</guid>
		<description><![CDATA[Recently I updated our existing &#8220;Forgotten Password&#8221; design within our APEX/Ext applications. I decided to simplify things and opt for a password reset form on the login page rather than having the user navigate to another page. The old version was similar in design to the way APEX IDE behaves if your password has expired. [...]]]></description>
			<content:encoded><![CDATA[<p>Recently I updated our existing <strong>&#8220;Forgotten Password&#8221;</strong> design within our APEX/Ext applications. I decided to simplify things and opt for a password reset form on the login page rather than having the user navigate to another page. The old version was similar in design to the way APEX IDE behaves if your password has expired. The new version reuses your existing login page (generally page 101) and pops up an Ext message dialog to enter your email address:</p>
<p><a rel=\"attachment wp-att-1558\" href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwcGxpY2F0aW9uLWV4cHJlc3MtYmxvZy5lLWRiYS5jb20vP2F0dGFjaG1lbnRfaWQ9MTU1OA=="><img class="aligncenter size-full wp-image-1558" title="login" src="http://application-express-blog.e-dba.com/wp-content/uploads/2010/08/login.png" alt="" width="390" height="225" /></a></p>
<p><a rel=\"attachment wp-att-1557\" href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwcGxpY2F0aW9uLWV4cHJlc3MtYmxvZy5lLWRiYS5jb20vP2F0dGFjaG1lbnRfaWQ9MTU1Nw=="><img class="aligncenter size-full wp-image-1557" title="forgotten-password-prompt" src="http://application-express-blog.e-dba.com/wp-content/uploads/2010/08/forgotten-password-prompt.png" alt="" width="389" height="223" /></a></p>
<p>If successful, an APEX process sends an email is sent to the address with a password reset link.</p>
<p><a rel=\"attachment wp-att-1548\" href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwcGxpY2F0aW9uLWV4cHJlc3MtYmxvZy5lLWRiYS5jb20vP2F0dGFjaG1lbnRfaWQ9MTU0OA=="><img class="aligncenter size-full wp-image-1548" title="forgotten-password-alert" src="http://application-express-blog.e-dba.com/wp-content/uploads/2010/08/forgotten-password-alert.png" alt="" width="533" height="229" /></a></p>
<p>Otherwise a dialog displays and advises that the email address is not stored within the database.</p>
<p><a rel=\"attachment wp-att-1549\" href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwcGxpY2F0aW9uLWV4cHJlc3MtYmxvZy5lLWRiYS5jb20vP2F0dGFjaG1lbnRfaWQ9MTU0OQ=="><img class="aligncenter size-full wp-image-1549" title="forgotten-password-alert-failed" src="http://application-express-blog.e-dba.com/wp-content/uploads/2010/08/forgotten-password-alert-failed.png" alt="" width="457" height="229" /></a></p>
<p>The email reset link takes you back to the login page and sets an APPLCIATION item via the URL with a password reset token. The token is generated by SYS_GUID(), so it&#8217;s just one big random number. If this application  item has been set I have a conditional PLSQL process which will popup an Ext Window with the form for you to submit your new password. Here&#8217;s an example of the URL:</p>
<p>http://portal.e-dba.com/apex/f?p=EDBA_PORTAL:LOGIN:2434321017503577::::PWD_RESET_ID:190113209148692319491537939948722019703</p>
<p><a rel=\"attachment wp-att-1550\" href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwcGxpY2F0aW9uLWV4cHJlc3MtYmxvZy5lLWRiYS5jb20vP2F0dGFjaG1lbnRfaWQ9MTU1MA=="><img class="aligncenter size-full wp-image-1550" title="forgotten-password-form" src="http://application-express-blog.e-dba.com/wp-content/uploads/2010/08/forgotten-password-form.png" alt="" width="390" height="291" /></a></p>
<p>To enhance security I&#8217;ve coded in a couple of checks.</p>
<ol>
<li>The username must be supplied with the password and a check is performed to see if the username matches the user record attached to the email address that was supplied.<br />
<a rel=\"attachment wp-att-1559\" href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwcGxpY2F0aW9uLWV4cHJlc3MtYmxvZy5lLWRiYS5jb20vP2F0dGFjaG1lbnRfaWQ9MTU1OQ=="><img class="aligncenter size-full wp-image-1559" title="forgotten-password-form-failed1" src="http://application-express-blog.e-dba.com/wp-content/uploads/2010/08/forgotten-password-form-failed11.png" alt="" width="390" height="318" /></a></li>
<li>The token has an expiry time on it and must be used within X many hours, which is configurable by an application substitution string. Otherwise it defaults to 24<a rel=\"attachment wp-att-1556\" href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwcGxpY2F0aW9uLWV4cHJlc3MtYmxvZy5lLWRiYS5jb20vP2F0dGFjaG1lbnRfaWQ9MTU1Ng=="><img class="aligncenter size-full wp-image-1556" title="forgotten-password-form-failed6" src="http://application-express-blog.e-dba.com/wp-content/uploads/2010/08/forgotten-password-form-failed6.png" alt="" width="384" height="365" /></a></li>
<li>The token can only be used once, i.e. once a successful reset operation has been performed the token becomes invalid.<br />
<a rel=\"attachment wp-att-1555\" href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwcGxpY2F0aW9uLWV4cHJlc3MtYmxvZy5lLWRiYS5jb20vP2F0dGFjaG1lbnRfaWQ9MTU1NQ=="><img class="aligncenter size-full wp-image-1555" title="forgotten-password-form-failed5" src="http://application-express-blog.e-dba.com/wp-content/uploads/2010/08/forgotten-password-form-failed5.png" alt="" width="385" height="350" /></a></li>
<li>The IP address captured from the person submitting the password reset request must match the IP address from the person entering the password details into form. A number of IP ranges can be excluded, or only allow IP ranges from within the UK, it depends upon the user base and where the employees are located, working from home etc.<br />
<a rel=\"attachment wp-att-1553\" href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwcGxpY2F0aW9uLWV4cHJlc3MtYmxvZy5lLWRiYS5jb20vP2F0dGFjaG1lbnRfaWQ9MTU1Mw=="><img class="aligncenter size-full wp-image-1553" title="forgotten-password-form-failed3" src="http://application-express-blog.e-dba.com/wp-content/uploads/2010/08/forgotten-password-form-failed3.png" alt="" width="386" height="364" /></a></li>
<li>Only X many attempts can be used to try and reset the password, i.e. if you have a valid reset token but don&#8217;t know which username you use you can only try to reset the password X many times before the token becomes invalidated. This is also configurable with an application substitution string, otherwise it defaults to 3 if its not set.<br />
<a rel=\"attachment wp-att-1554\" href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwcGxpY2F0aW9uLWV4cHJlc3MtYmxvZy5lLWRiYS5jb20vP2F0dGFjaG1lbnRfaWQ9MTU1NA=="><img class="aligncenter size-full wp-image-1554" title="forgotten-password-form-failed4" src="http://application-express-blog.e-dba.com/wp-content/uploads/2010/08/forgotten-password-form-failed4.png" alt="" width="385" height="365" /></a></li>
<li>On a successful attempt the following dialog is displayed<br />
<a href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwcGxpY2F0aW9uLWV4cHJlc3MtYmxvZy5lLWRiYS5jb20vP2F0dGFjaG1lbnRfaWQ9MTU4OA==" rel=\"attachment wp-att-1588\"><img src="http://application-express-blog.e-dba.com/wp-content/uploads/2010/08/forgotten-password-form-success-alert.png" alt="" title="forgotten-password-form-success-alert" width="389" height="224" class="aligncenter size-full wp-image-1588" /></a></li>
</ol>
<p>What I like about Ext forms is that for the failure message notification all I need to do is return a JSON object from my &#8220;On Demand&#8221; process with the ID of the form item and message in order to display the validation failure. Very simple e.g.</p>
<pre class="brush: jscript;">
{
    success: false,
    errors: [{
        id: &quot;username&quot;,
        &quot;msg&quot;: &quot;Server Error: ORA-20003: The supplied reset token has expired, you have 24 hours in which to use it. Please submit a new password retrieval request.&quot;
    }]
}
</pre>
<p>Here&#8217;s most of the supporting code from our initial prototype to give you an idea of how it hangs together (<strong>Note:</strong> it&#8217;s not fully complete, it&#8217;s missing an email routine as ours is quite comprehensive and a little overkill for this post. It also uses the <a href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL3R5bGVybXV0aC53b3JkcHJlc3MuY29tLzIwMDkvMTEvMDMvbG9nZ2VyLWEtcGxzcWwtbG9nZ2luZy1hbmQtZGVidWdnaW5nLXV0aWxpdHkv" target=\"_blank\">LOGGER</a> package from Tyler Muth for exception handling, so simply remove the logger.log_error references if you don&#8217;t wish to use LOGGER):</p>
<p><span style="text-decoration: underline;"><strong>Forgotten Password Anchor link</strong></span></p>
<p><strong>Note:</strong> it&#8217;s a Display Only Item, Settings: &#8220;Output of PLSQL Code&#8221;</p>
<pre class="brush: sql;">
htp.p('&lt;a href=&quot;#&quot; class=&quot;ext-label&quot; onclick=&quot;Ext.app.promptDialog({title: ''Forgotten Password'', msg:''Please enter your email address to send the password reset link to:'', width: 300 }, Ext.app.passwordReset);&quot;&gt;Forgotten Password?&lt;/a&gt;');
</pre>
<p><span style="text-decoration: underline;"><strong>Email Prompt Dialog</strong></span></p>
<pre class="brush: jscript;">
Ext.app.promptDialog = function (config, fn) {
   Ext.MessageBox.show({
      title    : config.title,
      msg      : config.msg,
      width    : config.width,
      buttons  : Ext.MessageBox.OKCANCEL,
      multiline: true,
      fn       : fn
   });
}
</pre>
<p><strong><span style="text-decoration: underline;">Conditional HTML region on page 101, only displays when our APPLICATION ITEM is set with a value.</span></strong></p>
<p><strong>Note:</strong> it&#8217;s value is cleared on page submission.</p>
<pre class="brush: xml;">
&lt;style&gt;
.app-msg {
   text-align:center;
   font-size: 1.6em;
   height: 90px;
   line-height: 90px;
   vertical-align:middle;
}
.app-img {
   float:left;
   margin-right:10px;
}

&lt;/style&gt;
&lt;script&gt;
Ext.onReady(function () {
   pwdResetWin = new Ext.Window({
      layout: 'form',
      width: 375,
      modal: true,
      autoHeight: true,
      closeAction: 'close',
      items: [passwordResetForm]
   });
   pwdResetWin.show();
});
&lt;/script&gt;
</pre>
<p><strong><span style="text-decoration: underline;">Password Reset Form</span></strong></p>
<p>(I pretty much just used an exact copy from the following <a href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL3d3dy5wYWNrdHB1Yi5jb20vYXJ0aWNsZS9sb2FkLXZhbGlkYXRlLXN1Ym1pdC1mb3Jtcy1leHQtanMtMy4wLXBhcnQy" target=\"_blank\">URL</a>)</p>
<pre class="brush: jscript;">
var passwordResetForm = {
   xtype: 'form',
   id: 'password-reset-form',
   labelWidth: 150,
   bodyStyle: 'padding:15px;background:transparent',
   border: false,
   url: widgetURL,
   items: [{
      xtype: 'box',
      autoEl: {
         tag: 'div',
         html: '&lt;div class=&quot;app-msg&quot;&gt;&lt;img src=&quot;'+gcExtBase+'/../../img/icons/icon_password_reset.png&quot; class=&quot;app-img&quot; /&gt;Password Reset Form&lt;/div&gt;'
      }
   },
   {
      xtype: 'textfield',
      id: 'username',
      fieldLabel: 'Username',
      name: 'x01',
      allowBlank: false,
      minLength: 3,
      maxLength: 64,
	  msgTarget: 'under',
      anchor: '90%'
   },
   {
      xtype: 'textfield',
      id: 'pwd',
      name: 'x02',
      fieldLabel: 'New Password',
      inputType: 'password',
      allowBlank: false,
      minLength: 6,
      maxLength: 32,
	  msgTarget: 'under',
      anchor: '90%',
      minLengthText: 'Password must be at least 6 characters long.'
   },
   {
      xtype: 'textfield',
      id: 'pwd-confirm',
      name: 'x03',
      fieldLabel: 'Confirm New Password',
      inputType: 'password',
      allowBlank: false,
      minLength: 6,
      maxLength: 32,
      anchor: '90%',
      minLengthText: 'Password must be at least 6 characters long.',
	  msgTarget: 'under',
      vtype: 'password2',
      initialPassField: 'pwd'
   },{
      xtype: 'hidden',
      name: 'p_widget_name',
      value: 'Ext.app.passwordResetSubmit'
   }],
   buttons: [{
      text: 'Submit',
      handler: function () {
         Ext.getCmp('password-reset-form').getForm().submit({
            success: function () {
               pwdResetWin.hide();
               alert('Your password has been successfully reset!');
            }
         });
      }
   },
   {
      text: 'Cancel',
      handler: function () {
         pwdResetWin.hide();
      }
   }]
}
</pre>
<p><strong><span style="text-decoration: underline;">AJAX Javascript</span></strong></p>
<pre class="brush: jscript;">
var u = (window.location.href.indexOf(&quot;?&quot;) &gt; 0) ? window.location.href.substring(0, window.location.href.indexOf(&quot;?&quot;)) : window.location.href;
var baseURL = u.substring(0, u.lastIndexOf(&quot;/&quot;));
var widgetURL = baseURL + '/wwv_flow.show?p_flow_id=' + Ext.getDom('pFlowId').value + '&amp;p_flow_step_id=' + Ext.getDom('pFlowStepId').value + '&amp;p_instance=' + Ext.getDom('pInstance').value + '&amp;p_request=APPLICATION_PROCESS=Ext.app.widget';
Ext.app.passwordReset = function (buttonId, text, opt) {

   (buttonId == 'ok') ? Ext.Ajax.request({
      url: widgetURL,
      disableCaching: false,
      method: &quot;GET&quot;,
      params: {
         p_widget_name: &quot;Ext.app.passwordReset&quot;,
         x01: text
      },
      success: function (response, opts) {
         var obj = Ext.decode(response.responseText);
         (obj.success) ? alert(&quot;A password reset link has been emailed to the address provided: &quot; + obj.email) : alert(&quot;The supplied email address was not found in the system: &quot; + obj.email);
      },
      failure: function (response, opts) {
         (console) ? console.log('server-side failure with status code ' + response.status) : alert('server-side failure with status code ' + response.status);
      }
   }) : null;
}
</pre>
<p><strong><span style="text-decoration: underline;">Application Level Process named &#8220;Ext.app.widget&#8221;</span></strong></p>
<pre class="brush: sql;">
portal_widgets.widget
( p_widget_name         =&gt; wwv_flow.g_widget_name
, p_widget_mod          =&gt; wwv_flow.g_widget_mod
, p_widget_action       =&gt; wwv_flow.g_widget_action
, p_widget_action_mod   =&gt; wwv_flow.g_widget_action_mod
, p_widget_num_return   =&gt; NULL
, x01                   =&gt; wwv_flow.g_x01
, x02                   =&gt; wwv_flow.g_x02
, x03                   =&gt; wwv_flow.g_x03
, x04                   =&gt; wwv_flow.g_x04
, x05                   =&gt; wwv_flow.g_x05
, x06                   =&gt; wwv_flow.g_x06
, x07                   =&gt; wwv_flow.g_x07
, x08                   =&gt; wwv_flow.g_x08
, x09                   =&gt; wwv_flow.g_x09
, x10                   =&gt; wwv_flow.g_x10
);
EXCEPTION
  WHEN OTHERS
  THEN
    logger.log_error('APPLICATION_PROCESS=Ext.app.widget',SQLERRM);
    RAISE;
</pre>
<p><span style="text-decoration: underline;"><strong>Password Reset PLSQL</strong></span></p>
<pre class="brush: sql;">
  PROCEDURE passwordResetSubmit
  ( p_widget_name         IN VARCHAR2 DEFAULT NULL
  , p_widget_mod          IN VARCHAR2 DEFAULT NULL
  , p_widget_action       IN VARCHAR2 DEFAULT NULL
  , p_widget_action_mod   IN VARCHAR2 DEFAULT NULL
  , p_widget_num_return   IN VARCHAR2 DEFAULT NULL
  , x01                   IN VARCHAR2 DEFAULT NULL
  , x02                   IN VARCHAR2 DEFAULT NULL
  , x03                   IN VARCHAR2 DEFAULT NULL
  , x04                   IN VARCHAR2 DEFAULT NULL
  , x05                   IN VARCHAR2 DEFAULT NULL
  , x06                   IN VARCHAR2 DEFAULT NULL
  , x07                   IN VARCHAR2 DEFAULT NULL
  , x08                   IN VARCHAR2 DEFAULT NULL
  , x09                   IN VARCHAR2 DEFAULT NULL
  , x10                   IN VARCHAR2 DEFAULT NULL
  ) AS
    l_reset_id           portal_lost_passwords.reset_id%TYPE;
    l_user_id            portal_lost_passwords.user_id%TYPE;
    l_request_ip_address portal_lost_passwords.request_ip_address%TYPE;
    l_browser_agent      portal_lost_passwords.request_browser_agent%TYPE;
    l_username           portal_users.username%TYPE := lower(trim(regexp_replace(x01,CHR(10)||'|'||CHR(13),''))); --lower(extjs_utils.striphtml(x01));
    l_new_password       portal_users.password%TYPE := x02;
    l_password_rec       portal_lost_passwords_vw%ROWTYPE;
    l_error_msg          VARCHAR(32767);

  BEGIN

    l_request_ip_address := owa_util.get_cgi_env('REMOTE_ADDR');
    l_browser_agent      := owa_util.get_cgi_env('HTTP_USER_AGENT');
    l_reset_id           := v('PWD_RESET_ID');
    BEGIN

      --
      -- Lets check our reset id is valid
      --
      SELECT *
      INTO   l_password_rec
      FROM   portal_lost_passwords_vw
      WHERE  reset_id = l_reset_id;

      --
      -- We have some extra security checks
      -- 1. We make sure you use the same IP address between the request and reset
      -- 2. You have 24 hours which you can use the token for
      IF l_password_rec.request_ip_address &lt;&gt; l_request_ip_address THEN
        raise_application_error(-20001,'You are trying to reset the password from a different IP address than the one used when completing the request form!!');
      ELSIF l_password_rec.password_reset = 'Y' THEN
        raise_application_error(-20002,'The supplied reset token has been previously used. Please submit a new password retrieval request.');
      ELSIF l_password_rec.created_on &lt; sysdate-(to_number(nvl(v('PASSWORD_TOKEN_LIFETIME'),portal_constants.gc_default_pwd_token_lifetime))/24) THEN
        raise_application_error(-20003,'The supplied reset token has expired, you have 24 hours in which to use it. Please submit a new password retrieval request.');
      ELSIF l_password_rec.failure_count &gt;= to_number(nvl(v('PASSWORD_TOKEN_MAX_FAILURES'),portal_constants.gc_default_pwd_token_failures)) THEN
        raise_application_error(-20004,'You have exceeded the maximum allowed number of failures for this reset token. Please submit a new password retrieval request.');
      END IF;

      l_reset_id := l_password_rec.reset_id;

    EXCEPTION
      WHEN NO_DATA_FOUND THEN
        raise_application_error(-20005,'The supplied reset token is not valid!');
    END;

    SELECT user_id
    INTO   l_user_id
    FROM   portal_users
    WHERE  username = l_username;

    UPDATE portal_lost_passwords
    SET reset_ip_address = l_request_ip_address
    ,   reset_browser_agent = l_browser_agent
    ,   password_reset = 'Y'
    ,   password_reset_on = sysdate
    WHERE reset_id = l_reset_id;

    --
    -- table has a trigger to encrypt the password
    --
    portal_security.change_password
    ( p_user_id   =&gt; l_user_id
    , p_username  =&gt; l_username
    , p_password  =&gt; l_new_password
    );

    htp.p('{ &quot;success&quot;: true, &quot;message&quot;: &quot;The password has been successfully reset!&quot;}');

  EXCEPTION
    WHEN NO_DATA_FOUND THEN
      INSERT INTO portal_lost_password_failures
      ( ip_address
      , browser_agent
      , username
      , error_message
      , reset_id
      ) VALUES
      ( l_request_ip_address
      , l_browser_agent
      , l_username
      , 'The username provided was not recognized'
      , l_reset_id
      );
      htp.p('{ &quot;success&quot;: false, errors: [{id:&quot;username&quot;, &quot;msg&quot;: &quot;The username provided was not recognized&quot;}] }');
    WHEN OTHERS
    THEN
      l_error_msg := SQLERRM;
      INSERT INTO portal_lost_password_failures
      ( ip_address
      , browser_agent
      , username
      , error_message
      , reset_id
      ) VALUES
      ( l_request_ip_address
      , l_browser_agent
      , l_username
      , l_error_msg
      , l_reset_id
      );

      logger.log_error(SQLERRM, 'portal_widgets.passwordResetSubmit');

      htp.p('{ &quot;success&quot;: false, errors: [{id:&quot;username&quot;, &quot;msg&quot;: &quot;Server Error: '||replace(regexp_replace(SQLERRM,CHR(10)||'|'||CHR(13),''),'&quot;','\&quot;')||'&quot;}] }');
  END passwordResetSubmit;

  PROCEDURE passwordReset
  ( p_widget_name         IN VARCHAR2 DEFAULT NULL
  , p_widget_mod          IN VARCHAR2 DEFAULT NULL
  , p_widget_action       IN VARCHAR2 DEFAULT NULL
  , p_widget_action_mod   IN VARCHAR2 DEFAULT NULL
  , p_widget_num_return   IN VARCHAR2 DEFAULT NULL
  , x01                   IN VARCHAR2 DEFAULT NULL
  , x02                   IN VARCHAR2 DEFAULT NULL
  , x03                   IN VARCHAR2 DEFAULT NULL
  , x04                   IN VARCHAR2 DEFAULT NULL
  , x05                   IN VARCHAR2 DEFAULT NULL
  , x06                   IN VARCHAR2 DEFAULT NULL
  , x07                   IN VARCHAR2 DEFAULT NULL
  , x08                   IN VARCHAR2 DEFAULT NULL
  , x09                   IN VARCHAR2 DEFAULT NULL
  , x10                   IN VARCHAR2 DEFAULT NULL
  ) AS
    l_reset_id           portal_lost_passwords.reset_id%TYPE;
    l_user_id            portal_lost_passwords.user_id%TYPE;
    l_request_ip_address portal_lost_passwords.request_ip_address%TYPE;
    l_browser_agent      portal_lost_passwords.request_browser_agent%TYPE;
    l_email_address      portal_lost_passwords.email_address%TYPE := lower(trim(regexp_replace(x01,CHR(10)||'|'||CHR(13),'')));
  BEGIN

    l_request_ip_address := owa_util.get_cgi_env('REMOTE_ADDR');
    l_browser_agent      := owa_util.get_cgi_env('HTTP_USER_AGENT');

    SELECT user_id
    INTO   l_user_id
    FROM   portal_users_vw
    WHERE  work_email = l_email_address
    AND    rownum = 1;

    INSERT INTO portal_lost_passwords
    ( user_id
    , request_ip_address
    , request_browser_agent
    , email_address
    ) VALUES
    ( l_user_id
    , l_request_ip_address
    , l_browser_agent
    , l_email_address
    ) RETURNING reset_id INTO l_reset_id;

    portal_notify.send_forgot_password_email
    ( p_user_id  =&gt; l_user_id
    , p_reset_id =&gt; l_reset_id
    );

    htp.p('{ &quot;success&quot;: true, &quot;message&quot;: &quot;Password retrieval email sent!&quot;, &quot;email&quot;: &quot;'||l_email_address||'&quot;}');

  EXCEPTION
    WHEN NO_DATA_FOUND THEN
      INSERT INTO portal_lost_password_failures
      ( ip_address
      , browser_agent
      , email_address
      , error_message
      ) VALUES
      ( l_request_ip_address
      , l_browser_agent
      , l_email_address
      , 'The email address provided was not recognized'
      );
      htp.p('{ &quot;success&quot;: false, &quot;message&quot;: &quot;The email address provided was not recognized&quot;, &quot;email&quot;: &quot;'||l_email_address||'&quot;}');
    WHEN OTHERS
    THEN
      logger.log_error(SQLERRM, 'portal_widgets.passwordReset');
      htp.p('{ &quot;success&quot;: false, &quot;message&quot;: &quot;Error: '||replace(regexp_replace(SQLERRM,CHR(10)||'|'||CHR(13),''),'&quot;','\&quot;')||'&quot; }');
  END passwordReset;

  PROCEDURE widget
  ( p_widget_name         IN VARCHAR2 DEFAULT NULL
  , p_widget_mod          IN VARCHAR2 DEFAULT NULL
  , p_widget_action       IN VARCHAR2 DEFAULT NULL
  , p_widget_action_mod   IN VARCHAR2 DEFAULT NULL
  , p_widget_num_return   IN VARCHAR2 DEFAULT NULL
  , x01                   IN VARCHAR2 DEFAULT NULL
  , x02                   IN VARCHAR2 DEFAULT NULL
  , x03                   IN VARCHAR2 DEFAULT NULL
  , x04                   IN VARCHAR2 DEFAULT NULL
  , x05                   IN VARCHAR2 DEFAULT NULL
  , x06                   IN VARCHAR2 DEFAULT NULL
  , x07                   IN VARCHAR2 DEFAULT NULL
  , x08                   IN VARCHAR2 DEFAULT NULL
  , x09                   IN VARCHAR2 DEFAULT NULL
  , x10                   IN VARCHAR2 DEFAULT NULL
  ) AS

    l_exists BOOLEAN DEFAULT FALSE;
    l_proc VARCHAR2(200);

  BEGIN
    CASE p_widget_name
      WHEN 'Ext.app.passwordReset' THEN
        portal_widgets.passwordReset
        ( p_widget_name         =&gt; p_widget_name
        , p_widget_mod          =&gt; p_widget_mod
        , p_widget_action       =&gt; p_widget_action
        , p_widget_action_mod   =&gt; p_widget_action_mod
        , p_widget_num_return   =&gt; p_widget_num_return
        , x01                   =&gt; x01
        , x02                   =&gt; x02
        , x03                   =&gt; x03
        , x04                   =&gt; x04
        , x05                   =&gt; x05
        , x06                   =&gt; x06
        , x07                   =&gt; x07
        , x08                   =&gt; x08
        , x09                   =&gt; x09
        , x10                   =&gt; x10
        );
      WHEN 'Ext.app.passwordResetSubmit' THEN
        portal_widgets.passwordResetSubmit
        ( p_widget_name         =&gt; p_widget_name
        , p_widget_mod          =&gt; p_widget_mod
        , p_widget_action       =&gt; p_widget_action
        , p_widget_action_mod   =&gt; p_widget_action_mod
        , p_widget_num_return   =&gt; p_widget_num_return
        , x01                   =&gt; x01
        , x02                   =&gt; x02
        , x03                   =&gt; x03
        , x04                   =&gt; x04
        , x05                   =&gt; x05
        , x06                   =&gt; x06
        , x07                   =&gt; x07
        , x08                   =&gt; x08
        , x09                   =&gt; x09
        , x10                   =&gt; x10
        );

     ELSE
        htp.p('{ &quot;success&quot;: false, &quot;message&quot;: &quot;Widget '||p_widget_name||' not defined in portal_widgets!&quot;}');
    END CASE;

  EXCEPTION
    WHEN OTHERS
    THEN
      logger.log_error(SQLERRM, 'portal_widgets.widget');
      RAISE;

  END widget;
</pre>
<p><strong><span style="text-decoration: underline;">Finally the supporting DDL</span></strong></p>
<pre class="brush: sql;">
DROP TABLE &quot;PORTAL_LOST_PASSWORDS&quot;
/
CREATE TABLE &quot;PORTAL_LOST_PASSWORDS&quot;
(&quot;RESET_ID&quot; NUMBER NOT NULL ENABLE,
&quot;USER_ID&quot; VARCHAR2(100) NOT NULL ENABLE,
&quot;REQUEST_IP_ADDRESS&quot; VARCHAR2(100 BYTE),
&quot;REQUEST_BROWSER_AGENT&quot; VARCHAR2(4000 BYTE),
&quot;EMAIL_ADDRESS&quot; VARCHAR2(500 BYTE) NOT NULL ENABLE,
&quot;RESET_IP_ADDRESS&quot; VARCHAR2(100 BYTE),
&quot;RESET_BROWSER_AGENT&quot; VARCHAR2(4000 BYTE),
&quot;PASSWORD_RESET&quot; VARCHAR2(1 BYTE) DEFAULT 'N' NOT NULL ENABLE,
&quot;PASSWORD_RESET_ON&quot; DATE,
&quot;CREATED_BY&quot; VARCHAR2(500 BYTE) NOT NULL ENABLE,
&quot;CREATED_ON&quot; DATE NOT NULL ENABLE,
&quot;LAST_UPDATED_BY&quot; VARCHAR2(500 BYTE),
&quot;LAST_UPDATED_ON&quot; DATE,
&quot;FLEX_01&quot; VARCHAR2(4000 BYTE),
&quot;FLEX_02&quot; VARCHAR2(4000 BYTE),
&quot;FLEX_03&quot; VARCHAR2(4000 BYTE),
&quot;FLEX_04&quot; VARCHAR2(4000 BYTE),
&quot;FLEX_05&quot; VARCHAR2(4000 BYTE),
 CONSTRAINT &quot;PORTAL_LOST_PASSWORDS_PK&quot; PRIMARY KEY (&quot;RESET_ID&quot;)
);
DROP TABLE PORTAL_LOST_PASSWORD_FAILURES
/
CREATE TABLE &quot;PORTAL_LOST_PASSWORD_FAILURES&quot;
(&quot;ATTEMPT_ID&quot; NUMBER NOT NULL ENABLE,
&quot;IP_ADDRESS&quot; VARCHAR2(100 BYTE),
&quot;BROWSER_AGENT&quot; VARCHAR2(4000 BYTE),
&quot;EMAIL_ADDRESS&quot; VARCHAR2(500 BYTE),
&quot;USERNAME&quot; VARCHAR2(500 BYTE),
&quot;RESET_ID&quot; NUMBER,
&quot;ERROR_MESSAGE&quot; VARCHAR2(4000 BYTE) NOT NULL ENABLE,
&quot;CREATED_BY&quot; VARCHAR2(500 BYTE) NOT NULL ENABLE,
&quot;CREATED_ON&quot; DATE NOT NULL ENABLE,
&quot;LAST_UPDATED_BY&quot; VARCHAR2(500 BYTE),
&quot;LAST_UPDATED_ON&quot; DATE,
&quot;FLEX_01&quot; VARCHAR2(4000 BYTE),
&quot;FLEX_02&quot; VARCHAR2(4000 BYTE),
&quot;FLEX_03&quot; VARCHAR2(4000 BYTE),
&quot;FLEX_04&quot; VARCHAR2(4000 BYTE),
&quot;FLEX_05&quot; VARCHAR2(4000 BYTE),
 CONSTRAINT &quot;PORTAL_LOST_PWD_FAILURES_PK&quot; PRIMARY KEY (&quot;ATTEMPT_ID&quot;)
);

CREATE OR REPLACE TRIGGER &quot;PORTAL_LOST_PASSWORDS_BIU&quot;
BEFORE INSERT OR UPDATE ON PORTAL_LOST_PASSWORDS
FOR EACH ROW
BEGIN
    IF INSERTING THEN
        IF :NEW.reset_id IS NULL THEN
           SELECT TO_NUMBER(SYS_GUID(), 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')
            INTO :NEW.reset_id
            FROM DUAL;
        END IF;
        :NEW.created_on := SYSDATE;
        :NEW.created_by := NVL(v('APP_USER'),USER);
    END IF;

    :NEW.last_updated_on := SYSDATE;
    :NEW.last_updated_by := NVL(v('APP_USER'),USER);

END;
/

CREATE OR REPLACE TRIGGER &quot;PORTAL_LOST_PWD_FAILURES_BIU&quot;
BEFORE INSERT OR UPDATE ON PORTAL_LOST_PASSWORD_FAILURES
FOR EACH ROW
BEGIN
    IF INSERTING THEN
        IF :NEW.attempt_id IS NULL THEN
           SELECT TO_NUMBER(SYS_GUID(), 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')
            INTO :NEW.attempt_id
            FROM DUAL;
        END IF;
        :NEW.created_on := SYSDATE;
        :NEW.created_by := NVL(v('APP_USER'),USER);
    END IF;

    :NEW.last_updated_on := SYSDATE;
    :NEW.last_updated_by := NVL(v('APP_USER'),USER);

END;
/

CREATE OR REPLACE VIEW portal_lost_passwords_vw
AS
SELECT ( SELECT count(*)
         FROM   portal_lost_password_failures
         WHERE  reset_id = o.reset_id
       ) failure_count
,      o.*
FROM   portal_lost_passwords o
/
</pre>
 <img src="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?view=1&post_id=1545" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://application-express-blog.e-dba.com/?feed=rss2&#038;p=1545</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Opening the same page in multiple iframes&#8230; dealing with AJAX, PPR, &amp; session state</title>
		<link>http://application-express-blog.e-dba.com/?p=1517</link>
		<comments>http://application-express-blog.e-dba.com/?p=1517#comments</comments>
		<pubDate>Wed, 18 Aug 2010 15:45:53 +0000</pubDate>
		<dc:creator>mnolan</dc:creator>
				<category><![CDATA[APEX Hacks]]></category>
		<category><![CDATA[ExtJS Integration]]></category>
		<category><![CDATA[apex ajax iframe session state]]></category>
		<category><![CDATA[apex ¤a_report]]></category>

		<guid isPermaLink="false">http://application-express-blog.e-dba.com/?p=1517</guid>
		<description><![CDATA[Currently most of the apps we build are built with an Ext JS multi-pane tabbed layout. We normally open up content in the main center pane in a new tab within an iframe. Now this is normally fine if we&#8217;re not opening the same APEX page, but sometimes we do. e.g. we have a single APEX [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwcGxpY2F0aW9uLWV4cHJlc3MtYmxvZy5lLWRiYS5jb20vP2F0dGFjaG1lbnRfaWQ9MTUyMg==" rel=\"attachment wp-att-1522\"><img src="http://application-express-blog.e-dba.com/wp-content/uploads/2010/08/apextjs-viewport-3-1-nested-tabpanel-585x256.png" alt="" title="apextjs-viewport-3-1-nested-tabpanel" width="325"  class="alignright size-medium wp-image-1522" /></a>Currently most of the apps we build are built with an Ext JS multi-pane tabbed layout. We normally open up content in the main center pane in a new tab within an iframe. </p>
<p>Now this is normally fine if we&#8217;re not opening the same APEX page, but sometimes we do. e.g. we have a single APEX page which lists all our customer information and we simply change an APEX ITEM with a new customer ID to change the page to a new customer. Now this works fine until we open up several customers at the same time into multiple iframes/tabs. Once we&#8217;ve done this our page items in session state are updated with the details of the last customer which we&#8217;ve opened. If we tab switch back to the first customer and perform, say a grid refresh, we now see the contents of the grid change to the last customer we opened. This is simply because we perform a PPR operation on the report and session state is used for the binds in our report which happen to hold the values for our last customer.</p>
<p>In order to deal with the issue I came up with the following approach to add a <strong>&#8220;beforeload&#8221;</strong> event on our Ext Stores or an interceptor function to be called before an APEX PPR call. We don&#8217;t always use Ext grids for report data, sometimes we still use APEX reports, usually with the one column unordered list template wrapped in an Ext Panel. We then use an Ext button in the Panel toolbar to control the refresh of the report reusing APEX&#8217;s $a_report/PPR function to perform the AJAX refresh.</p>
<p>For the APEX PPR side of things I&#8217;ve written an override (based on something similar <strong><a href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL3d3dy5hcGV4LXRoZW1lcy5jb20v" target=\"_blank\">Stephen Blair</a></strong> has done with jQuery to fade in and out the report contents on refresh) of the $a_report function to call an interceptor function which will be responsible for updating session state just before the actual AJAX report refresh call. There&#8217;s one slight issue with the override which I&#8217;m hoping someone will resolve for me is that for every refresh an extra DIV wraps around the report contents, this is not visible to the end user, but it is if you look at the DOM. Unfortunately I just don&#8217;t have the time to perfect it, and it works just fine for me but things like this really bug me!</p>
<pre class="brush: jscript;">
// Ext $a_report override
$a_report = function (pId, pMin, pMax, pFetched, pSort) {
    var url = 'f?p=' + Ext.get('pFlowId').getValue() + ':' + Ext.get('pFlowStepId').getValue() + ':' + Ext.get('pInstance').getValue() + ':FLOW_PPR_OUTPUT_R' + pId + '_';
    if ( !! pSort) {
        url += pSort + '::RP::' + Math.random() + '&amp;fsp_region_id=' + pId;
    } else {
        url += 'pg_R_' + pId + ':NO:::' + Math.random() + '&amp;pg_max_rows=' + pMax + '&amp;pg_min_row=' + pMin + '&amp;pg_rows_fetched=' + pFetched;
    }
    Ext.get('report_' + pId + '_catch').fadeOut({
        duration: .75,
        endOpacity: .3
    });
   // Page could have an interceptor call defined by the Developer to execute before the refresh
   if (typeof(Ext.app.$a_report_interceptor) !== &quot;undefined&quot;) {
      Ext.app.$a_report_interceptor.call();
   }
   Ext.Ajax.request({
      url: url,
      disableCaching: false,
      method: &quot;GET&quot;,
      success: function (response, opts) {
         var div = Ext.get('report_' + pId + '_catch');
         var parent = div.parent();
         div.remove();
         Ext.get(Ext.DomHelper.append(parent.fadeIn({
            duration: 2,
            endOpacity: 1
         }), {
            html: response.responseText
         }));
      },
      failure: function (response, opts) {
         console.log('server-side failure with status code ' + response.status);
      }
   });
}
</pre>
<p>Then in our page footer we simply add our custom interceptor function, we get the benefit of APEX doing the substitution at page generation time, so we don&#8217;t need to perform any javascript function to read the value from a hidden form item on the page. This could also be handled by a PLSQL process, it&#8217;s up to you to decide which place works best.</p>
<pre class="brush: jscript;">
Ext.app.$a_report_interceptor = function () {
   Ext.app.apexHttpPost('Do Nothing', ['P13_CUST_ID'], ['&amp;P13_CUST_ID.']);
}
</pre>
<p>See the following earlier <strong><a href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwcGxpY2F0aW9uLWV4cHJlc3MtYmxvZy5lLWRiYS5jb20vP3A9MTI4Nw==" target=\"_blank\">post</a></strong> for the Ext.app.apexHttpPost function code.</p>
<p>For our Ext grids, here&#8217;s a snippet of how we add a <strong>&#8220;beforeload&#8221;</strong> event on our store to call a function defined in our report meta data JSON object defined in our page header</p>
<pre class="brush: jscript;">
var pReportMetaData = {
   ...........
   &quot;beforeStoreLoadFn&quot;: function (store, options) {
      Ext.app.apexHttpPost('Do Nothing', ['P13_CUST_ID'], ['&amp;P13_CUST_ID.']);
   }
}
</pre>
<pre class="brush: jscript;">
// Lets define a before load function on our store if one is defined in our meta object
// usually used to update session state
if (pReportMetaData.beforeStoreLoadFn) {
   var fn = pReportMetaData.beforeStoreLoadFn;
   pStore.on(&quot;beforeload&quot;, function (store, options) {
      fn.call(store, options);
   });
}
</pre>
<p>Hopefully this gives you one idea of how you can potentially workaround session state issues when using the same page, multiple iframes, and AJAX.</p>
 <img src="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?view=1&post_id=1517" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://application-express-blog.e-dba.com/?feed=rss2&#038;p=1517</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Merging the last 2 blog posts together</title>
		<link>http://application-express-blog.e-dba.com/?p=1490</link>
		<comments>http://application-express-blog.e-dba.com/?p=1490#comments</comments>
		<pubDate>Tue, 17 Aug 2010 08:52:28 +0000</pubDate>
		<dc:creator>mnolan</dc:creator>
				<category><![CDATA[APEX Hacks]]></category>
		<category><![CDATA[ExtJS Integration]]></category>
		<category><![CDATA[PLJSON]]></category>
		<category><![CDATA[apex 4.0]]></category>
		<category><![CDATA[apex button menu]]></category>
		<category><![CDATA[apex hack]]></category>

		<guid isPermaLink="false">http://application-express-blog.e-dba.com/?p=1490</guid>
		<description><![CDATA[In the last blog post I talked about the idea of using region plugins for defining query sources rather than acting as a standalone plugin, and the post before that: transforming apex button references into different Ext form items. This post is focused on bringing those two approaches together. i.e. in our development environment we [...]]]></description>
			<content:encoded><![CDATA[<p>In the last blog post I talked about the idea of using region plugins for defining query sources rather than acting as a standalone plugin, and the post before that: transforming apex button references into different Ext form items. This post is focused on bringing those two approaches together. i.e. in our development environment we used a stripped down region plugin to define a query source which is used to supply the list of radio items in an Ext Button Menu within the parent region/report.</p>
<p>Perhaps the following two images will explain what I&#8217;m failing to do with words&#8230;</p>
<p><a rel=\"attachment wp-att-1493\" href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwcGxpY2F0aW9uLWV4cHJlc3MtYmxvZy5lLWRiYS5jb20vP2F0dGFjaG1lbnRfaWQ9MTQ5Mw=="><img class="aligncenter size-medium wp-image-1493" title="apex-filter-menu" src="http://application-express-blog.e-dba.com/wp-content/uploads/2010/07/apex-filter-menu-585x280.png" alt="" width="585" height="280" /></a></p>
<p><a rel=\"attachment wp-att-1492\" href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwcGxpY2F0aW9uLWV4cHJlc3MtYmxvZy5lLWRiYS5jb20vP2F0dGFjaG1lbnRfaWQ9MTQ5Mg=="><img class="aligncenter size-medium wp-image-1492" title="apex-filter-menu-ide" src="http://application-express-blog.e-dba.com/wp-content/uploads/2010/07/apex-filter-menu-ide-585x363.png" alt="" width="585" height="363" /></a></p>
<p>In the above two images; the first shows the display of two separate menu&#8217;s with different items (it&#8217;s been photoshopped to show you two tabs of the tabpanel as the menu&#8217;s are on separate tabs). The second shows the APEX IDE and the menu plugins which are used to define the query source for the menu items.</p>
<p>The difference in approach to the<strong><a href="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?url=aHR0cDovL2FwcGxpY2F0aW9uLWV4cHJlc3MtYmxvZy5lLWRiYS5jb20vP3A9MTQ1MA==" target=\"_self\"> &#8220;A button is not always a button&#8221;</a></strong> post is that instead of building a json object in the page header with our shortcuts and using a function to clean some malformed JSON outputted by our templates. We changed the design to generate the entire region toolbar JSON object in the page header, as it allowed a more cleaner and maintainable approach and opened up the ability to query the APEX data dictionary and process sub regions as we looped through each parent region at a time.</p>
<p>The following code example from our initial prototype (which only works for one menu button per region) should give you an idea of how we query the APEX data dictionary to build the toolbar object</p>
<pre class="brush: sql;">

--
-- Lets loop through all our page regions to create our region toolbar for each
--
FOR c IN
( SELECT *
  FROM   apex_application_page_regions
  WHERE  application_id = v_app_id
  AND    page_id        = v_page_id
  AND    source_type    NOT IN ('EXT_BUTTON_MENU') -- ignore our stripped pseudo plugins
  AND    extjs_utils.auth_condition_check(condition_type,condition_expression1,condition_expression2,authorization_scheme) = 0
)  LOOP
  v_toolbarObj := JSON_LIST();
  --
  -- Lets loop through all our page buttons to create our region toolbar
  --
  FOR c1 IN
  ( SELECT pb.region_id
    ,      label
    ,      nvl(redirect_url,'apex.submit('''||button_name||''');') link
    ,      button_attributes
    ,      template
    FROM   apex_application_page_buttons pb
    ,      apex_application_temp_button  tmp
    ,      apex_applications             app
    WHERE  pb.application_id  = v_app_id
    AND    pb.page_id         = v_page_id
    AND    pb.region_id       = c.region_id
    AND    tmp.application_id = pb.application_id
    AND    app.application_id = pb.application_id
    AND    tmp.template_name  = pb.button_template
    AND    tmp.theme_number   = app.theme_number
    AND    extjs_utils.auth_condition_check(pb.condition_type,pb.condition_expression1,pb.condition_expression2,pb.authorization_scheme) = 0
    ORDER by pb.button_sequence
  ) LOOP
    --
    -- Lets check our button attributes for shortcuts
    --
    v_shortcut_name := regexp_replace(c1.button_attributes, '.*&quot;(EXT_\w+)&quot;.*','\1');
    v_shortcut_name := CASE v_shortcut_name
                         WHEN c1.button_attributes THEN NULL
                         ELSE v_shortcut_name
                       END;
    IF v_shortcut_name IS NOT NULL THEN
 
      FOR c2 IN
      ( SELECT shortcut_name
        ,      shortcut
        FROM   apex_application_shortcuts
        WHERE  application_id = v_app_id
        AND    shortcut_name  = v_shortcut_name
      )  LOOP
        v_shortcut := c2.shortcut;
        --
        -- Lets loop through our shortcut and replace our menu references with a JSON object
        --
        WHILE instr(v_shortcut,'#MENU:') &gt; 0 LOOP
          FOR c3 IN
          ( SELECT source_type
            ,      region_source menu_sql
            FROM   apex_application_page_regions
            WHERE  application_id   = v_app_id
            AND    page_id          = v_page_id
            AND    parent_region_id = c.region_id
            AND    source_type      = 'EXT_BUTTON_MENU'
          ) LOOP
            --
            -- Lets execute our query and return the result encoded in a JSON object
            --
            v_json := sql_to_json( p_sql =&gt; c3.menu_sql );
            v_shortcut := regexp_replace(v_shortcut,'(\$MENU:\w+\$)',v_json,1,1)..........
</pre>
<p>In the above we query all the regions on the page and ignore any of our pseudo plugin regions. We then build up the toolbar JSON object with the assistance of PLJSON and then loop through each of the buttons defined for the region and use the defined button template and make the necessary string replacements e.g. <strong>#BUTTON_ATTRIBUTES#</strong>. But before we do, we check the button attributes to see if a custom MENU shortcut has been defined and if so we extract the shortcut name and query the shortcut source. We then extract any MENU definition in the shortcut source and then in the c3 cursor loop we query <strong>&#8220;apex_application_page_regions&#8221;</strong> and check the source type matches our plugin name and we use this SQL definition and execute it to get the results for the named menu.</p>
<p>So what it allows us to do from a development point of view is define a single button template e.g. radio menu, and use our pseudo plugins SQL to define the menu items. Essentially our toolbar menu button is made up of a button template, shortcut, and plugin region. All three components can be subscribed and published across all applications in the workspace so we get the benfit of code centralization and any additional custom config for the button menu can be defined by either using multiple shortcuts or some additional config in <strong>#BUTTON_ATTRIBUTES#</strong>.</p>
<p>In order to make sure the plugin region never displays, we use a template named either &#8220;Ext.Exclude&#8221; which wraps the content in a DIV with style=&#8221;display:none;&#8221;. Optionally we can use another template named &#8220;Ext.Destroy&#8221; which uses the Ext.onReady function and removes the region/DIV from the DOM on page load. Both come in very handy in different situations. We simply ignore regions which have these templates assigned to them within our viewport generation code.</p>
<p><strong>Note:</strong> you will see this error in the region source when you use a stripped plugin like we have</p>
<pre id="line308">ORA-20100: No render function has been defined for plug-in PLUGIN_EXT_BUTTON_MENU</pre>
<p> </p>
<p>however this is never visible because the template we define either destroys the region or hides it from display.</p>
 <img src="http://application-express-blog.e-dba.com/wp-content/plugins/feed-statistics.php?view=1&post_id=1490" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://application-express-blog.e-dba.com/?feed=rss2&#038;p=1490</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
