2022年3月24日木曜日

メンテナンスなどの通知をアプリケーションに表示する

 APEXアプリケーションの画面にメンテナンスの告知などの通知を表示させるには、という質問を見かけたので、試しにいくつか実装してみました。

APEX標準の機能としては、グローバル通知があります。それ以外にも、グローバル・ページにリージョンを作成することにより、通知を実装できます。

以下の4つを試してみました。

  1. アプリケーション定義グローバル通知を使う。
  2. グローバル・ページ静的コンテンツのリージョンを作成する - 表示はブラウザ側のJavaScriptで制御する。
  3. グローバル・ページ静的コンテンツのリージョンを作成する - 表示はAPEXのユーザー・プリファレンスを使って、サーバー側で制御する。
  4. グローバル・ページインライン・ダイアログのリージョンを作成する。

通知の実装には、サンプル・データセットEMP/DEPTをインストールすると作成できるアプリケーションデモ - 従業員 / 部門を使用します。アプリケーションの内容に依存する作業はないため、どのようなAPEXアプリケーションを元にしても、通知の実装を試すことができます。

SQLワークショップユーティリティサンプル・データセットから、EMP/DEPTのデータセットをインストールします。すでにインストール済みの場合は、アプリケーションを作成するために更新を実行します。


画面の案内に従ってデータセットのインストールまたは既存のデータセットのリフレッシュを実行すると、アプリケーションの作成を呼び出す画面に移ります。

アプリケーションの作成をクリックします。


アプリケーション作成ウィザードが起動したら、機能は使わないので、すべてをチェックをクリックし、チェックを全部外してアプリケーションに組み込まれないようにします。

アプリケーションの作成をクリックすると、今回の実装の元になるアプリケーションが作成されます。



アプリケーション定義のグローバル通知を使う



以下のように通知が表示される実装を行います。


アプリケーション定義グローバル通知として、以下を記述します。

<div class="global-notification-area" align="center">
<pre class="banner-content">
2022年4月17日午前9:00より10:00まで、メンテナンスのため本アプリケーションは利用できません。
詳しくは<a href="http://........./notice.html">http://........./notice.html</a>を参照してください。
</pre>
</div>

変更の適用をクリックします。


ページを実行すると、以下の表示になります。


グローバル通知の説明にあるように、ここで記述している文字列はページ・テンプレートに含まれる置換文字列#GLOBAL_NOTIFICATION#を置き換えます。それだけの処理なので、このままだと見栄えが今ひとつです。

通知文にはCSSのクラスbanner-contentを指定しています。このクラスによって、通知の表示スタイルを調整します。

CSSの定義はアプリケーションに含まれるすべてのページから参照できるよう、静的アプリケーション・ファイルに記述します。複数のアプリケーションから参照させる場合は、静的ワークスペース・ファイルとして作成します。

共有コンポーネント静的アプリケーション・ファイルを開きます。


ファイルの作成を実行します。


ファイル名としてmy-notification-class.cssを入力し、作成をクリックします。


ファイルの内容として以下を記述します。

.banner-content {
    box-sizing: border-box;
    width: 80%;
    border: solid rgba(73, 73, 85, 0.2) 2px;
    padding: 5px;
    box-shadow: 12px 12px 2px 1px rgba(73, 73, 85, 0.2);
    white-space: pre-wrap;
    background-color: rgba(255, 255, 0, 0.2);
}

変更の保存をクリックします。変更が保存されたら、静的アプリケーション・ファイルの一覧へ戻ります。


CSSファイルであれば、自動的にMinifyされます。MinifyされたCSSファイルの、参照として表示されているパス#APP_FILES#my-notification-class#MIN#.cssをクリップボードにコピーします。


アプリケーション定義ユーザー・インターフェースを開きます。カスケード・スタイルシートのセクションのファイルURLとして、静的アプリケーション・ファイルとして作成したCSSファイルの参照のパスを貼り付けます

変更の適用をクリックします。


以上で、最初の画像のように通知が表示されます。

グローバル通知はアプリケーションのすべての画面で表示されます。以下のようにログイン画面も含みます。


サインインしていない状態では、通知を表示しないようにします。

ページ・デザイナにてグローバル・ページ(ページ番号)を開き、左ペインで動的アクション・ビューを表示させます。

イベントページのロード上でコンテキスト・メニューを表示させ、動的アクションの作成を実行します。


作成された動的アクションを選択します。

識別名前onPageLoad Globalとします。タイミングページのロードです。クライアント側の条件タイプとして、JavaScript式を選択し、JavaScript式に以下を記述します。

apex.env.APP_PAGE_ID == 9999

ページ番号が9999、つまりログイン・ページであればTRUEアクションを実行します。


TRUEアクションを選択し、識別アクションとして、スタイルの設定を選択します。設定スタイル名displaynoneと記述します。影響を受ける要素選択タイプとしてjQueryセレクタを選択し、jQueryセレクタとして.global-notification-areaを指定します。


