[ECCUBE4] プラグインに同梱したリソースファイルのコピー

今日もECCUBE4の勉強を進めています

dataTablesを使うプラグインを試作中

プラグインを作った際、JSやCSSのリソースファイルを同梱したいのはよくある話かと思います
プラグインに同梱したリソースファイルを実際に使うには本体側へのコピーが必要になるのですが、ECCUBE3ではそれはプラグインの製作者がプラグインマネージャーにて自前でやっていました

EC-CUBE 3.0 リソースファイル、ブロック

これがECCUBE4ではプラグインインストール時に自動で行われるそうです

リソースコピーの使い方

[プラグインディレクトリ]/Resource/assets

にあるファイルが

[ECCUBEインストールディレクトリ]/html/plugin/[プラグインcode]/assets

にコピーされます

twig内でリソースを使いたい際は以下のようになります

{{ asset('[プラグインcode]/assets/[リソースのパス]', 'plugin') }}

// 例えばHogeHogeプラグインでcss/datatables.min.cssを読み込む場合は
{% block stylesheet %}
<link rel="stylesheet" href="{{ asset('HogeHoge/assets/css/datatables.min.css', 'plugin') }}"/> 
{% endblock %}

参考ページ
4系プラグインのPluginManagerの書き方

[iPhone] iOS13パブリックベータを入れてみた

iOS13のパブリックベータが公開されましたね!

最悪データを失ってもいいiPhone 6Sがあるのでそちらに入れて遊んでみることにしました

導入は特に難しいことはありませんでした
iPhone 6SでApple Beta Software ProgramにApple IDでログインした後、プロファイルをダウンロード
落としてきたプロファイルをインストールした後、ソフトウェア・アップデートをかけるとiOS13が降ってきます

立ち上げてまず驚いたのが、マクドナルドアプリがbluetooth接続の許可を求めてきたこと
iOS 13のセキュリティ機能で「Wi-FiとBluetooth使用時の位置情報に関するプライバシーの強化」というものがあるので、それがこれなんだと思います
マクドナルドアプリの機能といえばクーポン配布とメニュー・店舗検索なのですが、店舗の中にいるかどうか正確な位置情報取得のために使っているのかな・・・?
でもそこまで正確な位置要らなくない?と思い使用を拒否しておきました
この端末に入れていたアプリでは他に「weathernews」と「Google」もbluetooth許可を求めてきました

あとiOS13の機能で気になっていたのは3本指コピペ
うん、たしかに便利
きっとiPadでは多用するようになると思います
でもiPhoneだと机にでも置かないと3本指ジェスチャーは難しいかな・・・

iPadOSのSidecarもすごく気になる – うちのiMac(Retina 5K, 27-inch, Late 2014)で使えるのか否か – んだけど、iMacが死ぬと仕事にならないから、これは正式リリースまで待つしかないか

[ECCUBE] ECCUBE4のプラグインを学ぶために商品レビュープラグインを改造して返信機能をつけてみる

ECCUBE4学習の3日目です
プラグインサンプルとして提供されている商品レビュープラグインを改造してみようと思います

githubからzipを落としてきて app/Plugin の下に置きます
2019年6月24日の段階では開発ドキュメントにProductReviewの名前で展開するように書かれていますが、実際はProductReview4とします
(そうしなければ次のインストールに失敗します)

$ cd ~/apps/ec-cube/app/Plugin
$ wget https://github.com/EC-CUBE/ProductReview-plugin/archive/4.0.zip
$ unzip 4.0.zip
$ mv ProductReview-plugin-4.0 ProductReview4
$ rm 4.0.zip

落としてきたProductReview4プラグインのインストール&有効化

$ cd ~/apps/ec-cube/
$ bin/console eccube:plugin:install --code=ProductReview4
$ bin/console eccube:plugin:enable --code=ProductReview4
ProductReview4プラグインのインストール完了

基本設定(プラグイン名やバージョン)は composer.json に書く、と。ここで追加のcomposerも書けるのかー
ふむふむふむ…

見てるだけでは頭に入らないので、改造することでもう少し理解を進めてみます
レビューにショップオーナーからの返信機能を付け加えてみます

