PrestaShop 1.7.x テーマ(テンプレート)のカスタマイズ(Webpack)

イーコマースCMSであるPrestaShopをストアサイトに採用しました。従来のMagentoによるストアサイトは、構成ファイルの階層の深さによるカスタマイズの煩雑さや、バージョンアップ毎の不具合頻度の多さから採用を取りやめました。

PrestaShopのテーマのカスタマイズにはWebpackによる開発環境が必要です。Webpackについては以下のサイトを参照して下さい。インストール先サーバはUbuntu16.04です。

Node.jsのインストール

以下サイトを参照しNode.jsの最新バージョンをインストールします。

$ curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -

$ sudo apt-get install -y nodejs

Node.jsのパッケージマネージャnpmも同時にインストールされます。

Webpackのインストール

Node.jsのWebpackパッケージをグローバル環境にインストールします。

$ sudo npm i -g webpack

PrestaShop Classicテーマのダウンロード

以下Githubのサイトからdevelopのブランチをダウンロードします。ここから /themes/classic/_dev を展開してサーバ側の該当フォルダと入れ換えます。

Node関連パッケージのインストール

_dev内のpackage.jsonに記述されているパッケージをインストールします。

$ cd /your_store_site/themes/classic/_dev

$ npm install

Webpackによるコンパイル

以下コマンドで_dev内の関連ファイルをコンパイル、assetsフォルダにサイトを構成するcss、jsファイルを出力します。Webpack設定ファイルは_dev内のwebpack.config.jsです。

$ npm run build

または

$ webpack

cssの変更は_dev内のscssファイルを編集、コンパイルすることでサイトに反映されます。Node.jsによるAPIは下記サイトを参照願います。

npmは/usr/bin/npmにインストールされるため、

$ which npm
/usr/bin/npm

以下リンクを作成します。

$ sudo ln -s /usr/bin/npm /usr/local/bin/npm

How to Prevent Permissions Errors

https://docs.npmjs.com/getting-started/fixing-npm-permissions

ホームホルダー内で

$ mkdir ~/.npm-global
$ npm config set prefix ‘~/.npm-global’
$ export PATH=~/.npm-global/bin:$PATH
$ source ~/.profile

npxパッケージを利用する代替案もあります。(npmバージョン5.2以上)

まとめ

パッケージの依存関係が複雑で問題が必ず発生します。Nodejsは安定版を利用すること。

Nodeの最新安定版をインストール

インストールは上記本家から直接ダウンロードして該当フォルダにコピーするか、記事の手順

に従います。

webpackのインストール

上記の説明ではグローバル環境にインストールすることになっていましたが、本家サイトでは利用するローカルフォルダにインストールすることを推奨しています。

webpackの実行は、

$ sudo node_modules/webpack/bin/webpack.js

テーマの変更・追加

数箇所の変更であれば、直接テーマのcssファイルを変更して下さい。
変更後管理画面の 拡張パラメータ ---> パフォーマンス からキャッシュをクリアします。

/themes/classic/assets/css/theme.css

.carousel .carousel-item .caption .display-1 {
    font-size: 3rem;
    font-weight: 700;
    color: lightgreen;
}

商品説明タグの移動

themes/classic/templates/catalog/product.tpl

下記クラスの右半分の領域で表示されていた商品説明タグを独立させて横幅全体に表示。
<div class="row product-container js-product-container">

追加ブロック <div class="col-md-12"> .... </div> 内の箇所

    <div class="row product-container js-product-container">
      <div class="col-md-6">
        {block name='page_content_container'}
          <section class="page-content" id="content">
            {block name='page_content'}
              {include file='catalog/_partials/product-flags.tpl'}

              {block name='product_cover_thumbnails'}
                {include file='catalog/_partials/product-cover-thumbnails.tpl'}
              {/block}
              <div class="scroll-box-arrows">
                <i class="material-icons left">&#xE314;</i>
                <i class="material-icons right">&#xE315;</i>
              </div>

            {/block}
          </section>
        {/block}
      </div>
      <div class="col-md-6">
          {block name='page_header_container'}
            {block name='page_header'}
              <h1 class="h1">{block name='page_title'}{$product.name}{/block}</h1>
            {/block}
          {/block}
          {block name='product_prices'}
            {include file='catalog/_partials/product-prices.tpl'}
          {/block}

          <div class="product-information">
            {block name='product_description_short'}
              <div id="product-description-short-{$product.id}" class="product-description">{$product.description_short nofilter}</div>
            {/block}

            {if $product.is_customizable && count($product.customizations.fields)}
              {block name='product_customization'}
                {include file="catalog/_partials/product-customization.tpl" customizations=$product.customizations}
              {/block}
            {/if}

            <div class="product-actions js-product-actions">
              {block name='product_buy'}
                <form action="{$urls.pages.cart}" method="post" id="add-to-cart-or-refresh">
                  <input type="hidden" name="token" value="{$static_token}">
                  <input type="hidden" name="id_product" value="{$product.id}" id="product_page_product_id">
                  <input type="hidden" name="id_customization" value="{$product.id_customization}" id="product_customization_id" class="js-product-customization-id">

                  {block name='product_variants'}
                    {include file='catalog/_partials/product-variants.tpl'}
                  {/block}

                  {block name='product_pack'}
                    {if $packItems}
                      <section class="product-pack">
                        <p class="h4">{l s='This pack contains' d='Shop.Theme.Catalog'}</p>
                        {foreach from=$packItems item="product_pack"}
                          {block name='product_miniature'}
                            {include file='catalog/_partials/miniatures/pack-product.tpl' product=$product_pack showPackProductsPrice=$product.show_price}
                          {/block}
                        {/foreach}
                    </section>
                    {/if}
                  {/block}

                  {block name='product_discounts'}
                    {include file='catalog/_partials/product-discounts.tpl'}
                  {/block}

                  {block name='product_add_to_cart'}
                    {include file='catalog/_partials/product-add-to-cart.tpl'}
                  {/block}

                  {block name='product_additional_info'}
                    {include file='catalog/_partials/product-additional-info.tpl'}
                  {/block}

                  {* Input to refresh product HTML removed, block kept for compatibility with themes *}
                  {block name='product_refresh'}{/block}
                </form>
              {/block}

            </div>
            {block name='hook_display_reassurance'}
              {hook h='displayReassurance'}
            {/block}
        </div>
      </div>
      <div class="col-md-12">
            {block name='product_tabs'}
              <div class="tabs">
                <ul class="nav nav-tabs" role="tablist">
                  {if $product.description}
                    <li class="nav-item">
                       <a
                         class="nav-link{if $product.description} active js-product-nav-active{/if}"
                         data-toggle="tab"
                         href="#description"
                         role="tab"
                         aria-controls="description"
                         {if $product.description} aria-selected="true"{/if}>{l s='Description' d='Shop.Theme.Catalog'}</a>
                    </li>
                  {/if}
                  <li class="nav-item">
                    <a
                      class="nav-link{if !$product.description} active js-product-nav-active{/if}"
                      data-toggle="tab"
                      href="#product-details"
                      role="tab"
                      aria-controls="product-details"
                      {if !$product.description} aria-selected="true"{/if}>{l s='Product Details' d='Shop.Theme.Catalog'}</a>
                  </li>
                  {if $product.attachments}
                    <li class="nav-item">
                      <a
                        class="nav-link"
                        data-toggle="tab"
                        href="#attachments"
                        role="tab"
                        aria-controls="attachments">{l s='Attachments' d='Shop.Theme.Catalog'}</a>
                    </li>
                  {/if}
                  {foreach from=$product.extraContent item=extra key=extraKey}
                    <li class="nav-item">
                      <a
                        class="nav-link"
                        data-toggle="tab"
                        href="#extra-{$extraKey}"
                        role="tab"
                        aria-controls="extra-{$extraKey}">{$extra.title}</a>
                    </li>
                  {/foreach}
                </ul>

                <div class="tab-content" id="tab-content">
                 <div class="tab-pane fade in{if $product.description} active js-product-tab-active{/if}" id="description" role="tabpanel">
                   {block name='product_description'}
                     <div class="product-description">{$product.description nofilter}</div>
                   {/block}
                 </div>

                 {block name='product_details'}
                   {include file='catalog/_partials/product-details.tpl'}
                 {/block}

                 {block name='product_attachments'}
                   {if $product.attachments}
                    <div class="tab-pane fade in" id="attachments" role="tabpanel">
                       <section class="product-attachments">
                         <p class="h5 text-uppercase">{l s='Download' d='Shop.Theme.Actions'}</p>
                         {foreach from=$product.attachments item=attachment}
                           <div class="attachment">
                             <h4><a href="{url entity='attachment' params=['id_attachment' => $attachment.id_attachment]}">{$attachment.name}</a></h4>
                             <p>{$attachment.description}</p>
                             <a href="{url entity='attachment' params=['id_attachment' => $attachment.id_attachment]}">
                               {l s='Download' d='Shop.Theme.Actions'} ({$attachment.file_size_formatted})
                             </a>
                           </div>
                         {/foreach}
                       </section>
                     </div>
                   {/if}
                 {/block}

                 {foreach from=$product.extraContent item=extra key=extraKey}
                 <div class="tab-pane fade in {$extra.attr.class}" id="extra-{$extraKey}" role="tabpanel" {foreach $extra.attr as $key => $val} {$key}="{$val}"{/foreach}>
                   {$extra.content nofilter}
                 </div>
                 {/foreach}
                </div>
              </div>
            {/block}
        </div>
    </div>

フッターリンクの変更

themes/classic/templates/_partials/footer.tpl

<div class="footer-container">
  <div class="container">
    <div class="row">
      {block name='hook_footer'}
        {hook h='displayFooter'}
      {/block}
    </div>
    <div class="row">
      {block name='hook_footer_after'}
        {hook h='displayFooterAfter'}
      {/block}
    </div>
    <div class="row">
      <div class="col-md-12">
        <p class="text-sm-center">
          {block name='copyright_link'}
            <a href="https://ficus.myvnc.com" target="_blank" rel="noopener noreferrer nofollow">
              {l s='%copyright% %year% - FICUSONLINE F9E STORE by %ficusonline%' sprintf=['%ficusonline%' => 'FICUSONLINE™', '%year%' => 'Y'|date, '%copyright%' => '©'] d='Shop.Theme.Global'}
            </a>
          {/block}
        </p>
      </div>
    </div>
  </div>
</div>