Showing posts with label Apex tips and techniques. Show all posts
Showing posts with label Apex tips and techniques. Show all posts

Monday, May 19, 2025

Ensuring that PL/SQL conditional compilation depending on custom identifiers is based on actual option values

I have a package that in case Oracle APEX is installed, calls a procedure attempting to send a PWA notification and this procedure requires certain APEX objects to exist.

In order to avoid errors at compile time in case Oracle APEX is not installed, I embedded the code within a block that is compiled only if a PLSQL_CCFLAGS identifier returns TRUE and this flag indicates whether or not APEX is installed.

Now, this leads to a potential problem: this flag must be initialized at the session level before compiling the code, and I would like to avoid that the code is not included only because I forgot to set the flag in advance.

I wish there would be some special constants for optional components of the database such as APEX so that I can avoid this type of tricks, but until then I can stick to the following method.

Here the $ERROR directive comes in handy, right after the BEGIN statement of the procedure I put this block of pseudo-code:

BEGIN
-- ensure conditional compilation is based on proper PLSQL_CCFLAGS configuration
$IF $$APEX IS NULL $THEN
$ERROR q'[PLSQL_CCFLAGS named APEX is undefined, 
execute ALTER SESSION SET PLSQL_CCFLAGS = 'APEX:TRUE|FALSE']'
$END
$END


-- conditional compilation in case APEX is installed
$IF $$APEX $THEN
...
$ELSE -- APEX not installed
...
$END
END;

After compiling the code, you can ensure that the code contains the expected logic by calling the following procedure:

set serveroutput on
EXEC DBMS_PREPROCESSOR.PRINT_POST_PROCESSED_SOURCE('PROCEDURE','TOOLS','SEND_NOTIFICATION');

In case you forgot to set the flag, here is what you get at compile time:

PLS-00179: $ERROR: PLSQL_CCFLAGS for APEX is undefined,
execute ALTER SESSION SET PLSQL_CCFLAGS = 'APEX:TRUE|FALSE'



Thursday, November 14, 2024

APEX tip of the day: translate tooltip when TITLE = "some text" is added to the link attributes of a IR report column

The "link attributes" of an interactive report allows a developer to specify additional attributes for a column displayed as a link in a interactive report.

A tooltip that will be displayed when a user hovers over the link text with the pointer can be specified using the attribute TITLE, for instance: TITLE="open page xyz".

This column attribute however is not picked up by the translation process of APEX, so it's not something that can be be found in the translation repository.

An easy way to work around the problem if you need to translate the text is as follows:

  1. add a new column to the IR report containing:
    APEX_LANG.MESSAGE('MSGOPENXYZ') as MSG1
  2. Make the column hidden.
  3. Update the LINK ATTRIBUTES column attribute adding TITLE="#MSG1#"
  4. Add the message MSGOPENXYZ to the message repository for the main language and for the additional languages.
  5. Repeat the steps 1-4 if you have more links needing this.
  6. Seed you application
  7. Publish the application in the additional languages
  8. Enjoy the translated tooltip.

 



Thursday, February 29, 2024

APEX Master Detail side-by-side form ICON_CLASS and ICON_COLOR_CLASS explained

In case you are wondering what to put in the ICON_CLASS and ICON_COLOR_CLASS columns of the query underlying the left side column of a Master Detail side-by-side form, here is an example.

select "FILE_ID",
    null LINK_CLASS,
    apex_page.get_url(p_items => 'P210_FILE_ID', p_values => "FILE_ID") LINK,
    case 
      when x.ACTIVE = 'Y' then 'fa fa-badge-check' 
      else 'fa fa-low-vision' 
    end AS ICON_CLASS,
    null LINK_ATTR,
    case 
      when x.ACTIVE = 'Y' then 'u-color-4' 
      else 'u-color-15' 
    end AS ICON_COLOR_CLASS,
    case when coalesce(:P210_FILE_ID,'0') = x."FILE_ID"
      then 'is-active' 
      else ' '
    end LIST_CLASS,
    (substr("FILE_NAME", 1, 50)||( case when length("FILE_NAME") > 50 then '...' else '' end )) LIST_TITLE,
    (substr("REPORT_NAME", 1, 50)||( case when length("REPORT_NAME") > 50 then '...' else '' end )) LIST_TEXT,
    null LIST_BADGE
from "V_BATCH_REPORT_FILES" x
where (:P210_SEARCH is null
        or upper(x."FILE_NAME") like '%'||upper(:P210_SEARCH)||'%'
        or upper(x."REPORT_NAME") like '%'||upper(:P210_SEARCH)||'%'
    )
  and REPORT_ID = :P200_REPORT_ID
order by "REPORT_ID","FILE_SEQUENCE"

ICON_CLASS refers to the icons of the font awesome set therefore you must provide both the generic fa class and the specific icon class to get the desired icon to show up.

ICON_COLOR_CLASS refers to the set of colors defined by the classes u-color-dd whose actual value depends on selected theme.

The result for the report template Media Type (Show Icons and Show Descriptions in the template options) will be as follows:

 



Monday, November 09, 2015

About displaying images using APEX_UTIL.GET_BLOB_FILE_SRC in non trivial situations

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.

In a perfect situation, when we need to display an inline image inside an Apex report, we might simply pick the BLOB column and apply the special formatting required in these cases, I mean that weird format mask containing a list of column attributes separated by colons where each member represents a column name in the table being queried:

SELECT ...
       dbms_lob.getlength(thumbnail) as image,
       ...
  FROM image_table...
 
The format mask specified in the column attributes of the report is something like:



Note also the call to dbms_lob.getlength that for some reason is still NOT properly documented and it is absolutely necessary to make work this type of reports otherwise you will incur into the rather obscure error message when you try to run the report:
 
report error:
ORA-06502: PL/SQL: numeric or value error: character to number conversion error 

But as I said, sometimes we are not in a perfect situation.
We might have some rows with an image and some without images.
How do we display an alternate image in case the BLOB value is null?
 
SELECT ...
       nvl2(thumbnail,
          '<img src="'||APEX_UTIL.GET_BLOB_FILE_SRC ('P4_X',id)||'" />',
          '<img height="72" src="#IMAGE_PREFIX#1px_trans.gif" width="72" />'
       ) as image,
       ... 
 FROM image_table...

the SQL above displays the BLOB image stored in image_table only if the value is not null (it might still be an empty BLOB, but that's another story and you'd better to avoid this further annoyance), otherwise it displays a transparent image that you could replace with anything that suits better your needs, say an icon with a question mark or whatever.

Now, the problem with a report column defined in a SQL statement like this is that you can no longer use Apex's built-in report image formatting, but you need to add some additional pieces here and there.

