Application Express: tips, tricks and best practice   Application Express: tips, tricks and best practice
Advanced options for the Rich Text Editor in APEX 5.1
Published APEX version Database version Cloud or on Premise
December 2017 5.1 and higher 11.2 and higher both

The Rich Text Editor item in Application Express allows to edit formatted text - unlike the simple textarea item which just allows to work with plain ASCII text. The rich text editor generates the formatted text as HTML code and sends that to Application Express on page submit. Most applications then simply store that HTML text into a database table.

The Rich Text Editor item type in Application Express

The Rich Text Editor item type in Application Express

The rich text editor is based on the Javascript CKEditor library. Page Designer does not offer too much configuration options ...

Rich Text editor in Page Designer

Rich Text editor in Page Designer

... but the CKEditor library offers a huge variety of configuration options. And Application Express 5.1 makes such advanced configurations very easy.

Configure Rich Text Editor with Javascript

In Page Designer, navigate to the settings of your Rich Text editor item and look up the Advanced section. The attribute Initialization Javascript Code is your entry to leverage the configuration options of CKEditor. A typical example for such a Javascript initialization function is as follows.

function ( configObject ) {
    configObject.{attribute-name-1} = {attribute-value-1};
    configObject.{attribute-name-2} = {attribute-value-2};
    :
    return configObject;
}

Application Express will call your function just before the editor is being initialized, passing in a Javascript object with the default configuration. In your code, that object is being changed and finally returned to Application Express. The Rich Text editor will then actually be initialized with the changed configuration object. To get an impression what this configuration object can do, have a look into CKEditor's API documentation.

Let's start with a few simple tests. The following Javascript function will set the editor's base color to green and we'll disable the resizing capability, so end users won't be able to resize the editor any more. After saving the function, reload the page to see the effects.

function ( configObject ) {
    configObject.uiColor        = "#AADC6E";
    configObject.resize_enabled = false;

    return configObject;
}
Rich Text editor, configured with Javascript

Rich Text editor, configured with Javascript

CKEditor offers many tweaks to control, how the actual HTML code will be generated from user inputs. For instance, hitting the Return key by default produces a new paragraph with the <p> HTML Tag.

By default, hitting [Return] ... ... produces a new paragraph.

By default, hitting [Return] ... produces a new paragraph

We can change that - now we want to have the editor produce simple line feeds using the <br> HTML Tag.

function ( configObject ) {
    configObject.enterMode      = 2;
    configObject.uiColor        = "#AADC6E";
    configObject.resize_enabled = false;

    return configObject;
}
Now, hitting [Return] ... ... produces a simple line feed.

Now, hitting [Return] ... produces a simple line feed

If you don't like the toolbar options which are provided by Application Express, simply configure your own set of options.

function ( configObject ) {
    configObject.enterMode      = 2;
    configObject.uiColor        = "#AADC6E";
    configObject.resize_enabled = false;
    configObject.toolbar        = [
            ['Link','Unlink','Anchor'],
            ['Image','Table','HorizontalRule','Smiley','SpecialChar'],
            '/',
            ['Bold','Italic','Underline','Strike','-','Subscript','Superscript','-', 'RemoveFormat'],
            ['NumberedList','BulletedList','-','Outdent','Indent','Blockquote']
        ];

    return configObject;
}
The Rich Text editor has a customized tool bar

The Rich Text editor has a customized tool bar

In CKEditor's API documentation you'll find much more attributes and explanations how these work. It's advisable to invest some time into this - at the end of the day you'll be able to get a Rich Text editor as your application requires it. All functionality can be leveraged.

Add an image uploading facility to the Rich Text Editor

Very often, end users want to add images to their rich text. When the Full tool bar is chosen or the 'Image' option is added to a custom tool bar, end users will have an icon in their tool bar which opens a dialog to add an image.

Add images to the rich text

Add images to the rich text

As the above image shows, that dialog expects an URL to the image. That is understandable from a technical point of view: The rich text editor produces HTML, and images are embedded into HTML documents with an <img> HTML Tag - the "href" attribute contains the URL to the actual image. So we need to upload the image and then reference it. What the end user actually wants, is a user-friendly dialog to upload an image or to choose from a list of already existing images. The concrete URL should be entered automatically.

