PowerCMS™

PowerCMS ブログ

ホーム > PowerCMS ブログ

完成イメージ

記事編集画面に次の様なフィールドを作成するのが本記事のゴールとなります。

完成イメージの管理画面キャプチャ画像

機能としては下記要件を満たしたものを作ります。

  • リンクテキストとリンク URL がセットになった入力欄を作成
  • 入力欄は追加する事が出来る
  • 入力欄数の上限はなし ( JavaScript によって入力欄を増減 ) [1]

さて、それでは実際に作成していきましょう。

  1. ステップ 1. カスタムフィールドを作成
  2. ステップ 2. 入力画面用のテンプレートモジュールを作成
  3. ステップ 3. フロント用のテンプレートモジュールを作成
  4. ステップ 4. 記事アーカイブテンプレートに作成したテンプレートモジュールをインクルード

※ なお、本記事の動作確認は執筆時点で最新の PowerCMS 5.12 で行っております。

ステップ 1. カスタムフィールドを作成

先ず、何は無くともカスタムフィールドを作成します。

「既定値」には編集画面用のテンプレートを記入するのですが、今回はそこそこの行数となります関係で別途テンプレートモジュールを作成してインクルードする事にします。

項目名 設定値
システムオブジェクト 記事
名前 関連リンク
種類 スニペット
オプション customfield_entry_outerlink_title,customfield_entry_outerlink_url
既定値 [2] <mt:Include module="cf-entry_outerlink" blog_id="1" />
ベースネーム entry_outerlink
テンプレートタグ entry_outerlink

ステップ 2. 入力画面用のテンプレートモジュールを作成

入力画面用のテンプレートモジュールです。

実装上のポイントは『同名の name 属性を持つ <input type="text"> 要素を複数用意し、一つのオプションに対して複数の値を保存する』ところにあります。

ユーザーガイドから引用を行いますと、

複数の値を持つ入力欄を作成する

例えば複数選択可能なチェックボックスなど、同じ name 属性の input 要素を指定することができます。
下記は複数選択可能なチェックボックスのテンプレート (既定値欄に入力するテンプレート) の例です。name 属性値に「_loop」を付け加えた名前のループ変数を MTLoop ブロックタグでループして snipet_option という変数で値を受け取ります。

『PowerCMS ユーザーガイド』(PDF) より

と記載されています。

以下のテンプレートモジュールでは入力欄用の HTML と CSS 、そして JavaScript による入力欄の増減する機能を記述しています。

テンプレート名 cf-entry_outerlink

cf-entry_outerlink テンプレートモジュール

<div class="customfield_entry_outerlink-content">
  <mt:LocalVars>
    <mt:SetVar name="titles">
    <mt:Loop name="customfield_entry_outerlink_title_loop">
      <mt:SetVarBlock name="titles" function="push"><mt:Var name="snippet_option"></mt:SetVarBlock>
    </mt:Loop>
    <mt:SetVar name="urls">
    <mt:Loop name="customfield_entry_outerlink_url_loop">
      <mt:SetVarBlock name="urls" function="push"><mt:Var name="snippet_option"></mt:SetVarBlock>
    </mt:Loop>
    <mt:If name="titles">
      <mt:Loop name="titles">
        <mt:Var name="__counter__" op="--" setvar="index">
        <div class="customfield_entry_outerlink-wrap">
          <button type="button" class="js-outerlink__btn--remove">削除</button>
          <label for="customfield_entry_outerlink_title_<mt:Var name="index">" id="customfield_entry_outerlink_title_<mt:Var name="index">-label">リンクテキスト</label><br>
          <input name="customfield_entry_outerlink_title" id="customfield_entry_outerlink_title_<mt:Var name="index">" type="text" value="<mt:Var name="titles" index="$index" escape="html">">
          <label for="customfield_entry_outerlink_url_<mt:Var name="index">" id="customfield_entry_outerlink_url_<mt:Var name="index">-label">URL</label><br>
          <input name="customfield_entry_outerlink_url" id="customfield_entry_outerlink_url_<mt:Var name="index">" type="text" value="<mt:Var name="urls" index="$index" escape="html">">
        </div>
      </mt:Loop>
    <mt:Else>
      <div class="customfield_entry_outerlink-wrap">
        <button type="button" class="js-outerlink__btn--remove">削除</button>
        <label for="customfield_entry_outerlink_title_0" id="customfield_entry_outerlink_title_0-label">リンクテキスト</label><br>
        <input name="customfield_entry_outerlink_title" id="customfield_entry_outerlink_title_0" type="text" value="">
        <label for="customfield_entry_outerlink_url_0" id="customfield_entry_outerlink_url_0-label">URL</label><br>
        <input name="customfield_entry_outerlink_url" id="customfield_entry_outerlink_url_0" type="text" value="">
      </div>
    </mt:If>
  </mt:LocalVars>
