これからCKEditor5を使った画像のアップロードと、画像を含んだ文章の表示を実装します。
今回の記事で、アプリケーションは完成です。
最初に前回作成したREST APIのURLを、アプリケーションの置換文字列として参照できるようにします。
アプリケーション定義の置換を開き、置換文字列としてG_IMAGE_URL、置換値としてREST APIの完全なURLを入力します。
変更の適用をクリックします。
ページ・デザイナにてページ番号2のフォームのページを開きます。
ページ・プロパティのJavaScriptのファンクションおよびグローバル変数の宣言に、これからいくつかの場所に現れる値を宣言しておきます。
const apexSession = apex.env.APP_ID + ',' + apex.env.APP_SESSION;
const imageUrl = "&G_IMAGE_URL.";
ページ・アイテムP2_CONTENTの詳細のJavaScript初期化コードに、以下を記述します。
JavaScript初期化コードでCKEditor5のeditingDowncastコンバーターを実装しています。この機能によりイメージURLに引数?_apex_session=セッションIDを付加し、REST APIの認証が通るようにしています。しかし、editingDowncastコンバーターが初期化されるのはページ・アイテムにデータがフェッチされた後(プロセス - 初期化フォームDocumentの実行後)になります。そのため、このままではeditingDowncastコンバーターが働きません。
CKEditor5の初期化後にデータが読み直されるよう、ページのロードのタイミングで動的アクションを作成して、ページ・アイテムP2_CONTENTにデータを再読み込みします。作成した動的アクションの識別の名前はP2_CONTENTの再ロードとします。
TRUEアクションの識別の名前はCONTENTの設定とします。アクションは値の設定です。設定のタイプの設定としてSQL Statementを選択し、SQL文に以下を記述します。
select content from rte_documents where id = :P2_ID
送信するアイテムとしてP2_IDを選択します。影響を受ける要素の選択タイプはアイテム、アイテムはP2_CONTENTを選択します。実行の初期化時に実行はOFFにします。
以上でページのロード時に、ページ・アイテムP2_CONTENTの内容が再度読み込まれます。
CKEditorを使ってアップロードされた画像は、他のドキュメントと共有されることはないという前提です。(手作業で画像URLを打ち込まない限りは、そのような状況は発生しない。)
画像の紐付け処理を行うため、プロセスを作成します。作成したプロセスは、ダイアログを閉じるの前に配置します。
識別の名前を画像の紐付け、タイプはコードの実行とし、以下のPL/SQLコードを記述します。
対話モード・レポートにも画像が表示されるようにします。
ページ・デザイナにてページ番号1の対話モード・レポートのページを開きます。
対話モード・レポートのソースのタイプをSQL問合せに変更し、SQL問合せとして以下を記述します。
select
ID
, TITLE
-- , CONTENT
, regexp_replace(
content
, :G_IMAGE_URL || '/([0-9]+)'
, :G_IMAGE_URL || '/\1?_apex_session=' || :APP_ID || ',' || :APP_SESSION
) content
, AUTHOR
, PUBLISHED
, CREATED
, CREATED_BY
, UPDATED
, UPDATED_BY
from RTE_DOCUMENTS
以上でアプリケーションは完成です。
今回作成したアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/ckeditor-image-sample.zip
REST APIのエクスポートです。
https://github.com/ujnak/apexapps/blob/master/exports/ORDS_REST_WKSP_APEXDEV_rteditor_2023_07_04.sql
Oracle APEXのアプリケーション作成の参考になれば幸いです。
追記
置換文字列の指定について
今回のアプリケーションでは、アプリケーション定義の置換文字列としてG_IMAGE_URLを定義しています。その値にREST APIのURLを記載していますが、このような値をアプリケーションのエクスポートには含めたくない、といった場合は多いと思います。
このような場合、最初に置換値を共有しても問題ない文字列に置き換えます。例えば、G_IMAGE_URLであれば、以下のように置き換えます。
https://changeyouridentifier-apexdev.adb.us-ashburn-1.oraclecloudapps.com/ords/apexdev/rteditor/image
サポートするオブジェクトを開きます。
アプリケーション置換文字列を開きます。
置換文字列G_IMAGE_URLのプロンプトにチェックを入れ、プロンプト・テキストに画像アップロードURLを設定します。
この設定を行なった後に、アプリケーションをエクスポートします。
このアプリケーションをインポートすると、インポート中に置換文字列G_IMAGE_URLの設定を求められます。
ユーザーは置換文字列に新しい値を入力する必要があります。
インポートする環境に依存して変更が必要な値や、共有が望ましくない値について、このような設定を使うことができます。
マークダウンだとツールによる画像への設定が保存されない
今回は列CONTENTのフォーマットにマークダウンを選択しています。マークダウンにすると、編集ツールによる画像への設定が保存されません。
ページ・アイテムP2_CONTENTの再読み込みを行なう動的アクションの、特殊文字をエスケープはOFFにします。
CKEditorは、HTMLやMarkdownを一旦内部でmodelに変換するため、クロスサイト・スクリプティングの対応が行われていると考えられます。ただ、データベースにはHTMLが保存されていて、CKEditorを使わずに更新が行われる可能性もあります。
データから画像URLを見つける方法が甘い
現在は以下のコードにて、文章から画像URLを見つけています。
l_link := regexp_substr(:P2_CONTENT, :G_IMAGE_URL || '/([0-9]+)',1,i);
CKEditorで画像を貼り付ける以外の方法が使われないという前提です。厳密に行う場合は、マークダウンの場合は![](URL)、HTMLの場合は<img src="URL">といった書式まで考慮した上で文章から画像URLを見つける必要があるでしょう。
ハウスキーピングのジョブは別途必要です
文書に貼り付けた画像を削除すると、表RTE_IMAGESには列DOCUMENT_IDがNULLとなった状態で行が残ります。この画像は文書からは参照されていません。
不要な画像を削除するSQLは以下になります。これを定期的に実行します。Oracle APEXの自動化の機能が使えるでしょう。
delete from rte_images where document_id is null;
完