データベースの定義は・・・
それらしいのは Entity/ProductReview.php しか見つからない。ここに見よう見まねで付け加えてみましょうか
apps/ec-cube/app/Plugin/ProductReview4/Entity/ProductReview.php の末尾に以下のように追加

/**
 * @var string
 *
 * @ORM\Column(name="reply", type="text", nullable=true)
 */
private $reply;

/**
 * Get reply.
 *
 * @return string
 */
public function getReply()
{
    return $this->reply;
}

/**
 * Set reply.
 *
 * @param string $reply
 *
 * @return ProductReview
 */
public function setReply($reply)
{
    $this->reply = $reply;

    return $this;
}

DBに反映されるのはインストール時だと思うので、一度無効化>アンインストールしてから、再度インストール>有効化してみます

$ bin/console eccube:plugin:disable --code=ProductReview4
$ bin/console eccube:plugin:uninstall --code=ProductReview4
$ bin/console eccube:plugin:install --code=ProductReview4
$ bin/console eccube:plugin:enable --code=ProductReview4

mysqlにログインしてテーブル構造確認( show columns from plg_product_review )すると・・・

おお、ちゃんとカラムができてる!
このあともう少し調べてみたら、なにも再インストールをしなくとも
bin/console eccube:plugin:schema-update ProductReview4
だけでDB再定義できることもわかりました

では、お店からの返信を入力できるように管理画面用のFormとtwigを改造します

app/Plugin/ProductReview4/Form/Type/Admin/ProductReviewType.php
こちらのcommentの定義の後ろに追加

~~前略~~
            ->add('comment', TextareaType::class, [
                'constraints' => [
                    new Assert\NotBlank(),
                    new Assert\Length(['max' => $config['eccube_ltext_len']]),
                ],
                'attr' => [
                    'maxlength' => $config['eccube_ltext_len'],
                ],
            ])
            ->add('reply', TextareaType::class, [
                'required' => false,
                'constraints' => [
                    new Assert\Length(['max' => $config['eccube_ltext_len']]),
                ],
                'attr' => [
                    'maxlength' => $config['eccube_ltext_len'],
                ],
            ])
            ;

app/Plugin/ProductReview4/Resource/template/admin/edit.twig
こちらもコメントの後ろに追加

<div class="row mb-2">
    <div class="col-3">
        <span>{{ 'コメント'|trans }}</span>
        <span class="badge badge-primary ml-1">{{ 'product_review.common.required'|trans }}</span>
    </div>
    <div class="col">
        {{ form_widget(form.comment, {'attr': {'rows': '6'}}) }}
        {{ form_errors(form.comment) }}
    </div>
</div>

<div class="row mb-2">
    <div class="col-3">
        <span>{{ '返信'|trans }}</span>
    </div>
    <div class="col">
        {{ form_widget(form.reply, {'attr': {'rows': '6'}}) }}
        {{ form_errors(form.reply) }}
    </div>
</div>
管理画面でお店からの返信を入力できるかの確認

管理画面に返信欄を追加できました

フロント画面にも返信を反映させましょう
app/Plugin/ProductReview4/Resource/template/default/review.twig
前半のCSS部分と後半のHTML部分に追加

~~略~~

<style type="text/css">

    #product_review_area {
        border-top: 1px solid #E8E8E8;
        padding-bottom: 0;
        margin-bottom: 20px;
    }

    #product_review_area .ec-rectHeading {
        cursor: pointer;
        margin-top: 20px;
        margin-bottom: 20px;
    }

    #product_review_area .ec-rectHeading.is_active i {
        transform: rotate(180deg);
    }

    #product_review_area .review_list {
        padding-left: 25px;
    }

    #product_review_area .review_list li {
        margin-bottom: 16px;
    }

    #product_review_area .review_list .review_date {
        font-weight: bold;
    }

    #product_review_area .recommend_average {
        margin-left: 16px;
        color: #DE5D50;
    }

    #product_review_area .review_list .recommend_level {
        margin-left: 16px;
        color: #DE5D50;
    }

    #product_review_area .review_list .recommend_name {
        margin-left: 16px;
    }

    #product_review_area .review_list .review_reply {
        margin-left: 32px;
        margin-top: 12px;
        padding-left: 12px;
        color: #999;
        border-left: 2px solid #999;
    }