The first requirement comes from APEX_UTIL.GET_BLOB_FILE_SRC itself: the first parameter must be the name of a page item (P4_X in the code above), type FILE BROWSE, containing the format mask specifying a list of columns, similar to the picture above, but without the Blob table.

Now, where does Apex take the name of the table if it doesn't ask me to specify one here?
It will be soon clear as you try to run the page, because it will throw the following run-time error.

Error: No corresponding DML process found for page 4
Contact your application administrator
Technical Info (only visible for developers)

    is_internal_error: true
    apex_error_code: WWV_FLOW_NATIVE_ITEM.NO_DML_PROCESS_FOUND
    component.type: APEX_APPLICATION_PAGE_ITEMS
    component.id: 2568302329371214
    component.name: P4_X

This means that Apex is expecting to retrieve the name of the table from a built-in Row Fetch process where you specify the table name and it's primary key(s).

But wait a minute, I am not using a built-in Row Fetch in this page because I am running a report.
Never mind, you can create a *fake* Row Fetch process by specifying never as a condition in the process attributes.

After adding the fake Row Fetch process, the report will magically start working.

However there is one last refinement to be done:
the FILE BROWSE page item we added is probably undesired, visually speaking.
If that is the case, you need to add something like style="display:none;" in the HTML Form Element Attributes to hide it from the user.

I wish we could have a more streamlined version of APEX_UTIL.GET_BLOB_FILE_SRC in a future release of Apex such that we could supply all the necessary parameters without having to resort to this kind of tricks.

Wednesday, October 07, 2015

About downloading files from an Apex page without using download links

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.

Yesterday I needed to download a file as the final step in processing a user request involving a tabular form. For some reason this requirement is somewhat unusual if you look at the most typical scenarios where you either download files stored in a table by means of report links or a static link where the developer can leverage Apex's built-in features for this purpose.
The main difference with my need lies in that there is no file at all to download until the user has selected one or more checkboxes in a tabular form and I need to process the tabular form after the submit in order to create the file.
In a case like this you cannot simply put the file download logic inside an after submit process because the browser will reload the page after the submit, so the file download request won't be caught by the browser. An alternative would be to create the file and then reload the page showing the download link somewhere, but I preferred to avoid the unnecessary page reload.

Luckily enough I stumbled upon Joel Kallman's posting about the more generic "file downloading" requirement and more specifically a comment he made gave me this idea.

As Joel says one can invoke the file download by means of an on-demand process that can be triggered from an Apex standard URL, so what if I try to set up the parameters in the branch section as needed?

The simple answer is: it can be done and it works well, the browser won't reload the page but it will begin the file download instead.

configuring a branch to trigger a file download request

The prerequisites for the branch are as follows:
  • a DOWNLOAD button performing a normal page submit.
  • a process that handles the tabular form data and gathers the rows involved, creating a file and storing it temporarily or permanently in a table of your own choosing, running conditionally on the pressing of the DOWNLOAD button.
  • An application item called FILEID containing the key to retrieve the file from the table above or alternatively a page item if the file download application process doesn't need to be invoked elsewhere.
  • An application process named DOWNLOAD that is invoked as shown above which reads the blob from the table using the provided key value.
A final note about this method: as I am storing the file(s) in a table I wanted to delete them automatically right away once the download was finished.
Initially I thought it would be smart to delete the relevant row from the table after the call to WPG_DOCLOAD.DOWNLOAD_FILE but to my surprise rows are not deleted from the table even if I explicitly commit the operation just after the delete statement.

It looks like as if any code after WPG_DOCLOAD.DOWNLOAD_FILE doesn't get executed at all.

Failing to find a solution for this issue, I fell back to the simple workaround of deleting the rows older than 1 day when invoking the code for creating a new file, but one may opt for a batch procedure that does the housekeeping as well.

Thursday, July 23, 2015

About using APEX_COLLECTIONS in a tabular form with INSTEAD OF triggers

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.
 
In case you dreamed of updating an Apex tabular form built on top of a view based on APEX_COLLECTIONS by means of INSTEAD OF triggers, you must be ready to grant the following rights to the schema user attached to the workspace, otherwise you'll get an ORA-01031: Insufficient privileges run-time error.
Each privilege is only required for the corresponding action, if you don't need to delete from the view you can safely omit GRANT DELETE for instance.
 
GRANT UPDATE,INSERT,DELETE ON APEX_xxxxxx.APEX_COLLECTIONS TO user_schema;

This DDL can only be executed by the DBA or by the APEX_xxxxxx user.
Probably the DBA won't like to do it for the usual security issues.

Note also that this kind of permissions are not carried over during subsequent Apex upgrades, which means that tabular forms will stop working if you forget to re-execute the grants after a major upgrade.


See message translations for ORA-01031 and search additional resources.

Tuesday, October 14, 2014

Apex substitution strings in reports, not always replaced with their values, guess why?

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.

It's amazing to you find out how easy is to take for granted certain features of Apex for the simple fact that you have been using them for a long time without paying too much attention to certain specific details.

This morning I ran a report that I rarely use in an application and to my surprise there were some missing icons. How can it be, did I forgot to copy over the images during the last upgrade?
Did I miss some flag or inadvertently change some report attribute?
Did something change in Apex 4.2 that I was not aware of?

I quickly assessed not only that the missing icons were not missing at all, but for some reason the #IMAGE_PREFIX# substitution string was not replaced by its run-time value only in this specific report.
But why if the report has been cloned from a perfectly working one with only minor changes in the column values?

As it always happens in these cases, the first thing that comes off the top of your mind is to blame the software, no matter how improbable that could be, so I started comparing the two reports with the help of the APEX dictionary views trying to understand why one was working perfectly and the other one was failing miserably, may be some little flag makes the difference, but it turned out very quickly that Apex 4.2 wasn't the culprit here :-)

Before revealing the name of the killer, let's see how Apex deals with the substitution strings.

First of all you could define a simple classic report as follows, changing the IMG column definition from "Display text (does not save state)" to "Standard Report Column" and setting the report attribute "Strip HTML" to "No".

select htf.img('#'||'IMAGE_PREFIX#/copy.gif') img from dual
union all 
select htf.img('#IMAGE_PREFIX#/copy.gif') img from dual



The report will then display like this (I am using Firefox, on different browser it could be seen differently):

So, why does the first icon appear to be broken, while the second one displays just fine?
The reason is in that Apex report engine as part of its job is replacing #IMAGE_PREFIX# in the source SQL at some stage before executing the query. This explains why the first row is broken, it's because the string #IMAGE_PREFIX# results from the concatenation of the strings that occurs only at run time, while the second row gets its value before run-time.
Of course the same applies to queries containing functions that return values containing substitution strings at run-time or even functions returning a SQL query containing functions that return values containing substitution strings at run-time (sorry for the recursive definition, but Apex gives you also this powerful possibility).

