(佐賀→熊本)マークアップエンジニアのググる生活

地方のWEB制作会社で働く、フロントエンドエンジニア。
HTML、CSS、JavaScript(jQuery)、PHP、Smary、EC-CUBE、WordPress、Movable Type等々で解らない時に、Googleで検索したら解決した話や、「閃いた!」などをほとんど自分用にメモとして残すブログです。

※URLがあったら元記事を参照推奨です!
※ほとんどの事は検証はしていますが、あくまでも参考程度にお願い致します!
※記載が不正確であったことにより生じたいかなる損害に関しても、当ブログは責任を負いかねます。自己責任でよろしくおねがいいたします。

タグ:EC-CUBE

今回も2系の話になります。

EC-CUBEはSmartyというテンプレートエンジンを使用しています。
基本的にはurlなどの見た目通りの場所にtplが存在しているので、
作業の前にどこの階層のどのフォルダにあるな、ということが容易に想像することができます。

しかし、プラグイン等ではその当てがたまに外れることがあります。
特にファイルをいくつか複製する処理が入るタイプのものは、
同じ内容のtplファイルが3つぐらいサーバー上に生成される場合も…。

そのため、
編集したのに反映されない!
→そこのファイルじゃないよ…。
キャッシュファイルの影響かな?
→templates_c内のtpl.phpを削除しても微動だにせず。
という作業を繰り返す羽目になったり。

1つだけならどこを編集したらよいか、
すぐにわかりますが、複数だと非常に混乱します…。

そんなときに、どのファイルを編集するとよいかは、
templates_c内のtpl.phpファイルを見てみると本来のファイルパスが一目でわかります!

templates_c内のtpl.phpファイルをエディタで開くと、
phpのコメントで2行目に「compiled from」と書かれています。
~からコンパイル…とのことで、その横にファイルパスが書かれています。
つまりそのファイルパスのtplを元にしてtpl.phpを作ってますよといことです。

ということでそのファイルパスを辿って、編集するべきtplを簡単に特定することが可能になります!

これでもう迷わない…はず…。

ぐぐって出てくる情報でも基本的にはなるが…
・ログイン時に真っ白の画面にエラーコードだけになったり
・プラグインによっては参照してくれなかったり
などなど、よろしくないことになるので、自分用にまとめです。

2.13.5 で基本的に確認してますが、
2.13.x 系ならたぶんいいはず…。


■管理画面
[システム設定>パラメーター設定]
・TEMPLATE_NAME テンプレート名
・SMARTPHONE_TEMPLATE_NAME スマートフォンテンプレート名

これらの値を同一にする。
デフォルトの場合は"default"になり、
オリジナルの場合は"オリジナルのテンプレートコード"の名前になる。

これだけでもEC-CUBE標準で備わっているページは対応できるが、
追加したページの場合はスマートフォンでメインエリアが読み込まれなくなるので、
下記のコードが必要になる。


■SC_Display_Ex.php

/**
 * 端末種別を判別する。
 *
 * SC_Display::MOBILE = ガラケー = 1
 * SC_Display::SMARTPHONE = スマホ = 2
 * SC_Display::PC = PC = 10
 *
 * @static
 * @param          $reset boolean
 * @return integer 端末種別ID
 */
public static function detectDevice($reset = FALSE)
{
    return DEVICE_TYPE_PC;
}

extendsではない方では、ifで分岐してあるが、
必要ないのでストレートに PC のみを返却するように変更する。


これで大丈夫なはず・・・。

検索してもテキストかテキストエリアの拡張のみしか見当たらないあたり、
あまり需要がないのでしょうか…?

PDFを商品ごとにアップする必要がある案件を担当するにあたり、
右往左往しましたが、
ほとんど画像のアップロードを増やす要領で実現できたので、
メモします!

まずは、文字列(PDFファイル名)を保存する為の枠を作ります。
商品ページへの基本的な項目の追加は下記の記事がとてもわかり易いです。
[EC-CUBE] 2.13 商品詳細ページの項目追加(MySQL)
http://digipoke.com/archives/1668


