2023年1月31日火曜日

数値フィールドで選択できる仮想キーボードの種類

 Oracle APEX 22.2より、ページ・アイテムのタイプとして数値フィールドを選択したときに、設定仮想キーボードとしてDecimalNumericTextのどれかを選ぶことができるようになりました。

仮想キーボードの種類を確認するアプリケーションを作成しました。

https://apex.oracle.com/pls/apex/r/japancommunity/virtual-keyboard/home

PCのブラウザでは、仮想キーボードの設定に効果はありません。

iPhoneでアクセスしてみます。

仮想キーボードDecimalと設定した数値フィールドにフォーカスを当てた結果です。


Decimalの場合は、右下に小数点があります。

仮想キーボードNumericと設定した数値フィールドにフォーカスを当てた結果です。


仮想キーボードに小数点は割り当たっていません。

仮想キーボードがTextの場合は、通常のキーボードです。英数字というわけではなく、キーボードとしてフリック入力を使っていれば、それになるようです。


これらはinputmodeとして指定されています。

https://developer.mozilla.org/ja/docs/Web/HTML/Global_attributes/inputmode

Oracle APEX 22.2以前より、テキスト・フィールドサブタイプとしてPhone NumberEmailURLといった設定があります。


サブタイプPhone Numberのときのキーボードです。


+*#に切り替えると、以下になります。


サブタイプEmailのキーボードです。@の入力が容易になっています。


サブタイプURLの場合は、. / .jpの入力が容易になっています。


こちらは、input要素のtype=による指定です。

https://developer.mozilla.org/ja/docs/Web/HTML/Element/input/tel

スマホからのアクセスを想定していなくても、これらのタイプは設定しておくと良いように思います。

動的アクションのデバウンスとスロットル

 Oracle APEX 22.2にて追加された新機能に動的アクションのデバウンススロットルがあります。JavaScriptを書いてユーザー・インターフェースをカスタマイズする方には評価の高い新機能です。

動的アクションを実行するタイミングの調整に使用します。

動作について紹介するため、実装のサンプルを作成してみました。

表EMPを表示する対話モード・レポートを作成します。


ソースWHERE句に以下を記述しています。

ENAME like '%' || coalesce(:P1_ENAME_I, :P1_ENAME_D, :P1_ENAME_S) || '%'

送信するページ・アイテムとして、以下の3つのページ・アイテムを設定します。

P1_ENAME_I,P1_ENAME_D,P1_ENAME_S

ページ・アイテムP1_ENAME_IP1_ENAME_DP1_ENAME_Sのどれも、キー・プレスイベントが発生したときに対話モード・レポートをリフレッシュします。

ページ・アイテムP1_ENAME_I動的アクションは、実行タイプ即時にします。


ページ・アイテムP1_ENAME_D動的アクションは、実行タイプデバウンス、時間1000(つまり1秒)、即時OFFにします。


ページ・アイテムP1_ENAME_S動的アクションは、実行タイプスロットル、時間1000(つまり1秒)、即時OFFにします。


イベントキー・プレスです。

それぞれ、ページ・アイテムに文字を入力して動作を確認してみます。

ページ・アイテムP1_ENAME_Iに、文字列SCOTTを入力します。

動的アクション実行タイプ即時なので、キー・プレスの度にレポートのリフレッシュが実行されます。SELECT文は、最低でも5回実行されます。


ページ・アイテムP1_ENAME_Dに、文字列SCOTTを入力します。

動的アクション実行タイプデバウンス時間1秒なので、SCOTTを入力した1秒後に1度だけリフレッシュが実行されます。SELECT文は、1回実行されます。即時ONのときは最初に1回リフレッシュが実行され、1秒経過するまでは同じイベントによる動的アクションは起動されない、という動作になります。


ページ・アイテムP1_ENAME_Sに、文字列SCOTTを入力します。

動的アクション実行タイプスロットル時間1秒なので、SCOTTを入力を開始した1秒後にリフレッシュが実行されます。また、それから1秒経過したら再度リフレッシュが実行されます。実行回数は文字を入力している時間で決まります。即時ONのときは最初に1回リフレッシュが実行されるため、実行回数が1回増えます。


動的アクションを使って、ユーザーの入力に対してアプリケーションの応答を素早くしつつ、データベースへの負荷を減らすことができます。

今回の動作確認に使用したアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/debounce-and-throttle.zip

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

