2022年8月3日水曜日

CKEditor5による画像アップロードを実装する(3) - 完成

これからCKEditor5を使った画像のアップロードと、画像を含んだ文章の表示を実装します。

今回の記事で、アプリケーションは完成です。

最初に前回作成したREST APIのURLを、アプリケーションの置換文字列として参照できるようにします。

アプリケーション定義置換を開き、置換文字列としてG_IMAGE_URL置換値としてREST APIの完全なURLを入力します。

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

ページ・デザイナにてページ番号のフォームのページを開きます。

ページ・プロパティの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の内容が再度読み込まれます。


P2_CONTENTとしてアップロードされたデータに含まれている画像URLを見つけて、その画像と表CKE_DOCUMENTSの行を紐付けます。表CKE_IMAGESの列DOCUMENT_IDに、その画像を含んでいるドキュメントのIDを設定します。

CKEditorを使ってアップロードされた画像は、他のドキュメントと共有されることはないという前提です。(手作業で画像URLを打ち込まない限りは、そのような状況は発生しない。)

画像の紐付け処理を行うため、プロセスを作成します。作成したプロセスは、ダイアログを閉じるの前に配置します。

識別名前画像の紐付けタイプコードの実行とし、以下のPL/SQLコードを記述します。



対話モード・レポートにも画像が表示されるようにします。

ページ・デザイナにてページ番号の対話モード・レポートのページを開きます。

対話モード・レポートのソースタイプ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

CONTENTタイプリッチ・テキストに変更し、設定書式としてマークダウンを選択します。


以上でアプリケーションは完成です。

今回作成したアプリケーションのエクスポートを以下に置きました。
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設定書式HTMLにする必要があります。


対話モード・レポートの列CONTENT設定書式HTMLに変更します。


ページ・アイテム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;