動的アクションには非表示もありますが、これはスタイルとしてはvisibility: hiddenに対応しています。今回の用途ではdisplay: noneの方が適切なので、動的アクションとしては、スタイルの設定を選んでいます。

通知メッセージの一番外側のdivタグに、CSSクラスglobal-notification-areaを設定しています。そのクラスをjQueryセレクタに指定しています。

以上で、サインイン画面を除いたすべてのページで、メンテナンスに関する通知が表示されるようになりました。


グローバル・ページに静的コンテンツのリージョンを作成する - 表示はブラウザ側のJavaScriptで制御する



ページの先頭に通知を表示します。また、ボタンをクリックして通知を削除できるようにします。通知の削除は、JavaScriptによりブラウザ側で制御します。

通知はAPEX 21.2から導入されたBannerの位置に表示しています。そのため、APEX 21.2以降が実装の要件になります。


ページ・デザイナグローバル・ページを開きます。

コンポーネントまたはBody上でコンテキスト・メニューを開き、リージョンの作成を実行します。


リージョンが作成されます。

識別名前通知タイプとして静的コンテンツを選択します。ソースHTMLコードには、通知したい以下の文章を記述します。

2022年4月17日午前9:00より10:00まで、メンテナンスのため本アプリケーションは利用できません。
詳しくは<a href="http://........./notice.html">http://........./notice.html</a>を参照してください。

レイアウト位置Banner外観テンプレートとしてAlertを選択します。


テンプレート・オプションを開き、Bottom MarginNoneに変更します。


以上で、通知がBannerの位置に表示されるようになりました。

グローバル通知とは異なり、Bannerが位置として定義されているページ・テンプレートでのみ、リージョン通知が表示されます。ログイン・ページやダイアログにはBannerの位置が定義されていないため、通知は表示されません。


通知の削除を実装します。

リージョン通知にボタンを作成します。

識別ボタン名B_NOTIFICATION_HIDEとします。外観ボタン・テンプレートとしてIconを選択します(そのため、ラベルは表示されないので特に変更しません)。右端に表示されるようCSSクラスu-pullRightアイコンとしてfa-window-close-oを選択します。

動作アクションとして、動的アクションで定義を選択します。


ボタンに動的アクションを作成します。

識別名前onClick B_NOTIFICATION_HIDEとします。タイミングイベントクリック選択タイプボタンボタンB_NOTIFICATION_HIDEになります。


ここまでの実装は、ブラウザ側でもサーバー側でも一緒です(サーバー側での実装を説明するときは、この作業以降から始めます)。

ここから、ブラウザ側のJavaScriptによる実装になります。

作成済みのTRUEアクションを選択します。

識別アクションとしてスタイルの設定を選択します。設定スタイル名displaynoneとします。影響を受ける要素選択タイプとしてリージョンを選び、リージョン通知とします。実行オプション初期化時に実行OFFにします。


追加でTRUEアクションを作成します。

識別アクションとしてJavaScriptコードの実行を選択します。設定コードには以下を記述します。

sessionStorage.setItem("hide-global-notification-banner","true");
window.location.reload();

ブラウザのsesssionStoragehide-global-notification-bannerとしてtrueを保存した後、ページ全体をリロードしています。

もっと良いやり方があるはずですが、JavaScriptとCSSが得意ではないのでこれ以上は分かりませんでした。多分、スタイルの設定ではなく、もっと凝ったCSSクラスを静的アプリケーション・ファイルに定義し、動的アクションのアクションとしてはクラスの追加クラスの削除を使うことになる思います。


リージョン通知が初期状態で非表示になるよう、詳細カスタム属性に以下を記述します。

style="display: none"


ページのロード時にブラウザのsessionStoragehide-global-notification-bannerを評価して、trueでなければリージョン通知を表示するように、動的アクションを作成します。

右ペインにて動的アクション・ビューを開き、ページのロード動的アクションの作成を実行します。

作成された動的アクションの識別名前onPageLoad Globalとします。クライアント側の条件として以下を記述します。

sessionStorage.getItem("hide-global-notification-banner") != "true"


作成済みのTRUEアクションを選択します。

識別アクションとしてスタイルの設定を選択します。設定スタイル名displaycontentsとします。影響を受ける要素選択タイプリージョンリージョンとして通知を選択します。


今回は、リージョン通知カスタム属性としてstyle="display: none"を設定し、ページのロード時に(通知を表示する場合は)display: contentsを設定しています。この設定が逆(HTMLが生成されるときはdisplayの指定が無く、ページのロード時にdisplay: noneを設定して非表示にする)になっていると、ページが表示されるときに一瞬ですがリージョン通知が表示されてしまいます。

以上で通知の実装は完了です。

sessionStorageに通知の表示/非表示の選択を保存しているため、一旦通知を閉じるとブラウザを終了するまで通知が再表示されることはありません。