So, what to do in these cases where the #IMAGE_PREFIX# or other substitution values are generated only at the last second?
The solution is very simple and is what I initially overlooked, but it emerged very clearly when I ran the comparison query between the attributes of the two reports, because the bad one was missing one important attribute.


By specifying #IMG# in the "HTML expression" attribute, we can indeed force Apex to perform the substitution also at run-time but be sure to have set "Strip HTML" to "No" in the report definition otherwise you won't see anything.
This powerful attribute allows for the customization of the HTML code of each individual report column and gives also the possibility of referring to the current values of other columns which can be combined with HTML tags for building snippets of HTML of arbitrary complexity.

Interestingly enough the on-line help for "HTML Expression" doesn't mention the fact that Apex will perform the evaluation of built-in substitution strings as part of the processing of this piece of data, which is a life saver if you are generating HTML on-the-fly.


Another example of how useful Apex dictionary views can be when you need to find out what's wrong between two apparently identical report definitions...

Thursday, January 19, 2012

How to remove the thousands group separator in numbers representing years from apex flash charts

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.

A quick tip for formatting years when they show up as labels in flash charts inside Oracle Application Express (Apex).
The problem is in that Anychart is too smart, so when it encounters a label that looks like a number, it will format it with the separator for the thousands, i.e. "2012" will be displayed either as "2,012" or "2.012" depending on the application locale settings.

As I don't really liked to see years formatted that way, I thought I had to find a way to suppress the thousands separator without having to resort to using a custom XML chart source file. After a few attempts, it turned out that by prepending a non-breaking space to the string, I could fool anychart's super-smart engine eventually. The technique applies to UTF-8 encoded pages.

UNISTR('\00A0') || TO_CHAR(gl_date, 'YYYY')

Not a big deal but something that may come in handy if you are in a hurry and short of more brilliant ideas. May be there are other ways to achieve the same result, this was just the first one that occurred to me.

The chart clearly displays my annual income as apex freelance, just in case you are wondering :-P

Wednesday, February 16, 2011

Avoiding the flash chart security warning in Apex with EPG

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.

If you host multiple web sites on the same apex server (using Apache as proxy and EPG) and some of these applications display flash charts, you might incur in the annoying problem of the security warning:


Flash Security Error:
AnyChart can not be launched due to Flash Security Settings violation.
Please refer to Security Error Article in AnyChart Documentation to fix this issue.
There are at least two possibilities to fix this problem (see at the bottom of this post for an interesting update):
1. as per AnyChart documentation, by placing a policy file placed in the domain root folder.
2. by replacing the substitution string #HOST# with a valid and stable URL prefix in the flash chart region source.

As i couldn't implement the first solution due to inexplicable problems with the cumbersome flash security settings, i was left alone with the other option. I always feel very frustrated when I can't get to the bottom of a problem but after spending four hours trying to figure out what the hell the flash player is expecting and without any evidence of progress, no matter how hard I tried, I simply gave up in favor of the second strategy, which took five minutes to be done.



What is happening is easily explained:
if you inspect the page source at run time, you'll find out that apex is replacing the string #HOST# with the values taken from a couple of CGI environment variables called 'HTTP_HOST' and 'SCRIPT_NAME':
BEGIN
dbms_output.put_line ( 'http://'
|| OWA_UTIL.get_cgi_env ('HTTP_HOST')
|| OWA_UTIL.get_cgi_env ('SCRIPT_NAME')
|| '/');
END;
Unfortunately the content of these variables change with the http access conditions.
For instance in my case there is Apache acting as proxy server and I get different values depending on whether I access the site through the proxy server at port 80 or directly through port 8080. In the former case HTTP_HOST will contain 127.0.0.1:8080, in the latter www.somedomain.com:8080.
In other words, if I access the page containing the flash chart via apache I'll get the flash security message otherwise it will work correctly.

As the correct way of accessing the apex page for the end-user is via the proxy server on port 80, the valid and stable URL prefix that I need to specify in the apex chart region source is http://www.somedomain.com/apex matching the URL prefix of the page being displayed in production.

So one might be tempted to hard-code the URL in the chart source region but this not an ideal choice for a variety of reasons, starting from the fact that in this fashion our application won't display the charts in our test or development environment because the chart source region will refer to the production site.
My solution is to create an application item called BASE_URL that is initialized with the right URL basing on the setting of a build option. As i am already used to have a PRODUCTION build option in all applications enabling certain features like visitor tracking and google ads when its status is include, I simply created a couple of mutually exclusive application computations that run "on new instance" and update the value of BASE_URL with the static value http://www.somedomain.com/apex when PRODUCTION's status is include or the PL/SQL expression
'http://' || owa_util.get_cgi_env('HTTP_HOST') || owa_util.get_cgi_env('SCRIPT_NAME') || '/'

when the status is exclude.

May be this is not the panacea for all possible situations, but it worked out pretty well until now and it doesn't even require any action on your part if you chose "include" as default value on export for the build option.

Updated February 16 after David's comment
While i was writing this posting I was thinking of doing a little modification in my Apache server configuration, then I published the article without doing the test first. David's comment reminded me that I still had to do that task to complete my job.
I modified the configuration of Apache where it is instructed to forward the requests received against apex on to IP 127.0.0.1:8080, a typical configuration that you will find in several examples in the Apex OTN forum.
# previous config
# ProxyPass http://127.0.0.1:8080/apex
# ProxyPassReverse http://127.0.0.1:8080/apex

# new config
ProxyPass http://www.somedomain.com:8080/apex
ProxyPassReverse http://www.somedomain.com:8080/apex
Then i reverted the chart region source to its original version with the #HOST# string and to my surprise the chart was displayed without reporting the "infamous" security message.
As a further step I also added the web domain to /etc/hosts as an alias for 127.0.0.1 just to avoid some unnecessary roundtrip between the DNS server and the web server.

In the end, it looks like we can work around the problem by manipulating Apache's configuration and without the need of crossdomain.xml or changes in chart source regions, at least for "simple" architectures like this.

Thursday, October 14, 2010

Where-used feature for Apex LOVs - the missing bit

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.

When working on large Apex projects, it's easy to create components that shortly after must be discarded because the customer changed his mind on a certain feature or we found a better solution for a certain problem.
So, it may happen that shared components like list of values, in short LOVs, clutter the repository and you don't remember if a particular one is still used or not.

Oracle Application Express provides you with a where-used feature in the form of a list on the right hand side of the page containing the LOV definition (see the picture below).