</style>

<script>
    $(function() {
        $('#product_review_area').appendTo($('div.ec-layoutRole__main, div.ec-layoutRole__mainWithColumn, div.ec-layoutRole__mainBetweenColumn'));

        $('#product_review_area .ec-rectHeading').on('click', function() {
            $content = $('#reviewContent');
            if ($content.css('display') == 'none') {
                $(this).addClass('is_active');
                $content.addClass('is_active');
                $content.slideDown(300);
            } else {
                $(this).removeClass('is_active');
                $content.removeClass('is_active');
                $content.slideUp(300);
            }
            return false;
        });
    });
</script>

<!--▼レビューエリア-->
<div id="product_review_area">
    <div class="ec-role">
        {% set positive_avg_star = ProductReviewAvg %}
        {% set nagative_avg_star = 5 - positive_avg_star %}

        <div class="ec-rectHeading is_active">
            <h4>{{ 'product_review.front.product_detail.title'|trans }}
                <!--平均の星の数-->
                <span class="recommend_average">{{ stars.stars(positive_avg_star, nagative_avg_star) }}</span>
                <!--レビュー数-->
                <span>({{ ProductReviewCount }})</span>
                <span class="chevron pull-right">
                    <i class="fas fa-angle-up fa-lg"></i>
                </span>
            </h4>
        </div>
        <div id="reviewContent">
            {% if ProductReviews %}
                <ul class="review_list">
                    {% for ProductReview in ProductReviews %}
                        <li>
                            <p class="review_date">
                                <!--投稿日-->
                                {{ ProductReview.create_date|date_day }}

                                <!--投稿者-->
                                <span class="recommend_name">
                                            {% if ProductReview.reviewer_url %}
                                                <a href="{{ ProductReview.reviewer_url }}"
                                                   target="_blank">{{ 'product_review.front.product_detail.name'|trans({ '%name%': ProductReview.reviewer_name }) }}</a>
                                            {% else %}
                                                {{ 'product_review.front.product_detail.name'|trans({ '%name%': ProductReview.reviewer_name }) }}
                                            {% endif %}
                                        </span>

                                <!--星の数-->
                                {% set positive_star = ProductReview.recommend_level %}
                                {% set nagative_star = 5 - positive_star %}
                                <span class="recommend_level">
                                            {{ stars.stars(positive_star, nagative_star) }}
                                        </span>
                            </p>

                            <!--タイトル-->
                            <strong>{{ ProductReview.title }}</strong>

                            <!--レビューコメント-->
                            <p>{{ ProductReview.comment|nl2br }}</p>

                            <!--レビュー返信-->
                            {% if ProductReview.reply %}
                            <div class="review_reply">
                                <span class="text-info">お店からの返信</span>
                                <p>{{ ProductReview.reply|nl2br }}</p>
                            </div>
                            {% endif %}
                        </li>
                    {% endfor %}
                </ul>
            {% else %}
                <p>{{ 'product_review.front.product_detail.no_review'|trans }}</p>
            {% endif %}
        </div>
        <div>
            <a href="{{ url('product_review_index', { id: Product.id }) }}"
               class="ec-inlineBtn--action">{{ 'product_review.front.product_detail.post_review'|trans }}</a>
        </div>
    </div>
</div>
<!-- ▲レビューエリア -->
商品詳細画面でレビューにお店から返信も追加

これでお店からの返信機能が出来上がりました!

ここまでの改造を反映させた返信機能付き商品レビュープラグインを公開しておきます
ProductReview-custom.zip

[ECCUBE] ECCUBE4をカスタマイズしてみる

昨日ECCUBE4のインストールはできたので、今日はカスタマイズの実践をしてみたいと思います
実際の案件でよくある、商品情報に項目を追加したい!をやってみます

Entityのカスタマイズ

ECCUBE3となにより違うのは、app/Customizeというディレクトリがあること
ECCUBE3ではカスタマイズ用のベースプラグインを作ったりしてたけどそれが要らなくなったんですね

開発ドキュメントをそのままなぞる形で商品情報にメーカー名を入れるカスタマイズをします。

app/Customize/Entity/ProductTrait.php

<?php

namespace Customize\Entity;

use Doctrine\ORM\Mapping as ORM;
use Eccube\Annotation\EntityExtension;

/**
  * @EntityExtension("Eccube\Entity\Product")
 */
trait ProductTrait
{
    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     * @Eccube\Annotation\FormAppend(
     *     auto_render=false,
     *     type="\Symfony\Component\Form\Extension\Core\Type\TextType",
     *     options={
     *          "required": false,
     *          "label": "メーカー名"
     *     })
     */
    public $maker_name;
}

Entityを作ったらそこからProxyクラスを生成します
Proxy…ECCUBE3にはなかった用語ですね

$ bin/console eccube:generate:proxies
gen -> /opt/bitnami/apps/ec-cube/app/proxy/entity/Product.php

出来上がった app/proxy/entity/Product.php を見るとECCUBE3時代のEntityファイルのような内容
EC4のEntity ≒ EC3のdoctrine/*dcm.yml
EC4のProxy ≒ EC3のEntity
みたいな理解でいいのかな

Proxyができたらそれをデータベースに反映させます

$ bin/console doctrine:schema:update --dump-sql --force

テンプレートをカスタマイズ

さきほど作ったEntityに@Eccube\Annotation\FormAppendを記述したのでこれでもうフォームを使う準備は整っています
あとは管理画面の商品管理画面にメーカー名の項目を追加するよう、カスタマイズtwigを作成します

src/Eccube/Resource/template/admin/Product/product.twig
をそのまま以下にコピーして
app/template/admin/Product/product.twig

商品説明と販売価格(これはproduct_classがないときだけ出てくる項目)の間にメーカー名フォームを記述

 ~~ 前略 ~~

                    {{ form_widget(form.description_list, { attr : { rows : "4"} }) }}
                    {{ form_errors(form.description_list) }}
                </div>
            </div>
        </div>
    </div>
    
    {# maker_nameフォーム #}
    <div class="row">
        <div class="col-3">
            <div class="d-inline-block">
                <span>{{ form.maker_name.vars.label|trans }}</span>
            </div>
        </div>
        <div class="col mb-2">
            {{ form_widget(form.maker_name) }}
            {{ form_errors(form.maker_name) }}
        </div>
    </div>
    
    {% if has_class == false %}
        <div class="row">
            <div class="col-3">
                <div class="d-inline-block">
                    <span>{{ 'admin.product.sale_price'|trans }}</span>
                    
~~ 後略 ~~

以上で商品情報にメーカー名が追加できました!

[ECCUBE] GCPでECCUBE4事始め

ECCUBE4が正式リリースされたのが2018年10月。今が2019年6月なのでもう半年以上経ちました
まだ実際の案件として話は出てきてないんだけど、そろそろどんなものなのか触っておこうかと思いました

開発ドキュメントをざっとみたところ3.0よりも随分カスタマイズしやすくなってる予感!

GCPでLAMP環境立ち上げ

昔の印象が強いのか、どうしてもECCUBEはMySQLはダメでPostgreSQL推奨な気がしてしまいます
4系がどうなってるのかわからないけども、とりあえずは昔の印象通りLAPP (Linux+Apache+PostgreSQL+PHP) で構築してみます

初出ではLAPPで環境作成していましたが、後ほどカスタマイズする際にdoctrine:schema:updateでエラーを吐くため、LAMP(PostgreSQLではなくMySQL)で環境をつくるように変更しました
昔と違って今のECCUBEはMySQLのほうがよく検証されているのかな

Marketplaceにちょうどいいものがありましたのでこれを採用
LAPP Certified by Bitnami
LAMP Certified by Bitnami

さっそくデプロイ

ECCUBE4のインストールと設定

環境ができあがったら早速SSHログインしてみます
これ、最初からcomposerまで入ってる!
ほんとに手間なくていいなあ
次のステップとして初期パスワードを変えろとか静的IPにしたらとか出てるけど、ECCUBEのお試しができたら捨てるつもりの環境なのでそれらは無視して進めます

なんとなくbitnamiの流儀に従って/opt/bitnami/appsにECCUBEをインストールします

$ sudo chmod 777 /opt/bitnami/apps
$ cd /opt/bitnami/apps
$ sudo composer self-update
$ composer create-project ec-cube/ec-cube ec-cube "4.0.x-dev" --keep-vcs

composerをログインユーザで進めるとcacheを作る権限がないよと警告が出ていますが今は気にしない
暫く待つとインストール完了します

続いてECCUBEの動作設定をします
bitnamiの標準ではMySQLのrootユーザのパスワードはサーバ管理者と同じになっているそうです
管理者パスワードはGCPでデプロイしたときに表示されてるやつですね
本番ならrootユーザで運用なんてしないけど、今回はお試しなので手っ取り早く別ユーザー作らずにrootでいきましょう
データベース名はわかりやすく”eccube”で
Databaseの項目以外はデフォルトのまま設定を進めます

$ cd ec-cube
$ bin/console eccube:install
Database Url [sqlite:///var/eccube.db]:
> mysql://root:<管理者パスワード>@localhost/eccube
Database Server version [auto]:
>
Mailer Url [null://localhost]:
>
Auth Magic [9Vst7fuQkJUTZo8o]:
>

!                                                                                                                      
! [CAUTION] Execute the installation process. All data is initialized.                                                 
!                                                                                                                      

Is it OK? (yes/no) [yes]:
>

開発ドキュメントで書き込み権限が必要と書かれているディレクトリ・ファイルのパーミッションを変更しておきます

$ sudo chmod 777 .
$ sudo chmod 777 app
$ sudo chmod 777 app/Plugin
$ sudo chmod 777 app/PluginData
$ sudo chmod 777 app/proxy
$ sudo chmod 777 app/template
$ sudo chmod 777 html
$ sudo chmod 777 var
$ sudo chmod 777 vendor
$ sudo chmod 666 composer.json
$ sudo chmod 666 composer.lock
$ sudo chmod 666 .env
$ sudo chmod 666 .htaccess

Apacheの設定

ECCUBEの準備もできたのであとはapache設定

$ sudo sed -i -e 's/\/opt\/bitnami\/apache2\/htdocs/\/opt\/bitnami\/apps\/ec-cube/g' /opt/bitnami/apache2/conf/bitnami/bitnami.conf
$ sudo apachectl restart

これで http://<GCPのIPアドレス> でサイトが表示されました!

管理画面は http://<GCPのIPアドレス>/admin ですね
ID: admin PW: password
仮運用とはいえこれは単純すぎるのでログイン後に変更しておかないと

ここまで、なにもないゼロからECCUBE立ち上げまであっという間。
良い世の中になったもんです。

ですが今回インストールが目的じゃないのです。
明日以降カスタマイズ方法を学んでみます。
ECCUBE4をカスタマイズしてみる

[PHP] PDFlibでImage_Barcode2で作ったJANのバーコードを貼り付ける

昨日の続きです。

Image_Barcode2で作ったバーコードをPDFに貼り付けたい。
この案件ではPDF作成にPDFlibを使っています。

// $this->pdflib はPDFlibオブジェクト

protected function pdf_set_barcode($jancode, $x, $y, $opt='')
{
    $barcode_height = 30;
    $barcode_width = 1;
    $barcode_with_num = true;
    $pvf_filename = $jancode;
    
    ob_start(); 
    $gd = Image_Barcode2::draw($jancode, 'ean13', 'png', false, $barcode_height, $barcode_width, $barcode_with_num);
    imagepng($gd, null);
    imagedestroy($gd);
    $this->pdflib->create_pvf($pvf_filename, ob_get_clean(), "");
    $barcode_image = $this->pdflib->load_image('png', $pvf_filename, '');
    $this->pdflib->fit_image($barcode_image, $x, $y, $opt);
    $this->pdflib->close_image($barcode_image);
    $this->pdflib->delete_pvf($pvf_filename);
}

load_imageがファイルを要求しているんだけどいちいちtmpファイル保存するのもなんか気持ち悪いな、って思っていたところ、pvf(PDFlib Virtual file)というのがあったのでそれを使っています。

GDの出力データをob_使わずに取得する方法ないのかな…

[PHP] JANコードをバーコード表示するためにcomposer経由でImage_Barcode2をインストールする

PDF上でJANコードをバーコードを表示したいという案件がありました。
バーコードfontというものがあるようだけど、JANコード=EAN13コードの場合コード変換などが必要でめんどくさそう。
それよりもImage_Barcode2で画像として作っちゃうのが良さそうだったので、これを採用することにしました。
composerを使っている環境だったのでそちらを使ってインストールします。

$ php composer.phar require pear/image_barcode2:dev-master

stableがないということで:dev-masterを付けないとだめだよっていうのが今回のTIPS

続きがあります

[chromebook] Linux環境(Crostini)にDropboxをマウントする

最近自分の中で「最強の打ち合わせのお供」という評価をしているchromebook。
タブレット機らしい軽快さで、developer tool付きのデスクトップブラウザが使え、必要ならMS Office(android版)も使えて、さらにはLinuxまで載っているから各種コマンドまで使えちゃう。
なんて便利なコなんだろう!

愛機 ASUS Chromebook Flip C101PA

で、実用的に使うとなると、個人的にdropboxの使用は必須です。

chromebookでdropboxを使うにあたっては、WEBアプリを使ったり、androidアプリを使ったり、さらにはローカルストレージにように使えるFile System for Dropboxなんかを使ったりするのですが、どの方法もLinux環境(Crostini)では使用できません。
Crostiniにインストールしたエディタ(Visual Studio Codeを使っています)で編集したいときなど、dropboxにアクセスできないのは困ります。

Linux版の公式クライントを使えるのかもしれないですが、クラウド上のファイルをローカルにダウンロードされるのはストレージの少ないchromebookには非現実的。

そこでクラウドにファイルを置いたままファイルシステムにマウントできるdbxfsを使ってみました。

dbxfsのインストール

dbxfsはFUSE(Filesystem in Userspace)を使ってPythonで実装されています。そのインストールにはPythonのパッケージ管理システムのpipを使います。
ということで、それらが入っていなければインストール

$ sudo apt install fuse libfuse2 python3-pip

環境が整ったらdbxfs本体をインストール

$ pip3 install dbxfs

通常ならdbxfsは~/.local/binにインストールされるはず。
マウントポイント(この例では~/Dropbox)を作っておいてからdbxfsを起動します

$ mkdir ~/Dropbox
$ ~/.local/bin/dbxfs ~/Dropbox
We need an access token. Perform the following steps:
1. Go to https://www.dropbox.com/oauth2/authorize?response_type=code&client_id=********
2. Click "Allow" (you may have to log in first)
3. Copy the authorization code.
Enter authorization code (Ctrl-C to quit): 

こんなメッセージが帰ってくるので、表示されたURLにブラウザでアクセスして認証を済ませてコードをゲット。
そのコードを入力して次に進みます。

We're all connected. Do you want to save your credentials for future runs? [Y/n] 

認証情報を保存するかなのでここはY

Please enter password for encrypted keyring:

keyringのためのパスワードを聞かれます。ログインアカウントのパスワードを入力

Would you like to help us improve dbxfs by providing anonymous error reports? [Y/n] 
Do you want "/home/******/Dropbox" to be the default mount point? [Y/n]

改善のためにエラーレポートを送るか、なのでY(望まないならnでもいいはず)
指定したマウントポイントをデフォルトにするか、なのでY

以上で無事dropboxがマウントできました。
df -h で確認してみます

$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/vdb        6.8G  3.3G  3.2G  52% /
none            492K     0  492K   0% /dev
devtmpfs        1.4G     0  1.4G   0% /dev/tty
/dev/vdb        6.8G  3.3G  3.2G  52% /dev/wl0
tmpfs           100K     0  100K   0% /dev/lxd
run             1.4G   24K  1.4G   1% /dev/.host_ip
/dev/root       153M  150M  280K 100% /dev/.ssh/sshd_config
9p              1.9G  572K  1.9G   1% /mnt/chromeos
tmpfs           100K     0  100K   0% /dev/.lxd-mounts
tmpfs           1.4G     0  1.4G   0% /dev/shm
tmpfs           1.4G  8.2M  1.4G   1% /run
tmpfs           5.0M     0  5.0M   0% /run/lock
tmpfs           1.4G     0  1.4G   0% /sys/fs/cgroup
tmpfs           273M   20K  273M   1% /run/user/1000
dbxfs           2.1T   44G  2.0T   3% /home/******/Dropbox

ちゃんと2TBのディスクとしてマウントされてる!

認証情報を保存してデフォルトのマウントポイントも決めていれば、次回以降は引数なしの dbxfs コマンドだけでマウントできます。
なおアンマウントは下記の通り

$ fusermount -u ~/Dropbox

linux環境起動時に自動でマウントしたい

上記で十分使えるようになったんですが、できればlinux環境起動時に自動でマウントしたい。
自動マウントをする上での問題はkeyringのパスワードを聞かれることなので、これをexpectで力技で回避してみました。

まずexpectが入っていなければインストール

$ sudo apt install expect

expectで自動マウント用スクリプトを書きます。dbxfsコマンドと同じ場所に置いてみました。
~/.local/bin/dbxfsmount.exp

#!/usr/bin/expect

set PASSWORD "パスワード"

spawn env LANG=C /home/******/.local/bin/dbxfs
expect {
    "keyring:" {
        send "${PASSWORD}\n"
    }
}

expect {
    "\\\$" {
        exit 0
    }
}

パスワードのところにログインパスワードを記入します。
共有サーバでもないんでスクリプトに平文で書くことをお許しを・・・
spawnの行にはdbxfsコマンドの場所を自分の環境に合わせて書いてください。
書けたら実行権限を与えておきます。

$ chmod 700 ~/.local/bin/dbxfsmount.exp

