Oracle9iAS Portal Developer Kit 
Implementing End-User Personalization in PL/SQL Portlets

Oracle9iAS Portal provides a set of APIs for storing and retrieving individual preferences for each unique portlet instance in a persistent manner.  It provides a unique identifier for each individual, a preference store automatically mapped by user, and access mechanisms for storing and retrieving personalization information in your PL/SQL portlets.  For an introductory overview of Preference Storage Services, please refer to the Primer on End-User Personalization. 

By default, when you enable end-user personalization, the "Customize" link appears on the title bar of your portlet.  Typically, this link leads to a form where users can choose settings for that portlet.

End-user personalization options are available through the wwpre_api_name and wwpre_api_value packages.

This article describes how the end-user personalization services are implemented using the Services Example.  It provides a guideline for adding the end-user personalization service to your portlet functionality.   

Assumptions

How to Use End-User Personalization

The general model for working with the preference store can be described as follows:

1. Create the preference path using the wwpre_api_name.create_path method.
2. Create the preference using the wwpre_api_name.create_name method. 
3. Set the preference values by providing the preference name and scoping level that you want to set the value for.  You can use the wwpre_api_value.set_value_as_varchar2, or  set_value_as_number or set_value_as_date methods for this purpose.
4. Get the preference values by providing the preference name and path whenever you want to retrieve the preference value.  You can use the wwpre_api_value.get_value_as_varchar2, or  get_value_as_number or get_value_as_date methods for this purpose.

IMPLEMENTING PREFERENCE STORAGE

This section describes how the preference storage service is implemented using the Service Example in PDK.  In the Services Example, the objective is to achieve the following functionality:

Note: You may have NLS requirements of your personalization functionality.  Like in the Services Example, you would first want to load the required NLS strings to the database.  The article Implementing NLS Services describes how string definitions are loaded into the database.

Implementation

