From f58c983936525bae955ba070a25749b6c3d9a28d Mon Sep 17 00:00:00 2001 From: Jan Kvetina Date: Sun, 2 Jan 2022 13:46:27 +0100 Subject: [PATCH] Adjust navigation to multiple apps, shared pages --- apex/f770/application/pages/page_00910.sql | 85 ++++++++++++++++----- tables/navigation.sql | 7 +- triggers/navigation__.sql | 89 ++++++++++++++++++++++ views/nav_overview.sql | 83 +++++++++++++++----- views/nav_top.sql | 2 + 5 files changed, 224 insertions(+), 42 deletions(-) create mode 100644 triggers/navigation__.sql diff --git a/apex/f770/application/pages/page_00910.sql b/apex/f770/application/pages/page_00910.sql index 87bae5c..3e27265 100644 --- a/apex/f770/application/pages/page_00910.sql +++ b/apex/f770/application/pages/page_00910.sql @@ -22,7 +22,7 @@ wwv_flow_api.create_page( ,p_page_template_options=>'#DEFAULT#' ,p_required_role=>wwv_flow_api.id(9556407311505078) ,p_last_updated_by=>'DEV' -,p_last_upd_yyyymmddhh24miss=>'20220101183201' +,p_last_upd_yyyymmddhh24miss=>'20220102090736' ); wwv_flow_api.create_page_plug( p_id=>wwv_flow_api.id(9192009232668637) @@ -49,6 +49,7 @@ wwv_flow_api.create_page_plug( ,p_plug_display_point=>'BODY' ,p_query_type=>'TABLE' ,p_query_table=>'NAV_OVERVIEW' +,p_query_where=>'app_id = app.get_app_id()' ,p_include_rowid_column=>false ,p_plug_source_type=>'NATIVE_IG' ,p_plug_query_options=>'DERIVED_REPORT_COLUMNS' @@ -373,7 +374,7 @@ wwv_flow_api.create_region_column( ,p_item_type=>'NATIVE_DISPLAY_ONLY' ,p_heading=>'Css Class' ,p_heading_alignment=>'LEFT' -,p_display_sequence=>170 +,p_display_sequence=>180 ,p_value_alignment=>'LEFT' ,p_attribute_02=>'VALUE' ,p_attribute_05=>'PLAIN' @@ -399,7 +400,7 @@ wwv_flow_api.create_region_column( ,p_item_type=>'NATIVE_DISPLAY_ONLY' ,p_heading=>'Auth Scheme' ,p_heading_alignment=>'LEFT' -,p_display_sequence=>180 +,p_display_sequence=>190 ,p_value_alignment=>'LEFT' ,p_attribute_02=>'VALUE' ,p_attribute_05=>'PLAIN' @@ -471,7 +472,7 @@ wwv_flow_api.create_region_column( ,p_data_type=>'VARCHAR2' ,p_is_query_only=>true ,p_item_type=>'NATIVE_HIDDEN' -,p_display_sequence=>190 +,p_display_sequence=>200 ,p_attribute_01=>'Y' ,p_use_as_row_header=>false ,p_enable_sort_group=>true @@ -489,7 +490,7 @@ wwv_flow_api.create_region_column( ,p_item_type=>'NATIVE_DISPLAY_ONLY' ,p_heading=>'Sort Order' ,p_heading_alignment=>'LEFT' -,p_display_sequence=>200 +,p_display_sequence=>210 ,p_value_alignment=>'LEFT' ,p_attribute_02=>'VALUE' ,p_attribute_05=>'PLAIN' @@ -505,6 +506,39 @@ wwv_flow_api.create_region_column( ,p_is_primary_key=>false ,p_include_in_export=>true ); +wwv_flow_api.create_region_column( + p_id=>wwv_flow_api.id(12994588754936905) +,p_name=>'IS_SHARED' +,p_source_type=>'DB_COLUMN' +,p_source_expression=>'IS_SHARED' +,p_data_type=>'VARCHAR2' +,p_is_query_only=>false +,p_item_type=>'NATIVE_SINGLE_CHECKBOX' +,p_heading=>'Is Shared' +,p_heading_alignment=>'CENTER' +,p_display_sequence=>170 +,p_value_alignment=>'CENTER' +,p_attribute_01=>'N' +,p_attribute_02=>'Y' +,p_is_required=>false +,p_enable_filter=>true +,p_filter_operators=>'C:S:CASE_INSENSITIVE:REGEXP' +,p_filter_is_required=>false +,p_filter_text_case=>'MIXED' +,p_filter_exact_match=>true +,p_filter_lov_type=>'DISTINCT' +,p_use_as_row_header=>false +,p_enable_sort_group=>true +,p_enable_control_break=>true +,p_enable_hide=>true +,p_is_primary_key=>false +,p_duplicate_value=>true +,p_include_in_export=>true +,p_display_condition_type=>'EXPRESSION' +,p_display_condition=>'app.get_app_id() = app.get_core_app_id()' +,p_display_condition2=>'PLSQL' +,p_security_scheme=>wwv_flow_api.id(9556407311505078) +); wwv_flow_api.create_interactive_grid( p_id=>wwv_flow_api.id(9192298818668639) ,p_internal_uid=>9192298818668639 @@ -567,7 +601,7 @@ wwv_flow_api.create_ig_report_column( wwv_flow_api.create_ig_report_column( p_id=>wwv_flow_api.id(3864884157089) ,p_view_id=>wwv_flow_api.id(9266000528438793) -,p_display_seq=>14 +,p_display_seq=>15 ,p_column_id=>wwv_flow_api.id(10245444031410343) ,p_is_visible=>false ,p_is_frozen=>false @@ -665,7 +699,7 @@ wwv_flow_api.create_ig_report_column( wwv_flow_api.create_ig_report_column( p_id=>wwv_flow_api.id(9275579147438822) ,p_view_id=>wwv_flow_api.id(9266000528438793) -,p_display_seq=>11 +,p_display_seq=>12 ,p_column_id=>wwv_flow_api.id(9193367637668650) ,p_is_visible=>false ,p_is_frozen=>false @@ -677,7 +711,7 @@ wwv_flow_api.create_ig_report_column( wwv_flow_api.create_ig_report_column( p_id=>wwv_flow_api.id(9276444210438824) ,p_view_id=>wwv_flow_api.id(9266000528438793) -,p_display_seq=>12 +,p_display_seq=>13 ,p_column_id=>wwv_flow_api.id(9260068987429001) ,p_is_visible=>false ,p_is_frozen=>false @@ -685,7 +719,7 @@ wwv_flow_api.create_ig_report_column( wwv_flow_api.create_ig_report_column( p_id=>wwv_flow_api.id(9277393794438826) ,p_view_id=>wwv_flow_api.id(9266000528438793) -,p_display_seq=>13 +,p_display_seq=>14 ,p_column_id=>wwv_flow_api.id(9260126834429002) ,p_is_visible=>true ,p_is_frozen=>false @@ -715,6 +749,15 @@ wwv_flow_api.create_ig_report_column( ,p_is_visible=>true ,p_is_frozen=>false ); +wwv_flow_api.create_ig_report_column( + p_id=>wwv_flow_api.id(13011913824668274) +,p_view_id=>wwv_flow_api.id(9266000528438793) +,p_display_seq=>11 +,p_column_id=>wwv_flow_api.id(12994588754936905) +,p_is_visible=>true +,p_is_frozen=>false +,p_width=>100 +); wwv_flow_api.create_page_plug( p_id=>wwv_flow_api.id(12336291767960931) ,p_plug_name=>'Page Regions [GRID]' @@ -1103,6 +1146,18 @@ wwv_flow_api.create_interactive_grid( '}', '')) ); +wwv_flow_api.component_end; +end; +/ +begin +wwv_flow_api.component_begin ( + p_version_yyyy_mm_dd=>'2021.04.15' +,p_release=>'21.1.6' +,p_default_workspace_id=>9014660246496943 +,p_default_application_id=>770 +,p_default_id_offset=>0 +,p_default_owner=>'CORE' +); wwv_flow_api.create_ig_report( p_id=>wwv_flow_api.id(12685075256022330) ,p_interactive_grid_id=>wwv_flow_api.id(12336381739960932) @@ -1131,18 +1186,6 @@ wwv_flow_api.create_ig_report_column( ,p_is_frozen=>false ,p_width=>60 ); -wwv_flow_api.component_end; -end; -/ -begin -wwv_flow_api.component_begin ( - p_version_yyyy_mm_dd=>'2021.04.15' -,p_release=>'21.1.6' -,p_default_workspace_id=>9014660246496943 -,p_default_application_id=>770 -,p_default_id_offset=>0 -,p_default_owner=>'CORE' -); wwv_flow_api.create_ig_report_column( p_id=>wwv_flow_api.id(12685747957022334) ,p_view_id=>wwv_flow_api.id(12685246753022330) diff --git a/tables/navigation.sql b/tables/navigation.sql index 71df038..d27406c 100644 --- a/tables/navigation.sql +++ b/tables/navigation.sql @@ -7,6 +7,7 @@ CREATE TABLE navigation ( order# NUMBER(4), is_hidden CHAR(1), is_reset CHAR(1), + is_shared CHAR(1), -- only for CORE app -- updated_by VARCHAR2(30), updated_at DATE, @@ -26,7 +27,10 @@ CREATE TABLE navigation ( CHECK (is_hidden = 'Y' OR is_hidden IS NULL), -- CONSTRAINT ch_navigation_is_reset - CHECK (is_reset = 'Y' OR is_reset IS NULL) + CHECK (is_reset = 'Y' OR is_reset IS NULL), + -- + CONSTRAINT ch_navigation_is_shared + CHECK ((is_shared = 'Y' AND app_id = 770) OR is_shared IS NULL) -- CORE app_id ) STORAGE (BUFFER_POOL KEEP); -- @@ -38,4 +42,5 @@ COMMENT ON COLUMN navigation.parent_id IS 'Parent page id for tree structur COMMENT ON COLUMN navigation.order# IS 'Order of siblings'; COMMENT ON COLUMN navigation.is_hidden IS 'Y = dont show in menu'; COMMENT ON COLUMN navigation.is_reset IS 'Y = reset/clear all items not passed in url'; +COMMENT ON COLUMN navigation.is_shared IS 'Y = show these items in other apps, only for CORE app'; diff --git a/triggers/navigation__.sql b/triggers/navigation__.sql new file mode 100644 index 0000000..dbc48a6 --- /dev/null +++ b/triggers/navigation__.sql @@ -0,0 +1,89 @@ +CREATE OR REPLACE TRIGGER navigation__ +FOR UPDATE OR INSERT OR DELETE ON navigation +COMPOUND TRIGGER + + in_table_name CONSTANT user_tables.table_name%TYPE := 'NAVIGATION'; + -- + curr_log_id logs.log_id%TYPE; + curr_event_id log_events.log_id%TYPE; + curr_updated_by navigation.updated_by%TYPE; + curr_updated_at navigation.updated_at%TYPE; + -- + rows_inserted PLS_INTEGER := 0; + rows_updated PLS_INTEGER := 0; + rows_deleted PLS_INTEGER := 0; + -- + last_rowid ROWID; + + + + BEFORE STATEMENT IS + BEGIN + curr_log_id := app.log_trigger(in_table_name); + curr_updated_by := app.get_user_id(); + curr_updated_at := SYSDATE; + EXCEPTION + WHEN app.app_exception THEN + RAISE; + WHEN OTHERS THEN + app.raise_error('TRIGGER_FAILED', in_table_name); + END BEFORE STATEMENT; + + + + BEFORE EACH ROW IS + BEGIN + IF NOT DELETING THEN + :NEW.updated_by := curr_updated_by; + :NEW.updated_at := curr_updated_at; + END IF; + EXCEPTION + WHEN app.app_exception THEN + RAISE; + WHEN OTHERS THEN + app.raise_error('TRIGGER_FAILED', in_table_name); + END BEFORE EACH ROW; + + + + AFTER EACH ROW IS + new_json VARCHAR2(32767); + old_json VARCHAR2(32767); + BEGIN + IF INSERTING THEN + rows_inserted := rows_inserted + 1; + last_rowid := :NEW.ROWID; + ELSIF UPDATING THEN + rows_updated := rows_updated + 1; + last_rowid := :OLD.ROWID; + ELSIF DELETING THEN + rows_deleted := rows_deleted + 1; + last_rowid := :OLD.ROWID; + END IF; + EXCEPTION + WHEN app.app_exception THEN + RAISE; + WHEN OTHERS THEN + app.raise_error('TRIGGER_FAILED', in_table_name); + END AFTER EACH ROW; + + + + AFTER STATEMENT IS + BEGIN + app.log_success ( + in_log_id => curr_log_id, + in_rows_inserted => rows_inserted, + in_rows_updated => rows_updated, + in_rows_deleted => rows_deleted, + in_last_rowid => last_rowid + ); + EXCEPTION + WHEN app.app_exception THEN + RAISE; + WHEN OTHERS THEN + app.raise_error('TRIGGER_FAILED', in_table_name); + END AFTER STATEMENT; + +END; +/ diff --git a/views/nav_overview.sql b/views/nav_overview.sql index 93b53b9..1dabef5 100644 --- a/views/nav_overview.sql +++ b/views/nav_overview.sql @@ -1,15 +1,20 @@ CREATE OR REPLACE VIEW nav_overview AS WITH x AS ( SELECT - app.get_item('$PAGE_ID') AS page_id + app.get_item('$PAGE_ID') AS filter_page_id, + app.get_page_id() AS page_id, + app.get_core_app_id() AS core_app_id, + -- + COALESCE(CASE WHEN app.get_app_id() = app.get_core_app_id() THEN app.get_proxy_app_id() END, app.get_app_id()) AS app_id FROM users u WHERE u.user_id = app.get_user_id() ), t AS ( SELECT + n.app_id, n.page_id, - p.page_name, - p.page_title, + REPLACE(p.page_name, '&' || 'APP_NAME.', a.application_name) AS page_name, + REPLACE(p.page_title, '&' || 'APP_NAME.', a.application_name) AS page_title, p.page_alias, p.page_group, p.authorization_scheme, @@ -17,10 +22,13 @@ t AS ( LEVEL - 1 AS depth, CONNECT_BY_ROOT NVL(n.order#, n.page_id) AS page_root FROM navigation n + CROSS JOIN x LEFT JOIN apex_application_pages p ON p.application_id = n.app_id AND p.page_id = n.page_id - WHERE n.app_id = app.get_app_id() + LEFT JOIN apex_applications a + ON a.application_id = p.application_id + WHERE n.app_id IN (x.app_id, x.core_app_id) CONNECT BY n.parent_id = PRIOR n.page_id AND n.app_id = PRIOR n.app_id START WITH n.parent_id IS NULL @@ -34,7 +42,12 @@ SELECT t.page_alias, -- CASE WHEN r.page_id IS NULL - THEN REPLACE(LTRIM(RPAD('-', t.depth * 4), '-'), ' ', '&' || 'nbsp; ') || app.get_page_name(in_name => t.page_name) + THEN REPLACE(LTRIM(RPAD('-', t.depth * 4), '-'), ' ', '&' || 'nbsp; ') || + app.get_page_name ( + in_app_id => n.app_id, + in_page_id => n.page_id, + in_name => t.page_name + ) END AS page_name, -- t.page_title, @@ -42,6 +55,7 @@ SELECT -- n.is_hidden, n.is_reset, + n.is_shared, -- CASE WHEN t.authorization_scheme LIKE '%MUST_NOT_BE_PUBLIC_USER%' THEN app.get_icon('fa-check-square', 'MUST_NOT_BE_PUBLIC_USER') @@ -49,10 +63,15 @@ SELECT END AS auth_scheme, -- CASE WHEN n.page_id > 0 AND r.page_id IS NULL - THEN app.get_page_link(n.page_id, in_session_id => CASE WHEN n.page_id = 9999 THEN 0 END) + THEN app.get_page_link ( + in_page_id => n.page_id, + in_app_id => n.app_id, + in_session_id => CASE WHEN n.page_id = 9999 THEN 0 END + ) END AS page_url, -- 'UD' AS allow_changes, -- U = update, D = delete + -- t.page_root || '.' || t.depth || '.' || NVL(n.order#, n.page_id) AS sort_order, -- CASE @@ -61,19 +80,32 @@ SELECT END AS action, -- app.get_page_link ( - in_page_id => app.get_page_id(), - in_app_id => n.app_id, - in_names => 'P' || TO_CHAR(app.get_page_id()) || '_REMOVE_PAGE', - in_values => TO_CHAR(n.page_id) + in_page_id => x.page_id, + in_app_id => n.app_id, + in_names => 'P' || TO_CHAR(x.page_id) || '_REMOVE_PAGE', + in_values => TO_CHAR(n.page_id) ) AS action_url FROM navigation n CROSS JOIN x LEFT JOIN t - ON t.page_id = n.page_id + ON t.app_id = n.app_id + AND t.page_id = n.page_id LEFT JOIN nav_pages_to_remove r - ON r.page_id = n.page_id -WHERE (x.page_id = n.page_id OR x.page_id IS NULL) - AND n.app_id = app.get_app_id() + ON r.page_id = n.page_id +WHERE (x.filter_page_id = n.page_id OR x.filter_page_id IS NULL) + AND ( + n.app_id = x.app_id + OR ( + n.app_id = x.core_app_id + AND n.is_shared = 'Y' + AND n.page_id NOT IN ( + -- if page from CORE has same page number in current app, then skip it + SELECT n.page_id + FROM navigation n + WHERE n.app_id = x.app_id + ) + ) + ) -- UNION ALL SELECT @@ -83,35 +115,46 @@ SELECT n.order#, t.page_root || ' ' || n.page_group AS page_group, n.page_alias, - CASE WHEN n.parent_id IS NOT NULL THEN REPLACE(LTRIM(RPAD('-', 4), '-'), ' ', '&' || 'nbsp; ') END || app.get_page_name(in_name => n.page_name) AS page_name, + -- + CASE WHEN n.parent_id IS NOT NULL + THEN REPLACE(LTRIM(RPAD('-', 4), '-'), ' ', '&' || 'nbsp; ') + END || app.get_page_name(in_app_id => n.app_id, in_page_id => n.page_id, in_name => n.page_name) AS page_name, + -- n.page_title, n.css_class, -- 'Y' AS is_hidden, 'Y' AS is_reset, + NULL AS is_shared, -- CASE WHEN n.auth_scheme LIKE '%MUST_NOT_BE_PUBLIC_USER%' THEN app.get_icon('fa-check-square', 'MUST_NOT_BE_PUBLIC_USER') ELSE n.auth_scheme END AS auth_scheme, -- - app.get_page_link(n.page_id) AS page_url, + app.get_page_link ( + in_page_id => n.page_id, + in_app_id => n.app_id + ) AS page_url, + -- NULL AS allow_changes, -- no changes allowed + -- t.page_root || '.' || (t.depth + 1) || '.' || NVL(n.order#, n.page_id) AS sort_order, -- app.get_icon('fa-plus-square', 'Create record in Navigation table') AS action, -- app.get_page_link ( - in_page_id => app.get_page_id(), + in_page_id => x.page_id, in_app_id => n.app_id, - in_names => 'P' || TO_CHAR(app.get_page_id()) || '_ADD_PAGE', + in_names => 'P' || TO_CHAR(x.page_id) || '_ADD_PAGE', in_values => TO_CHAR(n.page_id) ) AS action_url FROM nav_pages_to_add n CROSS JOIN x LEFT JOIN t - ON t.page_id = n.parent_id -WHERE (x.page_id = n.page_id OR x.page_id IS NULL); + ON t.app_id = n.app_id + AND t.page_id = n.parent_id +WHERE (x.filter_page_id = n.page_id OR x.filter_page_id IS NULL); -- COMMENT ON TABLE nav_overview IS 'Enriched navigation overview used also for menu rendering'; -- diff --git a/views/nav_top.sql b/views/nav_top.sql index c13398b..bd5a0d5 100644 --- a/views/nav_top.sql +++ b/views/nav_top.sql @@ -21,9 +21,11 @@ SELECT THEN REGEXP_REPLACE(REPLACE(n.page_name, '&' || 'APP_USER.', APEX_ESCAPE.HTML(NVL(curr.user_name, curr.user_id))), '^(&' || 'nbsp; )+', '') ELSE '