</div>

<p style="margin: 0; text-align: right;"><button type="button" class="js-outerlink__btn--add">追加する</button></p>

<style>
.customfield_entry_outerlink-wrap {
  overflow: hidden;
  margin-bottom: 10px;
  padding: 10px;
  border: 1px solid #c0c6c9;
  background-color: #fff;
  border-radius: 3px;
  line-height: 1.5;
}
.js-outerlink__btn--remove {
  float: right;
  margin-bottom: 2px;
}
.customfield_entry_outerlink-wrap input[type="text"] {
  box-sizing: border-box;
  width: 100%;
}
</style>

<script>
;(function($){
  const createElement = function (options) {
    const index  = options.index || 0;
    const labels = options.labels || {};
    const values = options.values || {};

    const wrap = document.createElement('div');
    if (options.wrap_class) wrap.setAttribute('class', options.wrap_class);
    const btn = document.createElement('button');
    btn.textContent = '削除';
    btn.setAttribute('type', 'button');
    btn.setAttribute('class', 'js-outerlink__btn--remove');
    wrap.appendChild(btn);

    ['title', 'url'].forEach(function(value){
      const name = 'customfield_entry_outerlink_' + value;
      // create label
      if (labels[value]) {
        const label = document.createElement('label');
        label.textContent = labels[value];
        label.setAttribute('for', name + '_' + index);
        label.setAttribute('id', name + '_' + index + '-label');
        wrap.appendChild(label);
        wrap.appendChild(document.createElement('br'));
      }
      // create input
      const input = document.createElement('input');
      input.setAttribute('name', name);
      input.setAttribute('type', 'text');
      input.setAttribute('id', name + '_' + index);
      input.setAttribute('class', 'text');
      if (values[value]) input.setAttribute('value', values[value]);
      wrap.appendChild(input);
    });

    const e = document.createDocumentFragment();
    e.appendChild(wrap);

    return e;
  };

  $(function(){
    const $content = $('.customfield_entry_outerlink-content');
    const item_class = 'customfield_entry_outerlink-wrap';
    let index = $content.find('.' + item_class).length || 0;
    // add unit
    $(document).on('click', '.js-outerlink__btn--add', function(event){
      event.preventDefault();
      let element = createElement({
        index: ++index,
        labels: { title: 'リンクテキスト', url: 'URL' },
        wrap_class: item_class
      });
      if (element) {
        $content.append(element);
      }
    });
    // remove unit
    $(document).on('click', '.js-outerlink__btn--remove', function(event){
      event.preventDefault();
      if (window.confirm('削除しますか?')) {
        $(this).parent().remove();
      }
    });
  });
})(jQuery);
</script>

ステップ 3. フロント用のテンプレートモジュールを作成

同名のオプションに格納されたデータは、カスタムフィールドのタグに Vars を追加するとループ処理で取り出す事が可能です。

ユーザーガイドから引用を行いますと、

このケースでタグ名が MTEntrySnippet、オプションが foo の場合、下記のようにテンプレートを記述します。

テンプレート
<MTEntrySnippetVars key="foo">
  <$MTVar name="__value__"$>
</MTEntrySnippetVars>
コンテキスト (MTVar) にセットされる値
__first__: ループの最初
__counter__: ループの何回目か
__odd__: 奇数回目の出力
__even__: 偶数回目の出力
__last__: ループの最後
__value__: 値

『PowerCMS ユーザーガイド』(PDF) より

となっています。

今回は「リンクテキスト ( customfield_entry_outerlink_title ) 」と「リンク URL ( customfield_entry_outerlink_url ) 」をループで取り出します。

テンプレート名 関連リンク

関連リンク テンプレートモジュール

<mt:Ignore>** 関連リンク **</mt:Ignore>
<mt:LocalVars>
  <mt:SetVar name="titles" />
  <mt:SetVar name="urls" />
  <mt:entry_outerlinkVars key="customfield_entry_outerlink_title">
    <mt:SetVarBlock name="titles" function="push"><mt:Var name="__value__" /></mt:SetVarBlock>
  </mt:entry_outerlinkVars>
  <mt:entry_outerlinkVars key="customfield_entry_outerlink_url">
    <mt:SetVarBlock name="urls" function="push"><mt:Var name="__value__" /></mt:SetVarBlock>
  </mt:entry_outerlinkVars>
  <mt:If name="titles">
    <mt:SetVar name="list" />
    <mt:Loop name="titles">
      <mt:If name="__value__">
        <mt:Var name="__counter__" op="--" setvar="index" />
        <mt:If name="urls" index="$index">
          <mt:SetVarBlock name="list" function="push"><a href="<mt:Var name="urls" index="$index" escape="html" />" class="link-outside" target="_blank"><mt:Var name="__value__" escape="html" /></a>(別ウィンドウが開きます)</mt:SetVarBlock>
        </mt:If>
      </mt:If>
    </mt:Loop>
    <mt:Loop name="list">
      <mt:If name="__first__">
        <h3 id="relation_link">関連リンク</h3>
        <ul>
      </mt:If>
          <li><mt:Var name="__value__" /></li>
      <mt:If name="__last__">
        </ul>
      </mt:If>
    </mt:Loop>
  </mt:If>
