Chapter 12. Plug-ins

Plug-ins, new in APEX 4.0, allow you to extend the functionality of item types, region types, dynamic actions, and process types. The plug-in architecture includes a declarative development environment that lets you create custom versions of these built-in objects. For example, instead of using a standard select list or check box, you could build a "star rating" item that allows your user to provide feedback using a one-to-five star graphic. This new item type could then be used across all your applications.

While it was always possible to create custom functionality using tools like custom Ajax, the code could be located in multiple different places: inside the database, in JavaScript files, and so forth. You scan still use all the customization tools you are familiar with, but turning that code into a plug-in makes it much easier to use and manage since all of the code is contained in one object, your new type.

This new architecture also allows you to create plug-ins based on any jQuery component. The open source jQuery library is widely accepted and is loaded with useful JavaScript and Ajax functionality. Basing your plug-ins on existing jQuery functionality is a great way to add advanced features to your applications without generating mountains of new code manually.

In this chapter, we will explore the new world of plug-ins by creating a few from scratch while also reviewing some popular plug-ins that are available from the following online repositories:

  • Oracle Applications Express Plug-ins page (www.oracle.com/technetwork/developer-tools/apex/application-express/apex-plug-ins-182042.html)

  • APEX-PLUGIN.com (www.apex-plugin.com)

After seeing the number of plug-ins that are freely available, you may find yourself importing more plug-ins than you build. If you do build plug-ins of your own, you can use the downloadable ones as a guide and you can publish your own back to the online repositories. Think of this as a way to give a little back to your development community.

Creating Your First Plug-in

As with any development project, you first need to establish a goal; some new piece of functionality. Once the goal is defined, you can define the requirements to be mapped to your development framework. If your new functionality would be beneficial in other applications, you should consider creating it as a plug-in. Will it work as a plug-in? If your functionality is an extension of one of the standard types—item, region, process, dynamic action—then it is a candidate for making into a plug-in.

Running the APEX Plug-in Builder

The interface used to create APEX plug-ins is accessed under the Shared Components menu of the Application Builder. The plug-in builder interface is accessed via the Plug-ins link on the User Interface section. When you first enter the Plug-ins builder, as shown in Figure 12-1, you will notice that APEX 4.0 is delivered with no plug-ins. There is, however, a button on the page titled "View Plug-in Repository," which will direct you to a location on Oracle's corporate web site that houses plug-ins that are freely downloadable. We will explore this site later in this chapter, but for now we will focus on the mechanics of building a plug-in from scratch.

Plug-ins builder home page

Figure 12.1. Plug-ins builder home page

We'll start by creating a simple item type plug-in, as it is the easiest to complete and illustrates most of the mechanics of the plug-in architecture. To create a plug-in of any kind, you have to first decide what its purpose will be. In this example, we'll create a text item that will require a value and will have a red background until a value is entered. After at least one character is entered in the field, the background will return to standard white. We'll call this plug-in requiredInColor.

To get started, we need to take a look at the plug-in builder. When you click on the Create button, the plug-in builder page is displayed as shown in Figure 12-2. We filled in the Name, Internal Name, and Type fields in the figure to illustrate how plug-ins should be named. Following are guidelines on plug-in naming best practices:

Name:

The Name field will be used to identify the plug-in when adding them to pages using the standard builder pages. This name should be unique across your applications, but uniqueness is not required.

Internal Name:

The Internal Name of a plug-in is used by APEX 4.0 to identify the plug-in and is never displayed. While the plug-in Name is not required to be unique, the Internal Name must be unique to your current application. Because plug-ins can be published to public repositories, it is recommended that their Internal Names be constructed so that they will be unique worldwide. For example: com.enkitec.requiredInColor.

These guidelines guarantee that your plug-in's name will not be a duplicate of one that you download from another site. They do not ensure that names for plug-ins you create are unique within your organization, so you'll have to manage that on your own. You'll notice that any plug-ins you download from Oracle's plug-in repository have names that are prefixed with com.oracle.apex.

Plug-ins builder page

Figure 12.2. Plug-ins builder page

In the Subscription section of the plug-in builder, the Reference Master Plug-in From field allows you to use a copy of an existing plug-in by either entering the name or selecting it from a pop-up list of values. If you leave this field blank, by default the plug-in is considered a master. We will leave this field blank since we are creating a new plug-in.

The File Prefix, #PLUGIN_PREFIX#, is a virtual path that determines where any plug-in support files (like JavaScript files, CSS files, and so forth) will be stored. This prefix points to a location in the database. If you are using a separate HTTP server, you can get better performance if you store the supporting files there, as opposed to on the database. You can either create a new substitution string pointing to a new virtual directory or just use the #IMAGE_PREFIX# in place of the #PLUGIN_PREFIX#.

The next section, PL/SQL Code, is where the guts of the plug-in are built. This code is used to do the following:

  • Render or display the plug-in

  • Validate the plug-in

  • Execute the plug-in

You can write your code as an anonymous PL/SQL block in the PL/SQL Code section. You can also create your code as a database package and refer to that package from a block in this section. You'll get better performance from the package-based approach.

Each plug-in type must implement a specific interface, known as a render function, which was standardized so that future APEX enhancements do not break existing plug-ins. Since we are talking about item type plug-ins, we'll first take a look at the item type render function shown in Listing 12-1.

Example 12-1. Plug-in Render Function Interface

function <name of function> (
p_item                in apex_plugin.t_page_item,
p_plugin              in apex_plugin.t_plugin,
p_value               in varchar2,
p_is_readonly         in boolean,
p_is_printer_friendly in boolean )
return apex_plugin.t_page_item_render_result

Note

The process for building plug-ins is not completely documented. In fact, you will not find the function definition from Listing 12-1 in any of the APEX 4.0 documentation. The only place to find information on how plug-ins are defined is by clicking on the "Render Function Name" in the Callbacks section of the Plug-in builder page.