ここまで用意できたら起動時にこのスクリプトを実行するようにします。
.bash_profileを以下のようにしました(PATHの部分は環境に合わせて)

export PATH=$PATH:/home/*****/.local/bin
(dbxfsmount.exp > /dev/null 2>&1 &)

if [ -f ~/.bashrc ] ; then
. ~/.bashrc
fi

これでこれからはCrostiniを立ち上げると自動でdropboxがマウントされています。

[ECCUBE] ログイン後の遷移先をマイページに

ECCUBE 3.0系の話。
標準ではmypage/loginからログインするとトップページに遷移します。
それをマイページに変更したいという要望がありました。

これに対応するにはログイン処理のイベントを拾って云々…になるかと思いきや、意外にもtwigの改造だけで対処できることがわかりました。

Mypage/login.twig

{% if app.session.flashBag.has('eccube.login.target.path') %}
	{% for targetPath in app.session.flashBag.get('eccube.login.target.path') %}
		<input type="hidden" name="_target_path" value="{{ targetPath }}" />
	{% endfor %}
{% endif %}

上記のようになっている部分があります。
_target_pathというパラメータで遷移先を指定できるんですね。

ここでflashBagセッションに遷移先が入っていればそこを_target_pathに指定する処理が書かれていますが、mypage/loginからの一般的な操作だと特に指定がないためにデフォルト=トップページに飛んでいるようです。

そこでflashBagセッションに遷移先指定がない場合、_target_pathにマイページを指定するようにして対応しました。

{% if app.session.flashBag.has('eccube.login.target.path') %}
	{% for targetPath in app.session.flashBag.get('eccube.login.target.path') %}
		<input type="hidden" name="_target_path" value="{{ targetPath }}" />
	{% endfor %}
{% else %}
		<input type="hidden" name="_target_path" value="{{ url('mypage') }}" />
{% endif %}

[jQuery] class変更されたときにevent発火

対象elementのclassが変更されたときになにか処理を行いたい案件がありました。
もともとのclassを変更する処理のところに追記できればいいんですが、事情でそこには手が出せないので、なんとか外側からclassの変更を察知したい。

ということで、厳密に言ったら”class変更”時ではないのですが、jQueryでaddClassされたときにカスタムeventが発生するようにして対応しました。

まずどこかにカスタムイベント発火処理を書いて

// addClass時にイベントを起こす
(function(){
	// 元のmethodを保存
	var originalAddClassMethod = jQuery.fn.addClass;
	
	jQuery.fn.addClass = function(){
		// 元のmethodを実行
		var result = originalAddClassMethod.apply(this, arguments);
		
		// カスタムイベントを発火
		jQuery(this).trigger('cssClassAdd');
		
		return result;
	}
})();

あとはeventを受け取って処理したい内容を書く

$('#hoge').bind('cssClassAdd', function(){
    // やりたい処理内容
});

参考
jQuery – Fire event if CSS class changed