・625行目辺り「商品詳細の SQL を取得する」に追加
までは上記の手順でちゃちゃっと済ませておきます。

追加の項目名が不明だと分かりづらいので、
以降は manual と表現します。

次に、商品登録ページに入力欄を設置しますが、
ここはテキストではなく、input[type="file"] 形式にします。
一覧-メイン画像の tr を複製し利用します。

<tr>
    <!--{assign var=key value="main_list_image"}-->
    <th>一覧-メイン画像<br />[<!--{$smarty.const.SMALL_IMAGE_WIDTH}-->×<!--{$smarty.const.SMALL_IMAGE_HEIGHT}-->]</th>
    <td>
        <a name="<!--{$key}-->"></a>
        <a name="main_image"></a> // ←ここは削除
        <a name="main_large_image"></a> // ←ここは削除
        <span class="attention"><!--{$arrErr[$key]}--></span>
        <!--{if $arrForm.arrFile[$key].filepath != ""}-->
        <img src="<!--{$arrForm.arrFile[$key].filepath}-->" alt="<!--{$arrForm.name|h}-->" /> <a href="" onclick="selectAll('category_id'); eccube.setModeAndSubmit('delete_image', 'image_key', '<!--{$key}-->'); return false;">[画像の取り消し]</a><br />
        <!--{/if}-->
        <input type="file" name="main_list_image" size="40" style="<!--{$arrErr[$key]|sfGetErrorColor}-->" />
        <a class="btn-normal" href="javascript:;" name="btn" onclick="selectAll('category_id'); eccube.setModeAndSubmit('upload_image', 'image_key', '<!--{$key}-->'); return false;">アップロード</a>
    </td>
</tr>

上のようなテンプレートの記述です。
・// ←ここは削除 は必要ないので削除。
・th の名前を変更
・名前下の画像の縦x横は .pdf のみなどアップロードできる拡張子を明記しておくと良いともいます。
・main_list_image を manual(先程作った枠の項目名)に置き換え(2箇所)

入力欄はこれで完成です。


次に、商品ページ(LC_Page_Admin_Products_Product.php)の機能を拡張します。
・lfInitFormParam()
・lfInitFile()
の関数にそれぞれ付け加えます。


lfInitFormParam() は、上の記事でも作業を行ったところですね。

// こういうのを追加していた場合
$objFormParam->addParam('取扱説明書', 'manual', STEXT_LEN, 'KVa', array('SPTAB_CHECK', 'MAX_LENGTH_CHECK'));

// 下記2行を追加(データの受け渡し用で使用)
$objFormParam->addParam('temp_manual', 'temp_manual', '', '', array());
$objFormParam->addParam('save_manual', 'save_manual', '', '', array());


lfInitFile() はアップロードファイルパラメーター情報の初期化する関数です。
アップロードファイルをプログラムに送りつける前に事前に枠を置いておくような感覚でしょうか。

$objUpFile->addFile('取扱説明書', 'manual', array('pdf'), IMAGE_SIZE, false, 0, 0, false);


これで、完了です。


実際に、PDFを選択し、アップロードすると、
/upload/temp_image/ フォルダに保存されます。
そのまま商品ページを保存すると、
/upload/save_image/ フォルダに保存されます。

また、データベース上でも確認してみると、
manual(先程作った枠の項目名)のところにPDFファイル名が入力されているはずです。


アップロードの機能は画像の仕組みをそのまま利用できるので、
非常に少ない追記で行うことができました。
途中でサムネイル生成の判定が入りますが、
main_image 系のみ反応するようなので、特に作業の必要がなく回避できるのはありがたかったです。


IMAGE_SIZE はシステム設定>パラメーター設定にあります。
初期値が 1MB だと思います。普通に足りないと思うので、
10MB ぐらいにしておくと、無難かと思われます。
また PDF だけ 10MB にしたい場合は、
引数の IMAGE_SIZE を 10000 にするとOKです。


確認や実際の表示は Aタグで開くようにしてあげると良いでしょう。