APEXでCytoscape.jsを使ってみる

 以前にグラフ問合せを扱った記事を書いたことがありますが、問い合わせの結果をレポート、つまり表形式で印刷していました。それではイマイチなので、APEXでCytoscape.jsを使う方法を確認してみました。

Cytoscape.jsのページに記載されているGetting startedをAPEXのアプリケーションとして実装してみました。Getting Startedをそのまま埋め込むのではなく、アプリケーションのサンプルとして役立つように以下の実装を加えています。

  1. elementsとstyleは静的なJSONファイルではなく、サーバー側で生成した値を使う。
  2. ページ・ロード時にグラフを描画するのではなく、ブラウザのイベントより(今回はボタンの押下)生成する。
作成したアプリケーションは以下のように動作します。


簡単なアプリケーションですが、以下に実装を紹介します。

最初に空のアプリケーションを作成します。実装はホーム・ページに行います。

ページ・プロパティのJavaScriptファイルURLとして、Cytoscape.jsを指定します。バージョンなどは変わるため、実装時はCytoscape.jsのホームページよりリンクを取得してください。

https://unpkg.com/cytoscape@3.23.0/dist/cytoscape.min.js

グラフを描画する領域を作成します。

新規にリージョンを作成します。

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

<div id="cy"></div>

装飾は不要なので、外観テンプレートとしてBlank with Attributesを選択します。

HTMLコードとして記述したid="cy"のDIV要素にCytoscapeによるグラフが描画されます。描画領域をCSSで指定します。

ページ・プロパティのCSSインラインに以下を記述します。

#cy {
  width: 300px;
  height: 300px;
  display: block;
}

サーバー側でグラフのデータを生成するプロセスを作成します。

データを生成するプロセスはAjaxコールバックとして作成します。

識別名前GET_DATAとします。タイプとしてコードの実行を選択し、ソースPL/SQLコードとして以下を記述します。Getting Startedに記述されているelementsとstyleと同じデータを出力しています。

declare
l_response clob;
l_response_json json_object_t;
l_data_array json_array_t;
l_data json_object_t;
l_style_array json_array_t;
l_style json_object_t;
begin
/* elementsの作成 */
l_data_array := json_array_t();
l_data := json_object_t();
l_data.put('data', json_object_t(
json_object(
'id' value 'a'
)));
l_data_array.append(l_data);
l_data := json_object_t();
l_data.put('data', json_object_t(
json_object(
'id' value 'b'
)));
l_data_array.append(l_data);
l_data := json_object_t();
l_data.put('data', json_object_t(
json_object(
'id' value 'ab'
,'source' value 'a'
,'target' value 'b'
)));
l_data_array.append(l_data);
/* sytleの作成 */
l_style_array := json_array_t();
l_style := json_object_t();
l_style.put('style', json_object_t(
json_object(
'background-color' value '#666'
,'label' value 'data(id)'
)));
l_style_array.append(l_style);
l_style := json_object_t();
l_style.put('selector','node');
l_style.put('style', json_object_t(
json_object(
'background-color' value '#666'
,'label' value 'data(id)'
)));
l_style_array.append(l_style);
l_style := json_object_t();
l_style.put('selector','edge');
l_style.put('style', json_object_t(
json_object(
'width' value 3
,'line-color' value '#ccc'
,'target-arrow-color' value '#ccc'
,'target-arrow-shape' value 'triangle'
,'curve-style' value 'bezier'
)));
l_style_array.append(l_style);
/* 応答となるJSONを生成 */
l_response_json := json_object_t();
l_response_json.put('elements', l_data_array);
l_response_json.put('style', l_style_array);
l_response := l_response_json.to_clob();
apex_debug.info(l_response);
htp.p(l_response);
end;



Cytoscape.jsを呼び出してグラフを描画する処理は、APEXアクションとして実装します。

JavaScriptファンクションおよびグローバル変数の宣言に、以下を記述します。

var cy;
const RENDER_CY = {
name: "render-cy",
action: (event, element, args) => {
let result = apex.server.process("GET_DATA", {
x01: "get_data"
});
result.done( (data) => {
cy = cytoscape({
container: document.getElementById('cy'), // container to render in
elements: data.elements,
style: data.style,
layout: {
name: 'grid',
rows: 1
}
})
});
}
};
view raw render-cy.js hosted with ❤ by GitHub

また、ページ・ロード時に実行に以下の1行を記述します。

apex.actions.add([RENDER_CY]);


作成したアクションrender-cyを呼び出すボタンを作成します。

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