The name of the function is not important, it just has to follow Listing 12-1's specification. The first two parameters in the render function are of type apex_plugin. The name apex_plugin is actually a synonym for the apex_040000.wwv_flow_plugin package. This record type contains attributes that allow you to control the operation of the item on which the plug-in is being built. The ID attribute, for example, is the HTML ID of the item that can be used in JavaScript to manipulate the Document Object Model (DOM). Listing 12-2 shows an excerpt from the wwv_flow_plugin package showing the definition of the t_page_item and t_plugin record types.

Example 12-2. Plug-in Record Type Definitions

type t_page_item is record
(
    id                          number,
    name                        varchar2(255),
    label                       varchar2(4000),
    plain_label                 varchar2(4000),
    format_mask                 varchar2(255),
    is_required                 boolean,
    lov_definition              varchar2(4000),
    lov_display_extra           boolean,
    lov_display_null            boolean,
    lov_null_text               varchar2(255),
    lov_null_value              varchar2(255),
lov_cascade_parent_items    varchar2(255),
    ajax_items_to_submit        varchar2(255),
    ajax_optimize_refresh       boolean,
    element_width               number,
    element_max_length          number,
    element_height              number,
    element_attributes          varchar2(2000),
    element_option_attributes   varchar2(4000),
    escape_output               boolean,
    attribute_01                varchar2(32767),
    attribute_02                varchar2(32767),
    attribute_03                varchar2(32767),
    attribute_04                varchar2(32767),
    attribute_05                varchar2(32767),
    attribute_06                varchar2(32767),
    attribute_07                varchar2(32767),
    attribute_08                varchar2(32767),
    attribute_09                varchar2(32767),
    attribute_10                varchar2(32767)
)

type t_plugin is record
(
    name         varchar2(45),
    file_prefix  varchar2(4000),
    attribute_01 varchar2(32767),
    attribute_02 varchar2(32767),
    attribute_03 varchar2(32767),
    attribute_04 varchar2(32767),
    attribute_05 varchar2(32767),
    attribute_06 varchar2(32767),
    attribute_07 varchar2(32767),
    attribute_08 varchar2(32767),
    attribute_09 varchar2(32767),
    attribute_10 varchar2(32767)
)

As you can see in the t_page_item record, the name and ID of the item you are creating are available to you via this interface. When you want to operate on the value of your item type plug-in using JavaScript, you can pass the t_page_item.id to a JavaScript function and manipulate the DOM to control the appearance and utility of the item. You can also see that there are ten fields in both record types that start with "attribute." These are custom attributes that you can assign to your plug-in, which can be used to further define your item.

To define the features of your plug-in, you must construct a render function and, optionally, a validation function. The render function defines what the item will do and how it will be displayed. To protect against SQL injection attacks, a validation function should also be added, although it is not required. Listing 12-3 shows the contents of the PL/SQL code that implements our requiredInColor plug-in.

Example 12-3. Plug-in PL/SQL Code

FUNCTION requiredInColor (
  p_item                in apex_plugin.t_page_item,
  p_plugin              in apex_plugin.t_plugin,
  p_value               in varchar2,
  p_is_readonly         in boolean,
  p_is_printer_friendly in boolean )
  RETURN apex_plugin.t_page_item_render_result
IS
  l_name varchar2(30);
  l_result      apex_plugin.t_page_item_render_result;