This section describes how the preference storage service is implemented for end-user personalization.
  1. From the downloaded Services Example, review the services_portlet.pkb file which is the package body for this portlet.  An excerpt is provided below.
  2. The portlet path constant definition is added at the start for reference while creating the preference path.  This code segment is highlighted below.  
  3. The Services Example uses two preference names aliased PREFNAME_STRING and PREFNAME_TITLE. 
  4. Scroll to the section that contains the procedure register.  Your portlet needs to create a path for storing your preferences.  It makes a call to wwpre_api_name.create_path for creating the preference path as shown below.
  5. It calls wwpre_api_name.create_path for creating the preference name.  It takes the portlet path, name and description as input parameters.  Another input parameter is the type name that can indicate special value types. The NLSID type indicates that the value stored is an NLS id. The functions for setting and retrieving this type treat it the same as a number value.  Apart from that, whenever a preference store value of this type is exported or copied then so also are its associated NLS strings.  The last input parameter, the language, is obtained from a context API.  This code segment is highlighted below. 
  6.  

    create or replace package body services_portlet

    is

           --   Constants  --

        ...

        PORTLET_PATH constant varchar2(256):= 'oracle.portal.pdk.servicesportlet';

        --

        -- The following names are used to store user preferences in the preference store.

        --

        PREFNAME_STRING constant varchar2(30) := 'services_string';

        PREFNAME_TITLE constant varchar2(30) := 'services_title';

        ...

        procedure register

        (

            p_portlet_instance in wwpro_api_provider.portlet_instance_record

        )

        is

        begin

            --

            -- Create a path for the portlet instance. This is used to create

            -- the preferences for the portlet instance in the preference store.

            --

            wwpre_api_name.create_path( p_path => PORTLET_PATH 

                                                || p_portlet_instance.reference_path );

     

            --

            -- Create the names to store the portlet preferences.

            --

            wwpre_api_name.create_name( p_path => PORTLET_PATH 

                                                || p_portlet_instance.reference_path,

                                        p_name => PREFNAME_STRING,

                                        p_description => 'Single custom row in ' 

                                                   || 'Introductory Example portlet.',

                                        p_type_name => 'NLSID'

                                        p_language => wwctx_api.get_nls_language);

            wwpre_api_name.create_name( p_path => PORTLET_PATH 

                                                || p_portlet_instance.reference_path,

                                        p_name => PREFNAME_TITLE,

                                        p_description => 'Single custom row in ' 

                                                   || 'Introductory Example portlet.',

                                        p_type_name => 'NLSID'

                                        p_language => wwctx_api.get_nls_language);

         ...

     end SERVICES_PORTLET;

    /

  7. Similarly it makes a delete_path call in the deregister procedure.
  8.  

    CREATE OR REPLACE

    package body SERVICES_PORTLET

    is

        procedure deregister

        (

            p_portlet_instance in wwpro_api_provider.portlet_instance_record

        )

        is

        begin

            --

            -- Delete the path used by the portlet instance. This will delete

            -- all the names and all the values associated with the path.

            --

            wwpre_api_name.delete_path( p_path => PORTLET_PATH 

                                                || p_portlet_instance.reference_path );

        exception

            when others then

               raise;

        end deregister; 

         ...

     end SERVICES_PORTLET;

    /

  9. Now you need to call the wwpre_api_value.set_value and get_value methods to appropriately handle your functionality.
  10. Observe how the get_default_preference function is coded. This function loads the system level default values from the preference store. The default preferences are associated with an instance. These are NLS strings set in the database.
  11.  

    CREATE OR REPLACE

    package body SERVICES_PORTLET

    is

        function get_default_preference

        (

            p_language in varchar2,

            p_reference_path in varchar2

        )

        is

        begin

            --

            -- Try to find a previously entered portlet instance string preference, if any.

            -- A portlet instance string preference is stored in the preference

            -- store and has a level of SYSTEM_LEVEL_TYPE.

            --

            l_prefs.string_id := to_char(wwpre_api_value.get_value_as_number(

                       p_path => PORTLET_PATH || p_reference_path,

                       p_name => PREFNAME_STRING,

                       p_level_type => wwpre_api_value.SYSTEM_LEVEL_TYPE

                       ));

            --

            -- If the value returned above is null it is an indication that there is

            -- no default string yet. Initialize the string id to 0 to indicate this

            -- and load the default string value.

            --

            if (l_prefs.string_id is null or to_number(l_prefs.string_id) = 0) then

                  wwpre_api_value.set_value_as_number(

                               p_path => PORTLET_PATH || p_reference_path,

                               p_name => PREFNAME_STRING,

                               p_level_type => wwpre_api_value.SYSTEM_LEVEL_TYPE,

                               p_level_name => null,

                               p_value => 0

                               );

         ...

     end SERVICES_PORTLET;

    /

  12. Observe how the show procedure and show_edit procedure have been implemented.

    The show_edit method renders the screen in show edit or show edit defaults mode. It renders two text fields that allow the user to change the customizable values in a form with three buttons (Apply, OK and Cancel). Note that the this function uses the wwpro_api_adapter.open_form to create the HTML form with the correct action attribute for the <FORM> tag and with the correct hidden fields. It is important to use this procedure to create the <FORM> tag if you want to use the portlet with the Federated Portal Adapter from remote portal instances.

    Note also how the 'p_action' hidden item is populated to one of 'APPLY', 'CANCEL' or 'OK' when one of the buttons is pressed. Once the form is submitted the show procedure of the portlet is called again and if the 'p_action' parameter is not null then the save_prefs procedure is called to save the customisations and redirect to the relevant page once this is done.


        procedure show
        (
            p_portlet_record        wwpro_api_provider.portlet_runtime_record
        )
        is
            l_str varchar2(32000);
            l_pref_record preference_record;
            l_action   varchar2(10);
            l_names    owa.vc_arr;
            l_values   owa.vc_arr;
     
        begin
            ...
            --
            --  P_MODE = EDIT or EDIT DEFAULTS
            --
            elsif (p_portlet_record.exec_mode =
                          wwpro_api_provider.MODE_SHOW_EDIT)
                or (p_portlet_record.exec_mode =
                          wwpro_api_provider.MODE_SHOW_EDIT_DEFAULTS)

            then
     
                wwpro_api_parameters.retrieve(l_names, l_values);
                for i in 1..l_names.count loop
                    if (upper(l_names(i)) = upper('p_string')) then
                        l_pref_record.string := l_values(i);
                    elsif l_names(i) = 'p_title' then
                        l_pref_record.title_string := l_values(i);
                    elsif l_names(i) = 'p_action' then
                        l_action := l_values(i);
                    end if;
                end loop;
     
     
                if (l_action in (ACTION_OK,ACTION_APPLY,ACTION_CANCEL)) then
                    if (p_portlet_record.exec_mode =
                                          wwpro_api_provider.MODE_SHOW_EDIT) then
                        save_prefs(p_string => l_pref_record.string,
                                   p_title  => l_pref_record.title_string,
                                   p_action => l_action,
                                   p_level  => wwpre_api_value.USER_LEVEL_TYPE,
                                   p_portlet_record  => p_portlet_record);
                    else
                        save_prefs(p_string => l_pref_record.string,
                                   p_title  => l_pref_record.title_string,
                                   p_action => l_action,
                                   p_level  => wwpre_api_value.SYSTEM_LEVEL_TYPE,
     
                                   p_portlet_record  => p_portlet_record);
                    end if;
                else
                    show_edit(p_portlet_record  => p_portlet_record);
                end if;
            ...
            end;
        end show;

    procedure show_edit
    (
        p_portlet_record in wwpro_api_provider.portlet_runtime_record
    )
    is
        l_prefs    preference_record;
        l_text_prompt_string  varchar2(30);
        l_title_prompt_string varchar2(30);
    begin
        ... 
        htp.centeropen;
        htp.tableOpen(cattributes => 'BORDER="1" WIDTH=90%');
     
        htp.tableRowOpen;
        htp.p('<TD>');
     
        --
        --  This procedure call creates the <FORM> tags with a set of
        --  standard parameters.  Using this procedure makes the
        --  customisation page work through the Federated Portal adapter.
        --
        wwpro_api_adapter.open_form(p_formattr => 'NAME="services"',
                                    p_prr      => p_portlet_record);
     
        ...
        htp.p('<TD>');
        htp.tableRowClose;
        htp.tableClose;
        htp.centerclose;
     
        htp.formclose;
        ...
    end show_edit;
  13. Review the following procedures and functions which are related to the preference storage implementation in the Services Example:
    1. get_user_preference : Retrieves the user customized string and title for the portlet.
    2. save_prefs : When user hits the OK or Apply button after making customization changes, this is invoked to save the preferences to the preference store.
    3. entered_text_is_valid : Checks to see if the entered text in the customizable text fields are valid or not.

 

VIEW PORTLET FUNCTIONALITY

  1. Create a page and add the Services portlet to your page.  Click on the 'Customize' link and make changes to your preferences for the given string and title of the portlet. Take note of how the preferences are stored and retrieved from the preference store.

 


Revision History