識別ボタン名RENDERラベルRenderとします。レイアウト位置としてNextを選びます。

動作アクションとして動的アクションで定義を選択し、詳細カスタム属性として以下を記述します。

data-action="#action$render-cy"


以上で完成です。作成したアプリケーションを実行すると、記事の先頭のGIF動画のような動作をします。

今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/cytoscape-sample.zip

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

2023年1月27日金曜日

認証スキームの切り替えの有効化

 Oracle APEXの認証スキームには、セッションの切替えという機能があります。

ログイン・プロセスのセクションに含まれます。

オンライン・ヘルプに解説がありますが、URLパラメータのREQUESTAPEX_AUTHENTICATION=認証スキーム名という指定を渡すことにより、サインインに使われる認証スキームを指定できます。

切り替える認証スキームのログイン・プロセスセッションの切替え有効にする必要があります。

例えば、認証スキームとしてSCOTT、TIGERのふたつがあるとします。SCOTTはユーザー名がSCOTTのときのみサインインできます。TIGERはTIGERのときのみサインインできます。

認証スキームSCOTTがカレントの場合、アプリケーションにサインインできるユーザーはSCOTTのみです。


サインインの画面が開いているときにLogin URLの?以下を変更し、セッションを無効化して認証スキームをTIGERに切り替えます。

?session=0&REQUEST=APEX_AUTHENTICATION=TIGER


上記のURLでのアクセス後のURLの表示からREQUEST=APEX_AUTHENTICATION=TIGERの引数の指定が消えているときは、意図しないリダイレクトが発生して認証スキームは切り替わっていません。引数の指定を再度確認し、やり直します。


画面上の違いはありませんが、認証スキームがTIGERに切り替えられているため、ユーザー名TIGERでサインインできます。


認証スキームの切り替えの紹介は以上になります。

異なるアプリケーションでのセッション共有について

 認証スキームが異なるアプリケーションで、セッション共有を行なった際の動作を確認します。

まずMain Appという空のアプリケーションを作成します。

認証スキームカスタムとして、以下のコードで認証します。ユーザー名がSCOTTの場合だけ、アプリケーションにアクセスできます。

function my_authentication (
    p_username in varchar2,
    p_password in varchar2 )
    return boolean
is
begin
    if p_username = 'SCOTT' then
        return true;
    end if;
    return false;
end;

異なるアプリケーションとセッションを共有するため、セッション共有Cookie名TEST_SESSION_SHARINGを設定します。

同様に空のアプリを作成します。名前はLogin Appとします。

認証スキームセッション共有も含み、同じ設定を行いますが、コードだけは以下に変更します。こちらはユーザー名としてTIGERだけが許可されます。

function my_authentication (
    p_username in varchar2,
    p_password in varchar2 )
    return boolean
is
begin
    if p_username = 'TIGER' then
        return true;
    end if;
    return false;
end;

Login Appのホーム・ページにプロセスを作成します。以下のコードを実行することで、アプリケーションの認証直後に、すぐにMain Appへリダイレクトします。apex_page.get_urlの引数p_sessionのデフォルトは、すでに開始しているセッションになります。

declare
    l_url varchar2(4000);
begin
    l_url := apex_page.get_url(
        p_application => 131
        ,p_page => 1
    );
    apex_util.redirect_url(
        p_url => l_url
    );
end;

Login Appにアクセスします。

Login AppにサインインできるユーザーはTIGERだけです。


サインインした直後にMain Appにリダイレクトします。セッションを共有しているため、Main Appの認証スキームによるサインインはバイパスされます。

Main Appの認証スキームでは本来認証されないユーザーであるTIGERで、Main Appにアクセスしていることが確認できます。


認証スキームが異なっていても、セッション共有はできます。場合によってはセキュリティ・ホールになるため、設定には注意が必要です。

Twilio SendGridをOracle APEXから呼び出してみる

 Twilio SendGridを使用したメール送信を、Oracle APEXより行ってみました。

Twilio SendGridの無料アカウントを、以下のサイトより作成しました。クレジット・カードの入力は求められていません。

Pricing and Plans | SendGrid


アカウントを作成すると、各種の設定を行なう画面を開きます。

サイド・メニューにあるEmail APIIntegration Guideを開き、Web APIChooseをクリックします。

Web APIを使用するために必要な手順が表示されます。


cURLが一番PL/SQLでのアクセスの参考になるため、cURLを選択します。

Chooseをクリックします。


作業手順が表示されます。

My First API Key Name名前を入力し、Create Keyをクリックします。