The downside to this approach is in that you must open each and every LOV definition and see if this list is empty or not.
If the LOVs are just a few is not a big deal, but with dozens of them, it is certainly a waste of time.

Fortunately the apex dictionary views come to the rescue.

It doesn't take too long to come up with a couple of queries returning a list of all used and unused lists of values and this query is fairly simpler than the query i wrote for tracking build options time ago.

-- LOVs where used by app, page and component type

select app_id, named_lov, page_id, component_type, component_name
from (
select APPLICATION_ID as app_id,
LOV_NAMED_LOV as named_lov,
PAGE_ID,
'PAGE ITEM' as component_type,
ITEM_NAME as component_name
from apex_application_page_items
union all
select APPLICATION_ID as app_id,
NAMED_LIST_OF_VALUES as named_lov,
PAGE_ID,
'REPORT COLUMN' as component_type,
COLUMN_ALIAS as component_name
from APEX_APPLICATION_PAGE_RPT_COLS
-- comment out the following query if running on Apex 3.2
union all
select APPLICATION_ID as app_id,
NAMED_LOV,
PAGE_ID,
'INTERACTIVE REPORT COLUMN' as component_type,
COLUMN_ALIAS as component_name
from APEX_APPLICATION_PAGE_IR_COL
--
) a
where app_id = :APP_ID
and a.named_lov in (select LIST_OF_VALUES_NAME
from APEX_APPLICATION_LOVS b
where b.APPLICATION_ID = a.app_id)
order by app_id, named_lov, page_id;



-- unused LOVs

select APPLICATION_ID as app_id,
LIST_OF_VALUES_NAME as named_lov
from APEX_APPLICATION_LOVS
where APPLICATION_ID = :APP_ID
minus
select app_id, named_lov
from (
select APPLICATION_ID as app_id,
LOV_NAMED_LOV as named_lov
from apex_application_page_items
union
select APPLICATION_ID as app_id,
NAMED_LIST_OF_VALUES as named_lov
from APEX_APPLICATION_PAGE_RPT_COLS
-- comment out the following query if running on Apex 3.2
union
select APPLICATION_ID as app_id,
NAMED_LOV
from APEX_APPLICATION_PAGE_IR_COL
--
) a
where app_id = :APP_ID
and a.named_lov in (select LIST_OF_VALUES_NAME
from APEX_APPLICATION_LOVS b
where b.APPLICATION_ID = a.app_id);
I hope i caught all possible components, i found that LOV column names in the dictionary come in four distinct flavors out of five total possibilities! (unless i missed some...)

See more articles about Oracle Application Express or download tools and utilities.

Wednesday, October 13, 2010

How to copy an Apex region and its items within the same page

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.

Ever needed to duplicate a region along with its items within the same page?
If you attempt to copy a region and its items to the same page, you will get the following message in Apex 4.0:

This restriction can be easily circumvented by first copying the region to a different page and then, from there, back to the original page. So, you may want to create an unused page in Apex just for doing this sort of operations. Clearly this is a convenient method if the region contains a lot of items, if a region contains only one or two items you might be better off copying the region without items and then duplicating the items manually.

I know it seems fairly trivial, but when you get a show stopper message like that above, the first reaction is probably to give up when there is actually a fairly simple workaround in the Apex arsenal that does the trick in a matter of seconds.

See more articles about Oracle Application Express or download tools and utilities.

Tuesday, November 03, 2009

Zero ajax label help tooltips for Apex?

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.

A couple of weeks ago i finally managed to complete a quick demo page showing a technique for displaying label tooltips without using Ajax or Javascript.
In practice, this means displaying the help text associated with the page item inside a sort of balloon that pops up whenever the mouse cursor hovers above the label link.
The floating label tooltip is rendered using CSS styles, which means that the whole thing is carried out on the client side, without the need of an extra round trip to the server to get that piece of information.
If we are talking of performance improvements, any technique that saves an additional request to the database should be considered positively.

This technique has been invented by Stuart Nicholls and i merely adapted it for use with Apex, with minor differences.
Stuart's site is very inspiring for anyone who wants to leverage the power of CSS stylesheets to achieve cool visual effects without resorting to the omnipresent javascript and i encourage everyone to have a look at it.

Basically the CSS technique consists in retrieving the help text of all the items involved upfront, format them using certain HTML tags that are matched by rules in the CSS stylesheet.
This at first may seem a contradiction, as i stated that the whole purpose of this was to save a request to the database, but if you compare this to a technique involving Ajax requests, it should be clear the advantage:
with the Ajax approach, a request is sent *every* time the cursor moves over a label link, no matter if the user already did it a few seconds or minutes before, while in the other case, the database request is made using a fast bulk collect statement retrieving all the involved labels in one shot, then it can be rendered as many times as necessary without accessing the db anymore, even if the user moves back and forth from other pages as the content is cached in session state.

Of course there are also a few limitations with this technique which can make it unsuitable for certain situations, for instance it can become unpractical for large texts or when the text contains links that you expect a user to click on. In these situations it makes sense to stick to the Ajax approach, and you could easily mix both on the same page as desired by choosing different label templates. The other major objection to this approach can be in the requirement of creating hidden items to hold the text. Of course it would be much better if we could have a substitution string for retrieving the help text at page rendering time, but i don't know if the Apex team is willing to include the code to support this in a future release.