</mt:LocalVars>

ステップ 4. 記事アーカイブテンプレートに作成したテンプレートモジュールをインクルード

あとは記事アーカイブに作成したフロント用テンプレートモジュールをインクルードして完成です。

<mt:Include module="関連リンク" />

おわりに

スニペットフィールドは工夫次第で様々な入力項目を作成する事が可能です。ぜひご活用ください。

関連リンク

脚注

  1. 厳密にはデータベース側でサイズ上限がありますが、BLOB型 もしくは MEDIUMBLOB型 にバイナリとして保存されるので、リンクリスト程度のテキストではほぼ上限がないといえると思います。[^1]
  2. blog_id には、ステップ 2 で作成するテンプレートモジュール「cf-entry_outerlink」が属するブログの ID を設定してください。[^2]
カテゴリー
テンプレート作成Tips設定・管理画面カスタマイズ
投稿者
たはかし

TinyMCE の設定変更方法について、サポートによくいただくお問い合わせ第二弾です。第一弾はこちらです。

  • TinyMCE のバージョンによって利用可否、記述の方法が異なる場合があります。
  • PowerCMS 5 から TinyMCE 4(v4と表記)になりました。それ以前のバージョンでは TinyMCE 3(v3と表記)となります。
  • TinyMCE 4 と TinyMCE 3 とではプラグインの構成や機能等が異なっているため、TinyMCE 3 から 4 に移行した際に必要な機能が使用できなくなる可能性があります。そのため PowerCMS 5 では下位バージョンからのアップグレードの場合には TinyMCE 3 が維持されるようになっています。どのバージョンの TinyMCE を使用しているかは [システム]-[設定]-[PowerCMS] の TinyMCE 設定に表示されている「バージョン」の項目から確認することができます。使用するTinyMCEのバージョンは環境変数 TinyMCEVersion により変更することができます。

href の値で javascript: を許可したい

対象バージョン:v4

参考:https://www.tiny.cloud/docs-4x/configure/url-handling/#allow_script_urls

『高度な設定』への指定例

tinymce_editor.init['allow_script_urls'] = true;

カラーパレットの色を変更する

対象バージョン:v4

参考:https://www.tiny.cloud/docs-4x/plugins/textcolor/#textcolor_map

※plugins、advanced_buttons1 の設定は backcolor 以外はもともと入っている可能性があります。ご確認ください。

textcolor_map の指定例
設定欄ラベル 設定 備考
plugins textcolor カンマ区切りで追加
advanced_buttons1 forecolor,backcolor カンマ区切りで追加
高度な設定 tinymce_editor.init.textcolor_map = [
"993300", "Burnt orange",
"333300", "Dark olive",
"003300", "Dark green",
"003366", "Dark azure",
"000080", "Navy Blue",
"333399", "Indigo",
"333333", "Very dark gray",
"800000", "Maroon",
"FF6600", "Orange",
"808000", "Olive",
"008000", "Green",
"008080", "Teal",
"0000FF", "Blue",
"666699", "Grayish blue",
"808080", "Gray",
"FF0000", "Red"];
 

main タグが p タグで括られないようにしたい

対象バージョン:v4

参考:https://www.tiny.cloud/docs-4x/configure/content-filtering/#forced_root_block

『高度な設定』への指定例

tinymce_editor.init['forced_root_block'] = false;

利用の注意点

改行で p が入らなくなるのではなく br に変わる動きをするため、大元の動作が変わります。改行しようと shift + enter すると結局 p が復活するため注意してください。

カテゴリー
PowerCMS 4PowerCMS 5技術情報

前回の記事(次のURL)で、"郵便番号から住所を自動入力する"タイプのフォームを構築する方法を解説しましたが、

ContactFormでフォーム項目の種類に無い項目をCMSテンプレートのカスタマイズのみで作る(その3::郵便番号から住所自動入力)

実は、過去に次のような記事もありました(確認が漏れていた。。。)

カスタムオブジェクトを利用して郵便番号から住所を検索するAPIを作る

ちなみに、このカスタムオブジェクトを利用する方法の場合、CMSが郵便番号から住所を返すAPIを提供し、更にCMSのDBが郵便番号-住所の対照表となるデータを保持します。その為、次のメリットがあります。

  • 実装を全てPowerCMSで管理できる。
  • より細やかなカスタマイズが出来る。