グローバル・ページに静的コンテンツのリージョンを作成する - 表示はAPEXのユーザー・プリファレンスを使って、サーバー側で制御する



通知を削除するボタンに作成した、TRUEアクションの設定から始めます。

識別アクションとして、サーバー側のコードを実行を選択します。設定PL/SQLコードとして以下を記述します。

apex_util.set_preference(
    p_preference => 'HIDE_GLOBAL_NOTIFICATION_BANNER'
    , p_value => 'TRUE'
);

実行オプション初期化時に実行OFFです。


追加でTRUEアクションを作成します。

識別アクションとしてJavaScriptコードの実行を選択します。設定コードには以下を記述します。

window.location.reload();

今回はリージョン通知サーバー側の条件で非表示にします。そのため、ページの再読み込みを行う必要があります。


リージョン通知を選択し、サーバー側の条件を設定します。

サーバー側の条件タイプとして、ユーザー・プリファレンス != 値を選択します。プリファレンスHIDE_GLOBAL_NOTIFICATION_BANNERとし、値にはTRUEを指定します。


ユーザー・プリファレンスは一旦設定するとずっと維持されます。つまり永遠に通知が表示されなくなります。それだと困るため、サインアウトすると設定をリセットするようにします。

共有コンポーネントアプリケーション・プロセスを開きます。


作成済みのアプリケーション・プロセスの一覧が表示されます。

作成をクリックします。


名前RESET_HIDE_GLOBAL_NOTIFICATION_BANNERとします。ポイント認証後です。ユーザー・プリファレンスはユーザーに対する設定なので、一般ユーザーが操作するためには、ユーザー認証が完了している必要があります。

へ進みます。


コードとして以下を記述します。ユーザー・プリファレンスHIDE_GLOBAL_NOTIFICATION_BANNERを削除します。

apex_util.remove_preference(
    p_preference => 'HIDE_GLOBAL_NOTIFICATION_BANNER'
);

へ進みます。


条件タイプの設定は不要です。プロセスの作成を実行します。


通知の非表示設定をリセットするアプリケーション・プロセスが作成されました。


以上で通知の実装は完了です。

おおむねJavaScriptでの実装と同じ動作をしますが、サインインし直すと通知が再度表示されるところが異なります。

この例ではユーザー・プリファレンスを使用して通知の表示を制御していますが、サーバー側の条件であれば、他のデータベースに保存されている情報を元にして、通知の表示の有無を決めることができます。


グローバル・ページにインライン・ダイアログのリージョンを作成する



サインイン直後に通知として、以下のようにダイアログを開きます。


データセットから作成したアプリケーションを元に実装を始めます。

ページ・デザイナにてグローバル・ページを開き、リージョンを作成します。

作成されたリージョンの識別タイトル通知タイプ静的コンテンツとします。ソースHTMLコードに以下を記述します。

2022年4月17日午前9:00より10:00まで、メンテナンスのため本アプリケーションは利用できません。
詳しくは<a href="http://........./notice.html">http://........./notice.html</a>を参照してください。

レイアウト位置Bodyのまま、変更しません。

外観テンプレートとしてInline Dialogを選択します。


通知となるインライン・ダイアログが、サインイン後に開かれるように動的アクションを作成します。

左ペインで動的アクション・ビューを開き、ページのロード動的アクションの作成を実行します。

作成された動的アクションの、識別名前onPageLoad Globalとします。


作成済みのTRUEアクションを選択します。

識別アクションとしてリージョンを開くを選択します。影響を受ける要素選択タイプリージョンを選び、リージョンとして通知を指定します。実行オプション初期化時に実行OFFです。クライアント側の条件タイプとして、JavaScript式を選択し、JavaScript式に以下を記述します。

sessionStorage.getItem("hide-global-notification-dialog") != "true"
&
apex.env.APP_PAGE_ID != 9999

この後に、sessionStoragehide-global-notification-dialogtrueを設定するJavaScriptコードを実行するアクションを定義します。

ページ番号9999はログイン・ページです。結果としてログイン・ページではダイアログは表示されず、また、一度ダイアログが表示されるとブラウザを終了するまではダイアログが再表示されることはありません。


追加でTRUEアクションを作成します。

作成したTRUEアクション識別アクションとして、JavaScriptコードの実行を選択します。設定コードに以下を記述します。

sessionStorage.setItem("hide-global-notification-dialog","true");

クライアント側の条件は、先行するTRUEアクションリージョンを開くと同じ設定にします。アクションリージョンを開くが実行されていれば、hide-global-notification-dialogtrueが設定されます。


以上で通知の実装は完了です。

今回作成したアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/demo-notification-global.sql
https://github.com/ujnak/apexapps/blob/master/exports/demo-notification-javascript.sql
https://github.com/ujnak/apexapps/blob/master/exports/demo-notification-plsql.sql
https://github.com/ujnak/apexapps/blob/master/exports/demo-notification-dialog.sql

Oracle APEXのアプリケーション作成の参考になれば幸いです。