Entry_Summaryループ内クラス設定を運営者でも触れるようにする
現在提供中の標準テーマの一覧ページでは、横に並ぶコンテンツの数(2列、3列、4列)は、テンプレートの CSS クラスの設定によって決まる仕様になっています。 この記事では、タイトルの通り、運営側でもこの設定を変更できるようにする方法について考えてみます。
問題点の検証
実際にどのような問題が起こるのかを UTSUWA のテーマ「事業紹介」を例に説明していきます。
納品直後の状態
当初、打ち合わせ時には「事業紹介」については増えないと聞いており、コンテンツとしては 2つなので、col-6 と Class を設定し、横に2枚並ぶようにテンプレートに書かれていました。
事業が追加される事に
これがサイトを運営している途中で、新しい事業が増えて 3つになる事になりました。3つ目のコンテンツは下に表示され隣は空白となります。
3つ並べたいと相談される
同じページ内の「ブログ」コンテンツは横に4枚並べて表示していますし、横に3枚並べた方がいいと思う人も多い事でしょう。
CMS で運用している中で、よくある話ではないかと思います。 詳細ページについては、ある程度レイアウト調整もできるのですが、沢山の情報を管理可能な CMS では一覧ページについて自由度が低くなる傾向があります。
対策を考えてみる
対策としては、その制御を行っているのはテンプレート上に書かれている 1つの Class という事になるので、それを管理画面側から変更できるようにする事になります。
実は準備済みでした
実は Ver. 2.5.1(2015年8月) から、モジュールの設定に ループ内クラス という設定が追加されています。これを活用する事で、管理画面上から変更できるようになります。 テンプレートに直接書かれている Class を {entry:loop.class} という変数に置き換えるだけです。
運用担当者に分かるのか?
次の問題は、この記述内容が運用担当者に理解できるのかというところになります。
acms-col-6 acms-col-sm-4 acms-col-md-3
標準テーマの一覧ページでは、コンテンツの横幅を指定するために acms-col-* というクラスを使用します。 このクラスの数字は 12分割のグリッドシステム に基づいており、1行を12としたときに、各コンテンツが何列分の幅を占めるかを決定します。
具体的な例
- acms-col-6 → 12列中6列分を使用(2列表示)
- acms-col-sm-4 → 画面幅が smサイズ以上のとき、12列中4列分を使用(3列表示)
- acms-col-md-3 → 画面幅が mdサイズ以上のとき、12列中3列分を使用(4列表示)
つまり、画面のサイズによって自動的に 2列 → 3列 → 4列 とレイアウトが変化します。
| クラス名 | デフォルトのブレイクポイント |
|---|---|
| acms-col- | 全て |
| acms-col-sm- | 480px以上〜 |
| acms-col-md- | 768px以上〜 |
| acms-col-lg- | 1024px以上〜 |
| acms-col-xl- | 1440px以上〜 |
この設定では、制作者には理解できたとしても、サイトを運用している担当者には難しいと感じるのではないでしょうか。
実際の実装
モジュールフィールドに、各画面の幅の時のカラム数のカスタムフィールドを作る事で実装が可能になります。
さらに工夫を
カスタム設定で設定した SELECT( col-base , col-sm , col-md )を元に、表示設定の「ループ内クラス」{entry:loop.class} を自動で書くことができる JavaScript を用意しました。こうすることでテンプレートは標準的な記述のままで済ますことができ、独自に設定したい時にも対応が可能となります。
モジュールフィールドの設定
<select name="col-base" class="acms-admin-form-width-mini">
<option value="acms-col-12" {col-base:selected#acms-col-12}>1カラム</option>
<option value="acms-col-6" {col-base:selected#acms-col-6}>2カラム</option>
<option value="acms-col-4" {col-base:selected#acms-col-4}>3カラム</option>
<option value="acms-col-3" {col-base:selected#acms-col-3}>4カラム</option>
</select>
<input type="hidden" name="field[]" value="col-base" />
<select name="col-sm" class="acms-admin-form-width-mini">
<option value="" {col-sm:selected#}>未設定 (480px以上)</option>
<option value="acms-col-sm-12" {col-sm:selected#acms-col-sm-12}>1カラム</option>
<option value="acms-col-sm-6" {col-sm:selected#acms-col-sm-6}>2カラム</option>
<option value="acms-col-sm-4" {col-sm:selected#acms-col-sm-4}>3カラム</option>
<option value="acms-col-sm-3" {col-sm:selected#acms-col-sm-3}>4カラム</option>
</select>
<input type="hidden" name="field[]" value="col-sm" />
<select name="col-md" class="acms-admin-form-width-mini">
<option value="" {col-md:selected#}>未設定 (768px以上)</option>
<option value="acms-col-md-12" {col-md:selected#acms-col-md-12}>1カラム</option>
<option value="acms-col-md-6" {col-md:selected#acms-col-md-6}>2カラム</option>
<option value="acms-col-md-4" {col-md:selected#acms-col-md-4}>3カラム</option>
<option value="acms-col-md-3" {col-md:selected#acms-col-md-3}>4カラム</option>
</select>
<input type="hidden" name="field[]" value="col-md" />
<span id="col-etc"></span>今回の JavaScript
最初に INPUT name="entry_summary_loop_class" の値からカスタム設定の SELECT を調整します。その後、SELECT を修正すると INPUT のテキストを書き換え、INPUT 側をダイレクトに修正すると SELECT 側を書き換えるようにしています。
SELECT の OPTION に無い acms-col-2 などが INPUT に書かれた際には、自動的に SELECT に追加し、acms-col- , acms-col-sm- , acms-col-md- でない Class を書いた時には <span id="col-etc"></span> に表示するようにして追加で Class を書いていることも分かるようにしておきました。
<script>
ACMS.addListener("acmsReady", function() {
const loopClass = document.querySelector('input[name="entry_summary_loop_class"]');
const colBase = document.querySelector('select[name="col-base"]');
const colSm = document.querySelector('select[name="col-sm"]');
const colMd = document.querySelector('select[name="col-md"]');
const colEtc = document.getElementById("col-etc");
function updateLoopClass() {
const values = [colBase.value, colSm.value, colMd.value].filter(value => value !== "");
loopClass.value = values.join(" ");
}
function ensureOptionExists(select, value) {
if (!value || select.querySelector(`option[value="${value}"]`)) return;
const existingValues = new Set(Array.from(select.options).map(opt => opt.value));
if (!existingValues.has(value)) {
const option = document.createElement("option");
option.value = value;
option.textContent = value;
option.setAttribute("data-custom", "true");
select.appendChild(option);
}
}
function updateSelectValues() {
const values = loopClass.value.split(" ").map(val => val.trim()).filter(val => val !== "");
let baseValue = "", smValue = "", mdValue = "";
let extraValues = [];
values.forEach(val => {
if (/^acms-col-sm-\d+$/.test(val)) {
smValue = val;
} else if (/^acms-col-md-\d+$/.test(val)) {
mdValue = val;
} else if (/^acms-col-\d+$/.test(val)) {
baseValue = val;
} else {
extraValues.push(val);
}
});
ensureOptionExists(colBase, baseValue);
ensureOptionExists(colSm, smValue);
ensureOptionExists(colMd, mdValue);
colBase.value = baseValue;
colSm.value = smValue;
colMd.value = mdValue;
colEtc.textContent = extraValues.length > 0 ? extraValues.join(", ") : "";
}
colBase.addEventListener("change", updateLoopClass);
colSm.addEventListener("change", updateLoopClass);
colMd.addEventListener("change", updateLoopClass);
loopClass.addEventListener("input", updateSelectValues);
updateSelectValues();
});
</script>a-blog cms 独自の実装ポイント
通常の管理画面のモジュールID の設定でテストを行っていた際には
document.addEventListener("DOMContentLoaded", function () {
...
});のように記述して動作していましたが、モジュールIDの管理画面は表の表示画面の「編集」リンクからモーダル表示が可能です。その際には既に DOMコンテンツがロード済みという事になり、ここで書かれた JavaScript を動作させることができません。
そこで a-blog cms の方で用意されている ACMS.addListener("acmsReady" ... を利用します。詳しくは「組み込みJSのイベントハンドラ」をご覧ください。
ACMS.addListener("acmsReady", function() {
...
});追加のカスタマイズを考える
今回の JavaScript には input[name="entry_summary_loop_class" と Entry_Summary のコンフィグのフィールドが書かれていますが、他のモジュールでも使う事を考慮するような実装も考えられます。
このカスタム設定の SELECT 部分 + JavaScript のファイルを別ファイルにし target_input を変数化します。
@include("/admin/parts/loop-class.html", {"target_input": "entry_summary_loop_class"}) /admin/parts/loop-class.html の中の JavaScript 部分を
const loopClass = document.querySelector('input[name="{{target_input}}"]');のように指定する事で、他のモジュールでも同様に扱いたいことがあれば共用して利用することができるようにできます。
最後に
次のバージョン Ver. 3.2 で標準提供するテーマには {entry:loop.class} を活用したテーマにするようにしようと考えています。
また private/config.system.default.yaml に
entry_summary_loop_class : acms-col-6 acms-col-sm-4
のような設定を追加を検討します。どのような初期値がいいかは検討しないといけませんが、こうする事で標準テーマで表示設定にある項目が機能する事になります。
日付選択カレンダー(flatpickr)
従来、日付選択用のインターフェースにはjQuery UIを使用しておりましたが、Ver.2.8.0より導入したフラットでシンプルな日付選択用のJavaScriptライブラリ 「flatpickr」 の使い方を説明します。
指定したclass属性さえ適用すれば、任意の入力フィールドで自由にご使用いただけます。
デフォルトの設定
この機能の設定は、/js/config.jsの以下の箇所にあります。設定を変更する場合は、適用しているテーマ内にJavaScriptファイルを別途作成してください。
詳しくは「組み込みJSについて:設定を編集する」を参照してください。
flatDatePicker: '.js-datepicker2',
flatDatePickerConfig: {
allowInput: true,
locale: ACMS.i18n.lng,
dateFormat: 'Y-m-d'
},
flatTimePicker: '.js-timepicker',
flatTimePickerConfig: {
allowInput: true,
locale: ACMS.i18n.lng,
enableTime: true,
noCalendar: true,
dateFormat: "H:i:S",
time_24hr: true
},
使い方
日付選択のUI
<input type="text" name="date" class="js-datepicker2" size="15" placeholder="2009-06-23" />
このように記述すると、入力フィールドをクリックしたときに日付選択カレンダーが表示され、日付をクリックすると入力フィールドに日付が入力されます。
日付:
config.jsにはないFlatpickrの設定をする
js-datepicker2の日付選択のUIにはFlatpickrというライブラリを使用しており、公式ドキュメントには設定が多く公開されています。詳しくは公式ドキュメントをご覧ください。
公式ドキュメントに公開されているConfig OptionにACMS.Config.flatDatePickerConfig.を頭につけて値を渡すと、Flatpickrのその他の設定を利用できます。
例)モバイル端末でもFlatpickrを有効にする設定
ACMS.Ready(function() {
ACMS.Config.flatDatePickerConfig.disableMobile = true;
});
例)現在の日付から4日後〜20日後まで選択する設定
ACMS.Ready(function() {
var minDate = new Date();
var maxDate = new Date();
minDate.setDate(minDate.getDate() + 4);
maxDate.setDate(maxDate.getDate() + 20);
ACMS.Config.flatDatePickerConfig.minDate = minDate.getFullYear() + '-' + (parseInt(minDate.getMonth(), 10) + 1) + '-' + minDate.getDate();
ACMS.Config.flatDatePickerConfig.maxDate = maxDate.getFullYear() + '-' + (parseInt(maxDate.getMonth(), 10) + 1) + '-' + maxDate.getDate();
});
例)特定の曜日を無効化する設定
以下の例では、土曜日(6)と日曜日(0)が無効化されます。
ACMS.Ready(function() {
ACMS.Config.flatDatePickerConfig.disable = [
function(date) {
return (date.getDay() === 0 || date.getDay() === 6);
}
];
});
各曜日を指定する際は、下記の値を指定してください。
| 曜日 | 値 |
|---|---|
| 日曜日 | 0 |
| 月曜日 | 1 |
| 火曜日 | 2 |
| 水曜日 | 3 |
| 木曜日 | 4 |
| 金曜日 | 5 |
| 土曜日 | 6 |
時間選択のUI
<input type="text" name="date" class="js-timepicker" size="15" />
このように記述すると、入力フィールドをクリックしたときに時間選択のためのUIが表示され、時刻を選択するとその時刻が挿入されます。
時間:
【重要】Shoping Cart拡張アプリの EMV3Dセキュア導入義務化への対応について
経済産業省による3Dセキュア導入の義務化を遵守するため、ShoppingCart 拡張アプリ Ver. 2.3.0 より Square を利用したクレジットカード決済機能に3Dセキュア機能を搭載いたしました。
3Dセキュア導入について
2025年1月17日 Square にて3Dセキュア導入の義務化への対応に関しての記事が公開されました。
内容としては、2025年3月31日より3Dセキュアの自動有効化を行うため、2025年3月31日までに3Dセキュアに対応した新しい支払いフローを実装する必要があるというものでした。
今回のリリースでは、経済産業省による3Dセキュア導入の義務化を遵守するため、ShoppingCart 拡張アプリ Ver. 2.3.0 より Square を利用したクレジットカード決済機能に3Dセキュア機能に対応した新しい支払いフローを実装いたしました。
3Dセキュアを導入するためには以下の対応が必要になります。
- ShoppingCart 拡張アプリを Ver. 2.3.0 以上にアップデートする
- テーマ側で3Dセキュア機能が有効になるように追加実装をする
3Dセキュア導入に関する注意事項
- ShoppingCart 拡張アプリのアップデートは『3Dセキュア実施を可能とする』ためのものになります。アップデートにより自動的に3Dセキュアが実施されるものではございません。
- 3Dセキュアの実施をするには、テーマ側で3Dセキュア機能が有効になるように追加実装をしていただく必要があります。
- 実際にSquare側で3Dセキュアが有効になるのは2025年3月31日からになります。
3Dセキュア機能を有効にする方法
Square のカード情報入力コンポーネントを表示している div 要素に3Dセキュア機能の有効に必要な属性を追加してください。
EC テーマを改変せず利用している場合は、themes/square@ec/include/parts/card-form.html を次のように変更します。
<div
id="js-sq-card-container"
data-square-three-d-secure="true"
data-square-amount="\{total\}"
data-square-currency-code="JPY"
data-square-intent="CHARGE"
data-square-seller-keyed-in="false"
data-square-customer-initiated="true"
data-square-billing-contact-given-name="\\{name\\}"
data-square-billing-contact-family-name=""
data-square-billing-contact-email="\\{email\\}"
data-square-billing-contact-phone="\\{tel\\}"
data-square-billing-contact-country-code="JP"
data-square-billing-contact-postal-code="\\{postal-code\\}"
data-square-billing-contact-state="\\{pref\\}"
data-square-billing-contact-city="\\{address1\\}"
data-square-billing-contact-address-line1="\\{address2\\}"
data-square-billing-contact-address-line2="\\{address3\\}"
>
<!-- Squareのカード入力用コンポーネントが表示されます -->
</div>{total} は Order_Summary モジュールの変数を利用しており、{name} , {email} , {tel} , {postal-code} , {pref} , {address1} , {address2} , {address3} は ShoppingForm モジュールの変数を利用しています。テーマをカスタマイズして、変数名を変更している場合は適宜読み替えてください。
3Dセキュア機能を有効にするためには data-square-three-d-secure 属性の値に true を設定し、決済情報及び決済者情報を設定してください。
設定した決済情報及び決済者情報は、Square のサーバーに送信され、検証のための詳細情報として扱われます。詳細は以下のURLからご確認ください。
各種属性についての説明は以下になります。
| 属性名 | 説明 | デフォルト |
|---|---|---|
| data-square-three-d-secure | 3Dセキュアを有効にするかどうかを指定します。 true | false のどちらかになります。true にした場合、data-square-amount は必須になります。 | false |
| data-square-amount | クレジットカード支払いで請求される総額を指定します。(注1, 2) | |
| data-square-currency-code | ISO4217 の通貨コードを指定します。(注1, 2) | JPY |
| data-square-intent | 支払いのトランザクションインテントを指定します。CHARGE | CHARGE_AND_STORE のどちらかになります。(注1, 2) | CHARGE |
| data-square-seller-keyed-in | 販売者が顧客に代わって支払詳細を入力したことを示します。これは、支払いをMail Order / Telephone Order (MOTO)としてフラグを立てるために使用されます。true | false のどちらかで指定します。(注1, 2) | false |
| data-square-customer-initiated | 顧客が支払いを開始したかどうかを示します。true | false のどちらかで指定します。(注1, 2) | true |
| data-square-billing-contact-given-name | 購入者の名 (ファーストネーム)または、名前を指定します。(注1, 2) | |
| data-square-billing-contact-family-name | 購入者の苗字 (ファミリーネーム、「ラスト」ネーム)を指定します。(注1, 2)性と名を分けて指定できない場合は、フルネームを data-square-billing-contact-given-nameとして指定してください。 | |
| data-square-billing-contact-email | 購入者のメールアドレスを指定します。(注1, 2) | |
| data-square-billing-contact-phone | 購入者の電話番号を指定します。(注1, 2) | |
| data-square-billing-contact-country-code | 購入者の住所情報の内、 ISO 3166-1 の国名コードを指定します。(注1, 2) | |
| data-square-billing-contact-postal-code | 購入者の住所情報の内、郵便番号を指定します。(注1, 2) | |
| data-square-billing-contact-state | 購入者の住所情報の内、都道府県を指定します。(注1, 2) | |
| data-square-billing-contact-city | 購入者の住所情報の内、市区町村を指定します。(注1, 2) | |
| data-square-billing-contact-address-line1 | 購入者の住所情報の内、国名コード・郵便番号・都道府県・市区町村以降の住所を指定します。(注1, 2) | |
| data-square-billing-contact-address-line2 | 購入者の住所情報の内、国名コード・郵便番号・都道府県・市区町村以降の住所を指定します。(注1, 2) | |
| data-square-billing-contact-address-line3 | 購入者の住所情報の内、国名コード・郵便番号・都道府県・市区町村以降の住所を指定します。(注1, 2) |
- 注1:これらの値はSquareによるクレジットカード情報のトークン化が行われるとき、つまりクレジットカード情報を入力するフォームを送信するときに評価されます。
- 注2:これらの値は、まず
document.querySelector()での検索に使用され、見つかればその value を使い、なければ指定値を生の文字列として扱います。
詳細は 「Squareと連携してクレジットカード決済機能を実装する」でも説明しておりますので、こちらも合わせてご確認ください。
3Dセキュア機能をテストする
Square が提供する Sandbox Payments を利用して3Dセキュア機能が適切に動作しているかテストします。
Square Square の開発者用ダッシュボードから「Sandbox Test Accounts」のアカウントを起動し、ShoppingCart 拡張アプリの管理画面からSandboxモードを有効化後、再度認証ボタンを押下して、Sandbox モードで決済処理のテストができるようにしてください。
3Dセキュア機能をテストするために、3Dセキュアの追加認証を誘発するテストカードと、誘発しないテストカードの2枚でテストを行います。
まず、3Dセキュアの追加認証を誘発するテストカードでテストするために、 SCA testing in the Web Payments SDK に記載されているテストカードの内、Challenge type が Modal with Verification Code となっているカードを利用して決済を行います。
決済時に次のようなモーダルが表示されていれば、3Dセキュアの追加認証が正常にできていることが確認できます。
認証コードを入力し、「Authenticate」を選択します。認証後、アプリケーションはカード決済を完了するために決済リクエストを送信します。この後は、通常通り決済を完了します。
次に、3Dセキュアの追加認証を誘発しないテストカードでテストするために、 SCA testing in the Web Payments SDK に記載されているテストカードの内、Challenge type が No Challenge となっているカードを利用して決済を行います。
3Dセキュアの追加認証を誘発しないテストカードの場合、決済時にモーダルが表示されず、そのまま決済処理が完了していれば正常に動作していることが確認できます。
3Dセキュアとは
インターネット上のクレジットカードの不正利用を防ぐために、カード会社が提供している本人認証サービスです。
カード会社が不正リスクの判断をおこない、必要に応じて、ワンタイムパスワードなどによる本人認証が求められます。
なお、ShoppingCart 拡張アプリで連携可能な Square では、経済産業省の公開する「クレジットカード・セキュリティガイドライン」で求められる、「EMV 3-Dセキュア(3Dセキュア2.0)」を導入しております。
本人認証の方法について
本人認証の方法は、SMS、メールによるワンタイムパスワード認証やアプリ認証など、購入者がご利用になるクレジットカードの発行会社によって異なります。
購入者より、認証方法についてお問い合わせがありましたら、ご利用のカード発行会社にご確認いただくようご案内ください。
最後に
2025年3月末までの導入義務化に向け、ご協力のほど何卒よろしくお願いいたします。
今後もご報告いただいた内容に対して真摯に受け止め修正と改善を行ってまいります。 今後ともどうぞよろしくお願いいたします。