PowerCMS™
2025年1月1日購入分よりライセンスの価格を改定いたします。
[ブログ] PowerCMS 6 でのアップデートまとめ を追加しました。
[新着情報] アルファサードの冬期休業について を追加しました。
[よくあるご質問] サポートサイトへ複数のアカウントを作成できますか? を追加しました。

PowerCMS ブログ

ホーム > PowerCMS ブログ > 設定・管理画面カスタマイズ > スニペット・カスタムフィールドでカスタムフィールドをタブグループにまとめる

2014年06月13日

スニペット・カスタムフィールドでカスタムフィールドをタブグループにまとめる

PowerCMS に同梱されているスニペットフィールドを使うと、任意の UI でカスタムフィールドを作ることが可能です。

PowerCMS ユーザーガイドに掲載されているシンプルな管理画面 UI の作成から、MT タグや JavaScript を活用した高機能な管理画面 UI 作成まで、カスタマイズ次第であらゆる管理画面 UI を作ることができます。

今回はスニペットカスタムフィールドを利用して、任意のカスタムフィールドをタブでグループ化するカスタマイズ Tips を紹介します。

本記事で紹介するサンプルは、PowerCMS 4.03 にて動作確認をしています。

完成図

タブ1を表示した時のカスタムフィールドの状態

タブ2を表示した時のカスタムフィールドの状態

タブ3を表示した時のカスタムフィールドの状態

上記の完成図にするためのスニペット・カスタムフィールドのサンプルが下記になります。

スニペット・カスタムフィールドの設定

タブグループ化するための、記事のカスタムフィールドが複数あることを前提としています。

上記の完成図では、テキスト、チェックボックス、テキスト(複数行)、ラジオボタン、画像のカスタムフィールドが事前にあり、それをグループ化しました。

仮に、今回作成するカスタムフィールドは、ウェブサイトの記事のカスタムフィールドで、それぞれのカスタムフィールドのベースネームが下記であるとします。

  1. テキスト ( websiteentrytext )
  2. チェックボックス ( websiteentrycheckbox )
  3. テキスト複数行 ( websiteentrytextarea )
  4. ラジオボタン ( websiteentryradio )
  5. 画像 ( websiteentryimage )

以下は、スニペット・カスタムフィールドで作成するカスタムフィールドの具体的な設定項目とその値です。

名前

カスタムフィールドグループ

任意の名前を指定してください。

説明

なし

任意の説明文を指定してください。

種類

スニペット

保存後に表示される既定値の設定は後述します。

オプション

websiteentrytext,websiteentrycheckbox___websiteentrytextarea,websiteentryradio___websiteentryimage

スニペット・カスタムフィールドでは、オプションに指定した値をパラメータとしてリクエストに含めることで、任意の値を保存できる仕組みです。

今回のサンプルは、オプションの項目を、本来の用途ではなく、タブグループをどのようにグループ化するかを記述するために使っています。

指定する値は、タブグループに利用するカスタムフィールドのベースネームで、「___」(アンダースコアが三つ)がタブの区切りで、「,」(カンマ)がベースネームの区切りになります。

また、PowerRevision プラグインでは、このオプション値をバックアップファイルとして生成する XML ファイル中のタグ名として利用する制約があるため、「___」(アンダースコアが三つ)の前後に空白などの XML の要素名として利用できない文字を含めることはできません。

上記の設定は、1つ目のタブに、websiteentrytextwebsiteentrycheckbox を表示し、2つ目のタブに、websiteentrytextareawebsiteentryradio を表示し、最後のタブに、ベースネームが websiteentryimage であるカスタムフィールドを表示するという設定になります。

ベースネーム

websiteentrycfgroups

任意のベースネームを指定してください。

テンプレートタグ

websiteentrycfgroups

任意のテンプレートタグを指定してください。

今回のサンプルでは、スニペット・カスタムフィールド自体には値を保存しないため、このタグを利用する機会はありません。

既定値

<div id="cfgroups"></div>