SendGridを呼び出す際に使用するAPIキーが生成されます。


APIキーが生成されます。生成されたAPIキーをクリップボードにコピーして保存しておきます。再度表示することはできないので、忘れずに保存します。

Oracle APEXでWeb資格証明を作成する際に使用します。


作成したAPIキーはSettingsAPI Keysより、権限の更新や削除することができます。APIキー自体を表示することはできません。


Twilio SendGridのAPIキーが作成できました。

SettingsSender Authenticationの設定も行います。DNSの設定ができない場合は、Single Sender Verificationの設定を行なうことになるはずです。


Oracle APEXでの作業に移ります。

ワークスペース・ユーティリティよりWeb資格証明を開きます。


作成済みのWeb資格証明が一覧されます。作成をクリックします。


名前Twilio SendGridとしました。静的識別子TWILIO_API_KEYとしています。静的識別子は、APEX_WEB_SERVICE.MAKE_REST_REQUESTの引数p_credential_static_idとして指定します。

認証タイプとしてHTTPヘッダーを選択します。資格証明名はHTTPヘッダー名であるAuthorizationを記述します。

資格証明シークレットはAuthorizationヘッダーに与える値になります。Bearerで始めて空白で区切り、Twilio SendGridのAPIキーを続けます。

Bearer SendGridのAPIキー

URLに対して有効は、SendGridのエンドポイントとなるURLを指定します。

https://api.sendgrid.com/v3/mail/send

以上で作成をクリックします。


SQLコマンドを開き、以下のPL/SQLコードを実行します。l_recipients、l_senderを変更します。l_senderについては、Twilio SendGrid側でSingle Senderとして検証したメール・アドレスを指定します。

declare
l_request json_object_t;
l_email json_object_t;
l_email_array json_array_t;
l_email_to json_object_t;
l_personalizations_array json_array_t;
l_content json_object_t;
l_content_array json_array_t;
--
type t_recipients is table of varchar2(80);
l_recipients t_recipients;
l_sender varchar2(80);
l_subject varchar2(32767);
type t_types is table of varchar2(80);
l_types t_types;
type t_contents is table of varchar2(32767);
l_values t_contents;
--
l_response clob;
begin
l_recipients := t_recipients('受信者のメール・アドレス');
l_sender := '送信者のメール・アドレス';
l_subject := 'Sending with SendGrid is Fun';
l_types := t_types('text/plain');
l_values := t_contents('and easy to do anywhere, even with PL/SQL');
/*
* 電子メールのリクエストはl_requestに保持する。
* 受信者はl_personalizations_arrayに保持する。
*/
l_request := json_object_t();
l_personalizations_array := json_array_t();
/*
* メール・アドレス
*/
l_email_array := json_array_t();
for i in 1..l_recipients.count
loop
l_email := json_object_t();
l_email.put('email', l_recipients(i));
l_email_array.append(l_email);
end loop;
/*
* Toの宛先として保存する。
*/
l_email_to := json_object_t();
l_email_to.put('to', l_email_array);
l_personalizations_array.append(l_email_to);
/*
*  リクエストに宛先を設定する。
*/
l_request.put('personalizations', l_personalizations_array);
/*
* Fromを設定する。
*/
l_email := json_object_t();
l_email.put('email', l_sender);
l_request.put('from', l_email);
/*
* Subjectを設定する。
*/
l_request.put('subject', l_subject);
/*
* Contentを設定する。
*/
l_content_array := json_array_t();
for i in 1..l_values.count
loop
l_content := json_object_t();
l_content.put('type', l_types(i));
l_content.put('value', l_values(i));
l_content_array.append(l_content);
end loop;
l_request.put('content',l_content_array);
-- 確認
-- dbms_output.put_line(l_request.to_clob());
apex_web_service.clear_request_headers;
apex_web_service.set_request_headers('Content-Type','application/json',p_reset = false);
l_response := apex_web_service.make_rest_request(
p_url => 'https://api.sendgrid.com/v3/mail/send'
,p_http_method => 'POST'
,p_body => l_request.to_clob()
,p_credential_static_id => 'TWILIO_API_KEY'
);
dbms_output.put_line(l_response);
end;
正常に終了すると、応答となるデータは返ってこないようです。


件名がSending with SendGrid is Fun、本文がand easy to do anywhere, even with PL/SQLというメールの受信を確認できたら、Twilio SendGridの動作確認は終了です。

2023年1月26日木曜日

