Mautic Meetup KANSAI 「Mautic・MAについてみんなで話す会」をやりました

ちょっと動きが鈍ってたので、大阪・神戸共同企画で開催しました。

座談会形式で、Mautic / MAの話をする会

「Mauticのセッションやろうとすると、話せる人いまそんなにいないよね・・・」ということで、座談会やりました。 はじめは3グループにわかれて話す形でしたが、気がつけば全体でプロジェクターみながらいろいろ話すスタイルに。

出てきた話

  • MauticのFocus Itemがお知らせとかCTA誘導できそうで便利
  • ユーザーがどんなページを見たかトラッキングできるけども、メールアドレスがないと紐付けできない
  • さとりやカイロス・HubSpotなどのツールもあるから、そっちのユーザーはそこそこいるかも
  • 有料サービス(Mautic Cloud含む)はコンタクトやPV数での課金が多い
  • 「月間5000PVくらいのサイトとかだと、MA以前にやらないとなこと山積みじゃない?」
  • 機械学習とかMauticのダイナミックコンテンツでコンテンツを動的に出す仕組みとか今後いっぱい出てきそう
  • 「そこまでトラッキングするなら、サイトトップで見たい情報出してよ」
  • 「というかサイト見なくてもよしなに情報とか商品送る仕組みはよ」

おわりに

とりあえず箇条書きにしましたが、MAサービスの会社の中の人やマーケティングを仕事としてされている方も参加されていてかなり突っ込んだ話ができたなと思っています。 最後の方は「こうなるといいよねーからはじめて、MAとかでできそうなところどこか話そうぜ」という流れになって、かなり手塚治虫や星新一の作品に出てきそうな世界の話にまでなっていました。 次回は未定ですが、1〜2ヶ月周期では開催したいという話になっていますので、ぜひご参加ください。

Mauticアップデートで`Doctrine\ORM\Mapping\MappingException`が出た時の対応

Mautic(特に古いバージョンのもの)を最新版にアップデートする際、まれにsite is offlineになるケースがあります。

その場合は落ち着いて以下の2つを実施しましょう。

  • サーバーのエラーログを確認する
  • SSHして、symfonyコマンドを実行する

サーバーのエラーログを確認する

今回こちらで発生したケースでは、/var/log/httpd/ssl_error.logに以下のエラーが出ていました。

[Sun Jan 07 02:14:31.417360 2018] [:error] [pid 9078] [client 111.232.123.80:57156] PHP Warning:  require(/var/www/html/app/cache/prod/doctrine/orm/Proxies/__CG__MauticPluginBundleEntityPlugin.php): failed to open stream: No such file or directory in /var/www/html/vendor/doctrine/common/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php on line 209, referer: https://ec2-13-230-186-49.ap-northeast-1.compute.amazonaws.com/s/login
[Sun Jan 07 02:14:31.417396 2018] [:error] [pid 9078] [client 111.232.123.80:57156] PHP Fatal error:  require(): Failed opening required '/var/www/html/app/cache/prod/doctrine/orm/Proxies/__CG__MauticPluginBundleEntityPlugin.php' (include_path='.:/usr/share/pear:/usr/share/php') in /var/www/html/vendor/doctrine/common/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php on line 209, referer: https://ec2-13-230-186-49.ap-northeast-1.compute.amazonaws.com/s/login

SSHして、symfonyコマンドを実行する

php app/console cache:warmup --env=prodでMauticが持っているキャッシュを削除することができます。

このコマンドを実行して、復旧しないか確認してみましょう。

これを実行後にサイトが復旧すれば、キャッシュによるものと思われます。
が、今回遭遇したケースでは以下のようにエラーが発生しました。

$ php app/console cache:warmup --env=prod

 // Warming up the cache for the prod environment with debug false                                                      



  [Doctrine\ORM\Mapping\MappingException]                                                                                         
  Duplicate definition of column 'id' on entity 'Mautic\CoreBundle\Entity\MessageQueue' in a field or discriminator column mapping.                                                                                                                             


cache:warmup [--no-optional-warmers] [-h|--help] [-q|--quiet] [-v|vv|vvv|--verbose] [-V|--version] [--ansi] [--no-ansi] [-n|--no-interaction] [-s|--shell] [--process-isolation] [-e|--env ENV] [--no-debug] [--] <command>

Duplicate definition of column 'id' on entity 'Mautic\CoreBundle\Entity\MessageQueue' in a field or discriminator column mapping.の対応

以下のようにすることで、サイトを復旧できました。

# rm app/bundles/CoreBundle/Entity/MessageQueue.php
# rm -rf app/cache/prod/ or sudo -u apache php app/console cache:warmup --env=prod

app/bundles/CoreBundle/Entity/MessageQueue.phpを削除することに抵抗がある場合、mv app/bundles/CoreBundle/Entity/MessageQueue.php /tmpなどで関係のない場所に移動させればOKです。

参考

https://github.com/mautic/mautic/issues/1508
https://github.com/mautic/mautic/issues/5459

HubSpot CRM Freeのアカウントを作成する

HubSpot CRM Free

Mauticはさまざまなサービスと連携することができます。

今回はその中でも無料で始めることのできるCRMサービスHubSpot CRM Freeのアカウントを作成してみます。

HubSpotのページへ移動

HubSpot TOPページ
https://www.hubspot.jp/

[ソフトウェア]をクリックすると、[HubSpot CRM Free]が出てきますので選びましょう。

HubSpot CRM Free
HubSpot CRM Free

[試してみる]というボタンをクリックすると、アカウント作成のためのウィザードが始まります。

HubSpot CRM Freeアカウントを作成する

HubSpot CRM Freeのアカウント作成に必要なものは以下の4つです。

  • メールアドレス
  • 姓名
  • WebサイトURL
  • パスワード

無料のサービスですので、クレジットカード情報などの登録は不要でした。

アカウント情報を登録します
アカウント情報を登録します
姓名とパスワードの設定
姓名とパスワードを設定します。
姓名の位置が左右逆なので要注意。
準備完了
これで準備できました。
アンケート的なもの
アンケートがありますので、回答します。
これでウィザード完了
これでウィザードが完了です。

ウィザードが完了すると自動的にダッシュボードにログインします。

ダッシュボードTOP
ダッシュボードTOP

いろいろ試してみるためなのか、サンプルのコンタクトが登録されていました。

サンプルのコンタクトが2件登録されている
サンプルのコンタクトが2件登録されています。

アカウント作成後はメール認証を

アカウント作成後、登録したメールアドレスに確認メールが届いていますので、こちらも確認しておきましょう。

Email認証が必要なので確認を
Email認証が必要なので確認を

WooCommerceとMauitcを連携するプラグインを作ってみた話

公開プラグインとして作ったものではないので、一般に利用できるものではないですがいろいろやってみました。

そのときに感じたことをもろもろとまとめます。

WordPressとMautic API Libの相性はちょっとよくない

いきなりネガティブなことになりますが、WPの仕様との相性はあまりよくないです。

というのもWordPressでは_SESSIONを使うなというのがスタンダードなコーディングスタイルだからです。

参考:Code Review: What We Look For – WordPress.com VIP: Enterprise content management platform

そのためMautic API Lib for PHPを使うとWordPress向けに設定されたphpcsがwarningをはいてきますが、ライブラリごと作り直すくらいしか対応方法が思いつかないのである程度割り切りましょう。

WooCommerce / WordPressはフックが多いため、データ取得などはやりやすい

全購入ステップ1つ1つに割り込みできるんじゃないかと思えるくらいフックが豊富なのがWordPress / WooCommerceの特徴の1つかなと思います。

WooCommerceの顧客IDとMauticのコンタクトIDのマッピングさえできていれば、特定の行動(カゴ落ち・キャンセル)などに対してセグメント追加やタグ付けなどのアクションを発火させることも可能そうです。

WooCommerceの顧客IDとMauticのコンタクトIDはそれぞれ内部に持たせておくのが無難

WordPressもMauticもIDからユーザー情報を取得します。
ですのでカスタムフィールドやユーザーメタを用いてWordPressにはMauticのコンタクトIDを、MauticにはWordPressのユーザーIDを保存しておくのが無難かなと思います。

ということでざっとですが、以上です。

React + react-routerでPageviewをトラッキングする

通常ではindex.htmlに以下のようなコードを書くだけでOKです。

    <script>
      (function(w,d,t,u,n,a,m){w['MauticTrackingObject']=n;
          w[n]=w[n]||function(){(w[n].q=w[n].q||[]).push(arguments)},a=d.createElement(t),
          m=d.getElementsByTagName(t)[0];a.async=1;a.src=u;m.parentNode.insertBefore(a,m)
      })(window,document,'script','https://whoge.mautic.net/mtc.js','mt');

      mt('send', 'pageview')
  </script>