2017/11/24 追記
特に不具合は無かったのですが、
商品を複製する際に、「イメージの形式が不明です。」とエラーが出て処理が止まるようになってしまいました。
アップロード時のサムネイル生成は key 名で判定が入るので、カスタマイズ不要でしたが、
複製の際は別途機能を使うみたいなので、次のように追記する必要がありました。

/data/module/gdthumb.php

/*
* サムネイル画像の作成
* string $path
* integer $width
* integer $height
*/
function Main($path, $width, $height, $dst_file, $header = false) {
  // 追記
  if(strpos($path, '.pdf') !== false || strpos($path, '.PDF') !== false) {
    return array(1, false);
  }

  ~元々の処理~
}


元ネタ ( 2.11 ) :
http://xoops.ec-cube.net/modules/newbb/viewtopic.php?viewmode=flat&order=ASC&topic_id=4787&forum=7

上記をそのままだと 2.13 にて動かなかったので getMode() 関数を追ってみると、
mode を $_REQUEST['mode'] で判断していたので、それを追記したら下記ようになりました。


//**
 * Page を初期化する.
 *
 * @return void
 */
function init()
{
    parent::init();

    /**
     * [カスタマイズ] ページ表示時、「この条件で検索する」ボタンと同じ動作をする
     */
    if ( !isset( $_REQUEST['mode'] ) ) : $_REQUEST['mode'] = "search"; endif;
    if ( !isset( $_POST['mode'] ) ) : $_POST['mode'] = "search"; endif;
    if ( !isset( $_POST['search_page_max'] ) ) : $_POST['search_page_max'] = SEARCH_PMAX; endif;
}


この PHP を追記するファイル
  • LC_Page_Admin_Customer_Ex.php
  • LC_Page_Admin_Mail_Ex.php
  • LC_Page_Admin_Order_Ex.php
  • LC_Page_Admin_Products_Ex.php
  • LC_Page_Admin_Products_Review_Ex.php

確認バージョン 2.12.2

dtb_category の rank が並び順に超影響しているようで、
parent_category_id とか level とか関係なしに
rank の降順で並ぶようです。

管理画面からはちょっと厳しいので、
データベースから rank を整理してあげると正常になりました。

バージョン 2.13.5
にて確認しました。TOP_URL などの定数が使えるバージョンならば他でも大丈夫だと思います。

<meta property="og:url" content="http://www.yahoo.co.jp/">
<link rel="canonical" href="http://www.yahoo.co.jp/">

これを自動で出したい。




LC_Page_Ex.php

修正 [2016/02/25] - strpos で調べる値が空だったら PHP の Warning が出ないようにいたしましたm(_ _)m

/**
 * URLを返却
 *
 * @param string 分岐したい mode を入力 ( 'canonical' | 'ogp' )
 * @return string パラメーターが整理された URL を返却
 */
public function customPublishUrl($mode)
{
    $protocol    = ( empty($_SERVER['HTTPS']) ? 'http://' : 'https://');
    $http_host   = $_SERVER['HTTP_HOST'];
    $request_uri = $_SERVER['REQUEST_URI'];
    $script_name = $_SERVER['SCRIPT_NAME'];
    $top_url     = TOP_URL;
    $dynamic_url = $protocol . $http_host . $request_uri;
    $static_url  = $protocol . $http_host . $script_name;

    // 出力用
    $publish_url = $static_url;
    $param_all   = '';

    // URL から 不要なファイル名を調査
    if (
           strpos( $script_name, 'index.php' ) !== false
        || strpos( $script_name, 'confirm.php' ) !== false
        || strpos( $script_name, 'complete.php' ) !== false
       ) :

        // トップページならば
        if ( $static_url === $top_url || $static_url === $top_url . 'index.php' ) :
            $publish_replace = '';
            $publish_search  = '/\/index.php/u';

        // それ以外のページ
        else :
            $publish_replace = '';
            $publish_search  = '/index.php|confirm.php|complete.php/u';
        endif;

        // 不要なファイル名を削除する
        $publish_url = preg_replace($publish_search, $publish_replace, $publish_url);

    endif;

    // パラメーター算出用の URL を綺麗にする
    $clean_url     = $dynamic_url;
    $clean_search  = '/\"(.*)\"|\'(.*)\'|%22(.*)%22/u';
    $clean_replace = '';
    $clean_url     = preg_replace($clean_search, $clean_replace, $clean_url);

    // パラメーターを配列化
    $hatena = explode('?', $clean_url);
    $and = explode('&', $hatena[1]);

    // チェックするパラメーター
    switch ( $mode ) :

        // $check_pattern
        // 'only'   -&gt; URL に含まれるパラメーターを配列だけにする
        // 'remove' -&gt; 配列のパラメーターは不要なので URL から必ず除外する

        case 'canonical' :
            $check_pattern = 'only';
            $check_array   = array('category_id', 'maker_id', 'name', 'product_id');
            break;

        case 'ogp' :
            $check_pattern = 'remove';
            $check_array   = array('transactionid', 'admin');
            break;

    endswitch;

    // $hatena が空で無ければ
    if ( $hatena[1] !== '' ) :

        // パラメーター配列の数だけループ
        foreach ( $and as $each ) :

            $each_split = explode('=', $each);
            $param = $each_split[0];
            $value = $each_split[1];
            $flag = ( $check_pattern === 'only' ) ? false : true;

            // $check_array の数だけループ
            foreach ( $check_array as $each2 ) :

                // strpos で調べる値が空だったら PHP の Warning が出るので事前に $param_all をチェック
                if ( $param_all !== '' ) :

                    // 値の重複を調査( $param_all が $param を含んでいたら フラグを無くす )
                    if ( strpos( $param_all, $param ) !== false ) :
                        $flag = false;
                    endif;

                endif;

            endforeach;

            // 値の重複を調査( $param_all が $param を含んでいたら フラグを無くす )
            if ( strpos( $param_all, $param ) !== false ) :
                $flag = false;
            endif;

            // $flag が true なら $param_all に $each を追加
            if ( $flag ) :
                $param_all .= ( $param_all === '' ) ? '?' . $each : '&' . $each;
            endif;

        endforeach;

    endif;

    // もし $param_all の値が ? だけだったら $param_all を空にする
    $param_all = ( $param_all === '?' ) ? '' : $param_all;

    // ただし $mode が canonical かつ パラメーターに pageno を含んでいたら $publish_url と $param_all を空にする
    if ( $mode === 'canonical' && strpos( $clean_url, 'pageno=' ) !== false ) :
        $publish_url = '';
        $param_all = '';
    endif;

    return $publish_url . $param_all;
}




呼び出し
LC_Page_Ex.php

修正 [2016/02/24] - init() から process() に変更しましたm(_ _)m

/**
 * Page のプロセス.
 *
 * @return void
 */
public function process()
{
    parent::process();

    /**
     * 下記はシステムエラー時無視されるので、
     * ・LC_Page_Error.php
     * ・LC_Page_Error_DispError_Ex.php
     * ・LC_Page_Error_SystemError_Ex.php
     * にて再取得する
     */
    
    // og:url
    $this->custom_ogurl = $this->customPublishUrl('ogp');

    // カノニカルURL
    $this->custom_canonical = $this->customPublishUrl('canonical');
}




LC_Page_Ex.php
だけだとエラー時に空になっちゃうので、
・LC_Page_Error.php
・LC_Page_Error_DispError_Ex.php
・LC_Page_Error_SystemError_Ex.php
でも呼び出す

/**
 * Page のアクション.
 *
 * @return void
 */
function action()
{
    parent::action();

    /**
     * LC_Page_Ex.php にて定義
     */

    // og:url
    $this->custom_ogurl = LC_PAGE_Ex::customPublishUrl('ogp');

    // カノニカルURL
    $this->custom_canonical = LC_PAGE_Ex::customPublishUrl('canonical');
}




出力
site_frame.tpl

<!--{if $custom_canonical|strlen <= 1}--><meta name="robots" content="noindex, follow"><!--{/if}-->
<meta property="og:url" content="<!--{$custom_ogurl|h}-->">
<!--{if $custom_canonical|strlen >= 1}--><link rel="canonical" href="<!--{$custom_canonical|h}-->"><!--{/if}--><!--{strip}-->



最近の SEO 系のブログ記事の傾向を見て、
canonical を出さないようにしているページがあります(ページネーションが発生するページ)。
そこでは、代わりに noindex follow を入れるようにしています。



※ここまで書きましたが、
独自ロジックを EC-CUBE 全体に反映させるにはどこで呼び出したらいいんだろう。
反映箇所が多いと漏れがあると思うし、決済モジュールなどでは、モジュールごとに追記が必要になるので...。
ググっても個々のページ用の情報ばかりなので、よくわかりませんでした。勉強します。

バージョン 2.12.2

その表示不具合、
プラグインが原因かもしれません...。

私が追加修正したサイトでは、
販売期間設定というプラグインを無効にすると正常に表示されました

タイトル通りを EC-CUBEバージョン 2.13.2 にて実装。

取り急ぎ先人様のやり方を調べてみたところ、
いろいろ触らないといけないファイルが多かった&上手くいかなかったので、
jQueryを使って実装してみました。


□まずは普通に list.tpl と detail.tpl に税別を表示させます。

list.tpl 変更前
<!--★価格★-->
<div class="pricebox sale_price">
    <!--{$smarty.const.SALE_PRICE_TITLE}-->(税込):
    <span class="price">
        <span id="price02_default_<!--{$id}-->"><!--{strip}-->
            <!--{if $arrProduct.price02_min_inctax == $arrProduct.price02_max_inctax}-->
                <!--{$arrProduct.price02_min_inctax|number_format}-->
            <!--{else}-->
                <!--{$arrProduct.price02_min_inctax|number_format}-->~<!--{$arrProduct.price02_max_inctax|number_format}-->
            <!--{/if}-->
        </span><span id="price02_dynamic_<!--{$id}-->"></span><!--{/strip}-->
        円</span>
</div>
list.tpl 変更後
<!--★価格★-->
<div class="pricebox sale_price sale_price_inctax">
    <!--{$smarty.const.SALE_PRICE_TITLE}-->(税込):
    <span class="price">
        <span id="price02_default_<!--{$id}-->"><!--{strip}-->
            <!--{if $arrProduct.price02_min_inctax == $arrProduct.price02_max_inctax}-->
                <!--{$arrProduct.price02_min_inctax|number_format}-->
            <!--{else}-->
                <!--{$arrProduct.price02_min_inctax|number_format}-->~<!--{$arrProduct.price02_max_inctax|number_format}-->
            <!--{/if}-->
        </span><span id="price02_dynamic_<!--{$id}-->"></span><!--{/strip}-->
        円</span>
</div>
<div class="pricebox sale_price sale_price_noinctax">
    <!--{$smarty.const.SALE_PRICE_TITLE}-->(税抜):
    <span class="price">
        <span id="price02no_default_<!--{$id}-->"><!--{strip}-->
            <!--{if $arrProduct.price02_min == $arrProduct.price02_max}-->
                <!--{$arrProduct.price02_min|number_format}-->
            <!--{else}-->
                <!--{$arrProduct.price02_min|number_format}-->~<!--{$arrProduct.price02_max|number_format}-->
            <!--{/if}-->
        </span><span id="price02no_dynamic_<!--{$id}-->"></span><!--{/strip}-->
        円</span>
</div>
detail.tpl 変更前
<!--★販売価格★-->
<dl class="sale_price">
    <dt><!--{$smarty.const.SALE_PRICE_TITLE}-->(税込):</dt>
    <dd class="price">
        <span id="price02_default"><!--{strip}-->
            <!--{if $arrProduct.price02_min_inctax == $arrProduct.price02_max_inctax}-->
                <!--{$arrProduct.price02_min_inctax|number_format}-->
            <!--{else}-->
                <!--{$arrProduct.price02_min_inctax|number_format}-->~<!--{$arrProduct.price02_max_inctax|number_format}-->
            <!--{/if}-->
        <!--{/strip}--></span><span id="price02_dynamic"></span>
        円
    </dd>
