PowerCMS Xに限らずですが、フォームの添付ファイル(<input type="file">)はページ遷移を行うとファイル選択状態が保持されません。また、ブラウザの仕様上プログラムから添付ファイルに直接値を設定することもできないため、確認画面から入力画面に戻った際に選択状態を復元することもできません。
※PowerCMS Xでは、セッションに選択ファイル自体は保存されているため再選択の必要はありませんが、見た目上(ブラウザの仕様上)選択されていないように見えます
この記事では、PowerCMS Xのフォームで確認画面から入力画面に戻った際に、添付ファイルに選択されていたファイル名を復元する方法を紹介します。
前提
- フォーム入力画面 -> 確認画面 -> 送信完了と遷移するフォーム
- 画面遷移はパラメータ __mode の値を変更してPOSTする
- 確認画面 -> 入力画面であれば __mode=confirm から __mode=view に変更
添付ファイル設問のビュー
<div class="p-contactForm__field<mt:If name="$error_name"> -hasError</mt:If>">
<mt:Ignore>設問のラベル</mt:Ignore>
<label class="p-contactForm__label" id="label-<mt:Var name="question_basename" escape />" for="question_<mt:Var name="question_basename" escape /><mt:If name="question_questiontype_id" eq="3">_1</mt:If>">
<mt:Var name="question_label" _eval escape />
<mt:If name="question_required">
<span class="p-contactForm__required"><mt:Trans phrase="Required" /></span>
</mt:If>
</label>
<mt:Ignore>設問の説明</mt:Ignore>
<mt:If name="question_description">
<p class="p-contactForm__supplement"><mt:Var name="question_description" escape /></p>
</mt:If>
<mt:Ignore>設問のエラー</mt:Ignore>
<mt:Unless name="confirm_ok">
<mt:If name="$error_name">
<div class="p-contactForm__error">
<mt:Var name="$error_name" escape />
</div>
</mt:If>
</mt:Unless>
<mt:Ignore>設問の入力フィールド</mt:Ignore>
<mt:SetVarBlock name="file_name">filename_<mt:Var name="question_basename" escape /></mt:SetVarBlock>
<mt:SetVarBlock name="field_name">request.question_<mt:Var name="question_basename" escape /></mt:SetVarBlock>
<mt:SetVarBlock name="error_name">question_<mt:Var name="question_basename" escape />_error</mt:SetVarBlock>
<mt:Var name="$file_name" setvar="file_value" />
<mt:Var name="$field_name" setvar="question_value" />
<mt:If name="confirm_ok">
<mt:Var name="file_value" escape />
<mt:Else>
<div class="p-contactForm__fileContent">
<input type="file" id="question_<mt:Var name="question_basename" escape />" name="question_<mt:Var name="question_basename" escape />">
<button type="button" id="question_file_<mt:Var name="question_basename" escape />" class="p-contactForm__file"><mt:Trans phrase="Select..." /></button>
<input type="text" id="filename_<mt:Var name="question_basename" escape />" class="p-contactForm__fileText" value="<mt:Var name="file_value" escape />" aria-label="<mt:If name="question_aria_label"><mt:Var name="question_aria_label" escape /><mt:Else><mt:Trans phrase="File Name" /></mt:If>" placeholder="<mt:If name="question_placeholder"><mt:Var name="question_placeholder" escape /><mt:Else><mt:Trans phrase="Select File..." /></mt:If>">
</div>
<script>
(() => {
const questionFile = document.querySelector('#question_<mt:Var name="question_basename" escape />');
const questionFileButton = document.querySelector('#question_file_<mt:Var name="question_basename" escape />');
const questionFileText = document.querySelector('#filename_<mt:Var name="question_basename" escape />');
questionFileButton.addEventListener('click', () => {
questionFile.click();
});
questionFile.addEventListener('change', () => {
questionFileText.value = questionFile.value.replace('C:\\fakepath\\', '');
});
})();
</script>
</mt:If>
<mt:Ignore>設問のヒント</mt:Ignore>
<mt:Unless name="confirm_ok">
<mt:If name="question_hint">
<div class="p-contactForm__hint">
<mt:Var name="question_hint" _eval escape />
</div>
</mt:If>
</mt:Unless>
</div>
<input type="file">タグは非表示
<input type="file" id="question_<mt:Var name="question_basename" escape />" name="question_<mt:Var name="question_basename" escape />">
<button type="button" id="question_file_<mt:Var name="question_basename" escape />" class="p-contactForm__file"><mt:Trans phrase="Select..." /></button>
<input type="text" id="filename_<mt:Var name="question_basename" escape />" class="p-contactForm__fileText" value="<mt:Var name="file_value" escape />" aria-label="<mt:If name="question_aria_label"><mt:Var name="question_aria_label" escape /><mt:Else><mt:Trans phrase="File Name" /></mt:If>" placeholder="<mt:If name="question_placeholder"><mt:Var name="question_placeholder" escape /><mt:Else><mt:Trans phrase="Select File..." /></mt:If>">
上記ビューの<input type="file">タグはCSSで非表示にして、<button>タグクリックでファイル選択画面が開くようにします。<input type="text">には、選択中のファイル名を表示するようにします。
ファイル名を取得して変数に設定
今回はpost_init フックで実行するプラグインを作成して、確認画面から戻った際にファイル名を取得・変数に設定します。post_init フックで実行するプラグインの雛形は、プラグインのスケルトンの作成 (PluginStarterプラグイン)を活用すると手軽に作成できます。
if ($app->id !== 'Bootstrapper') {
return;
}
// パラメータからフォームを取得
// 一度もPOST(確認画面に遷移するなど)していない時は処理を中断する
$form = null;
$formId = $app->param('form_id');
if ($formId) {
$form = $app->db->model('form')->load((int) $formId);
}
if (!$form) {
return;
}
// フォームにリレーションされている設問を取得してループ
$questions = $app->get_related_objs($form, 'question', 'questions');
foreach ($questions as $question) {
$questionType = $question->questiontype;
$questionClass = $questionType->class ? $questionType->class : $questionType->basename;
// 設問タイプのクラスorベースネームがfileの時
if ($questionClass && $questionClass === 'file') {
$identifer = $app->param('magic_token') ?? $app->magic();
$basename = $question->basename;
$fileToken = $identifer . '-' . $basename;
$sess = $app->db->model('session')->get_by_key(['name' => $fileToken, 'kind' => 'UP']);
if ($sess->id) {
$app->ctx->vars['filename_' . $basename] = $sess->value;
}
}
}
上記コードでは、変数filename_[設問のベースネーム]にファイル名を設定しています。
まとめ
確認画面から入力画面に戻った際に、<input type="file">にファイルが選択された状態にすることは(ブラウザの仕様上)できませんが、セッションに保存された情報からファイル名は取得できます。取得したファイル名をページに表示することで、選択状態が維持されているように表示することができました。
なお、今回はプラグインで実装していますが、class.PTFormにviewメソッドがあればそちらで実行しても同様のことが可能です。
今回の実装は、class.PTFormを読むことでヒントを得ました。弊社ではPowerCMS Xの実装実績が多数ありますので、お困りのことがあればご相談お待ちしております。