Create an Automatic Seed & Publisher for your APEX Translated Applications

Create an Automatic Seed & Publisher for your APEX Translated Applications

In this blog we are going to automate the Seed & Publish feature in Oracle APEX for Translated Applications. This is normally a manual process that is run before test of translated applications or before export.

This manual process is time consuming and can often be forgotten leading to errors and frustration. Therefore we are going automate it using an APEX Automation. The benefit is that developers can code in piece, with the knowledge that seeding & translating is going on in the background.

We are going to configure the Automation so that

  • It runs every minute (this can be tuned to your preference, e.g Daily out of hours for example).

  • It only runs on Development (Accomplished through a Build Option. APEX Automations are usually deactivated on import anyway... however this is a belt and braces approach)

  • It only executes when a synchronization is required

  • It only logs when a synchronization is required (i.e the Execution Log is not flooded with No Action events)

  • It logs a bunch of timing statistics when it does run

Steps

  1. Click Shared Components > Build Options > Create and create a Build Option like this

  2. Click Shared Components > Automations > Create and create an automation like this. Note: Type the name exactly as below.

  3. Click Next and use this query

     SELECT *
       FROM apex_application_trans_map
      WHERE primary_application_id = :APP_ID
        AND requires_synchronization = 'Yes'
    

    like this

    Click Create

  4. Click the pencil icon next to New Action in the Actions region

  5. Change the Name from New Action to Seed & Publish

  6. Add this to the code section

     DECLARE 
       l_primary_application_id_c    CONSTANT apex_application_trans_map.primary_application_id%TYPE DEFAULT :PRIMARY_APPLICATION_ID;
       l_translated_application_id_c CONSTANT apex_application_trans_map.primary_application_id%TYPE DEFAULT :TRANSLATED_APPLICATION_ID;
       l_translated_app_language_c   CONSTANT apex_application_trans_map.translated_app_language%TYPE DEFAULT :TRANSLATED_APP_LANGUAGE;
    
       l_start_time   PLS_INTEGER;
       l_seed_time    PLS_INTEGER;
       l_publish_time PLS_INTEGER;
       l_total_time   PLS_INTEGER;
     BEGIN
       -- Start
       apex_automation.log_info( p_message => apex_string.format('Seeding & Publishing Translated Application %0 with Language %1 for Primary Application %2',
        l_translated_application_id_c,
        l_translated_app_language_c,
        l_primary_application_id_c
        ));
    
       -- Start timing for the whole process
       l_start_time := DBMS_UTILITY.GET_TIME;
    
       -- Seeding
       l_seed_time := DBMS_UTILITY.GET_TIME; -- Start timing for Seeding
       apex_lang.seed_translations(
           p_application_id => l_primary_application_id_c,
           p_language => l_translated_app_language_c);
    
       l_seed_time := DBMS_UTILITY.GET_TIME - l_seed_time; -- Calculate Seeding duration
    
       FOR c1 IN (SELECT COUNT(*) thecount
                    FROM apex_application_trans_repos
                   WHERE application_id = l_primary_application_id_c) LOOP
           apex_automation.log_info('Potentially translatable strings found: ' || c1.thecount);
       END LOOP;
    
       -- Publishing
       l_publish_time := DBMS_UTILITY.GET_TIME; -- Start timing for Publishing
       apex_lang.publish_application(
           p_application_id => l_primary_application_id_c,
           p_language => l_translated_app_language_c);
    
       l_publish_time := DBMS_UTILITY.GET_TIME - l_publish_time; -- Calculate Publishing duration
    
       -- Calculate total time
       l_total_time := DBMS_UTILITY.GET_TIME - l_start_time;
    
       -- Logging the timings
       apex_automation.log_info(p_message => 'Seeding took: ' || l_seed_time / 100 || ' seconds');
       apex_automation.log_info(p_message => 'Publishing took: ' || l_publish_time / 100 || ' seconds');
       apex_automation.log_info(p_message => 'Total process took: ' || l_total_time / 100 || ' seconds');
    
     END;
    
  7. Click Apply Changes

  8. Tick the Stay on Page Checkbox

  9. Change the Settings > Schedule Status to Active

  10. Set the Advanced > Build Option to Dev Only and click Save Changes

  11. Change Server-Side Condition > Condition Type to Rows returned

  12. Set Server-Side Condition > Condition Type to

    SELECT *
      FROM apex_application_trans_map
     WHERE primary_application_id = :APP_ID
       AND requires_synchronization = 'Yes'
    
  13. Click Save & Run. This will Activate the schedule and perform the first run.

  14. ENJOY!

BONUS

Create an alert to inform you that the Translated Application is not yet synchronized

  1. Open Page 0

  2. Create a Dynamic Content Region named Translated Application not synchronized in the Banner Slot Position of the page.

  3. Set the PL/SQL Function Body returning a CLOB to

     DECLARE
        l_body               CLOB DEFAULT NULL;
        l_session_lang_c     CONSTANT VARCHAR2(32) DEFAULT apex_util.get_session_lang;
        l_sync_now           VARCHAR2(512) DEFAULT 
                                 '<button type="button" class="t-Button t-Button--icon t-Button--iconLeft" onclick="apex.event.trigger(document,''sync-now'','''')"><span aria-hidden="true" class="t-Icon t-Icon--left fa fa-bolt"></span>Synchronize now</button>';
     BEGIN
    
         FOR x in (
             SELECT *
               FROM apex_application_trans_map
              WHERE primary_application_id = :APP_ID
                AND translated_app_language = l_session_lang_c
                AND requires_synchronization = 'Yes'
                 )
         LOOP
             l_body := 
                 apex_string.format( 'The <b>%0</b> language for application <b>%1</b> has not synchronized with the latest changes. The scheduled synchronization job last ran %2. %3',
                                     l_session_lang_c,
                                     v('APP_TITLE'),
                                     apex_util.get_since( apex_automation.get_last_run_timestamp(p_static_id => 'automatic-seed-publisher') ),
                                     CASE WHEN apex_debug.tochar( apex_automation.is_running(p_static_id => 'automatic-seed-publisher' ) ) = 'true'
                                     THEN 'It is currently synchronizing.'
                                     ELSE 'It is not currently synchronizing. ' || l_sync_now
                                     END
                                      );
    
         END LOOP;
    
         RETURN l_body;
     END;
    
  4. Change the Template to Alert & Highlight the Background in Template Options

  5. Set the Server-side Condition > Type to Rows returned

  6. Set the SQL Query to

     SELECT *
       FROM apex_application_trans_map
      WHERE primary_application_id = :APP_ID
        AND translated_app_language = apex_util.get_session_lang
        AND requires_synchronization = 'Yes'
    
  7. Hook up the button functionality by adding a Dynamic Action to Page 0 like this

  8. Create an Execute Server-side Code true action

     apex_automation.execute(
     p_static_id => 'automatic-seed-publisher',
     p_run_in_background => true );
    
  9. Create an Execute JavaScript Code true action

     apex.page.cancelWarnOnUnsavedChanges();
     window.location.reload();
    

Try it out. This alert will only appear if the Translated Application is not yet synchronized. Clicking the button will spark off a new Synchronization run and refresh the page.

Conclusion

Some handy tricks yeah? It wouldn't take much effort to modify the above to run on non-development environments in the case that Developers forgot to synchronize before export. However I'll leave that to you, since I'm not a fan of dynamic modification of application metadata on non-development environments.

What's the picture? Its All Saints Church in Spofforth which dates back to Norman times. Visit Yorkshire!