一方で、前回の記事で紹介したJSライブラリ「ajaxzip3」を使う方法は外部サービスに依存しますが、カスタムオブジェクトを利用する方法と比べて簡単に実装が出来ます。

これから実装を試みられる方は、前述の両者の特徴を踏まえた上で、状況にマッチする方法を選択されるとよいでしょう。

さて、ここからが本題。

はじめに

前回は企業の担当者様からのお問い合わせを受け付けるフォームを想定しましたが、住所入力欄は "1つ" しかありませんでした。だた、ウェブ上で見られるフォームの中には、複数の住所入力欄を持つものもあります。

そこで今回は前回の応用編として、2つの住所入力欄「本店所在地」「ご担当者さまの住所」を持つフォームを構築する方法を解説します。

※今回は本文冒頭の前回記事にて構築したフォームに機能追加する、という形で説明致します。

作成手順

フォーム項目「ご担当者さまの住所」の追加

次の通り、フォーム項目「ご担当者さまの住所」を追加する為のCSVファイルを設けました。

フォーム項目(郵便番号から住所自動入力__ご担当者さまの住所).csv

当該CSVを用いて、次の手順でフォーム項目を作成します。

1.フォームを追加する対象のウェブサイトの管理画面のメニュー「フォーム>フォーム項目」をクリック。

 →「フォーム項目の管理」画面が開く。

2.フォーム項目一覧上端のテキストリンク「CSVからインポート」をクリック。

 →「参照」ボタンが表示される。

3.「参照」ボタンをクリックし、前述のインポート用CSVファイルを選択する。

 →「参照」ボタン右側に、テキストリンク「送信」が表示される。

4.テキストリンク「送信」をクリック。

 →CSVのインポートが実行され、CSVに記載されたフォーム項目が作成される。

作成が成功すると、「フォーム項目の管理 」画面にて次のフォーム項目が追加された事が分かります。

  • ご担当者様の住所

フォーム項目の概要は、次の通りです。

  • ご担当者さまの住所:
    • フォーム項目「本店所在地」と同様の、住所入力欄のフォーム項目。
      「本店所在地」とほぼ同じだが、次の点で異なる。
      • 「本店所在地」との併用が前提。
      • チェックボックス「本店と同じ」がある。

これを、対象のフォームの「本店所在地」の下に来るよう配置してください。

カスタマイズ後のイメージ

フォームの入力画面は次のようになります。

190723_1620__フォーム初期表示.png

「ご担当者さまの住所」は「本店所在地」と同様に、郵便番号から住所を自動入力できます。

これに加え「ご担当者様の住所」では、「本店所在地」に入力がある場合でチェックボックス「本店と同じ」にチェックすると、入力を省く事ができます。

190723_1623__チェックボックス「本店と同じ」.png

「本店と同じ」にチェックを入れた上で、他の入力必須項目に入力し、「確認」ボタンから確認画面に進むと、入力内容は次のように表示されます。

190723_1628__確認画面.png

フォーム項目「ご担当者さまの住所」の仕組み

この辺りのロジックを「ご担当者さまの住所」のCMSテンプレートから見てみると、次のようになっています。

jQuery(function ($) {

    const str__same_as_company  = '(本店所在地と同じ)';
    const $addrRow = $('#<mt:var name="field_basename">-field'),
        $addr = $('[name="<mt:var name="field_basename">"]' ),
...
(中略)
...

    $( 'input[name="<mt:var name="field_basename" />_same_as_company"]').on( 'change', function(){
        if ( $addr.val() === str__same_as_company ) {
            $addr.val( '' );
            setValueToField();
        }
        else{
            $addr.val( str__same_as_company );
        };
    } );

変数 $addr は、「ご担当者さまの住所」の本体となるinput要素です。

入力画面で「確認」ボタンが押されてシステムが入力チェックを行う際、input要素の入力値が空か否かを見るのですが、「本店と同じ」がチェックされると、その際の住所の入力・未入力に関わらず、必ず文字列「(本店所在地と同じ)」がinput要素のvalue属性の値として入ります。

これにより、複数の住所入力欄に同じ住所を入力する際に、入力を省く機能を実現しています。

最後に

今回は、"郵便番号から住所自動入力するタイプの住所入力欄" が複数あるフォームを紹介しました。
法人様からのお問い合わせを受け付けるフォームの構築では、今回のような実装が要件に上がる事が少なくないのではと思われます。
その際にもPowerCMSを用いて対応する手段がある、という事を知っていただければ幸いです。

カテゴリー
PowerCMS 4PowerCMS 5テンプレート作成Tips技術情報

Recent Entries