For this very part of the article, we have an important requirement: We will add a REST service later on, so your APEX installation must use ORDS ORDS (Oracle REST Data Services) as the APEX web server. With the old mod_plsql or the PL/SQL Embedded Gateway this part of an image uploading functionality will not work.

The first question to be answered is where the uploaded images are being stored. In an APEX application, a table is the natural choice. So, as a first step, let's create this table. This example just contains the few minimal attributes like the image BLOB, the mime type or the file name columns.

create table tab_editor_images(
    id            number primary key,
    filename      varchar2(255),
    image_comment varchar2(4000),
    mimetype      varchar2(255),
    image         blob,
    uploaded_at   date,
    uploaded_by   varchar2(255) );

create sequence seq_editor_images;

create or replace trigger tr_pk_editor_images
before insert on tab_editor_images
for each row
begin
    :new.id          := seq_editor_images.nextval;
    :new.uploaded_at := sysdate;
    :new.uploaded_by := coalesce( v( 'APP_USER' ), sys_context( 'userenv', 'current_user' ) );
end;
/

Then we need a page where the end user can actually upload their images. That will, of course, be an APEX page. So, create a new page and add a Classic Report with the following SQL query. Configure the layout and attributes of the report columns as you wish.

select id,
       filename,
       image_comment,
       dbms_lob.getlength( image ) as image
  from tab_editor_images