<script type="application/x-mt-js-template" id="cfgroups-tabs-tmpl">
    <div id="cfgroups-tabs">
    <ul>
    [# jQuery.each(tabs, function (i) { #]
        <li><a href="#cfgroups-tabs-[#= i + 1 #]">タブ[#= i + 1 #]</a></li>
    [# }) #]
    </ul>
    [# jQuery.each(tabs, function (i) { #]
    <div id="cfgroups-tabs-[#= i + 1 #]"></div>
    [# }) #]
    </div>
</script>

<script>
    jQuery(function ($) {
        'use strict';

        // 変数を宣言する
        var $me, $me_display_option, $cfgroups, options, tabs, html;

        // スニペット・カスタムフィールド自身の jQuery オブジェクトをキャッシュする
        $me = $('#customfield_<$mt:var name="basename" encode_js="1"$>-field');
        // スニペット・カスタムフィールド自身の表示オプションチェックボックスの jQuery オブジェクトをキャッシュする
        $me_display_option = $('#custom-prefs-customfield_<$mt:var name="basename" encode_js="1"$>');

        // div#cfgroups の jQuery オブジェクトをキャッシュする
        $cfgroups = $('#cfgroups');

        // オプションの値を変数に代入する
        options = '<$mt:var name="options" encode_js="1"$>';

        // オプションの値を配列に変換する
        tabs = $.map(options.split('___'), function (option) {
            return [option.split(/\s*,\s*/)];
        });

        // script#cfgroups-tabs-tmpl の innerHTML をテンプレートとして、テンプレートをビルドする
        // テンプレート内で利用する tabs という変数に、上記の tabs 変数をアサインしている
        html = Template.process('tabs', { tabs: tabs }, { tabs: $('#cfgroups-tabs-tmpl').html() });

        // スニペット・カスタムフィールド自身が表示されているかどうかを判定するための関数
        function isHidden ($me) {
            return $me.is(':hidden');
        }

        // flatten で利用するためのユーティリティ関数
        function identity (a) {
            return a;
        }

        // ネストされた配列(2階層まで)をフラットにする関数
        function flatten (arr) {
            return $.map(arr, identity);
        }

        // タブグループをレンダリングするための関数
        function render () {
            // div#cfgroups の innerHTML にテンプレートのビルド結果を反映する
            $cfgroups.html(html);

            // カスタムフィールドをタブグループの該当タブに移動する
            $.each(tabs, function (i) {
                var basenames = this;
                $.each(basenames, function () {
                    var basename, $customfield;
                    basename = this.valueOf();
                    $customfield = $('#customfield_' + basename + '-field');
                    // カスタムフィールドの見出し部分はソート不可にするため、
                    // ソート対象になるクラス名を除去し、最低限のスタイル調整をする
                    $customfield
                        .find('.field-header')
                        .removeClass('field-header')
                        .css('padding', '5px 10px');
                    // カスタムフィールドを該当のタブグループに移動する
                    $customfield.appendTo('#cfgroups-tabs-' + (i + 1));
                });
            });

            // div#cfgroups-tabs をタブ化する
            $cfgroups.find('#cfgroups-tabs').tabs();
        }

        // カスタムフィールドを通常の位置に戻すための関数
        function restore () {
            $.each(flatten(tabs), function () {
                // タブグループ内のカスタムフィールドの jQuery オブジェクトを取得する
                var $field = $('#customfield_' + this + '-field');
                // カスタムフィードの見出し部分に、クラス名 .field-header を再設定し、ソート可能にする
                $field.children().first().addClass('field-header');
                // フィールドの並びの最後に移動する
                $field.appendTo('#sortable');
            });
        }

        // 自身の表示オプションが変更された時、HTML を組み立て直すためのイベントハンドラ
        $me_display_option.on('change', function () {
            if ($(this).is(':checked')) {
                // 表示オプションが ON になったら、タブグループを表示する
                render();
            }
            else {
                // 表示オプションが OFF になったら、タブグループから、本来の場所にカスタムフィールドを移動する
                restore();
            }
            return true;
        });

        // 自身が非表示であれば何もしない
        if (isHidden($me)) return;

        // 自身が非表示でなければタブグループを組み立てて表示する
        render();
    });
</script>

上記が、タブグループを実現するための、スニペット・カスタムフィールドの具体的なロジックになります。

冒頭の <div id="cfgroups"></div> は、JavaScript で生成した DOM を挿入するためのラッパー要素です。

次の、<script type="application/x-mt-js-template" id="cfgroups-tabs-tmpl"> ... </script> で囲まれた部分は、タブグループを描画する HTML の JavaScript テンプレートになります。

JavaScript テンプレートは、Movable Type の管理画面でも利用されている、Movable Type 標準の Template.js という JavaScript テンプレートエンジンを利用しています。

script 要素の type 属性にブラウザが理解できない Media タイプが指定された時、その内容を無視するという特徴を利用して、テンプレートの記述のために利用しています。
※ 本来は script 要素の type 属性に指定する Media タイプは、RFC にて定義された妥当な Media タイプを指定するべきですが、すでに広く使われている手法のため、今回はこの方法を採用しました。

その後の script 要素にて、具体的な処理内容を記述しています。

処理内容を大きく捉えると、共通で使う変数の初期化、ユーティリティ関数の定義、イベントハンドラの登録、メインの処理といった内容が記述されています。コメントをヒントにご確認ください。

jQuery の ready イベントが発火するまでは、通常のカスタムフィールドとして表示され、その要素自体をタブグループ内に移動するため、並び順や表示オプションの状態などはそのまま引き継がれます。(タブグルーブ内の並びはオプションの指定順によって変更します)また、非表示の時は何もしないようにしています。

本サンプルの利点は、スニペット・カスタムフィールド自身で UI を作り込むわけではないため、通常のカスタムフィールドの編集や追加(スニペット・カスタムフィールドのオプションの値を追加する必要はあります)の操作にて UI を変更できる点にあります。

以上、スニペット・カスタムフィールドのカスタマイズの一例をご紹介しました。

スニペット・カスタムフィールドは、管理画面 UI の柔軟なカスタマイズが可能ですので、ぜひご活用ください。

追記

投稿当初、オプション値タブでグループ化する範囲の区切り文字を「|」(バーティカルバー)にしておりましたが、PowerRevision との組み合わせで問題があることが確認されたため、記事中の「|」は、「___」(アンダースコアが三つ)に変更いたしました。


カテゴリー
設定・管理画面カスタマイズ

Recent Entries