Given the aforementioned prerequisites, here are the components to get it done in Apex (item names refer to the demo page hosted on apex.oracle.com:
-- before header process, conditional on (P11_ITEM1_HELP is null)
-- the process will run only the first time the page is loaded
-- thereafter values cached in session state will be used.
-- Each item with tooltip needs a hidden item to store the text
-- i.e. P11_ITEM1 is matched by P11_ITEM1_HELP.
declare
type vc2_4k_tab is table of varchar2(4000) index by binary_integer;
type vc2_255_tab is table of varchar2(255) index by binary_integer;
type vc2_coll is table of varchar2(4000) index by varchar2(255);
apex_item_names vc2_255_tab;
apex_item_help_text vc2_4k_tab;
apex_item_help_coll vc2_coll;
begin
select item_name, item_help_text
bulk collect into
apex_item_names, apex_item_help_text
from apex_application_page_items
where application_id = :APP_ID
and page_id = :APP_PAGE_ID
and item_label_template = 'Optional Label with Tooltip';

for i in 1..apex_item_names.count loop
apex_item_help_coll(apex_item_names(i)) := apex_item_help_text(i);
end loop;

:P11_ITEM1_HELP := apex_item_help_coll('P11_ITEM1');
:P11_ITEM2_HELP := apex_item_help_coll('P11_ITEM2');
end;


-- content of the CSS stylesheet (either inline or external)

a.t18tooltip {
text-decoration:none;
font-weight: bold;
}

a.t18tooltip cite {
display:none;
}

a.t18tooltip:hover {
cursor: help;
border:0;
position:relative;
z-index:500;
text-decoration:none;
}

a.t18tooltip:hover cite {
cursor: help;
display:block;
position:absolute;
top:20px;
left:-25px;
padding:5px;
font-weight:normal;
font-style:normal;
color:#000;
border:1px solid #888;
background:#ffc;
width:150px;
text-align: left;
}

a.t18tooltip:hover cite em {
position:absolute;
left:20px;
top:-6px;
width:11px;
height:6px;
background:#fff url(#WORKSPACE_IMAGES#tooltip.gif) 0 0;
display:block;
font-size:1px;
}

<a class="t18tooltip" href="javascript:popupFieldHelp('#CURRENT_ITEM_ID#','&SESSION.')">
Test Item 1<cite><em></em>&P11_ITEM1_HELP.</cite>
</a>


Please note that #WORKSPACE_IMAGES# in the case of Apex with EPG (Embedded PL/SQL Gateway), will generate one db roundtrip. As of Apex 3.1 images in the repository are cached by the browser, however there is still one request to retrieve the E-TAG attribute and compare it with the cached version. If the HTTP server is not EPG, then there won't be any db requests, just a web server request. This tiny image is required to show the nice "spike" pointing to the label, but it can be omitted or perhaps you can dare to implement the second method for rendering CSS tooltips slightly more elaborated to achieve the effects by means of border graphics only.

Updated January 26, 2010
After further thinking, i made some improvements to the technique shown above.
First of all i found out a sort of hack that allows me to put all the required HTML code inside the label template, thereby simplifying the whole thing. No more HTML tags inside the label text and no more explicit reference to the hidden item holding the help text:

-- this code goes into the before label template
<label for="#CURRENT_ITEM_NAME#" tabindex="999">
<a class="t18tooltip" href="" tabindex="999">


-- this code goes into the after label template
<cite><em></em>&#CURRENT_ITEM_NAME#_HELP.</cite><
</a>
</label>


The trick consists in building a dynamic substitutions by means of #CURRENT_ITEM_NAME#, appending the suffix _HELP. Apex processes the label template substitution variable first, then it uses the resulting string for the subsequent substitution of the resulting page item (at  a later stage of the label rendering), on the assumption that the help text is held by a hidden page item whose name is made up of the item name plus the _HELP suffix.

As i said this gives me two advantages compared to the initial method:
  1. i do not need to use HTML tags inside the label text
  2. i do not need to manually specify the hidden item name every time
Cool!

Secondly, i came up with a generalized on-demand process that i invoke as a before header process in the relevant pages (the pages containing items with tooltips). This code is completely generic so i don't even need to bother specifying the items names one by one, so this is another improvement over the initial version.


procedure init_page_tooltips (
p_app_id in pls_integer,
p_page_id in pls_integer,
p_temp_name in varchar2
as
type vc2_4k_tab is table of varchar2(4000) index by binary_integer;
type vc2_255_tab is table of varchar2(255) index by binary_integer;
apex_item_names vc2_255_tab;
apex_item_help_text vc2_4k_tab;
begin
select item_name, item_help_text bulk collect
into apex_item_names, apex_item_help_text
from apex_application_page_items
where application_id = p_app_id
and page_id = p_page_id
and item_label_template = p_temp_name;

for i in 1..apex_item_names.count loop
APEX_UTIL.SET_SESSION_STATE(apex_item_names(i)||'_HELP', apex_item_help_text(i));
end loop;
end;


Note that APEX_UTIL.SET_SESSION_STATE uses a rather uncommon approach when it comes to report errors.
Undefined items will not generate an error at the time of calling the API (that is inside the loop), but the Apex error page will be displayed later. In other words we cannot trap the error inside our procedure because this API sets some status variable inside the Apex engine. As a consequence, be aware that a missing help element will "break" the page.
I hoped i could work around this, but, up to date, i could not find how. This is not a big problem though because we do want to create such elements anyway, so the workaround is to change the label template to a different value until the help item holder has been added to the page.

Another optimization consists in specifying a non default value for the aforementioned on-load before header process: instead of keeping the default setting of "Once per page visit" and then making the process conditional, as illustrated in the article above, i chose "Once per session or when Reset", so it runs only the first time you load a page.

Last, be sure to remove any nowrap or nowrap="nowrap" from the HTML Table Cell Attributes in the Label section of the page item (not the help container), otherwise the tooltip will not fit in the box.

See more articles about Oracle Application Express or download tools and utilities.

Monday, August 03, 2009

How to let a user override an Apex validation rule

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.

Recently i had to find out a way of overriding an Apex validation rule on user request.
Imagine the following scenario:

if item longer than 10 bytes then raise error
else if item longer than 5 bytes then ask the user and continue if confirmed

In other words, the concept here is to discourage the user from entering a value that exceeds a certain amount without blocking him/her completely.

The first validation is easily implemented, it's just a normal PL/SQL Expression validation type.
The second validation type is apparently not supported by Oracle Application Express, there are no provisions in the validation attributes supporting this kind of "interaction", a rule is either validated or not validated in which case an error message is shown and after submit processes are not executed.

In reality the solution is rather simple, just make a smart use of the validation error message.

The validation error message can contain arbitrary HTML code, so what about stuffing some tags in the message?
Nothing prevents us from copying the HTML of a button template and use it inside the error message, replacing certain substitutions with custom text.

For instance, here is a sample (click on picture to enlarge):


Validation Expression 1 contains the PL/SQL expression, if it evaluates to FALSE, then the error message will be displayed.
In the Error Message field i copied the HTML source code taken from the HTML button template, so the button will look like the others. I replaced a couple of substitution strings as follows:
#LINK# with javascript:doSubmit('forceSUBMIT');
#TEXT# with confirm
The validation is conditional, such that it will not run the validation if previous validations have failed, as I've already explained some time ago in another posting. This is necessary in order to avoid displaying a misleading message in presence of other failing prerequisite validations.
The validation however is only executed when a "standard" SUBMIT request is generated by clicking on the corresponding button, but it will be skipped when the "special" forceSUBMIT request is generated by the custom button inside the error message and this does the trick.
All other validations are executed in either case, just to ensure that everything is ok even if a user changes something between the first round of validations (SUBMIT) and the second one (forceSUBMIT).

Another possibility that i didn't implement, but should work, is to skip the validation if the user checks a box.
This technique however requires an additional item and is somewhat more complex to deal with, especially if the checkbox item is to be displayed only after the first warning. I opted for a simpler solution, with fewer moving parts, in the true spirit of Apex.

You can try also a minimalistic demo of this functionality at apex.oracle.com.

See more articles about Oracle Application Express or download tools and utilities.

Tuesday, July 21, 2009

In case Apex report partial page refresh (PPR) won't work

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.

Every day is a good day for learning something new.
For instance yesterday i learned that there are two distinct ways of enabling partial page refresh for reports in Oracle Application Express. The second thing i learned is that one method excludes the other one...

Just to clarify the matter, partial page refresh aka PPR is the Apex feature that allows a user to refresh only the portion of page containing the report whenever a pagination or column sort link is clicked. This effect is achieved by means of built-in AJAX code.

The old, traditional way was to specify the built-in report template called "Standard (PPR)" (or a user-modified copy thereof) which included the necessary javascript calls inside the template source code.
As of version 3.1 a new declarative setting was added to the report attributes, a flag called "Enable partial page refresh". This new attribute MUST NOT BE used together with the PPR template. The picture below shows an INCORRECT setting:

If you mix up PPR template and PPR flag, you'll get a report where pagination links do not work at all.
If you are using Firefox with Firebug installed, you should be able to spot this problem quickly:

I prepared a sample page on apex.oracle.com where you can see the old and the new method on the same page, as well as the third wrong method that "breaks" report pagination.

So, if your PPR report doesn't work, you might want to check out if you inadvertently turned on the PPR flag against an old PPR template.

Given the increased flexibility allowed by the declarative approach, which allows you to pick any report template of your liking, probably it's time to migrate the old reports based on the PPR report template:


See more articles about Oracle Application Express or download tools and utilities.

Tuesday, May 12, 2009

A view for tracking build options usage in Apex

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.

Did you ever need to find out where a certain build option was specified inside an apex application?

Build options are a useful feature of Oracle Application Express that allows a developer to enable or disable certain functions or components by simply changing the value of the associated flag (Include/Exclude).
In other words build options allow you change the configuration of an application without modifying the metadata.

Unfortunately, unlike other components, Apex doesn't help much the developer with a report on the usage of build options throughout the application and given their ubiquity it is really useful to run a single query that summarizes the information.

Follow the link to download the source code of APX_BUILD_OPTION_USAGE view.


While testing the view i realized also that there was a bug in view APEX_APPLICATION_PAGE_REGIONS.
In the picture above, all page regions build options are reported as {Not production}, but the real build option value is Production.
After checking the source code of the view i can confirm that BUILD_OPTION is returning the wrong value owing to a mistake in a subquery, so in the particular case of this view you should consider only the presence of the build option name, not its reported inclusion or exclusion.
This bug affects Apex 3.2, 3.1.2 and probably earlier versions.

See more articles about Oracle Application Express or download tools and utilities.

Thursday, April 30, 2009

Apex tree region warning: Tree root ID "0" not found

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.

I was testing a recently created tree region in an Oracle Application Express application when upon displaying the page, instead of nice tree diagram, i got the following message:
Warning: Tree root ID "0" not found.
The first thing that came to my mind was to check if the underlying hierarchical query was returning any results, may be a typed something wrong, but i executed the query in SQL developer without a hitch.
Then i started wandering through the tree components in search of light.
I must say that i'm not using trees every day, so it was a while since the last time i built one. Struggling to understand what's wrong with my tree, fortunately the bulb lighted on suddenly: let's switch to page debug mode and see if i can get any clues from the output. Bingo.
Here is what i found:
Warning: Tree root ID "0" not found.
ORA-06550: line 3, column 100: PL/SQL: ORA-00904: "A2": invalid identifier
ORA-06550: line 2, column 22: PL/SQL: SQL Statement ignored
ORA-06550: line 22, column 64: PLS-00364: loop index variable 'I' use is invalid
ORA-06550: line 22, column 15: PL/SQL: Statement ignored
This stack of errors brought my attention to the fact that in my tree source query i didn't include neither column A1 nor A2. Apex documentation is a bit vague about these two additional columns, the Application Express User's Guide through version 3.2 mentions only ID, PID, NAME and LINK columns and the helper text at the bottom of the query source field contains the following information (see picture):

According to my tests the only correct and working syntax is that shown in the sample SELECT statement of the picture, if you do not include columns A1 and A2 you'll get the warning message instead of the tree (please correct me if I'm wrong).
As shown in the picture, if you do not use columns A1 and A2 for some reason inside the tree template, then set them to null.

Addendum
This message can appear in translated forms as well:

Avvertenza: ID radice struttura "0" non trovato
Advertencia: No se ha encontrado el identificador de raíz del árbol "0".
Warnung: Baum-Root-ID "0" nicht gefunden.
Avertissement : ID de racine d'arborescence "0" introuvable
Advertência: ID da raiz da árvore "0" não encontrado.
編集 警告: ツリー・ルートID「0」が見つかりません。
경고: 트리 루트 ID "0"을(를) 찾을 수 없습니다.
警告: 找不到树根节点 ID "0"。


See more articles about Oracle Application Express or download tools and utilities.

Friday, March 06, 2009

SAQ: Seldom Asked Questions about Apex Globalization

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.

I like jokes as you can understand from the title of this posting, however i was in the mood for it because sometimes i happen to read FAQ lists where there are questions that i'd never ask in centuries, hence the idea of a SAQ list.

Apex globalization mechanism is extremely powerful and flexible, yet you need to know what you are doing if you don't like last minute surprises. In other words you need to master the globalization process if you don't want to accidentally delete translated text or put in jeopardy the whole application with a wrong action.

Before delving into the list of questions, let's take a tour of the main translation page of Oracle Application Express.

The Apex globalization process is managed from the page below, that you can easily access from the shared components page of the application builder:

The central region of this page outlines the steps from the beginning to the end of the globalization process and provides the links to the key phases of the process.

Step 1 in this list is a one-off task that needs to be performed only when:
  1. you need to create a new translated application
  2. you need to change an existing mapping or delete it altogether.
Step 2 is the real starting point each time you need to update an existing application when you've changed something that needs to be propagated to the various translations.

Step 3 is a curious fake link. Indeed it's just a reminder of what you need to do next, that is either translate the text yourself or ask someone else to do it for you.

Step 4 takes you to the page where you can see the list of XLIFF files loaded in the repository and you can apply one of them or you upload a freshly translated one.

Step 5 points to the page where you manage the static message translations and is optional. If you don't use translatable text messages, those retrieved via the API call to APEX_LANG.MESSAGE you don't need to go there. Translatable messages are extremely useful for translating static text that needs to be stored inside page items or application items or when the value needs to be returned through a PL/SQL function in the current language. See the linked page for a practical example.

Step 6 leads you to an even more sophisticated feature, the dynamic translation of text returned by dynamic LOVs, that is list of values based on queries. While text descriptions coming from static LOVs are automatically retrieved by Apex and inserted into the repository, this is not possible with dynamic LOVs that are based on user defined queries on arbitrary tables, however Apex gives you the possibility of performing dynamic translations, so, if you have a query like:
SELECT fruit_name d, fruit_id r
FROM fruits
you can get "apple" when viewing the page in English or "Apfel" when viewing it in German.


The menu called Navigate on the right hand side is a fine grained list of tasks and gives you instant access to the critical features.

The menu below it, called Translation Utilities, is also important because it's the only place where you find a link to the page for manually translating the text.

For some reason the link to the page that you'll visit most frequently has been named Export Translations, followed by the link Export XLIFF, which is the real page where you actually download a dump of the translation repository.

Why do i need to map an application?
Apex globalization mechanism works by creating a sort of ghost application with its own application ID.
Mapping means assigning a number to a translated application. In an hosted environment like apex.oracle.com, where there are thousands of application IDs already taken by other users, it can be tricky to find out a free application ID because Apex does not automatically suggest the next available number.
Note that Apex doesn't actually reserve the application ID until you publish the translation, so it might happen that you choose an ID but then it's also taken by another user before you had the time to publish the translation.
If that happens, you'll get a run-time error when you attempt to publish (but not at time of seeding):
ORA-20001: Error during execution of wwv_flow_copy: WWV_FLOWS
ORA-00001: unique constraint (FLOWS_030100.WWV_FLOW_FLOW_PK) violated
Each application ID maps to a language code, like it, fr, de, zh-cn, ja and so on.
Apex maintains a consistent session state whenever you switch from one language to another by any means, within the same apex session, which is a good thing.


1. What does it mean to seed an application?
Seeding is the process of preparing a translated version of your primary application.
When you seed the text, Apex inserts/updates/deletes the translation repository with the most recent version of the translatable strings. Seeding alone does not change anything in the currently running translated applications until you publish the translated version.
Note also that when you seed an application you are doing a twofold action: you are not only updating the source text but also the target text. An important thing to bear in mind is that the target text won't be touched only if the old source text and the new source text are equal, (which means that a single space makes a difference!), otherwise it will be "reset". See later on for a typical scenario.


2. What happens when i publish an application?
Publishing is the final step in the globalization process.
The application is created by assembling the metadata from the primary application and merging it with the translated text. Note that you cannot access directly a translated application by invoking Apex with the mapped application ID, if you attempt to do so, you'll get a page like this:



3. What if i need to update only a few new translated strings?
whenever you add a new element which is a candidate for a translation, like a page item or a new shared component like a list entry, translated applications won't pick them up until you seed the application to the desired language. After seeding the application, the fastest way to get a string translated is to manually edit it. After editing the translation, you can go straight to the publishing phase, without importing anything.
  1. seed the application
  2. click on translation home
  3. click on manually edit translations
  4. update the translations
  5. export the XLIFF file for future reference ( this step is not required but highly recommended!)
  6. publish the selected translation
4. What if i need to update a few existing strings because i changed the corresponding source element?
you must be careful when changing the text of a source element because Apex will automatically wipe out the translation upon seeding! Imagine you have a large text in the source language and you realize there is a grammatical error for instance. This grammatical error is probably absent in the target language(s) so you could easily have a situation where you want to change just the source element, not the existing translations.
So, you fix the source text, seed the application and bang, the old translation is lost!
The target element is erased and replaced with the new untranslated text.
This example demonstrates why you ought to export a full XLIFF file whenever you translate something, the file will be the backup in case something goes wrong.
Back to our problem, if you change a translatable string of an existing element, if you seed the application again, then you need to recover the translation from the XLIFF file, if it still applies, or provide a new one if it does not apply or if you didn't export the XLIFF file. See entry #3 on how to manually edit a translation.

5. What is an XLIFF?
XLIFF is an international file format standard based on XML tailored for translation tools. You can find out more on XLIFF on wikipedia.

6. Do i necessarily need to translate the content of the XLIFF file to translate an application?
No, you don't. You can manually edit the translations after seeding the translation. However, after doing so, you should immediately take an export in XLIFF format as a backup of your work!
The XLIFF format comes in handy if you are going to use some tool to translate the text, otherwise you can easily break the XML file format by deleting or inserting unwanted characters, especially if the translated text contains some HTML tags. There is a free on-line tool kindly provided by iAdvise, that allows you to manipulate XLIFF files generated by Apex.

7. Is there any apex dictionary view containing the translations?
At time of writing (version 3.1.2) there isn't any built-in apex dictionary view available and unless i overlooked it, there isn't any view in the just released version 3.2.

8. Why when i query certain apex dictionary views like apex_application_pages columns like help_text or label are always returned in the primary language?
The apex dictionary view doesn't currently support a language "context", so it always returns the text stored in the primary application, even when the query is originated from a translated application.
Hopefully one day apex dictionary views will fully support translated applications.

9. How can i compare two XLIFF files?
There are some tools around to do this specifically on XML files, besides some powerful plain text editors or Unix and DOS commands.
Some years ago i bought a license of XMLSpy, a powerful Windows based XML editor that comes with a document comparison function. I used it just yesterday to understand why i got two different links in two different application translations. As far as i know you can install a trial of XMLSpy valid for 30 days, so if it is a one-off requirement, it won't cost you a penny. Altova released also a stand-alone utility called diffDog for comparing files, so you might want to check it out as well.
As i am working more and more time on the Mac, today i searched for a file compare utility for Mac OS X and i found what it seems to me like a perfect match, a multiplatform utility called DeltaWalker.
I quickly installed the trial and it worked like a charm although i didn't perform any "stress test". Even in this case you have days ahead to evaluate the product before buying a license.
This was not meant to be a comprehensive software review, so you may want to conduct a deeper search on the web.

10. The Application Language Derived From attribute (in the Globalization Attributes page) contains several options, two of them are called Application Preference and Item Preference: what's the difference between the two?
The former option automatically retrieves the language from a user preference, supposing you have some process that calls the API procedure APEX_UTIL.SET_PREFERENCE. The preference name is called FSP_LANGUAGE_PREFERENCE, a name that may easily lead to some confusion with the other option.
This approach makes sense when the application requires authentication because it stores the value in a sort of user profile. The advantage is in that a returning user doesn't need to switch the language after the login, it will be automatically set by Apex (but he/she can still change it at any time afterwards).
The latter option works by storing the language option inside an application item called FSP_LANGUAGE_PREFERENCE (that you need to create yourself, don't forget it), so the user might have to change it every time if the primary language is not the preferred one. The advantage of this approach is in that it works also with public pages, where you can pick a specific language by clicking on an icon or selecting from a list. The logic for updating the application item must be written by the developer.

11. Is there any way to set a given language from the URL?
This problem has haunted me for quite some time.
The solution i found works well with real users but doesn't work well with web spiders like googlebot who is very picky with web redirects.
First of all you need to set the globalization attribute "Application Language Derived From" to "Item Preference (use item containing preference)".
Secondly you must set up an application process that runs before header performing a conditional redirect using the following PL/SQL call:
begin
:FSP_LANGUAGE_PREFERENCE := :REQUEST;
htp.init;
owa_util.redirect_url('f?p='||:APP_ID||':'||:APP_PAGE_ID||':'||:APP_SESSION);
end;
and the condition is "Request is contained within Expression1", where expression1 contains the list of the expected language codes like "en,de,fr,..." as shown in this sample page:

http://apex.oracle.com/pls/otn/f?p=multilangdemo:1:0:en
http://apex.oracle.com/pls/otn/f?p=multilangdemo:1:0:es

As i stated initially, redirecting the page may adversely affect the page indexing process, for instance Google spiders don't like at all redirects and this may prevent the link from being harvested. This is not a problem if the application is not aimed to the public and you don't expect users to reach you through a web search.

Note that setting the item FSP_LANGUAGE_PREFERENCE directly from the browser achieves curious results owing to the sequence of operations that Apex does when building a page. If you set this application item from an URL, apex will start building the page using the current language, then, at a certain point it will set the new value for the variable in the session state. This may result in pages with mixed languages, where static text is still in the old language and dynamic content is in the new language. If you reload the page though, the new language will be used. This is clearly a suboptimal solution.
There must be a reason if the Apex team decided to retrieve the current page language before setting values in session state, if not, may be one day we will be able to change the current language before the page loads without the need for an additional redirect.
Updated on march 13:
i simplified the argument in the call to OWA_UTIL.REDIRECT_URL, instead of using an absolute path, now i'm using a relative path, which makes the transition from a development or test environment to a production environment much easier because there is no need to worry about the current service path.

12. How to show the current page in a different language by pressing a button?
With a logic similar to that i explained in question #11, but replacing the redirect process with an application computation that updates the value of FSP_LANGUAGE_PREFERENCE after submitting the page, you can switch language in every page of your application by clicking on a button defined as illustrated below:
The second component is a simple conditional after submit application computation (a computation that will automatically run on each and every page if a condition is met) defined as follows:


With just a few components like a region of buttons (or icons or whatever you prefer) defined on page zero and a single application computation, you can easily enable language switching on each page. If you prefer you can move this function to the navigation bar, where you can define each entry as an URL containing a javascript call like "javascript:doSubmit('es');".
See how it works in a live demo application.

More infrequently asked questions to be added in the future.


See more articles about Oracle Application Express or download tools and utilities.

Wednesday, February 25, 2009

Presetting workspace name and username: quick tip for the lazy apex developer

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.

A quick tip for the lazy apex developers like me.
If you manage many workspaces on different machines and servers, you may want to create several bookmarks to quickly preset the workspace name and the workspace administrator name or an application developer name if you prefer.

Being Apex a tool written with Apex itself, it's easy to preset the page items in the following fashion:
http://hostname/path/f?p=4550:1:::::F4550_P1_COMPANY,F4550_P1_USERNAME:WORKSPACE,USERNAME
just replace the various the parameters like hostname, path, WORKSPACE and USERNAME with the corresponding values.

For instance:
http://localhost/apex/f?p=4550:1:::::F4550_P1_COMPANY,F4550_P1_USERNAME:INTERNAL,ADMIN
the bookmark works for the standard apex super administrator in a typical Oracle XE installation and it is practically equivalent to:
http://localhost/apex/apex_admin
If you have an account on apex.oracle.com, supposing you have a workspace named ACME with an administrator called CERBERUS, then your bookmark becomes:
http://apex.oracle.com/pls/otn/f?p=4550:1:::::F4550_P1_COMPANY,F4550_P1_USERNAME:ACME,CERBERUS


If you manage a single workspace this is of little use, but if you deal with dozens of them it can definitely help.

Do not use this method to store the password in clear text inside the bookmark, if you don't want to type the password every time, then turn on the password wallet feature of the browser.

See more articles about Oracle Application Express or download tools and utilities.

PS: if you ever watched looney tunes cartoons, then you'll remember that ACME was the brand printed on any box of any item handled by bugs bunny, wil coyote, duffy duck, speedy gonzalez and so on. I read somewhere (on IMDB perhaps?) that ACME stood for "A Company that Makes Everything" :-)

Thursday, December 18, 2008

A progress bar indicator for Oracle Apex

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.

One of my favorite mottoes is "do not reinvent the wheel" and this posting shows you how to "recycle" a standard Apex component like a flash chart to display a nicely integrated and animated progress bar, a widget that may come in handy for "entertaining" users during certain long-lasting operations.

So, instead of creating something from scratch, i prefer to re-use an existing and supported component of Apex to achieve the same result, which complies with my initial statement.

The need for a progress bar arose while developing a new page for the recently released DBMS_PROFILER companion application, where i let the user import data from a remote db and owing to the duration of the operation, i wanted to show the current status of the operation occurring in background.

Let me summarize the basic requirements for a successful and meaningful implementation of this widget in your application:
  1. the operation must take several seconds, otherwise why bother?
  2. the process must be executable in background;
  3. user must have the flash player installed but this should not be a problem nowadays.
  4. you must be using Apex 3.1 or newer.
A short full screen video of the apex progress bar indicator is available at the download page linked below or you can click on the image below to see a cropped version.



In order to make the progress bar indicator work in your application, you will need to perform the following actions:
  1. decide whether you want to display the progress bar inside a region of the same page where you execute the process or in a different page;
  2. copy the flash chart region to the desired place;
  3. change the display condition to suit your needs;
  4. create an after submit process that submits your procedure as a background job (an example is provided);
  5. create a page validation based on the code supplied in the demo application (an example is provided);
  6. modify the "core" procedure to make use of the YY_UPD_PROGRESS_BAR procedure (sample procedure YY_TEST_PROGRESS_BAR is provided).
  7. adjust the chart parameters or XML source code to fine tune the progress bar indicator look (optional).
Those interested in a hands-on experience with the progress bar indicator demo can download the auto-installing application from yocoya.com or see a live demo at apex.oracle.com.

See more articles about Oracle Application Express or download tools and utilities.

yes you can!

Two great ways to help us out with a minimal effort. Click on the Google Plus +1 button above or...
We appreciate your support!

latest articles