Then add another region of the Static Content type either below or above the report region. Add a File Browse item (PX_IMAGE), Textarea (PX_COMMENT) and a Hidden item (PX_ID) to that region. Configure the File Browse item as follows (look up the Settings section on the property pane in Page Designer).

  • Storage Type: BLOB column specified in Item Source attribute
  • MIME Type Column: MIMETYPE
  • Filename Type Column: FILENAME
  • BLOB Last Updated Column: UPLOADED_AT
  • Display Download Link: No
  • File Types: image/*

In the Source section, set the Type to Database Column and enter IMAGE as the column name.

Configuring the File Browse item in Page Designer

Configuring the File Browse item in Page Designer

Then add a button to your page. Choose a good label (e.g. "Upload" or "Upload image"), but take care to set the (technical) button name to CREATE. Leave Button Action being set to Submit Page.

Before testing the page, navigate to the page attributes and there to the Page Template. Choose one without the navigation menu, e.g. "Minimal (No Navigation)". Having done that, the page should look as follows:

The layout of the image upload page is finished

The layout of the image upload page is finished

The page looks complete, but it's not. Right now, we can upload images, but nothing will be stored in the table. The page process, which actually stores a row into the table, is still missing. So let's create it. In Page Designer add a process to the After Submit section. Choose a Automatic Row Processing (DML) process and configure it as follows.

  • Table Name: TAB_EDITOR_IMAGES
  • Primary Key Column: ID
  • Primary Key Item: P2_ID
  • Supported Operations: Insert

Then restart the page and try uploading an image.

We now can upload images

We now can upload images

Next, we'll need download functionality. Each uploaded image must be accessible with a URL (that URL will be embedded into the HTML code by the Rich Text editor). Take care at this point: The HTML generated by Rich Text editor will be stored permanently in a table - so it's not a good idea to have APEX Session IDs or page items values in these URLs. We actually need typical web server URLs, which just consist of a server and a path part (http://server.com/path/to/the/image.png).

That can be achieved very easily by employing a RESTful service. Such a service can be created in APEX SQL Workshop within just a few minutes. Navigate to SQL Workshop and there to the RESTful Services area.

REST Services in SQL Workshop

REST Services in SQL Workshop

Click the Create button in order to create a new RESTful service. In the RESTful Service Module, set attributes as follows:

  • Name: Image Download Service
  • URI Prefix: image/ (note the trailing slash)

In the Resource Template area, use download/{id} as URI Template. In the Resource Handler section, choose GET as Method and Media Resource as Type. Use the following SQL query as Source.

select mimetype, image
  from tab_editor_images
 where id = :id

Then save everything by clicking the Create Module button. After that, click the new GET entry in the tree on the left and change the Requires Secure Access attribute to No.

Now you can test your new REST service. The URL scheme is as follows:

https://{apex-server}/ords/{workspace-name}/utilities/getImage/{image-id}

When using an ID for which an uploaded image exists, the REST service will return that image. If no image with that ID exists, it will respond with HTTP-404 ("Not found").

Now we have finished all preparations. The next task is to integrate these components with the Rich Text editor. Thus navigate to the page with your Rich Text editor on it, open Page Designer and navigate to the Rich Text editors Javascript Initialization Code attribute. Add javascript code to set CKEditors configuration attribute filebrowserBrowseUrl. The URL should point to the APEX page we just have created.

function ( configObject ) {
    configObject.filebrowserBrowseUrl = 
        "f?p=" + $v( "pFlowId" ) + ":2:" + $v( "pInstance" ) + "::" + $v( "pdebug" );
    configObject.filebrowserWindowWidth = 640;
}

Then run the page with the Rich Text editor. On the tool bar, click the icon to open the image dialog. That dialog now has a new button Browse Server. After clicking it, we'll be redirected to the APEX page we just have created.

The "Browse Server" button now redirects to our APEX page

The "Browse Server" button now redirects to our APEX page

On this page we want to choose one of the images from the report, which is then supposed to be passed back to CKEditor. The CKEditor API Documentation describes the process and also provides some Javascript code snippets. Thus navigate to the "Upload Image" APEX page and add the following Javascript code to the Function and Global Variable Declaration section within the page attributes. Take care to adjust the target URL pointing to the download image REST service to your environment.

function returnFileUrl( pId ) {
    var funcNum = getUrlParam( 'CKEditorFuncNum' ),
        // Achtung: hier anpassen!
        fileUrl = "{workspace-name}/utilities/getImage/" + pId;

    function getUrlParam( paramName ) {
        var reParam = new RegExp( '(?:[\?&]|&)' + paramName + '=([^&]+)', 'i' );
        var match = window.location.search.match( reParam );

        return ( match && match.length > 1 ) ? match[1] : null;
    }

    
    window.opener.CKEDITOR.tools.callFunction( funcNum, fileUrl );
    window.close();
}

Finally navigate to the classic report and there to the FILENAME column. Choose Link as Display Type and set the Link Target as follows:

javascript:returnFileUrl(#ID#)

Then you're done. Open the Image dialog within your Rich Text editor, click the Browse Server button, upload an image and click the file name in the Classic Report. The APEX page will be closed and the image URL (pointing to the REST service) is already filled in. You might adjust the image size and finally click the OK button.

Rich Text editor, with the upload image functionality Rich Text editor, with the upload image functionality
Rich Text editor, with the upload image functionality Rich Text editor, with the upload image functionality

Rich Text editor, with the upload image functionality

One final step is missing. When you open your new "Image dialog", using Rich Text Editor, you'll see that the page URL is indeed an APEX URL, but it contains some additional parameters specific to the Rich Text Editor.

...f?p={Application ID}:{Page ID}:{Session}::&CKEditor=P1_EDITOR&CKEditorFuncNum=1&langCode=en

The Javascript code, you have added in the section above, needs these parameters in order to pass the uploaded image URL back to the Rich Text Editor. However, if you now upload a new image (Upload button) the page will be submitted and then reloaded. Now the additional parameters will be lost. When you then click one of the images, the URL will not be passed back correctly.

So we need to tweak the redirection which APEX executes after a new image has been uploaded. First, use Page Designer to navigate to the Page Attributes, then to Advanced. Change the Reload on Submit to Always.

Change "Reload on Submit" Attribute to "Always"

Change "Reload on Submit" Attribute to "Always"

Add a new hidden item to your page. This item will store the additional URL parameters specific to the Rich Text Editor. Name this item P2_CKEDITOR_PARAMS. Then add a PL/SQL process being executed in the Pre-Rendering section, Before Header. Use the following PL/SQL code.

declare
    l_query_string        varchar2(32767);
    l_ckeditor_params_pos pls_integer;
begin
    l_query_string := owa_util.get_cgi_env( 'QUERY_STRING' );
    l_ckeditor_params_pos := instr( l_query_string, '&CKEditor=');
    if l_ckeditor_params_pos > 0 then
        :P2_CKEDITOR_PARAMS := substr( l_query_string, l_ckeditor_params_pos );
    end if;
end;
Add the P2_CKEDITOR_PARAMS item and PL/SQL code to capture the CKEditor URL parameters

Add the P2_CKEDITOR_PARAMS item and PL/SQL code to capture the CKEditor URL parameters

Then navigate to the Processing section and add a new Branch to be executed the After Processing. Use PL/SQL procedure as the branch type and use the following PL/SQL code. Don't forget to choose your File Upload button as the When Button Pressed condition.

begin
    owa_util.redirect_url('f?p=' || :APP_ID || ':' || :APP_PAGE_ID || ':' || :SESSION || '::' || :P2_CKEDITOR_PARAMS );
end;
Add a branch to your page to re-append the CKEditor URL Parameters after Image Upload

Add a branch to your page to re-append the CKEditor URL Parameters after Image Upload

Now you're done. You can now navigate to your editor, open the image dialog from Rich Text editor and either pick one of the existing images or upload a new one and use that after upload. Your Rich Text Editor has its own image upload facility - all done with APEX.

Responsive Rich Text editor

Last, but not least, the Rich Text editor can be made responsive. Responsive means, it automatically adjusts to the size of the container region.

Note that, starting with Application Express 18.1, the Rich Text Editor is responsive out of the box. The behavior is controlled by the Responsive Component setting of Rich Text Editor. If you are on APEX 18.1 or higher, first review the Responsive component setting by navigating to Shared Components, Component Settings, Rich Text Editor.

Review "Responsive" Component Setting of the Rich Text Editor

Review "Responsive" Component Setting of the Rich Text Editor

Changing the component setting to Yes (which is the default for new applications) leads all Rich Text Editors in your application to become responsive. This is recommended if your application uses the Universal Theme. If you are on APEX 5.1 or your application uses a legacy theme, you might apply the following instructions to manually enable responsiveness for your Rich Text Editor.

By default, the size of the Rich Text editor is determined by the Width and Height attributes, which are specified as (character) rows and columns, like for a text area. In the lower right corner, the Rich Text editor then provides a handle allowing the end user to manually resize the component.

The end user can manually resize the Rich Text editor

The end user can manually resize the Rich Text editor

However, when the browser window or the container region is too small, end users cannot even see (or use) the handle. It's sized to large; and does not respond to the screen size. What we need is a responsive Rich Text editor.

The Rich Text editor is not responsive out of the box

The Rich Text editor is not responsive out of the box

But with a few pieces of Javascript code, it's easy to add responsiveness to a Rich Text editor. This tipp has already been published on Joel Kallmans blog. Your application must use the Universal Theme for this to work. Again, the Javascript Initialization Code attribute is our entry poinz. Add the following configuration changes (and take care to adjust the "{PX_item-name}" placeholders with your Rich Text editor item name.

function ( configObject ) {
    :

    configObject.width      = $( "#{PX_item-name}" ).closest(".t-Form-inputContainer").width() - 5;
    configObject.height     = 300;  // Specify your desired item height, in pixels
    configObject.resize_dir = 'vertical'

    : 
}

After reloading your page, the Rich Text editor already adjusted to the width of the container region on page load. Since a horizontal resize does not make sense any more, we disabled that by setting CKEditor's resize_dir attribute to vertical. When the browser window is resized, the editor component right now does not respond, but this will change with the next step.

Create a new Dynamic Action which listens to the Resize event. Choose Execute Javascript Code as Action and use the following Javascript code (replace the {PX_item-name} placeholders with your actual item name).

CKEDITOR.instances.{PX_item-name}.resize( $("#{PX_item-name}" ).closest( ".t-Form-inputContainer" ).width() - 5, 300 );

Then reload the page. Now the Rich Text editor adjusts on page load as well as upon the browser window is resized.

We have a responsive Rich Text editor - 1
We have a responsive Rich Text editor - 2

We have a responsive Rich Text editor

The CKEditor component, which the APEX Rich Text editor is based on, offers a broad range of configuration options. This can be nicely combined with APEX dynamic actions, pages or other components like RESTful services in order to provide much more functionality than the default configuration options in Page Designer allow.

back to blogs.oracle.com/apex