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
Click Shared Components > Build Options > Create and create a Build Option like this
Click Shared Components > Automations > Create and create an automation like this. Note: Type the name exactly as below.
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
Click the pencil icon next to New Action in the Actions region
Change the Name from New Action to Seed & Publish
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;
Click Apply Changes
Tick the Stay on Page Checkbox
Change the Settings > Schedule Status to Active
Set the Advanced > Build Option to Dev Only and click Save Changes
Change Server-Side Condition > Condition Type to Rows returned
Set Server-Side Condition > Condition Type to
SELECT * FROM apex_application_trans_map WHERE primary_application_id = :APP_ID AND requires_synchronization = 'Yes'
Click Save & Run. This will Activate the schedule and perform the first run.
ENJOY!
BONUS
Create an alert to inform you that the Translated Application is not yet synchronized
Open Page 0
Create a Dynamic Content Region named Translated Application not synchronized in the Banner Slot Position of the page.
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;
Change the Template to Alert & Highlight the Background in Template Options
Set the Server-side Condition > Type to Rows returned
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'
Hook up the button functionality by adding a Dynamic Action to Page 0 like this
Create an Execute Server-side Code true action
apex_automation.execute( p_static_id => 'automatic-seed-publisher', p_run_in_background => true );
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!