BEGIN
  IF p_is_readonly OR p_is_printer_friendly THEN
    APEX_PLUGIN_UTIL.print_hidden_if_readonly (
      p_item_name           => p_item.name,
      p_value               => p_value,
      p_is_readonly         => p_is_readonly,
      p_is_printer_friendly => p_is_printer_friendly );
    APEX_PLUGIN_UTIL.print_display_only (
      p_item_name        => p_item.name,
      p_display_value    => p_value,
      p_show_line_breaks => false,
      p_escape           => true,
      p_attributes       => p_item.element_attributes );
  ELSE
    l_name := APEX_PLUGIN.get_input_name_for_page_item(p_is_multi_value=>false);
    HTP.P('<input type="text" name="'||l_name||'" id="'||p_item.name||'"
'||'value="'||htf.escape_sc(p_value)||'" size="'||p_item.element_width||'"
'||'maxlength="'||p_item.element_max_length||'" '||p_item.element_attributes||'
onKeyUp="fldRequired('''||p_item.name||''')" />'),
    APEX_JAVASCRIPT.add_library (p_name=> 'requiredInColor',p_directory =>
p_plugin.file_prefix,p_version => null );
  END IF;
  RETURN l_result;
END requiredInColor;

The code in Listing 12-3 adheres to the requirement shown in Listing 12-2. Following the declaration section, the first thing all plug-ins should do is detect whether the item has been identified as being read-only or printer-friendly. The l_name variable is used to store the name of the item on which the plug-in is based. To get the item name, the following function is called:

l_name := APEX_PLUGIN.get_input_name_for_page_item(p_is_multi_value=>false);

The call to this function includes a parameter, p_is_multi_value=>false. That parameter identifies the page item on which the plug-in is based as a single-value item. If the page item were a select list, for example, the value of the parameter would be set to true. The next two lines of code constitute most of the functionality of the plug-in. The line which begins with HTP.P actually renders the item as an HTML input item.

Note

Because of the way single and double quote marks are used in PL/SQL, reading the code in Listing 12-3 is difficult at best. To make writing this code a little easier, use an HTML editor to build the code that you want and paste it in to the PL/SQL section of the plug-in builder. You can then modify the code one piece at a time replacing literals with the appropriate variables.

Notice that the value of the item, p_value, is being "escaped" using the htf.escape_sc() function, which changes values like "<" to "&lt." This function should always be used when passing values from an HTML page, as a malicious user can easily modify input values to submit SQL injection commands.

Paste Listing 12-3's code into the PL/SQL Code section of the plug-in builder and set the Render Function Name in the Callbacks section to the name of the render function from the PL/SQL code. If you forget this step, you will get an error saying that there is no render function for the plug-in when you run a page that contains the item-based plug-in. Figure 12-3 shows the populated PL/SQL Code section along with the correct setting for the Render Function Name.

PL/SQL Code section of plug-in builder

Figure 12.3. PL/SQL Code section of plug-in builder

Since we want to change the background color of the field as soon as a character is entered, the onKeyUp JavaScript function is called. You can see that the HTP.P function uses onKeyUp to call a JavaScript program called fldRequired. So where is this JavaScript? It's contained within an "included file" called requiredInColor.js that has to be uploaded to a location where APEX can find it.

To keep things simple, we used the #PLUGIN_PREFIX# substitution string so that uploaded files are stored in the database instead of on the HTTP server. The contents of the requiredInColor.js file are shown in Listing 12-4. The file has but one simple function: to change the color of the background of the item ID passed to it depending on whether any text has been entered. The background is set to #FEA5AD, which is a light red color when empty and white when populated. This file is uploaded using the Files section of the plug-in builder, as shown in Figure 12-4.

Example 12-4. requiredInColor.js JavaScript File

function fldRequired(fldName)
{
  vValue = document.getElementById(fldName).value;
  if (!vValue)
    document.getElementById(fldName).style.background="#FEA5AD";
  else
   document.getElementById(fldName).style.background="#FFFFFF";
}

Note

The "Files" section referred to in Figure 12-4 will not be visible until you create the plug-in.

File Upload section of plug-in builder

Figure 12.4. File Upload section of plug-in builder

Once the JavaScript file is uploaded, the plug-in is ready to be used. Before moving on to using the new plug-in, it is important to discuss how you make changes to the uploaded JavaScript file. It is not as simple as making changes to the file and uploading it again, because APEX 4.0 does not allow the same file to be uploaded more than once. Instead, you have to delete the uploaded file and then re-upload it. While not insurmountable, the process is nothing short of annoying, as you must repeat the change/delete/upload cycle over and over while you're developing and testing your plug-in.

Note

Since you are building standard JavaScript to support your plug-in, you can build a very simple HTML page that uses your JavaScript so that you can more easily troubleshoot the code. When you have it working, you can then go through the delete/upload cycle in the plug-in builder.

The last step in preparing the plug-in for use is to set the Standard Attributes. In this example, we only need to set the Is Visible Widget attribute, which tells APEX to display items based on this plug-in. If you want any plug-in to be visible, this attribute must be set. Figure 12-5 shows the Standard Attribute settings for this plug-in and the verification that our JavaScript file has been uploaded.

Plug-in Standard Attributes

Figure 12.5. Plug-in Standard Attributes

Using Your New Plug-in

Now that your plug-in has been created, you can include it in any page in your application. It is important to note at this point that the plug-in you just created will only show up on the plug-in home page of the application where it was created. As discussed earlier in this chapter, existing plug-ins can be referenced in the Subscription section of the plug-in builder as opposed to being imported or re-created from scratch. Later in this chapter, we will cover sharing plug-ins by using the export and import features of the plug-in builder.

To test your new requiredInColor plug-in, you need to add it to a page. We modified an update form in the Buglist application. To add the new item type plug-in, edit the page (page 28 in our example) and create a new item. In Figure 12-6, the new item type is set to Plug-in.

Creating item type plug-in

Figure 12.6. Creating item type plug-in

In the next step of the process, the installed plug-ins are displayed as shown in Figure 12-7. In our environment, you can see there are several available plug-ins and we have selected Required In Color. This is the name we assigned to our plug-in at the time it was created. The internal name (com.enkitec.requiredInColor) will never show up in a list of available items.

Select an existing item type plug-in

Figure 12.7. Select an existing item type plug-in

From this point on, the item creation process is the same as it would be if you were adding a standard, APEX text item. The only deviation, as we will see shortly, is if you have added custom attributes to your plug-in. In that case, you will have the opportunity to populate those new attributes during the item creation process. Our first plug-in does not contain custom attributes, so the creation process will be very familiar. In Figure 12-8, notice that we are naming the item as normal. The field name is defaulted with a value like P28_X and we modify it to read P28_REQUIRED.

Name the new item type plug-in

Figure 12.8. Name the new item type plug-in

To actually make this new plug-in-based item useful, we need to associate it with some database column. In the final step of the create item wizard, you would normally choose Database Column as the source type and some existing column as the source. Let's say we want to use this new item for the cost column to show that this column is required. If we assign it to the cost column now, we will get an error at runtime since there is already another item assigned to that column. So for now, leave the Source Type set to the default (Static Assignment), as shown in Figure 12-9. The item will be added to the page and will operate the way we want it to, but the values entered in this item will not be stored in the database.

New item type plug-in, unpopulated

Figure 12.9. New item type plug-in, unpopulated

When you finish the creation process, run your page. In our case, this means executing a report and clicking the update icon to access the update form that contains our new item type plug-in. The update form, shown in Figure 12-10, contains our new, unpopulated field with a red background (you'll have to take our word for it since this page is monochrome).

The intention of this plug-in was to show the user that there is a field on the page that must contain a value. This is done visually by coloring the background of the page when the field is blank. When we enter a value, as shown in Figure 12-11, the characters show up and the background is changed back to standard white. Remember that our code only checks for a value, any value, and then changes the background color of the field. To make this plug-in more useful, you could further validate the input to ensure it meets the specifications of the system.

New item type plug-in, unpopulated

Figure 12.10. New item type plug-in, unpopulated

New item type plug-in, populated

Figure 12.11. New item type plug-in, populated

The intention of this plug-in was to show the user that there is a field on the page that must contain a value. That is done visually by coloring the background of the page item when it is blank. When we enter a value, as shown in Figure 12-11, the characters show up and the background is changed back to standard white.

Remember that this new item doesn't really do anything other than rotate the color of the background. To make this new item actually useful, we need to associate it with a database column so that we can use it just like the current Cost item. To make this happen, a few simple steps are required:

  1. Delete the current cost item from the page

  2. Modify the P28_REQUIRED item, changing its name to P28_COST

  3. Change the label to "Cost"

  4. Change the Source Type to Database Column

  5. Set the Item Source Value to the cost column

  6. Re-position the new item, if you like

Now when you run the application and put a value in the Cost item, the value will be written to the database when you submit the form. If you do a little testing, you will notice that when the form containing the plug-in-based item displays a record that has a cost value, the item is displayed with a red background. The reason for this is simple. The render function we wrote executes the fldRequired JavaScript function using the onKeyUp event. This event will not be fired when the form is displayed, so we need to add some code that will execute our JavaScript function. Listing 12-5 shows the addition of the APEX_JAVASCRIPT.on_load_code function, which does just what you'd expect it to. The nice thing about this function is that it operates in conjunction with an onLoad script you may be using in the HTML Body of the form.

Example 12-5. requiredInColor.js JavaScript File

function requiredInColor (
  p_item                in apex_plugin.t_page_item,
  p_plugin              in apex_plugin.t_plugin,
  p_value               in varchar2,
  p_is_readonly         in boolean,
  p_is_printer_friendly in boolean )
  return apex_plugin.t_page_item_render_result
is
  l_name varchar2(30);
  l_result      apex_plugin.t_page_item_render_result;

BEGIN
  IF p_is_readonly OR p_is_printer_friendly THEN
    APEX_PLUGIN_UTIL.print_hidden_if_readonly (
      p_item_name           => p_item.name,
      p_value               => p_value,
      p_is_readonly         => p_is_readonly,
      p_is_printer_friendly => p_is_printer_friendly );
    APEX_PLUGIN_UTIL.print_display_only (
      p_item_name        => p_item.name,
      p_display_value    => p_value,
      p_show_line_breaks => false,
      p_escape           => true,
      p_attributes       => p_item.element_attributes );
  ELSE
    l_name := APEX_PLUGIN.get_input_name_for_page_item(p_is_multi_value=>false);

    htp.p('<input type="text" name="'||l_name||'" id="'||p_item.name||'"
'||'value="'||htf.escape_sc(p_value)||'" size="'||p_item.element_width||'"
'||'maxlength="'||p_item.element_max_length||'" '||p_item.element_attributes||'
onKeyUp="fldRequired('''||p_item.name||''')"/>'),

    APEX_JAVASCRIPT.add_library (p_name=> 'requiredInColor',p_directory =>
p_plugin.file_prefix,p_version => null );

    APEX_JAVASCRIPT.add_onload_code (p_code => ' fldRequired('''||p_item.name||''')'),

  END IF;
  RETURN l_result;
END requiredInColor;

Replace the code in the PL/SQL section of the Required In Color plug-in and execute the page again. You can see that the Cost item is displayed with a white background for records that contain a Cost value. There is still much more you could do with this item. Adding real validation code to ensure that a value is entered and it is within specifications, for example, would be easy to do and you should be able to do that on your own.

Using Custom Attributes

Custom attributes are used to store metadata that can be added to any plug-in type. When adding a plug-in-based item to a page, you will be presented a prompt for each custom attribute attached to the type. You can assign up to ten custom attributes per plug-in, which allows for a great deal of flexibility. To keep things simple, we will assign a custom attribute to the Required In Color plug-in, which allows the developer to choose the color of the background of this item. We could add two attributes to control both the populated and unpopulated colors, but the mechanics are identical so we will stick with one in the interest of brevity.

The process of adding attributes is very simple. You navigate to the plug-in home page and edit the Required In Color plug-in. You'll notice that the Custom Attributes section reports that there are no attributes defined for this plug-in. Clicking the Add Attribute button displays the Edit Attribute page, as shown in Figure 12-12.

Edit Attribute page

Figure 12.12. Edit Attribute page

Starting at the top of the Attribute definition, the Scope of the attribute refers to how the attribute can be used once the plug-in has been created. The Scope has two settings: Component and Application. If we set it to Application, then the developer using this plug-in would only be able to set the value of this attribute one time for the entire application. Using the Component settings allows for this attribute to be defined each time the plug-in is used.

The Attribute field is set to a numeric value that will be used to identify the attribute in code. The Display Sequence determines the order in which the Attributes are displayed in the APEX Builder and the Label is used to describe the attribute. We set the label to "Required Field Color."

The Settings section allows you to define the method the developer will use to set the value of the attribute. As you can see in Figure 12-13, there are several options from which to choose. For this example, we will use the Text type, as we want the developer to be able to enter a color value. We set the default value to #FEA5AD, so the text item's background will be rendered in color even if the developer forgets to set the value. You can see the definitions of the other settings by clicking on their labels.

Attribute type selection

Figure 12.13. Attribute type selection

The Conditions and Help Text settings are the same as with other APEX objects. For this example, we left them blank and clicked the Create button to finish the Attribute creation process. So now we have a custom attribute, but it doesn't change anything because our code doesn't know about it. You can set the value of this attribute by editing the item that is based on this plug-in, as shown in Figure 12-14.

Item edit page

Figure 12.14. Item edit page

Your plug-in's custom attribute (or attributes) now show up in their own section on the item edit page called "Settings." For plug-ins with no custom attributes, the Settings section will not be displayed. You can see the default value that was set when the custom attribute was created, but you can change it to any value you like (just make sure it is a valid HTML color). To include our new attribute in the action, we have to make some code changes. First, we need to change the plug-in's PL/SQL code so that it has access to the attribute's value. Then we will modify the requiredInColor.js JavaScript file to use the new attribute value to paint the background of the item. Listing 12-6 shows the changes we made to the PL/SQL code. A new variable, l_background, is used to store the value of the single, custom attribute we added to the plug-in. We used the following declaration to gain access to the attribute's value:

l_background apex_application_page_items.attribute_01%type := p_item.attribute_01

When the l_background variable was defined, we had to know that the attribute used to store the background color was attribute number 1 because we can't refer to it by name. This was easy in our example since we only have one, but if you had multiple custom attributes on the plug-in, you would have to be sure about which ones you were using.

We also had to change the calls to htp.p and the APEX_JAVASCRIPT.add_onload_code to include the l_background parameter in when the fldRequired JavaScript function is called.

Note

When you're making changes to HTML code within the PL/SQL section, it can be excruciatingly difficult to get the single and double quotes right. A trick we use is to cut a piece of the code out and modify it slightly so that it can be executed in a simple, "select <some HTTP> from dual" statement in SQL*Plus. Once you see what the output looks like, you can change it a little at a time to make sure you have it right.

Example 12-6. Plug-in PL/SQL Code Modified to Use Custom Attributes

function requiredInColor (
  p_item                in apex_plugin.t_page_item,
  p_plugin              in apex_plugin.t_plugin,
  p_value               in varchar2,
  p_is_readonly         in boolean,
  p_is_printer_friendly in boolean )
  return apex_plugin.t_page_item_render_result
is
  l_name varchar2(30);
  l_result      apex_plugin.t_page_item_render_result;
  l_background  apex_application_page_items.attribute_01%type := p_item.attribute_01;
BEGIN
  IF p_is_readonly OR p_is_printer_friendly THEN
    APEX_PLUGIN_UTIL.print_hidden_if_readonly (
      p_item_name           => p_item.name,
      p_value               => p_value,
      p_is_readonly         => p_is_readonly,
      p_is_printer_friendly => p_is_printer_friendly );
    APEX_PLUGIN_UTIL.print_display_only (
p_item_name        => p_item.name,
      p_display_value    => p_value,
      p_show_line_breaks => false,
      p_escape           => true,
      p_attributes       => p_item.element_attributes );
  ELSE
    l_name := APEX_PLUGIN.get_input_name_for_page_item(p_is_multi_value=>false);

    htp.p('<input type="text" name="'||l_name||'" id="'||p_item.name||'"
'||'value="'||htf.escape_sc(p_value)||'" size="'||p_item.element_width||'"
'||'maxlength="'||p_item.element_max_length||'" '||p_item.element_attributes||'
onKeyUp="fldRequired('''||p_item.name||''','''||l_background||''')"/>'),

    APEX_JAVASCRIPT.add_library (p_name=> 'requiredInColor',p_directory =>
p_plugin.file_prefix,p_version => null );

        APEX_JAVASCRIPT.add_onload_code (p_code => '
fldRequired("'||p_item.name||'","'||l_background||'")')

  END IF;

  RETURN l_result;

END requiredInColor;

To round out the changes related to our custom attribute, we need to modify the requiredInColor.js file, as it needs to receive two parameters instead of one. All we need to do is add a parameter, bkgColor, in the declaration of the fldRequired function and then use that variable name to color the item's background when it's empty. The new code is shown in Listing 12-7.

Example 12-7. requiredInColor JavaScript Modified to Use Custom Attributes

function fldRequired(fldName,bkgColor)
{
  vValue = document.getElementById(fldName).value;
  if (!vValue)
  {
    document.getElementById(fldName).style.background=bkgColor;
  }
  else
  {
    document.getElementById(fldName).style.background="#FFFFFF";
  }
}

We're not done just yet, however. Remember that we have to upload the requiredInColor.js file so the plug-in has access to our changes. If a file with this name is already associated with the plug-in (which it is), we must first delete it as APEX 4.0 does not allow you implicitly replace an uploaded file. Using the edit plug-in page as shown in Figure 12-15, you must edit the existing requiredInColor file so you can delete it; then upload it again so our new changes are available.

Item edit page

Figure 12.15. Item edit page

Once you apply the changes to the plug-in, you can execute your form. It should look just like it did before because the default value of the plug-in's attribute is the same. Try modifying the value of the "Required Field Color" setting on the P28_COST_REQUIRED item to some other color. Figure 12-16 shows our attribute being changed to yellow.

Custom Attribute setting changed

Figure 12.16. Custom Attribute setting changed

When you execute the page now, as shown in Figure 12-17, the Cost item's background is yellow when the value is null. You may have to back out the existing value to see the color change. Since we're talking in black and white, you'll have to take our word for it.

Custom Attribute setting changed

Figure 12.17. Custom Attribute setting changed

The functionality we created with this plug-in was pretty simple, but it took quite a bit of work to get it done. Now that it's done, though, it can be used everywhere and made available to fellow APEX developers worldwide.

Note

Once you create an object that is based on a plug-in, you will not be able to delete the plug-in. This makes sense since the items using the plug-in would cease to operate if you delete it. This can be confusing, however, since the Delete button does not show up on the Edit Plug-in page if the plug-in is in use. To quickly locate the where the plug-in is use, you can view the plug-in Utilization report.

Exporting Plug-ins

When plug-ins are created, they are visible only to the application in which they were created. To make your plug-ins available to other applications, they must be exported and subsequently imported into other applications. If you choose to share your plug-ins with the APEX developer community, you would export them and upload them to an APEX plug-in repository, like the following:

  • Oracle Applications Express Plug-ins page (www.oracle.com/technetwork/developer-tools/apex/application-express/apex-plug-ins-182042.html)

  • APEX-PLUGIN.com (www.apex-plugin.com)

There are three ways to export plug-ins, but only one of them works. Following is the one approach that works:

  • Exporting a plug-in from within the application in which it was created

However, the following two approaches do not work:

  • Exporting a plug-in from the Application Builder home page

  • Exporting a plug-in from an application other than where it was created

That only one approach works can be confusing since all three processes force you to select the application from which to export the plug-in (as we will see shortly), even if you are in the application where the plug-in was created. All three of the plug-in export processes will tell you that your plug-in was successfully exported, but you may not realize that the promise is false until you try to import the plug-in. The two processes that don't work generate an invalid export file. This is likely a bug that will be corrected in a later version of APEX 4.0. For now, just use the export plug-in feature inside your application.

So, to perform a successful plug-in export, navigate to the Plug-ins home page in the Shared Components section of the application in which you created the plug-in. Figure 12-18 shows the plug-ins home page for Application 101 and the "Export Plug-in" link in the Tasks region.

Export Plug-in Task on Plug-in home page

Figure 12.18. Export Plug-in Task on Plug-in home page

Notice that the plug-in we want to export, Required in Color, exists in this application. To start the actual export process, you click on the Export Plug-in link in the Tasks window. In Figure 12-19, you can see that even though you are editing the application that contains the Required in Color plug-in, you are still required to select the application that contains the plug-in you want to export.

Set Application step in the Export Plug-in process

Figure 12.19. Set Application step in the Export Plug-in process

Clicking the Set Application button takes you to the next step in the process where you can select the plug-in. Figure 12-20 shows that you can select both the plug-in to export as well as the file format (UNIX or DOS) to create. We selected the requiredInColor plug-in and set the File Format to UNIX. The character set of the export file defaults to that of the application.

Select the Plug-in to Export and the File Format to use

Figure 12.20. Select the Plug-in to Export and the File Format to use

To complete the export plug-in process, press the Export Plug-in button. The interesting thing about this step is that APEX does not store an output file for you. Instead, it creates an export file on the fly, which is downloaded to your browser, as shown in Figure 12-21. You can either open the file or save it to your file system. The point of this exercise is to make the plug-in available to other applications, so you should save it. Listing 12-8 shows the valid file created during the export process. Notice that it is a SQL file that you could execute in SQL*Plus.

Browser download window for Plug-in Export

Figure 12.21. Browser download window for Plug-in Export

Example 12-8. Exported Plug-in File

set define off
set verify off
set serveroutput on size 1000000
set feedback off
WHENEVER SQLERROR EXIT SQL.SQLCODE ROLLBACK
begin wwv_flow.g_import_in_progress := true; end;
/
--       AAAA       PPPPP   EEEEEE  XX      XX
--      AA  AA      PP  PP  EE       XX    XX
--     AA    AA     PP  PP  EE        XX  XX
--    AAAAAAAAAA    PPPPP   EEEE       XXXX
--   AA        AA   PP      EE        XX  XX
--  AA          AA  PP      EE       XX    XX
--  AA          AA  PP      EEEEEE  XX      XX
prompt  Set Credentials...
begin
  -- Assumes you are running the script connected to SQL*Plus as the Oracle user APEX_040000
or as the owner (parsing schema) of the application.
wwv_flow_api.set_security_group_id(p_security_group_id=>nvl(wwv_flow_application_install.get_w
orkspace_id,1280317158978593));
end;
/
begin wwv_flow.g_import_in_progress := true; end;
/
begin
select value into wwv_flow_api.g_nls_numeric_chars from nls_session_parameters where
parameter='NLS_NUMERIC_CHARACTERS';
end;
/
begin execute immediate 'alter session set nls_numeric_characters=''.,''';
end;
/
begin wwv_flow.g_browser_language := 'en'; end;
/
prompt  Check Compatibility...
begin
-- This date identifies the minimum version required to import this file.
wwv_flow_api.set_version(p_version_yyyy_mm_dd=>'2010.05.13')
end;
/
prompt  Set Application ID...
begin
   -- SET APPLICATION ID
   wwv_flow.g_flow_id := nvl(wwv_flow_application_install.get_application_id,101);
   wwv_flow_api.g_id_offset := nvl(wwv_flow_application_install.get_offset,0);
null;
end;
/
prompt  ...plugins
--
--application/shared_components/plugins/item_type/com_enkitec_requiredincolor
begin
wwv_flow_api.create_plugin (
  p_id => 9050220872125872 + wwv_flow_api.g_id_offset
,p_flow_id => wwv_flow.g_flow_id
,p_plugin_type => 'ITEM TYPE'
,p_name => 'COM.ENKITEC.REQUIREDINCOLOR'
,p_display_name => 'Required In Color'
,p_image_prefix => '#PLUGIN_PREFIX#'
,p_plsql_code =>
'function requiredInColor ('||chr(10)||
'  p_item                in apex_plugin.t_page_item,'||chr(10)||
'  p_plugin              in apex_plugin.t_plugin,'||chr(10)||
'  p_value               in varchar2,'||chr(10)||
'  p_is_readonly         in boolean,'||chr(10)||
'  p_is_printer_friendly in boolean )'||chr(10)||
'  return apex_plugin.t_page_item_render_result'||chr(10)||
''||chr(10)||
'is'||chr(10)||
''||chr(10)||
'  l_name varchar2(30);'||chr(10)||
''||chr(10)||
'  l_result      apex_plugin.t_page_item_render_result;'||chr(10)||
''||chr(10)||
'  l_background  apex_applicati'||
'on_page_items.attribute_01%type := p_item.attribute_01;'||chr(10)||
''||chr(10)||
'BEGIN'||chr(10)||
''||chr(10)||
'  IF p_is_readonly OR p_is_printer_friendly THEN'||chr(10)||
'        '||chr(10)||
'    APEX_PLUGIN_UTIL.print_hidden_if_readonly ('||chr(10)||
'      p_item_name           => p_item.name,'||chr(10)||
'      p_value               => p_value,'||chr(10)||
'      p_is_readonly         => p_is_readonly,'||chr(10)||
'      p_is_printer_friendly => p_is_printer_friendly );'||chr(10)||
''||chr(10)||
'    APEX_PLUGIN_UTIL.print_display_only ('||chr(10)||
' '||
'     p_item_name        => p_item.name,'||chr(10)||
'      p_display_value    => p_value,'||chr(10)||
'      p_show_line_breaks => false,'||chr(10)||
'      p_escape           => true,'||chr(10)||
'      p_attributes       => p_item.element_attributes );'||chr(10)||
'     '||chr(10)||
'  ELSE'||chr(10)||
''||chr(10)||
'    l_name := APEX_PLUGIN.get_input_name_for_page_item(p_is_multi_value=>false);'||chr(10)||
''||chr(10)||
'    htp.p(''<input type="text" name="''||l_name||''" id="''||p_item.name||''"
''||''value="''||htf.escape_sc('||
'p_value)||''" size="''||p_item.element_width||''"
''||''maxlength="''||p_item.element_max_length||''" ''||p_item.element_attributes||''
onKeyUp="fldRequired(''''''||p_item.name||'''''',''''''||l_background||'''''')"/>''),'||chr(10)||
''||chr(10)||
'    APEX_JAVASCRIPT.add_library (p_name=> ''requiredInColor'',p_directory =>
p_plugin.file_prefix,p_version => null );'||chr(10)||
''||chr(10)||
'    APEX_JAVASCRIPT.add_onload_code (p_code => '' fldRequired("''||p_item.name||''",'||
'"''||l_background||''")''),'||chr(10)||
'    '||chr(10)||
''||chr(10)||
'  END IF;'||chr(10)||
''||chr(10)||
'  RETURN l_result;'||chr(10)||
''||chr(10)||
'END requiredInColor;'
, p_render_function => 'requiredInColor'
, p_standard_attributes => 'VISIBLE'
, p_help_text => '<br />'||chr(10)||
''
, p_version_identifier => '1.0'
  );
wwv_flow_api.create_plugin_attribute (
  p_id => 9198214807522510 + wwv_flow_api.g_id_offset
,p_flow_id => wwv_flow.g_flow_id
,p_plugin_id => 9050220872125872 + wwv_flow_api.g_id_offset
,p_attribute_scope => 'COMPONENT'
,p_attribute_sequence => 1
,p_display_sequence => 10
,p_prompt => 'Required Field Color'
,p_attribute_type => 'TEXT'
,p_is_required => false
,p_default_value => '#FEA5AD'
,p_display_length => 7
,p_max_length => 7
,p_is_translatable => false
  );
null;

end;
/
begin
wwv_flow_api.g_varchar2_table := wwv_flow_api.empty_varchar2_table;
wwv_flow_api.g_varchar2_table(1) :=
'66756E6374696F6E20666C64526571756972656428666C644E616D652C626B67436F6C6F72290D0A7B0D0A0D0A616
C65727428626B67436F6C6F72293B0D0A0D0A20207656616C7565203D20646F63756D656E742E676574456C656D656
E744279496428';
wwv_flow_api.g_varchar2_table(2) :=
'666C644E616D65292E76616C75653B0D0A0D0A202069662028217656616C7565290D0A20207B0D0A20202020646F6
3756D656E742E676574456C656D656E744279496428666C644E616D65292E7374796C652E6261636B67726F756E643
D626B67436F6C';
wwv_flow_api.g_varchar2_table(3) :=
'6F723B0D0A20207D0D0A2020656C73650D0A20207B0D0A20202020646F63756D656E742E676574456C656D656E744
279496428666C644E616D65292E7374796C652E6261636B67726F756E643D2223464646464646223B0D0A20207D0D0
A0D0A7D0D0A';
null;
end;
/
begin
wwv_flow_api.create_plugin_file (
  p_id => 9186432599158222 + wwv_flow_api.g_id_offset
,p_flow_id => wwv_flow.g_flow_id
,p_plugin_id => 9050220872125872 + wwv_flow_api.g_id_offset
,p_file_name => 'requiredInColor.js'
,p_mime_type => 'application/x-javascript'
,p_file_content => wwv_flow_api.g_varchar2_table
  );
null;
end;
/
commit;
begin
execute immediate 'begin dbms_session.set_nls( param => ''NLS_NUMERIC_CHARACTERS'', value =>
'''''''' || replace(wwv_flow_api.g_nls_numeric_chars,'''''''','''''''''''') || ''''''''),
end;';
end;
/
set verify on
set feedback on
prompt  ...done

As you can see in Listing 12-8, the plug-in export file contains everything required to rebuild your plug-in in another application, including your JavaScript function. Now, all you have to do is import this plug-in into another application (discussed in the next section) or push it up to one of the public, plug-in repositories.

Importing Plug-ins

One of us had a high school chemistry teacher who would not let students use their TI-30 calculators until they could pass a test with a slide rule. While at first this seemed ridiculous, in the end, the additional knowledge was a tremendous help in problem solving. What we just went through (building a plug-in from scratch) was the equivalent of using a slide rule in chemistry class. Although we didn't cover every variation of plug-in development, you've seen the steps required to build one and you have a working example.

When you start thinking about building plug-ins, we highly recommend that you first search the plug-in repositories to see if the capability you need has already been created. While plug-ins are relatively new, there is already a developer community that is building plug-ins and publishing them for anyone to use, free of charge. In many cases, plug-in developers are wrapping jQuery features in APEX plug-in code, taking advantage of jQuery's vast array of features. If you don't already know about jQuery, it's worth a serious look. In general, jQuery is an open source, JavaScript library that enables an amazing array of client-side features with very little coding. Although jQuery is based on JavaScript, it has its own scripting language so there is a learning curve involved. Thanks to the APEX development community, many of the most popular jQuery features have been used to create APEX plug-ins and published so you can use them in your applications. The two main repositories can be found at these locations:

  • Oracle Applications Express Plug-ins page (www.oracle.com/technetwork/developer-tools/apex/application-express/apex-plug-ins-182042.html)

  • APEX-PLUGIN.com (www.apex-plugin.com)

Now, instead of building JavaScript, jQuery, or Ajax into your pages manually, you can simply import existing plug-ins that utilize the standardized APEX plug-in interface. If you find a plug-in that almost fits your requirements but not exactly, you can extend an imported plug-in to get exactly what you need. As an added bonus, you can potentially save a lot of time and you may learn a great deal by reviewing code that someone else wrote. We will explore these capabilities by downloading and importing a plug-in from Oracle's repository on the Internet.

In keeping with the Buglist application development process, I wanted to demonstrate a useful jQuery plug-in that would benefit the application. I settled on using the jQuery Star Rating plug-in to allow user feedback on the Edit Bug page. Although I've never seen a bug tracking system that allows the user to rate the development team's ability to address issues, I think it's a good idea.

What this control does is allow the user to hover the mouse over a line of stars and click one of them to represent a 1 out of 9 rating. By default, this control produces a 9-star rating, but by using one of the plug-in's custom attributes, you can choose how many stars should be displayed. The result of using the star rating is an integer between 0 and the number of stars selected. To make this plug-in useful to our application, we'll have to add a column to the Buglist table to store the rating value.

apexdemo@10gR2> alter table buglist add (rating number);
Table altered.

We'll start the import process by downloading the Star Rating plug-in from Oracle's Plug-in Repository. You can quickly navigate to this page using the "View Plug-in Repository" button on the Plug-in home page. Figure 12-22 shows the repository page from which you can download the Star Rating plug-in.

Downloading the Star Rating plug-in

Figure 12.22. Downloading the Star Rating plug-in

The plug-in is downloaded as a ZIP file, the contents of which are shown in Figure 12-23. The file ending in .sql contains the APEX code related to the exported plug-in. The sub-directories, server, and source contain supporting files like Cascading Style Sheets, JavaScript, jQuery code, and so forth. Not all plug-ins will have this structure, but all of them will have a ".sql" file.

Contents of the Star Rating plug-in ZIP file

Figure 12.23. Contents of the Star Rating plug-in ZIP file

To import the plug-in, you can use the Import feature either at the Application Builder home page or via the Plug-in button on the Plug-in home page. The import process is the same regardless of where you start. In Figure 12-24, the downloaded Star Rating plug-in is selected in the Import wizard. If you start from the Plug-in home page, the file type will be set to Plug-in by default, otherwise you need to set it. The character sets defaults to that of the exported plug-in, but it is recommended that Unicode UTF-8 is used, as this will cover the use of both single and multi-byte character sets.

Importing the Star Rating plug-in

Figure 12.24. Importing the Star Rating plug-in

All that's left to do in the import process is to identify in which application this plug-in will be used. In our example, we chose application 101 BUGLIST. The result of the installation step is that you are presented with the Edit Plug-ins page populated with the details of the Star Rating, as shown in Figure 12-25. You still have to Apply Changes to finalize the import process.

Star Rating plug-in details

Figure 12.25. Star Rating plug-in details

Your imported plug-in is now ready to use. As discussed previously, we will add the Star Rating plug-in to the Edit Bug page so that the user can rate the developers (what a concept). You add the Star Rating to your page as an Item of type Plug-in. Aside from the normal item attributes, you get to set the number of stars you want in your rating, as shown in Figure 12-26. The default is 9, but of course you can change the plug-in so that it defaults to whatever number you want. For this example, we set the value to 5.

Star Rating plug-in Custom Attributes

Figure 12.26. Star Rating plug-in Custom Attributes

The final step in implementing the Star Rating plug-in is to identify the source for the P29_RATING item. We created a column in the Buglist table called RATING, so we set the source type to database column and the Database Column Name to RATING, leaving all other attributes at their default value. Now when we run the Edit Bug page, the Rating (or Star Rating to be exact) is shown below the Cost item (our built-from-scratch plug-in). As you can see in Figure 12-27, the user can easily set and view the rating given to this particular bug.

Edit Bug page with Star Rating plug-in

Figure 12.27. Edit Bug page with Star Rating plug-in

That was pretty easy, especially when compared to creating a simple plug-in from scratch. The simplicity of using this plug-in is a little deceiving in that the developer of the APEX plug-in had to know how to use the jQuery Star Rating feature and also how to wrap it in an APEX plug-in. We didn't show the text of the PL/SQL Code used to build this plug-in, as there just isn't enough space in this book to cover everything that's going on—including how jQuery itself works. Since the source of this plug-in is freely available, we encourage you to evaluate it, download, it and get to know it better my making some changes to it. If you can improve it significantly, maybe you can upload it to the repository so others can get the benefit as well.

You can also test the import process using the Required in Color plug-in we exported in the last section. The process will be the same as the Star Rating plug-in, except that the Required in Color plug-in does not have some of the attributes used in the Star Rating. Simply follow the steps in the import process and the import will be successful.

Summary

Plug-ins are a powerful new tool that should be exploited to the fullest. The modularization and standardization created by the plug-in architecture allow, for the first time, easy sharing of standard APEX building blocks. While it was always possible to build client-side code using JavaScript, sharing that code between pages or applications was a much more manual process.

The capabilities afforded by plug-ins is really only limited by a developer's desire (and maybe time and money). We didn't get into details on how to create process type, dynamic action, or region type plug-ins given that we only have one chapter to devote to the subject. We suggest that you go to Oracle's Plug-in repository and try them all out. If you find something useful, use it. If you can improve on it, do so and then give it back to the community.

In our description of the relative difficulty in creating plug-ins from scratch, we were certainly not suggesting that you never do it. What we were saying is that before you write some new, custom functionality, it may be well worth your time to scan the public, plug-in resources to see if what you are planning to do has already been done. Given the popularity and tremendous capability of the jQuery package, you will see more and more jQuery-based plug-ins pop up in the future.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset