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

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

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

サーチコンソールからの通知で、今更ながら初めて気が付きました。
EC-CUBE が標準提供している sitemap.php に相応しくない URL が含まれているなんて...。
今回は 2.13.4 と 2.13.5 のカスタマイズになります。

http://net-info-square.com/2015/08/22/post-60/
基本のやり方はググってみるとサクッと見つかりました。
ただしこちらは shopping だけだったので、ちょっと追記してみました。


// /data/class_extends/page_extends/LC_Page_Sitemap_Ex.php

/**
 * ブロック情報を取得する.
 *
 * @param  string $where  WHERE句
 * @param  array  $arrVal WHERE句の値を格納した配列
 * @return array  $arrPageList ブロック情報
 */
public function getPageData($where = '', $arrVal = '')
{
    $objQuery = SC_Query_Ex::getSingletonInstance();     // DB操作オブジェクト
    $sql = '';                      // データ取得SQL生成用
    $arrPageList = array();              // データ取得用

    // SQL生成(url と update_date 以外は不要?)
    $sql .= ' SELECT';
    $sql .= ' page_id';             // ページID
    $sql .= ' ,page_name';          // 名称
    $sql .= ' ,url';                // URL
    $sql .= ' ,filename';           // ファイル名称
    $sql .= ' ,header_chk ';        // ヘッダー使用FLG
    $sql .= ' ,footer_chk ';        // フッター使用FLG
    $sql .= ' ,author';             // authorタグ
    $sql .= ' ,description';        // descriptionタグ
    $sql .= ' ,keyword';            // keywordタグ
    $sql .= ' ,update_url';         // 更新URL
    $sql .= ' ,create_date';        // データ作成日
    $sql .= ' ,update_date';        // データ更新日
    $sql .= ' FROM ';
    $sql .= '     dtb_pagelayout';

    // where句の指定があれば追加
    if ($where != '') {
        // [カスタマイズ] http://net-info-square.com/2015/08/22/post-60/
        // [原案] $sql .= ' WHERE ' . $where;
        $sql .= ' WHERE NOT url LIKE \'%shopping%\' and NOT url LIKE \'%entry%\' and NOT url LIKE \'%regist%\' and NOT url LIKE \'%mypage%\' and NOT url LIKE \'%cart%\' and NOT url LIKE \'%complete\.php%\' and ' . $where;
    }

    $sql .= ' ORDER BY page_id';

    $arrPageList = $objQuery->getAll($sql, $arrVal);

    // URL にプロトコルの記載が無い場合、HTTP_URL を前置する。
    foreach ($arrPageList as $key => $value) {
        $arrPage =& $arrPageList[$key];
        if (!preg_match('|^https?://|i', $arrPage['url'])) {
            $arrPage['url'] = HTTP_URL . $arrPage['url'];
        }
        $arrPage['url'] = preg_replace('|/' . preg_quote(DIR_INDEX_FILE) . '$|', '/' . DIR_INDEX_PATH, $arrPage['url']);
    }
    unset($arrPage);

    return $arrPageList;
}

/data/class/pages/LC_Page_Sitemap.php ファイルに getPageData という関数があるので丸ごとコピーし、
/data/class_extends/page_extends/LC_Page_Sitemap_Ex.php にペースト。

$sql .= ' WHERE ' . $where;
のところに SQL を追記すると良いという具合です。
上記では、
/shopping/ 購入処理
/entry/ 会員登録
/regist/ 会員登録関連
/mypage/ マイページ
/cart/ カート
*/complete.php なんらかの完了ページ
を除外するようにしています。

これでサーチコンソールに怒られないかな?

EC-CUBE2 のままでは、PHP7 の恩恵を受けれないことから…、
ついに EC-CUBE3 を実務で使うことにしました。

プログラムがまったくもって新しくなっているので、
インストールの時点で数多くググることに...(>_<)

時間はかかりましたが、なんとかインストールしつつ、/html/ フォルダを URL から消したり、
テンプレートをカスタマイズできるようになりました。

そこで「レイアウト管理」にてブロックを設置できる場所を増やせないかと
ソースを追って、ちょっと追加してみたところ結構簡単に実装できたので、
メモしておきます!

バージョンは、EC-CUBE 3.0.15 になります。

#contents_top と #header の間に場所を設けたかったので、
#contents_over というのを作ることにしました。

まずは、PHP プログラムからです。

// /src/Eccube/Entity/PageLayout.php

// 配置ID
/** 配置ID: 未使用 */
const TARGET_ID_UNUSED = 0;
const TARGET_ID_HEAD = 1;
const TARGET_ID_HEADER = 2;
const TARGET_ID_CONTENTS_TOP = 3;
const TARGET_ID_SIDE_LEFT = 4;
const TARGET_ID_MAIN_TOP = 5;
const TARGET_ID_MAIN_BOTTOM = 6;
const TARGET_ID_SIDE_RIGHT = 7;
const TARGET_ID_CONTENTS_BOTTOM = 8;
const TARGET_ID_FOOTER = 9;
const TARGET_ID_CONTENTS_OVER = 10; // [カスタマイズ] レイアウト追加

まず、配置IDを追加します。
/src/Eccube/Entity/PageLayout.php の先頭に配置IDの定義があります。
0~9はすでに使用済みなので、10を追加しました。

// /src/Eccube/Entity/PageLayout.php

public function getFooterPosition()
{
    return $this->getBlocksPositionByTargetId(self::TARGET_ID_FOOTER);
}

// [カスタマイズ] レイアウト追加
public function getContentsOverPosition()
{
    return $this->getBlocksPositionByTargetId(self::TARGET_ID_CONTENTS_OVER);
}

同じく /src/Eccube/Entity/PageLayout.php の116行目ぐらいにて
ポジションを取得できる関数を用意する必要があるようですので、
フッターを参考に追加してみます。

// /src/Eccube/Entity/PageLayout.php

public function getFooter()
{
    return $this->getBlocksByTargetId(self::TARGET_ID_FOOTER);
}

// [カスタマイズ] レイアウト追加
public function getContentsOver()
{
    return $this->getBlocksByTargetId(self::TARGET_ID_CONTENTS_OVER);
}

同じく /src/Eccube/Entity/PageLayout.php の190行目ぐらいにて
IDを取得できる関数を用意する必要があるようですので、
こちらもフッターを参考に追加してみます。

PHP 側は以上です。

次は、管理画面のHTMLを追記します。

// /src/Eccube/Resource/template/admin/Content/layout.twig