</dl>
detail.tpl 変更後
<!--★販売価格★-->
<dl class="sale_price sale_price_inctax">
    <dt><!--{$smarty.const.SALE_PRICE_TITLE}-->(税込):</dt>
    <dd class="price">
        <span id="price02_default"><!--{strip}-->
            <!--{if $arrProduct.price02_min_inctax == $arrProduct.price02_max_inctax}-->
                <!--{$arrProduct.price02_min_inctax|number_format}-->
            <!--{else}-->
                <!--{$arrProduct.price02_min_inctax|number_format}-->~<!--{$arrProduct.price02_max_inctax|number_format}-->
            <!--{/if}-->
        <!--{/strip}--></span><span id="price02_dynamic"></span>
        円
    </dd>
</dl>
<dl class="sale_price sale_price_noinctax">
    <dt><!--{$smarty.const.SALE_PRICE_TITLE}-->(税抜):</dt>
    <dd class="price">
        <span id="price02no_default"><!--{strip}-->
            <!--{if $arrProduct.price02_min == $arrProduct.price02_max}-->
                <!--{$arrProduct.price02_min|number_format}-->
            <!--{else}-->
                <!--{$arrProduct.price02_min|number_format}-->~<!--{$arrProduct.price02_max|number_format}-->
            <!--{/if}-->
        <!--{/strip}--></span><span id="price02no_dynamic"></span>
        円
    </dd>
</dl>


□そして税抜き計算jQuery
jQuery(function($) {
$('div#undercolumn form[name^="product_form"], body.LC_Page_Products_Detail #form1').change(function() {
    var inctax = $(this).find('span[id^="price02_dynamic"]');
    var noinctax_f = $(this).find('span[id^="price02no_default"]');
    var noinctax_y = $(this).find('span[id^="price02no_dynamic"]');

    if ( inctax.is(":visible") ) {
        //console.log('計算したよー');
        var inctax_text = inctax.text();
        var inctax_value = inctax_text.replace(',','');
        var noinctax_value = Math.floor(inctax_value / 1.08);
        var noinctax_value = String( noinctax_value ).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,'); // http://so-zou.jp/web-app/tech/programming/javascript/grammar/data-type/string/comma-formatting.htm
        noinctax_f.hide();
        noinctax_y.show().text(noinctax_value);
    } else {
        //console.log('計算やめたー');
        noinctax_f.show();
        noinctax_y.hide();
    }
});
});
Math.floor(inctax_value / 1.08);
らへんが税率計算!消費税10%になったら1.10に。
とりあえず切り捨て(Math.floor)にしてるので変更したいのであればお好きにどうぞ!

3桁ずつ小数点は so-zou.jp 様より!

稚拙ではありますが、ieTesterでも動いたので大丈夫なはずです。

終わり!


★新規ページ 500エラー

一年ぶりにEC-CUBE触ったらいろいろ忘れまくっていたのでメモ。

新規ページを作って500エラー出たら、パーミッションを疑うことだ!

●環境
エックスサーバー
EC-CUBEバージョン 2.13.1
PHPバージョン PHP 5.3.3
DBバージョン MySQL 5.5.36

●作業
html/data/class/helper/SC_Helper_FileManager.php

    public function sfWriteFile($filename, $value)
    {
        if (!is_dir(dirname($filename))) {
            SC_Utils_Ex::recursiveMkdir(dirname($filename), 0777);
        }
        $fp = fopen($filename,'w');
        if ($fp === false) {
            return false;
        }
        if (fwrite($fp, $value) === false) {
            return false;
        }
        chmod($filename, 0755); //  ←を追記

        return fclose($fp);
    }


2.4系の情報ばっかりで埋もれてて見つかるまで時間かかった・・・。



■参考にさせて頂きました!
http://xoops.ec-cube.net/modules/newbb/viewtopic.php?topic_id=8251&forum=11







★user_dataを排除

.htaccessで書き換えの紹介が多かったので、
逆にこちらの方法を試してみました。

■そのまま紹介!

user_data フォルダの中身を
一個下の階層に移動して、
管理画面のパラメータの "user_data" を "" にする。

http://xoops.ec-cube.net/modules/newbb/viewtopic.php?topic_id=8475&forum=8





このページのトップヘ