しかしJS内でページ遷移が発生するSPAなどでは、初回ロードしかトラッキングしてくれません。

React + react-route (v4)の場合は、以下のようにすることで対応できました。

class AppRoutes extends React.Component {
  componentWillUpdate() {
    const MauticJS = 'MauticJS'
    if (window.hasOwnProperty(MauticJS)) {
      window[MauticJS].sendPageview()
    }
  }
  render () {
    return (
      <Switch>
        <Route exact path="/" component={RouteTop} exact />
        <Route path="/:slug" component={RouteSingle} />
      </Switch>
    )
  }
}

MauticJSオブジェクトが生成されるまですこしラグがあります。
また、初回ロードはhtmlに記載したmt関数でトラッキングできています。

そのため、routeタグをまとめたコンポーネントを用意して、componentWIllUpdateなどでページ遷移を検知します。
その後、window.MauticJSが存在すればページビューを送信するようにしています。

ページビューがトラッキングされている様子

もうちょっと良い方法などがあるかもですが、ひとまず現時点でわかっている方法として記録します。

"Problem binding to port 443"でLet’s Encryptの証明書更新に失敗した場合の対応メモ

Let’s Encryptの証明書更新に失敗した時の対応メモとは別パターンのエラーに遭遇したのでこちらもメモ。

現象

an unexpected error: Problem binding to port 443: Could not bind to IPv4 or IPv6.. Skipping.というエラーで更新に失敗する。

# /usr/local/letsencrypt/letsencrypt-auto renew
Saving debug log to /var/log/letsencrypt/letsencrypt.log

-------------------------------------------------------------------------------
Processing /etc/letsencrypt/renewal/example.com.conf
-------------------------------------------------------------------------------
Cert is due for renewal, auto-renewing...
Plugins selected: Authenticator standalone, Installer None
Renewing an existing certificate
Performing the following challenges:
tls-sni-01 challenge for example.com
Cleaning up challenges
Attempting to renew cert (example.com) from /etc/letsencrypt/renewal/example.com.conf produced an unexpected error: Problem binding to port 443: Could not bind to IPv4 or IPv6.. Skipping.
All renewal attempts failed. The following certs could not be renewed:
  /etc/letsencrypt/live/example.com/fullchain.pem (failure)

-------------------------------------------------------------------------------

All renewal attempts failed. The following certs could not be renewed:
  /etc/letsencrypt/live/example.com/fullchain.pem (failure)
-------------------------------------------------------------------------------

対応

Trying to renew cert on nginx but getting “Problem binding to port 443: Could not bind to IPv4 or IPv6” – Let’s Encryptを見る限り、443ポートを使用しているサーバーを止めてから作業すると動く様子。

# service httpd stop
Stopping httpd:                                            [  OK  ]

更新

[root@ip-172-31-24-26 ~]# /usr/local/letsencrypt/letsencrypt-auto renew
Saving debug log to /var/log/letsencrypt/letsencrypt.log

-------------------------------------------------------------------------------
Processing /etc/letsencrypt/renewal/example.com.conf
-------------------------------------------------------------------------------
Cert is due for renewal, auto-renewing...
Plugins selected: Authenticator standalone, Installer None
Renewing an existing certificate
Performing the following challenges:
tls-sni-01 challenge for example.com
Waiting for verification...
Cleaning up challenges

-------------------------------------------------------------------------------
new certificate deployed without reload, fullchain is
/etc/letsencrypt/live/example.com/fullchain.pem
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------

Congratulations, all renewals succeeded. The following certs have been renewed:
  /etc/letsencrypt/live/example.com/fullchain.pem (success)
-------------------------------------------------------------------------------

再起動

[root@ip-172-31-24-26 ~]# service httpd start
Starting httpd:                                            [  OK  ]

更新を確認

[root@ip-172-31-24-26 ~]# openssl x509 -noout -dates -in /etc/letsencrypt/live/example.com/fullchain.pem
notBefore=Oct 30 14:56:43 2017 GMT
notAfter=Jan 28 14:56:43 2018 GMT

CLIでMauticコアのアップデートが"Package could not be found!"となった場合の対応

Issueも立てているのですが、手動での対応法を覚書として残しておきます。

現象

2.9.xから2.10.1へのアップデート時に以下の様なエラーが出るケースがありました。