宣言的な動的アクションとJavaScriptによるコーディングの比較

 以前に書いた記事data-parent-elementを実装したサンプルを使って、宣言的な動的アクションとJavaScriptによるコーディングを比べてみます。

サンプル・アプリケーションでは、以下の2つの動的アクションを作成しています。

動的アクションonClick SELECT_JOBのTRUEアクションは以下です。

ボタンSELECT_JOBをクリックしたときに、ページ・アイテムP1_JOBの値をP1_JOB_SELECTに設定する。その後、インライン・ポップアップのリージョンを開く。

動的アクションonChange P1_JOB_SELECTのTRUEアクションは以下です。

ページ・アイテムP1_JOB_SELECTの値を変更したときに、ページ・アイテムP1_JOB_SELECTの値をP1JOBに設定する。その後、インライン・ポップアップのリージョンを閉じる。


上記の処理は、JavaScriptのコードで置き換えることができます。

最初にリージョン役職の選択に、静的IDとしてPOPUP_SELECT_JOBを設定します。


インライン・ポップアップを開く処理をJavaScriptで記述します。

TRUEアクションの識別名前インライン・ポップアップを開くとします。アクションとしてJavaScriptコードの実行を選択します。設定コードとして以下を記述します。
apex.items.P1_JOB_SELECT.setValue(
    apex.items.P1_JOB.getValue()
);
apex.theme.openRegion("POPUP_SELECT_JOB");
宣言的に作成した2つのTRUEアクションと同じ動きになります。


インライン・ポップアップを閉じるJavaScriptのコードは以下になります。
apex.items.P1_JOB.setValue(
    apex.items.P1_JOB_SELECT.getValue()
);
apex.theme.closeRegion("POPUP_SELECT_JOB");

さらに、apex.actionsとして呼び出すように変更してみます。

静的アプリケーション・ファイルとしてpage-actions.jsを作成します。

/*
* インライン・ポップアップを開く。
*/
const OPEN_INLINE_POPUP = {
name: "open-inline-popup",
action: (event, element, args) => {
apex.item(args.target).setValue(
apex.item(args.source).getValue()
);
apex.theme.openRegion(args.region);
}
};
/*
* インライン・ポップアップを閉じる。
*/
const CLOSE_INLINE_POPUP = {
name: "close-inline-popup",
action: (event, element, args) => {
apex.item(args.target).setValue(
apex.item(args.source).getValue()
);
apex.theme.closeRegion(args.region);
}
};
/*
* アクションの初期化。
*/
apex.jQuery(window).on('theme42ready', () => {
apex.actions.add([OPEN_INLINE_POPUP,CLOSE_INLINE_POPUP]);
});
view raw page-actions.js hosted with ❤ by GitHub

参照をコピーします。


ページ・プロパティJavaScriptファイルURLに、作成した静的アプリケーション・ファイル参照を記述します。

JavaScriptのコードを記述したファイルは、静的アプリケーション・ファイルとしてAPEXのアプリケーションに含めていますが、作成したファイルpage-actions.jsとMinifyされたファイルpage-actions.min.jsはブラウザからアクセスできれば、別の場所に配置することも可能です。


インライン・ポップアップを開く処理は、以下のカスタム属性の設定に置き換えられます。

data-action="#action$open-inline-popup?source=P1_JOB&target=P1_JOB_SELECT&region=POPUP_SELECT_JOB"


インライン・ポップアップを閉じる処理はJavaScriptのコードを以下に書き換えて、apex.actionsとして実装した処理を呼び出すようにします。

apex.actions.invokeを使用します。
apex.actions.invoke("close-inline-popup", this.browserEvent, this.triggeringElement, {
    source: "P1_JOB_SELECT",
    target: "P1_JOB",
    region: "POPUP_SELECT_JOB"
} );


開発するアプリケーションの規模が大きくなると、宣言的な設定ではアプリケーションの見通しが悪くなります。

サーバー・サイドではプロセス・タイプコードの実行を選んでPL/SQLコードを記述する代わりに、APIの呼出しを選んでPL/SQLのパッケージ、プロシージャ、ファンクションを指定できるようになりました。

クライアント・サイドではactionとして定義されたコードを呼び出せるようして、JavaScriptのコードをひとつのファイルにまとめることができるようになっています。

アプリケーション開発の容易さとメンテナンスの容易さのトレードオフの結果として、コーディングによる実装を行なうという選択肢もあり得るでしょう。

今回、改変したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/data-parent-element-sample-actions.zip

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