{# [カスタマイズ] レイアウト追加 #}
<tr>
    <td id="position_{{ constant('Eccube\\Entity\\PageLayout::TARGET_ID_CONTENTS_OVER') }}" class="ui-sortable" colspan="3">
        {% for BlockPosition in TargetPageLayout.ContentsOverPosition %}
            <div id="detail_box__layout_item--{{ BlockPosition.Block.id }}" class="sort{% if loop.first %} first{% endif %}">
                <input type="hidden" class="name" name="name_{{ loop_index }}" value="{{ BlockPosition.Block.name}}" />
                <input type="hidden" class="id" name="id_{{ loop_index }}" value="{{ BlockPosition.Block.id }}" />
                <input type="hidden" class="target-id" name="target_id_{{ loop_index }}" value="{{ BlockPosition.target_id }}" />
                <input type="hidden" class="top" name="top_{{ loop_index }}" value="{{ BlockPosition.block_row }}" />
                {{ BlockPosition.Block.name }}
                <label class="anywherecheck">
                    (<input type="checkbox" class="anywhere" name="anywhere_{{ loop_index }}" value="1" {% if BlockPosition.anywhere == 1 %} checked="checked"{% endif %} />全ページ)
                </label>
            </div>
            {% set loop_index = loop_index + 1 %}
        {% endfor %}
    </td>
</tr>

/src/Eccube/Resource/template/admin/Content/layout.twig が管理画面のレイアウト管理のファイルです。
123行目ぐらいに TARGET_ID_CONTENTS_TOP 用の があるので、その をまるごと複製してから、
・TARGET_ID_CONTENTS_TOP を TARGET_ID_CONTENTS_OVER へ
・ContentsTopPosition を ContentsOverPosition へ
置き換えます。HTML は以上です。

次は、管理画面の JavaScript を追記します。

// /html/template/admin/assets/js/layout_design.js

{# [カスタマイズ] レイアウト追加 #}
$(document).ready(function(){
    var els = [
        '#position_0',
        '#position_1',
        '#position_2',
        '#position_3',
        '#position_4',
        '#position_5',
        '#position_6',
        '#position_7',
        '#position_8',
        '#position_9',
        '#position_10' // [カスタマイズ] レイアウト追加
    ];

/html/template/admin/assets/js/layout_design.js 40行目ぐらいに、
ID を配列で指定している箇所があるので、必要なIDを追記します。JavaScript は以上です。

最後に、管理画面の Css を追記します。

// /html/template/admin/assets/css/dashboard.css

/* [カスタマイズ] レイアウト追加 */
.design-layout td#position_10:before {content: "#contents_over"}

/html/template/admin/assets/css/dashboard.css 2899行目ぐらいに、
疑似要素の content に ID名を記述している箇所があるので追記します。

以上で完了です!

データベースの変更なしでこういった作業ができるようになっているので、
すごく進化しているな~!と感じることができるカスタマイズでした。

ただ管理画面はちょっと見ずらいと思うので、カスタマイズをどんどんやっていきたいですね。

EC-CUBEの3系が浸透してきてからは2系が、
各種レンタルサーバーのクイックインストールから
案内が消えていますが…
まだまだ弊社ではEC-CUBEの2系が現役で、
インストールしたり保守したりしています。

そこで最近の新しいサーバに2系をインストールしようとしたところ、
画面が真っ白に…ということがありました。
弊社ではXserverを利用しておりますので、その前提で…。

これは原因は三つあって(場合によっては二つかも)
・Xserverでは標準でPHPのバージョンが7を採用してる
・接続するMySqlのバージョンが不釣り合い
・EC-CUBEに入っている.htaccessが邪魔をしている
というのが上手くいかない原因です。



・Xserverでは標準でPHPのバージョンが7を採用してる

これは結構簡単なことですが、ヒントが少ないのでハマりポイントのひとつです。
EC-CUBEの2系はPHP5.6までしか対応できていないので、
PHP7だと完全に動きません。
https://www.ec-cube.net/news/detail.php?news_id=290
↑のように、PHP7に対応したバージョンもリリースされているようですが、
アルファーバージョン(テスト段階)ですので、
実際にクライアントワークではあまり使えないと判断しています。
なので、サーバのPHPのバージョンを7から5.6まで下げざるを得ません。
レンタルサーバだったらコントロールパネルからすぐに設定が見つかると思います。



・接続するMySqlのバージョンが不釣り合い
難しいことはわかりませんが、データーベースへ接続する際に、
バージョンによってプログラムが少し違うようです。
https://xoops.ec-cube.net/modules/newbb/viewtopic.php?topic_id=16981&forum=2
↑の投稿のように、本体プログラムにすこし手を加えてあげないといけません。

// /data/class/db/dbfactory/SC_DB_DBFactory_MYSQL.php
// または
// /data/class_extends/db_extends/dbfactory/SC_DB_DBFactory_MYSQL_Ex.php

/**
 * 各 DB に応じた SC_Query での初期化を行う
 *
 * @param  SC_Query $objQuery SC_Query インスタンス
 * @return void
 */
public function initObjQuery(SC_Query &$objQuery)
{
    // [追記] MySqlのバージョンによって処理を分岐
    $val = $objQuery->getOne('select version()');
    if (strcmp($val,'5.7.5')) {
      $objQuery->exec('SET SESSION default_storage_engine = InnoDB');
    } else {
      $objQuery->exec('SET SESSION storage_enging = InnoDB');
    }
    
    $objQuery->exec("SET SESSION sql_mode = 'ANSI'");
}



・EC-CUBEに入っている.htaccessが邪魔をしている
サーバによると思いますが、EC-CUBEのファイルの中に標準で入っている
html/.htaccessが動作を邪魔している可能性があります。
そういうときは思い切ってそのファイルを削除すると上手くいきます。
そもそも念のために...という感じで入っているのでほとんどの場合問題が無いはずです。
またhtmlフォルダ直下以外の.htaccessは削除されない方がよいです。



間が空くと忘れてしまうので、これで忘れないはず…。

メモメモ

if (typeof $.fn.プラグイン名 === 'function') {
  // ~プラグインが読み込まれていた場合の処理~
}

本日、独自ドメインを設定しました!

取得したドメインは、mhkkr.meです。
ブログはサブドメインのblog.mhkkr.meに!

自身のやる気向上とドメイン設定の経験などをやっておかないとダメかな…と思った次第。

ドメインはネットオウル運営のスタードメインにて。
https://www.star-domain.jp

最初はエックスドメインにて取得を考えてましたが、
https://www.xdomain.ne.jp/

エックスドメインの上位レジストラがネットオウルになったとのことで、
https://www.xserver.ne.jp/news_detail.php?view_id=2040

スタードメインにしてみました。
ネットオウルは初めて契約しましたが、管理画面が見やすいので、
利用時間は浅いですが良い印象です!

ライブドアブログとスタードメインの設定(携帯版は使わない想定です)は、
◆[スタードメイン管理]→[管理ドメイン一覧]→[ドメイン管理ツール]→[DNSレコード編集]→[レコード追加]の画面にて、
 ・ホスト名をサブドメインならblogなどを入力
 ・コンテンツはAレコードを入力(125.6.146.14)
  http://help.blogpark.jp/archives/52240385.html
これでスタードメインは完了。
◆[ブログの設定]→[ブログURL設定]の画面にて、
 ・独自ドメインを使用するにチェック
 ・PC/スマートフォン版にドメインを入力
これでブログの設定は完了。

反映されるまでの最短・最長時間はわかりませんが、
このドメイン・ブログの場合は20分くらいで切り替わってました!はやい!

そして特に嬉しかったのが、旧アドレス(標準の)でアクセスすると、
何もしなくても新アドレスにリダイレクトしてくれるところ。
こういうの、本当にありがたいです。


次はブログの見た目を頑張ろう('_')

久々にエディタを変更したい欲がでてしまい、何か無いかと思っていたところ、
過去にEmEditorの永久ライセンスを取得していたことを思い出しました。
なんと約8年も前の2010年に!

永久ライセンスなので、そのまますぐに使えるのかな??と思い、
まずはダウンロードそして登録キーの入力しましたが、
古い登録キーは上手くいきませんでした。

購入方法 https://jp.emeditor.com/#purchase のページに、
「保守プランを延長するには、登録キーを入力してください」とあるので、
そこからアップデートできるのか?と思い試してみましたが、

古いバージョンの登録キーが指定されました。v14、v15、v16、または v17 の登録キーを指定してください。

と、弾かれてしまいます。
文面からしてアップデートのようなものではなさそうですね。。。

すこし難解になってきたので、お問い合わせ先を探していたら、
メニューバーのサポートの中に、「登録キーを再送」というページを発見!
https://support.emeditor.com/send_keys.php?lid=2

実際に、古い登録キー(バージョンはなんでも良いっぽい)を送信してみると、
購入に使用したメールアドレスに新しい登録キーが送付されました!

これで無事最新のEmEditor(v17)が心置きなく使えるように。
永久ライセンス、ありがたや!



今回も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);
  }

  ~元々の処理~
}


検索でヒットしなかったので、稀な現象なのでしょうか?
version 56 で確認しています。

メインブラウザを Firefox にしてますが、
ときどき新しいページを開いたときに応答なし(いわゆるプチフリ?)になるときがありました。

なんでかなーっで、ずっと思考停止してましたが、、、
不意に SNS ボタン(ソーシャルボタン)の読み込みが原因なのでは!?と思い、
広告をブロックするアドオンの uBlock Origin をインストール。

アドオンの管理画面→外部フィルター→Fanboy’s Social Blocking List に
チェックを入れて、右上の適用ボタンを押す。

これで完了です!

まだ長い時間経過を観察してないので、
別の問題かもしれませんが、今のところ結構改善されました。

Adblock Plus でも出来るみたいです。

このページのトップヘ