$ cd /PATH/TO/MAUTIC
$ php upgrade.php
Checking for new updates...updating to 2.10.1!
Extracting the update package...failed. Package could not be found!

コアのソースコードを見る限りでは、zipファイルの取得に失敗している様子です。

対応

Issueのコメントにて、「zip取れてないから、直接おいてやればいいよ」というコメントがありましたのでそれで対応しています。

$ wget https://github.com/mautic/mautic/releases/download/2.10.1/2.10.1.zip
$ mv 2.10.1.zip 2.10.1-update.zip
$ php upgrade.php
Checking for new updates...updating to 2.10.1!
Extracting the update package...done!
Moving files...done!
Clearing the cache...done!
Rebuilding the cache...done!
Applying migrations...done!
Cleaning up...done!

Success!

Mauticでは、{バージョン番号}-update.zipという命名規則でファイルがDLされます。(該当コードはこちら
ですのでzipファイルをDLして、その名前に変更してやることでアップデートスクリプトが問題なく実行される様になります。

そのほか

該当ファイルがある場合はDLせずにそのまま展開して実行していく様子です。
なのでリネーム時に最新版のバージョン名へ偽装すればバージョン指定のアップデートもできるのかもしれません。(おすすめはしませんが)

CLIでMauitcコアのバージョンを指定してアップデートする

諸刃の剣なのでまったくおすすめしませんが、できるのはできたのでメモ。
アドオンや運用時のローカルテストくらいにしか使えなさそうです。

手順

やり方は簡単で、古いバージョンのコアzipファイルを最新版のバージョン名で配置しておくだけです。

$ cd /PATH/TO/MAUTIC
$ wget https://github.com/mautic/mautic/releases/download/2.8.1/2.8.1.zip
$ mv 2.8.1-update.zip 2.10.1-update.zip
$ php upgrade.php
Checking for new updates...updating to 2.10.1!
Extracting the update package...done!
Moving files...done!
Clearing the cache...done!
Rebuilding the cache...done!
Applying migrations...done!
Cleaning up...done!

Success!

GUIからアクセスすると、2.8.1で動作していることが確認できます。

CLIからもアップデートが必要なバージョンであることを認識していることが確認できました。

# php app/console mautic:update:find
Mauticのバージョン 2.10.1 がダウンロード可能です。詳細は https://www.mautic.org/community/index.php/8475-2-10-1-released をお読みください。
アップデートするにはコマンドラインから'php app/console mautic:update:apply'を実行してください。

やっぱり最新版にしたい場合

偽装しているzipファイルを消してphp upgrade.phpを実行しましょう。
上記手順と同様にwgetなどで最新版のzipファイルを直接配置するのも1つです。

ただし

コアチームの意図している動きではないので、このアップデートで事故るケースの方がおそらく多いと思います。
Vagrant / Dockerなどの使い捨てにしてもいい環境で、バージョンを指定したテストしたい場合に使う程度にしてください。

2.9.2 -> 2.8.1 -> 2.10.1を試して出たエラー(一部)

# php app/console doctrine:migrations:migrate

  [Doctrine\ORM\Mapping\MappingException]
  Duplicate definition of column 'id' on entity 'Mautic\CoreBundle\Entity\MessageQueue' in a field or discriminator column mapping.

]# tail app/logs/mautic_prod-2017-10-25.php
[2017-10-25 17:25:07] mautic.ERROR: Symfony\Component\Debug\Exception\FatalErrorException: Notice: require(): Failed opening required '/var/www/html/app/cache/prod/doctrine/orm/Proxies/__CG__MauticStageBundleEntityStage.php' (include_path='.:/usr/share/pear:/usr/share/php') - in file /var/www/html/vendor/doctrine/common/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php - at line 209 [] []
$ tail -f /var/log/httpd/ssl_error_log
[Wed Oct 25 08:30:26.615227 2017] [:error] [pid 3345] [client 210.170.194.219:52779] PHP Warning:  require(/var/www/html/app/cache/prod/doctrine/orm/Proxies/__CG__MauticStageBundleEntityStage.php): failed to open stream: No such file or directory in /var/www/html/vendor/doctrine/common/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php on line 209, referer: https://13.114.212.68/s/login
[Wed Oct 25 08:30:26.615264 2017] [:error] [pid 3345] [client 210.170.194.219:52779] PHP Fatal error:  require(): Failed opening required '/var/www/html/app/cache/prod/doctrine/orm/Proxies/__CG__MauticStageBundleEntityStage.php' (include_path='.:/usr/share/pear:/usr/share/php') in /var/www/html/vendor/doctrine/common/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php on line 209, referer: https://13.114.212.68/s/login

そもそも

最新版で動く様に使って欲しいなと思う次第です。

Let's Encryptの証明書更新に失敗した時の対応メモ

エラー文

# /usr/local/letsencrypt/certbot-auto renew && /sbin/service httpd restart > /dev/null 2>&1
Upgrading certbot-auto 0.9.3 to 0.11.1...
Replacing certbot-auto...
Creating virtual environment...
Installing Python packages...
Installation succeeded.
Traceback (most recent call last):
  File "/root/.local/share/letsencrypt/bin/letsencrypt", line 7, in 
    from certbot.main import main
  File "/root/.local/share/letsencrypt/local/lib/python2.7/dist-packages/certbot/main.py", line 11, in 
    import zope.component
  File "/root/.local/share/letsencrypt/local/lib/python2.7/dist-packages/zope/component/__init__.py", line 16, in 
    from zope.interface import Interface
ImportError: No module named interface

復旧時に実行したコマンド

/root/.local/share/letsencrypt/bin/letsencrypt に問題がある様子だったので、一旦削除または/tmpなどの別ディレクトリへ移動してから再試行した。

# rm /root/.local/share/letsencrypt/bin/letsencrypt
# unset PYTHON_INSTALL_LAYOUT
# /usr/local/letsencrypt/certbot-auto renew && /sbin/service httpd restart > /dev/null 2>&1

参考

Mautic API LibraryでMautic APIをコールする(oAuth2認証編)

MauticのデータをMautic外で扱いたい時、Mauticが持つREST APIをコールすることができます。そしてREST APIの呼び出し方法については、公式のドキュメントに「PHPのライブラリがあるからそれを使ってください」と書かれています。

ということで何回かに分けて、「Mautic API Library」を使ってMauticのREST APIを利用する方法をご紹介します。

ライブラリのインストール

まずはライブラリをインストールします。
composerを使って手に入れることが可能ですので、作業ディレクトリに移動してから以下のコマンドでインストールします。

$ cd /PATH/TO/WORKSPACE
$ composer require mautic/api-library

実行すると、以下のようにcomposerによるインストールが始まります。

Using version ^2.1 for mautic/api-library
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Installing psr/log (1.0.2)
    Downloading: 100%

  - Installing mautic/api-library (2.1.1)
    Downloading: 100%

Writing lock file
Generating autoload files

MauticでAPIクレデンシャルを作成する

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-26-19-00-25

作成すると、Client KeyとClient Secret Keyが生成されますので、控えておきましょう。

oAuth2認証を実施する

それでは早速APIへの認証を始めます。

サンプルコード:開発ドキュメントのコードを参考に、以下のようなファイルを作成します。($settingsの値は自分の値に変更してください。)

index.php

<?php
// Bootup the Composer autoloader
include __DIR__ . '/vendor/autoload.php';
use Mautic\Auth\ApiAuth;

// ApiAuth::initiate will accept an array of OAuth settings
$settings = array(
    'baseUrl'      => 'https://YOUR_MAUTIC_URL',
    'version'      => 'OAuth2',
    'clientKey'    => 'YOUR_MAUTIC_CLIENT_KEY',
    'clientSecret' => 'YOUR_MAUTIC_CLIENT_SECRET',
    'callback'     => 'http://YOUR_WEBSITE_URL'
);
session_start();
// Initiate the auth object
$auth = ApiAuth::initiate($settings);

if ($auth->validateAccessToken()) {
    if ($auth->accessTokenUpdated()) {
        $accessTokenData = $auth->getAccessTokenData();
        foreach($accessTokenData as $key => $value ) {
            echo "$key: $value<br/>";
        }
    }
}

index.phpを実行する

あとは先程作成したファイルを実行すればOKです。

ブラウザからアクセスすると、Mauticの認証画面へリダイレクトされます。

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-26-19-04-22

許可をクリックすると、上で作成したコードの$accessTokenDataにアクセストークンなどのデータを含めた状態でコールバックURLにアクセスします。
%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-10-26-19-02-15

今回はここまで。
次回からはこのアクセストークンの情報を使って実際にデータの取得などを行ってみます。