アシアルブログ

アシアルの中の人が技術と想いのたけをつづるブログです

Monacaアプリでコピー&ペーストを実装するならクリップボードプラグイン!

こんにちは、渡辺です。

今回は、「VersoSolutions/CordovaClipboard」プラグインの使い方を紹介します。
このプラグインを利用することでアプリ内でのテキストコピー&ペーストができます。

■利用するプラグイン


VersoSolutions/CordovaClipboard
( https://github.com/VersoSolutions/CordovaClipboard )


■サンプルプロジェクトについて



・サンプルプロジェクトの動作確認環境


iPhone6 (iOS 8.1.2)
・GALAXY Note 2 SC-02E (Android4.3 )
デバッグビルドしたアプリで確認済
(注意:ストア版デバッガーでは、今回利用するプラグインが組み込めれていないため、動作しませんので注意してください。)


プラグインのインストール


こちらの過去記事に記述したましたので、参照ください。

■コピーする


まず「deviceready」イベント後に、テキストをコピーする処理を実行します。




var text = "Clipboard Plugin";
document.addEventListener('deviceready', function()
{
    cordova.plugins.clipboard.copy(text);
});



■貼り付ける


上で行ったコピー処理の対象を、貼り付けます。コールバックの引数に
コピーされたテキストが入っています。




cordova.plugins.clipboard.paste(function(text)
{
    alert(text);
});
    


■サンプルプロジェクトのコード


index.html




<!DOCTYPE HTML>
<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <script src="components/loader.js"></script>
    <link rel="stylesheet" href="components/loader.css">
    <link rel="stylesheet" href="css/style.css">
    <script>
        var text = "Clipboard Plugin";
        document.addEventListener('deviceready', function()
        {
            cordova.plugins.clipboard.copy(text);
            cordova.plugins.clipboard.paste(function(text)
            {
                alert(text);
            });
        });
    </script>
</head>

<body>
    This is a template for Monaca app.</body>

</html>



■参考情報


・VersoSolutions/CordovaClipboard
https://github.com/VersoSolutions/CordovaClipboard


■お知らせ


Monacaチームでは現在、新規開発メンバーを大募集中です。詳しくはこちらを参照ください!
https://ja.monaca.io/careers.html

MonacaアプリにネイティブFacebookプラグインを導入してみよう!

こんにちは、渡辺です。

今回は、「Wizcorp/phonegap-facebook-plugin」プラグインを利用してFacebookと連携するサンプルプロジェクトを作ります。

■利用するプラグイン


Wizcorp/phonegap-facebook-plugin
( https://github.com/Wizcorp/phonegap-facebook-plugin )


■サンプルプロジェクトについて


 サンプルプロジェクトでは、Facebookへログイン後にログインしたユーザーのメールアドレスと友達リストを取得します。取得した情報は、アラートで表示します。また、このプロジェクトはMonacaテンプレートの「最小限のテンプレート」をもとに作成しています。

 

・サンプルプロジェクトの動作確認環境


  ・iPhone6 (iOS 8.1.2)
  ・GALAXY Note 2 SC-02E (Android4.3 )
  ・デバッグビルドしたアプリで確認済
  (注意:ストア版デバッガーおよびカスタムデバッガーでは動作しないので注意してください。)


■準備


 準備としては、サンプルプロジェクトを作成する前に、まず下記の「サンプルプロジェクト Facebook クライアント」ページにある事前準備作業を行い、「App ID 」と「 APP NAME」を取得してください。

・サンプルプロジェクト Facebook クライアント
http://docs.monaca.mobi/cur/ja/sampleapp/samples/facebook


プラグインのインストール


  こちらの過去記事に記述したましたので、参照ください。



また、プラグインをインストールする際には、ZIP形式でのインポートをお薦めします。
注意する点として、今回のプラグインの場合、GitHubからダウンロードしたZIP内容に一部誤りのようなところがあります。対処法としては、一度解凍して再度WinArchiverなどのソフトを利用して圧縮すると正しい形式のZIPが作成されます。

プラグインの設定


プラグインのインストール作業が完了したら、次はプラグインで利用する「APP_ID」と「APP_NAME」をプラグインの設定画面にて指定します。
ここで使用する値は、「準備」の項目で用意したものとなります。





Facebookへログインする


「deviceready」イベントが発生した際に、Facebookへのログインが開始するようにしています。
ログインが成功した場合は、「fbLoginSuccess」を実施します。「fbLoginSuccess」は次の項目に記述しています。



document.addEventListener('deviceready', initApp, false);

function initApp() {
    // Login
    facebookConnectPlugin.login(["public_profile"], fbLoginSuccess, function (error) {
      alert("" + error)
    });
  }



■ユーザー情報を取得する


ログインが成功した際に呼ばれる処理です。ここでは Facebook Graph APIを利用して、ユーザー情報としてemailと友達リストを取得しています。



var fbLoginSuccess = function (userData) {
    alert("UserInfo: " + JSON.stringify(userData));

    // Get email and friends 
    facebookConnectPlugin.api(userData.authResponse.userID + "/?fields=id,email,friends", ["email", "user_friends"], function (result) {
      alert("Result: " + JSON.stringify(result));
    }, function (error) {
      alert("Failed: " + JSON.stringify(error));
    });
  };    



■サンプルプロジェクトのコード


index.html




<!DOCTYPE HTML>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <script src="components/loader.js"></script>
  <link rel="stylesheet" href="components/loader.css">
  <link rel="stylesheet" href="css/style.css">
</head>

<body>
<br/>This is a template for Monaca app.
<div id="fb-root"></div>
</body>
<script>
  document.addEventListener('deviceready', initApp, false);

  function initApp() {
    // Login
    facebookConnectPlugin.login(["public_profile"], fbLoginSuccess, function (error) {
      alert("" + error)
    });
  }

  var fbLoginSuccess = function (userData) {
    alert("UserInfo: " + JSON.stringify(userData));

    // Get email and friends 
    facebookConnectPlugin.api(userData.authResponse.userID + "/?fields=id,email,friends", ["email", "user_friends"], function (result) {
      alert("Result: " + JSON.stringify(result));
    }, function (error) {
      alert("Failed: " + JSON.stringify(error));
    });
  };

</script>

</html>



■参考情報


・Wizcorp/phonegap-facebook-plugin
https://github.com/Wizcorp/phonegap-facebook-plugin


■お知らせ


Monacaチームでは現在、新規開発メンバーを大募集中です。詳しくはこちらを参照ください!
https://ja.monaca.io/careers.html

MonacaアプリでInstagramに写真を投稿する!

こんにちは、渡辺です。

今回は、「vstirbu/InstagramPlugin」プラグインを利用してInstagramと連携するサンプルアプリを作ってみたいと思います。今回のプラグインは、利用方法が非常に簡単なので、はじめてプラグインを利用する人に是非チャレンジしてもらいたいです。

■利用するプラグイン


vstirbu/InstagramPlugin
( https://github.com/vstirbu/InstagramPlugin )

■サンプルプロジェクトについて


 サンプルプロジェクトでは、下の2つを対象にInstagramへ連携するアプリを作成します。連携できるものは、写真だけでなく、Canvas要素も連携することができます。
(1)写真の連携では、カメラで撮影したものをそのままInstagramへスグに投稿できるものを実装します。
(2)Canvas要素の連携では、Canvasで書いた星を投稿します。

また、このプロジェクトはMonacaテンプレートの「最小限のテンプレート」をもとに作成しています。

 

Instagramに連携できるもの


  (1) カメラで撮った写真
  (2) Cavas要素



 

・サンプルプロジェクトの動作確認環境


  ・iPhone6 (iOS 8.1.2)
  ・GALAXY Note 2 SC-02E (Android4.3 )
  ・デバッグビルドしたアプリで確認済

■準備


 準備としては、サンプルプロジェクトを作成する前に、まず、Instagramアプリを先にインストールして、Instagramへの登録を完了させておいて下さい。また、作成するプロジェクトでは下記のプラグインが有効となっていることが必要となります。

 ・Fileプラグイン(org.apache.cordova.file)
 ・Cameraプラグイン(org.apache.cordova.camera)
 ・MonacaPlugin(mobi.monaca.plugins.Monaca)

プラグインのインストール


  こちらの過去記事に記述したましたので、参照ください。

■端末にInstagramがインストールされていることを確認する


isInstalled( )で、端末にInstagramが既にインストールされているかどうかを確認することができます。変数installedに確認結果が返ってきます。



Instagram.isInstalled(function (err, installed) {
    if (installed) {
        console.log("Instagram is", installed); 
    } else {
        console.log("Instagram is not installed");
    }
});


Instagramへ連携する


  

ー 写真を撮って投稿する



まずカメラを起動させる処理を用意します。「show_pic( )」でカメラを起動させる処理( navigator.camera.getPicture( ) )を行っています。「navigator.camera.getPicture( )」の第一引数では、カメラでの撮影が成功した場合に実行する処理を定義しています。今回は、カメラで撮った写真をそのままInstagramへシェアしたいので、「share_pic( )」を呼び出して、その中で「share( )」を実行しています。



  show_pic: function()
    {
        navigator.camera.getPicture(this.share_pic, this.fail,
        {
            quality: 50,
            destinationType: Camera.DestinationType.DATA_URL,
            targetWidth: 400,
            targetHeight: 400
        });
    },
  fail: function(msg)
    {
        alert(msg);
    },
    share_pic: function(data)
    {
        Instagram.share("data:image/jpeg;base64," + data, 'example caption', function(err) {});
    }


  

Canvas要素を投稿する


次は、Canvas要素をシェアする方法です。この処理は、サンプルプロジェクト起動時に描画される星をタップした時に実行されるようにしました。「Instagram.share( )」では、Instagramへ連携する対象の要素idを第一引数に指定しています。第二引数は、オプションとなっていてタイトルを設定します。



    share_canvas: function()
    {
        Instagram.share('instagram', 'canvas element share', function(err) {});
    }


■サンプルプロジェクトのデバッグ方法


外部のプラグインを利用したプロジェクトをデバッグする場合は、ストア版Monacaデバッガーではなく、「カスタムデバッガー」を作成する必要があります。「カスタムデバッガー」の作り方は、通常のビルドと同様の画面で「デバッガーのビルド」を選択して作成します。



※ストア版Monacaデバッガーでは、コアプラグインと呼ばれる組み込み済のプラグインが利用できます。一方、コアプラグイン以外のプラグインを利用する場合は、外部プラグインを組み込むことができるカスタムデバッガーを作成する必要があります。

■サンプルプロジェクトのコード


index.html




<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <link rel="stylesheet" type="text/css" href="css/style.css" />
    <script src="components/loader.js"></script>
    <title>Instagram plugin project</title>
</head>

<body>
    <canvas id="instagram" onclick="app.share_canvas();"></canvas>
    <button class="button--cta" onclick="app.show_pic();">Get a Picture</button>
</body>
<script type="text/javascript" src="js/app.js"></script>
<script type="text/javascript">
    app.initialize();
</script>

</html>


app.js




var app = {
    // Application Constructor
    initialize: function()
    {
        this.bindEvents();
    },
    bindEvents: function()
    {
        document.addEventListener('deviceready', this.onDeviceReady, false);
    },
    onDeviceReady: function()
    {
        app.receivedEvent('deviceready');
    },
    receivedEvent: function(id)
    {
        this.draw_star();
        Instagram.isInstalled(function(err, installed)
        {
            if (installed)
            {
                alert("Instagram is installed!!");   
            }
            else
            {
                alert("Instagram is not installed");
            }
        });
    },
    draw_star: function()
    {
        var canvas = document.getElementById('instagram');
        var ctx = canvas.getContext('2d');
        ctx.canvas.width = window.innerWidth;
        ctx.canvas.height = window.innerHeight - 200;
        ctx.fillStyle = "rgba(250, 220, 0, 0.6)";
        star(ctx, 100, 100, 90, 5, 0.5);

        function star(ctx, x, y, r, p, m)
        {
            ctx.save();
            ctx.beginPath();
            ctx.translate(x, y);
            ctx.moveTo(0, 0 - r);
            for (var i = 0; i < p; i++)
            {
                ctx.rotate(Math.PI / p);
                ctx.lineTo(0, 0 - (r * m));
                ctx.rotate(Math.PI / p);
                ctx.lineTo(0, 0 - r);
            }
            ctx.fill();
            ctx.restore();
        }
    },
    show_pic: function()
    {
        navigator.camera.getPicture(this.share_pic, this.fail,
        {
            quality: 50,
            destinationType: Camera.DestinationType.DATA_URL,
            targetWidth: 400,
            targetHeight: 400
        });
    },
    fail: function(msg)
    {
        alert(msg);
    },
    share_pic: function(data)
    {
        Instagram.share("data:image/jpeg;base64," + data, 'example caption', function(err) {});
    },
    share_canvas: function()
    {
        Instagram.share('instagram', 'canvas element share', function(err) {});
    }
};


style.css




html,
body {
    width: 100%;
    height: 100%;
    margin: 0px;
    background: #222;
}
#canvas-container {
    width: 100%;
    text-align: center;
}
canvas {
    display: inline;
}


■参考情報


・vstirbu/InstagramPlugin
  https://github.com/vstirbu/InstagramPlugin
・vstirbu/instagramplugin-example
  https://github.com/vstirbu/instagramplugin-example

■お知らせ


Monacaチームでは現在、新規開発メンバーを大募集中です。詳しくはこちらを参照ください!
https://ja.monaca.io/careers.html

プラグインで簡単に実装できるローカル通知機能!!

こんにちは、渡辺です。

今回は、Manacaで「ローカル通知」プラグインを利用する方法を説明したと思います。
ローカル通知機能のプラグインは、GitHub上で複数ありますが、ここでは下のプラグインを利用します。

katzer/cordova-plugin-local-notifications

ー 目次 ー


 ・ローカル通知とは
 ・サンプルプロジェクトについて
 ・Monacaでのプラグイン組み込み方法
 ・通知許可の確認(アクセス権限の確認)(iOS8のみ)
 ・通知を予約する(スケジュール)
  ー 1分間隔で通知
  ー 1回のみスグに通知
 ・通知スケジュールを取り消す(キャンセル)
 ・サンプルプロジェクトの全体コード
 ・参考情報

ー 確認環境 ー


iPhone6 (iOS 8.1.2)
・GALAXY Note 2 SC-02E (Android4.3 )
デバッグビルドしたアプリで確認済
(※ビルドして確認をお願いします。)

■ローカル通知とは


ローカル通知とは、アプリケーション自身がスケジューリングしてユーザーへ通知する仕組みです。
プッシュ通知(リモート通知)とは違い、インターネット経由での外部通信なしでユーザーへの通知ができます。
カレンダーアプリやToDOアプリなど時間ベースで動くアプリが、ローカル通知に向いているアプリの代表です。

■サンプルプロジェクトについて


サンプルプロジェクトのイメージです。「Onsen UI最小限のテンプレート」をもとに作成しています。

「Granted?」ボタンで、ローカル通知の使用権限を確認します。
「Once」と「Every min」ボタンは、ローカル通知のスケジュール実行ボタンです。
「All」で、スケジュールした通知を解除します。

Monacaでのプラグイン組み込み方法


Monacaでのプラグイン組み込み方法は、2種類あります。
  ・Zip形式のプラグインをアップロードする
  ・名前もしくはURLを指定する





Zip形式のアップロードの場合は、GitHubの「Download ZIP」ボタンをクリックしてZipを取得してください。もう一方の、「名前もしくはURLを指定」を利用する場合は、GitHubのURL「https://github.com/katzer/cordova-plugin-local-notifications」を指定してください。

Monacaが提供しているコアプラグイン以外の外部プラグインをインポートするには、GOLDプラン以上が必要となります。GOLDプラン以上でない方は、まずは無料で試せる14日間のトライアルをご利用してください。

■通知許可の確認(アクセス権限の確認)(iOS8のみ)



iOS8では、アプリがローカル通知を使用する前に、ユーザーから許可をもらう必要があります。「Granted?」ボタンでは、許可を得ているかの確認をしています。許可を得ていない場合は、初回起動にダイアログで許可するかどうかの確認が表示されるはずです。それ以外は、設定画面から通知の許可を与える必要があります。



  //check permission
  hasPermission = function () {
    cordova.plugins.notification.local.hasPermission(function (granted) {
      alert(granted ? 'Yes' : 'No');
    });
  };


■通知を予約する(スケジュール機能)



・1分間隔で通知


ここからローカル通知の処理です。ここでは1分間隔で通知するよう設定しています。プロパティ「every」には、"second", "minute", "hour", "day","week","month","year"と指定できます。また、プロパティ「sound」では、通知した時に流す音源を指定できます。音源は、参考情報に記載したサンプルプロジェクトが提供しているものを使用しています。
※プロパティ「every」に"second"を設定してみましたが、1秒間隔で動かなかったので、もしかしたら、"second"指定でプラグインにバグがあるかもしれません。



scheduleMinutely = function () {
    var sound = device.platform == 'Android' ? 'file://sound.mp3' : 'file://beep.caf';
    cordova.plugins.notification.local.schedule(
      {
        id: 1,
        text: 'Scheduled every min',
        every: 'minute',
        sound: sound
      });
  };


・1回のみスグに通知


一度のみの通知の場合も、schedule( )を使用して通知します。プロパティ「every」がない場合の、デフォルト状態が、1回のみの通知となっています。



//schedule
  var id = 1;
  schedule = function () {
    cordova.plugins.notification.local.schedule(
      {
        id: 1,
        text: 'Test Message 1',
        sound: null,
        data: {
          test: id
        }
      });
  };


■通知スケジュールを取り消す(キャンセル)


さっき設定した1分間隔での通知をキャンセルしましょう。一気に全部キャンセルする場合は、「cancelAll( )」を呼ぶだけです。キャンセルする対象を指定する場合は、「cancel( )」の第一引数にIDを指定します。



  //cancel all notifications
  cancelAll = function () {
    cordova.plugins.notification.local.cancelAll(function () {
      alert("done");
    }, this);
  }

  // cancel single notification
  cordova.plugins.notification.local.cancel(1, function() {
      alert("cancel id 1");
  });


■サンプルプロジェクトの全体コード


・index.html


<!DOCTYPE HTML>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <script src="components/loader.js"></script>
  <link rel="stylesheet" href="components/loader.css">
  <link rel="stylesheet" href="css/style.css">
  <script>
  </script>
</head>

<body>
<div>
  <h2>Local Notification</h2>
  <!-- permission -->
  <h4 class="section">〜 Permission (iOS8) 〜</h4>

  <div class="container">
    <button class="button button--outline" onclick="hasPermission()">Granted?</button>
  </div>
  <!-- schedule -->
  <h4 class="section">〜 Schedule 〜</h4>

  <div class="container">
    <button class="button button--outline" onclick="schedule()">Once</button>
    <button class="button button--outline" onclick="scheduleMinutely()">Every min</button>
  </div>
  <!-- cancel -->
  <h4 class="section">〜 Cancel 〜</h4>

  <div class="container">
    <button class="button button--outline" onclick="cancelAll()">All</button>
  </div>
</div>
<script type="text/javascript">
  var id = 1;
  //permission
  hasPermission = function () {
    cordova.plugins.notification.local.hasPermission(function (granted) {
      alert(granted ? 'Yes' : 'No');
    });
  };
  //schedule
  schedule = function () {
    cordova.plugins.notification.local.schedule(
      {
        id: 1,
        text: 'Test Message 1',
        sound: null,
        data: {
          test: id
        }
      });
  };
  scheduleMinutely = function () {
    var sound = device.platform == 'Android' ? 'file://sound.mp3' : 'file://beep.caf';
    cordova.plugins.notification.local.schedule(
      {
        id: 1,
        text: 'Scheduled every minute',
        every: 'minute',
        sound: sound
      });
  };
  //cancel
  cancelAll = function () {
    cordova.plugins.notification.local.cancelAll(function () {
      alert("done");
    }, this);
  }
</script>
</body>

</html>


・style.css


.button {
    position: relative;
    display: inline-block;
    vertical-align: top;
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
    -webkit-background-clip: padding-box;
    background-clip: padding-box;
    padding: 0;
    margin: 0;
    font: inherit;
    color: inherit;
    background: transparent;
    border: none;
    line-height: normal;
    font-family: 'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    font-weight: 400;
    font-size: 17px;
    cursor: default;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    text-overflow: ellipsis;
    white-space: nowrap;
    overflow: hidden;
    height: auto;
    text-decoration: none;
    padding: 4px 10px;
    font-size: 17px;
    line-height: 32px;
    letter-spacing: 0;
    color: #fff;
    vertical-align: middle;
    background-color: rgba(24, 103, 194, 0.81);
    border: 0px solid currentColor;
    -webkit-border-radius: 3px;
    border-radius: 3px;
    -webkit-transition: none;
    -moz-transition: none;
    -o-transition: none;
    transition: none;
}

.button:hover {
    -webkit-transition: none;
    -moz-transition: none;
    -o-transition: none;
    transition: none;
}

.button:active {
    background-color: rgba(24, 103, 194, 0.81);
    -webkit-transition: none;
    -moz-transition: none;
    -o-transition: none;
    transition: none;
    opacity: 0.2;
}

.button:focus {
    outline: 0;
}

.button:disabled,
.button[disabled] {
    opacity: 0.3;
    cursor: default;
    pointer-events: none;
}

.button--outline {
    background-color: transparent;
    border: 1px solid rgba(24, 103, 194, 0.81);
    color: rgba(24, 103, 194, 0.81);
}

.button--outline:active {
    background-color: rgba(24, 103, 194, 0.2);
    border: 1px solid rgba(24, 103, 194, 0.81);
    color: rgba(24, 103, 194, 0.81);
    opacity: 1;
}

.button--outline:hover {
    border: 1px solid rgba(24, 103, 194, 0.81);
    -webkit-transition: 0;
    -moz-transition: 0;
    -o-transition: 0;
    transition: 0;
}

@import url(http://fonts.googleapis.com/css?family=Oswald);
div {
    text-align: center;
    font: bold 21px 'Oswald', sans-serif;
    text-transform: uppercase;
}

body {
    background-image: -webkit-repeating-radial-gradient(center center, rgba(0, 0, 0, .2), rgba(0, 0, 0, .2) 1px, transparent 1px, transparent 100%);
    background-image: -moz-repeating-radial-gradient(center center, rgba(0, 0, 0, .2), rgba(0, 0, 0, .2) 1px, transparent 1px, transparent 100%);
    background-image: -ms-repeating-radial-gradient(center center, rgba(0, 0, 0, .2), rgba(0, 0, 0, .2) 1px, transparent 1px, transparent 100%);
    background-image: repeating-radial-gradient(center center, rgba(0, 0, 0, .2), rgba(0, 0, 0, .2) 1px, transparent 1px, transparent 100%);
    -webkit-background-size: 3px 3px;
    -moz-background-size: 3px 3px;
    background-size: 3px 3px;
}


■参考情報


より詳しい情報は下のサンプルを参考にしてください。
・サンプルプロジェクト
https://github.com/katzer/cordova-plugin-local-notifications/wiki
https://github.com/katzer/cordova-plugin-local-notifications/tree/example

AndroidでカードUIの実装に便利なプラグイン

こんにちは、細井です。

Androidから少し離れている内に、GoogleMaterial DesignとかAndroid Studioを使え、とか言っていて取り残されています。。
この記事では、Material Designの概念のうち、カードUIをAndroidで実装する際に便利なプラグインを紹介します。

ListViewAnimations
https://github.com/nhaarman/ListViewAnimations
カードのスクロールにアニメーションを付けたり、カードのドラッグアンドドロップやスワイプでの削除を簡単に行えるようになります。
カードUIでは、スクロールで読み込む前のカードにはアニメーションを付けて、読み込んだ後のカードにはアニメーションを付けないことで、何処まで読み込んだか感覚的に分かるようにすることが推奨されているようです。
今回は、それも実現できるアニメーション付与の機能を試してみます。

StaggeredGridView
https://github.com/etsy/AndroidStaggeredGrid
カードをGridViewで複数列並べた時に、カードの高さが均一になってしまうのを解消してくれるGridViewです。

使ってみた
実際に使って、カードUIの一覧画面っぽいものを作ってみました。
アニメーションは、スクロール時に下からどんどん現れてくるような動きをします。



実装
どちらもGradleでモジュールをダウンロードできます。
build.gradleに以下のdependenciesを追加します。



dependencies {
    // ListViewAnimations
    compile 'com.nhaarman.listviewanimations:lib-core:3.1.0@aar'
    compile 'com.nhaarman.listviewanimations:lib-manipulation:3.1.0@aar'
    compile 'com.nhaarman.listviewanimations:lib-core-slh:3.1.0@aar'
    // Githubでは指定無しですが、無いと動かなかったです
    compile 'com.nineoldandroids:library:2.4.0'
    
    // StaggeredGridView
    compile 'com.etsy.android.grid:library:1.0.5'
    
    // Lollipopで追加されたCardViewも使ってみます
    // support-v7で用意されているので、下位バージョンでも使用できます
    compile "com.android.support:cardview-v7:+"
}


LayoutのxmlはMainActivityのものと、GirdViewに表示するカードのものを用意しました。

【activity_main.xml


<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="5dp">

    <com.etsy.android.grid.StaggeredGridView
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/grid_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:item_margin="5dp"
        app:column_count="2"/> <!-- GridViewの列数を指定 -->

</RelativeLayout>


【view_card.xml


<android.support.v7.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    card_view:cardCornerRadius="3dp"> <!-- カードの角丸を指定 -->

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="10dp">

        <ImageView
            android:id="@+id/card_image"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:adjustViewBounds="true"
            android:src="@drawable/card1" />

        <TextView
            android:id="@+id/card_message"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/card_message_1"
            android:paddingTop="10dp"/>

    </LinearLayout>

</android.support.v7.widget.CardView>


次は、Activity側のソースになります。
基本的にはGridViewやAdapterのオブジェクトを置き換えるだけです。簡単。

【MainActivity.java


public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // GridViewの代わりに、StaggeredGridViewを使用
        StaggeredGridView gridView = (StaggeredGridView) findViewById(R.id.grid_view);

        // 適当なAdapterを作成
        MyAdapter myAdapter = new MyAdapter();

        // 作成したAdapterをListViewAnimationsのクラスに渡して、AnimationAdapterに
        SwingBottomInAnimationAdapter animationAdapter = new SwingBottomInAnimationAdapter(myAdapter);
        animationAdapter.setAbsListView(gridView);

        // StaggeredGridViewにAnimationAdapterをセット
        gridView.setAdapter(animationAdapter);
    }
    
    // BaseAdapterを継承した適当なAdapter
    private class MyAdapter extends BaseAdapter {
       ・・・
    }
}


ListViewAnimationsには、SwingBottomInAnimationAdapterクラス以外にも以下のクラスが用意されています。

・ AlphaAnimationAdapter
・ ScaleInAnimationAdapter
・ SwingLeftInAnimationAdapter
・ SwingRightInAnimationAdapter

クラス毎にアニメーションが異なります。
アニメーションの動作は、ListViewAnimationsのデモアプリで確認できます。

まとめ
既存のコードにも組み込みやすいプラグインなので、
あまり時間はかけられないけど、それっぽい画面を作りたい時には役立つと思います!

MonacaとappC cloudで手軽に広告表示をはじめてみよう

Monacaの新しいプラグインとしてappC cloudプラグインが追加されました!appC cloudはアプリ向け広告サービスで、アプリの収益化に役立てられるサービスになります。Monacaを使って作ったアプリであれば、iOS/Androidの両方に広告表示ができるようになります。



さらにMonacaユーザー限定のキャンペーンということで、appC cloud提供元のカイト株式会社様より3,000円分のプロモーションコードを提供頂きましたので是非ご活用ください。



appC cloudへの新規登録の際にこちらのプロモーションコードを入力いただくと、3000円分のクレジットがアカウント登録時付与されます。



コード:OFoX9GJ6

Monacaで作られたアプリの収益と合算された場合のみお支払されます。



早速導入手順を説明します!



必要なもの



Monacaのアカウント



MonacaのIDはこちらから取得できます。



appC cloudのアカウント



appC cloudのアカウントはこちらから取得できます(無料)。ユーザ登録が終わると確認メールが出ますので忘れずにクリックしてください。





appC cloudの新規登録フォーム



Monacaプロジェクトの作成



ではMonacaで新規プロジェクトを作成しましょう。今回は「appCデモ」とします。







テンプレートとしてRSSリーダーを選びました。











プロジェクトができあがったら、開くボタンを押して、Monaca IDEを立ち上げます。







プラグインのインストール



Monaca IDEが開いたら、設定メニューのCordovaプラグインの管理を選択します。







プラグイン一覧の中にあるappCCloudを有効にしてください。







この状態で有効になりました。







appC cloudでアプリ作成



次はappC cloudでの作業です。appC cloudを開き、ログインしてください。ダッシュボードに入ったら、アプリを登録するボタンをクリックします。







アプリ名は分かりやすいもの(Monacaと合わせるなど)を設定してください。







作成したら、次にプラットフォームを選択します。今回はiOSとしました。そしてアプリ詳細設定に進みます。







アプリ詳細設定画面で、アプリ名、Apple ID、BundleIDを入力してください。







情報の登録が終わると、メディアキーが表示されますのでコピーしてください。







後は広告設定ページで表示する広告を指定してください。



Monacaアプリに設定



メディアキーが発行されたら、再度Monaca IDEに戻ります。そしてheadタグの中に、次のように記述してください。






<script>
  document.addEventListener("deviceready",onDeviceReady,false);
    
  function onDeviceReady() {
    : // 他の処理
    appCCloud.deviceready();
    appCCloud.init(success,fail,{
      mediaKeyAndroid:'MediaKeyForAndroid',
      mediaKeyiOS : 'ここに先ほどのメディアキーを入力'
    });
  };
  function success() {
    // 広告初期化成功時
    alert("AppC Cloud Service successfully initialized");
  };
  function fail() {
    // 広告初期化失敗時
    alert("Unable to initialize AppC Cloud Service");
  };
</script>


また、ボタンタップで表示されるように次のようなHTML/JavaScriptを設定しておきます。





// JavaScript
$( ".ads" ).click(function() {
  console.log('Show ads.');
  appCCloud.openAdView();
});




<!-- HTML -->
<div class="button ads">Ads</div>

/* スタイルシート */
.button.reload {
  top: 10px;
  right: 10px;
  position: fixed;
  border: 1px solid #057cff;
  border-radius: 4px;
  background: #fff;
  padding: 5px 10px 5px 10px;
  color: #057cff;
  font-size: 14px;
  text-decoration: none;
  vertical-align: middle;
}


プレビューすると次のような表示になります。







ビルド設定



appC cloudはMonacaデバッガーでは確認できないようになっています。iOSビルド設定を行って、ビルドしてください。この記事中ではビルド設定の詳細は省きますが、appC cloudで設定したBundleIDをiOS Dev Centerで登録し、そのプロビジョニングファイルを使うようにします。また、アプリ設定ページでも同様にApp IDにBundleIDを指定してください。







ビルドが終わったらダウンロードし、iTunesやTestflightなどを使って実機にアプリをインストールしてください。



実行結果



このようにアプリが立ち上がります(テストなのでアラート表示しています)。







今回はこの左上のAdsボタンに広告表示イベントをつけています。メソッドを実行するだけなので、アプリ起動時や何かのイベントの終了時など自由に設定ができるでしょう。







タップすると下から広告がスライド表示されます。







広告表示タイプについて



appC cloudでは幾つかのパターンで広告表示が行えるようになっています。しかしiOSではリジェクトを避けるため、




  • リストビュー広告(今回利用した広告です)

  • マーキー広告

  • カットイン広告

  • ネイティブ広告



を使うのが良いようです。Androidではそれ以外の広告タイプも利用できます。



表示する広告は自由に選択できますので、アプリの収益化として利用してください!また、その際にはぜひ以下の3,000円分のプロモーションコードを忘れずにお使いください!



コード:OFoX9GJ6

Monacaで作られたアプリの収益と合算された場合のみお支払されます。

MonacaでiBeaconを試す

iOS7の新機能として登場したiBeaconですが、対応のデバイスも販売され始めており、徐々に広がりを見せています。そしてiBeaconに対応したアプリはMonacaでも開発が可能です。今回はiBeacon導入までのレクチャー記事になります。



iBeacon端末の用意



今回はiOSをiBeacon化するBeaconEggを使いました。iBeacon端末にするためにはアプリ内課金でiBeacon Senderを購入する必要があります。Mac OSX 10.9であれば、mttrb/BeaconOSXXBeaconが利用できます。



Cordovaプラグインのインポート



iBeaconは標準のMonacaだけでは利用できません。petermetz/cordova-plugin-ibeaconというCordovaプラグインを使います。





cordova-plugin-ibeaconのリポジトリ



Gitリポジトリをcloneするか、Zipファイルとしてダウンロードします。Zipでダウンロードした場合、一旦解凍して中のフォルダやファイルをすべて選択してZip圧縮する必要があります。cloneした場合も同じくcordova-plugin-ibeacon以下のファイルとフォルダをすべて選択してZip圧縮してください。







Monaca IDEでアップロード



iBeaconを試したいプロジェクトをMonaca IDEで開いて、設定メニューのCordovaプラグインの管理を選択します。





設定メニューを開く



開いた画面の中で、Cordovaプラグインのインポートをクリックします。





Cordovaプラグインのインポートをクリック



そしてZIP形式のプラグインをアップロードを選択して、ファイルダイアログで先ほど作成したZipファイルを選択、アップロードしてください。





Zipファイルをアップロード



無事完了するとプラグイン一覧の一番上に Proximity Beacon Plugin が表示されます。





プラグインがインストールされた状態



テストコード



例えば次のようなコードを記述します。





var logToDom = function (message) {
  var e = document.createElement('label');
  e.innerText = message;

  var br = document.createElement('br');
  var br2 = document.createElement('br');
  document.body.appendChild(e);
  document.body.appendChild(br);
  document.body.appendChild(br2);

  window.scrollTo(0, window.document.height);
};

var delegate = new cordova.plugins.locationManager.Delegate();

delegate.didDetermineStateForRegion = function (pluginResult) {

    logToDom('[DOM] didDetermineStateForRegion: ' + JSON.stringify(pluginResult));

};

delegate.didStartMonitoringForRegion = function (pluginResult) {
    console.log('didStartMonitoringForRegion:', pluginResult);
    logToDom('didStartMonitoringForRegion:' + JSON.stringify(pluginResult));
};

delegate.didRangeBeaconsInRegion = function (pluginResult) {

    logToDom('[DOM] didRangeBeaconsInRegion: ' + JSON.stringify(pluginResult));
    cordova.plugins.locationManager.appendToDeviceLog('[DOM] didRangeBeaconsInRegion: '
        + JSON.stringify(pluginResult));
};

var uuid = '1E21BCE0-7655-4647-B492-A3F8DE2F9A02';
var identifier = 'Sample Beacon1';
var minor = 1;
var major = 1;
var beaconRegion = new cordova.plugins.locationManager.BeaconRegion(identifier, uuid, major, minor);

cordova.plugins.locationManager.setDelegate(delegate);

// required in iOS 8+
cordova.plugins.locationManager.requestWhenInUseAuthorization(); 
// cordova.plugins.locationManager.requestAlwaysAuthorization()

cordova.plugins.locationManager.startMonitoringForRegion(beaconRegion)
    .fail(console.error)
    .done();

cordova.plugins.locationManager.startRangingBeaconsInRegion(beaconRegion)
    .fail(console.error)
    .done();



uuid、identifier、minorおよびmajorはBeaconEggのデフォルトのものに合わせています。DeviceReadyになってから実行してください。



iOSアプリとしてビルド



Cordovaプラグインを使っている機能は、Monacaデバッガーで試すことはできません。そこでiOSアプリとしてビルド、インストールします。下記に注意して行ってください。



Xcode6でビルド



cordova-plugin-ibeaconはXcode6のみ対応となっています。デフォルトのままビルドしようとしても失敗します。そこでiOSアプリ設定(設定メニューの中)のその他にあるXcodeバージョンを「バージョン6」にしてください。





Xcode6に変更



実行





立ち上げ時に位置情報取得について確認が出ます



実際にアプリを立ち上げた所です。別なiOSバイスでBeaconEggを実行し、iBeacon化しています。



その状態でMonacaアプリを立ち上げると、beaconsというデータに該当のiBeaconデータが入ってきます。rssiという強度部分が変化しています。距離もとれるので、それによって別な処理を実行すると言ったトリガーに使えると思います。ただし継続的にデータが送られ続けてくるので、一定のフィルタリング処理は必要そうです。





iBeaconに反応している状態



まとめ



Cordovaプラグインを使うことでiBeaconも簡単に実装ができます。今後さらに注目が高まるであろうIoT、O2O分野においてもMonacaアプリを活用するためにもぜひiBeaconと合わせてご利用ください!



Monaca - HTML5ハイブリッドアプリでアプリ開発を加速します

MacPortsのMySQLにQ4Mをインストールする方法

皆さん、こんばんは。笹亀です。

本日はブログを書くために環境をつくっているときに、間違えてMacPortでインストールしたものを削除してしまいました;;
悲しさとむなしさと戦いながらも本日のブログをお届けさせていただきます。
 ※そのため、内容はちょっと少なめになっています。ごめんなさい

今年のPHPカンファレンスでもQ4Mの発表があり、いつか使う日が来るだろうと思い、
個人的にもとても興味があったので実際に使ってみたいくなりました。


Q4M (Queue for MySQL) は MySQL 5.1 のプラガブル・ストレージ・エンジンとして動作するメッセージキューであり、堅牢・高速・柔軟であるよう設計されています。(転載させていただきました


早速インストールをと思ったのですが、上記のこともあり、今回はインストールのみの紹介です。

特にMacPortsMySQLをインスールしていて、そのMySQLQ4Mをインストールするときが大変でした。
さらにMacPortsでのQ4Mのインストールする説明サイトがあまりなかったので、
以下にまとめさせていただき手順についてご紹介させていただきます。

0.MacPortsMySQLの最新版をインストールします(mysql-5.1.39@2009/10/13現在

1.Q4Mの最新版のソースをダウンロードします。(http://q4m.31tools.com/dist/

2.mysqlのソースをコピーして展開しておきます(Q4Mコンパイル用)
  cp /opt/local/var/macports/distfiles/mysql5/mysql-5.1.39.tar.gz /var/tmp
  cd /var/tmp/
  tar xzvf mysql-5.1.39.tar.gz

3.mysqlのライブラリのシンボリックリンクlinuxの通常のインストール先に張る
  sudo ln -s /opt/local/include/mysql5/mysql/ /opt/local/include/mysql
  sudo ln -s /opt/local/lib/mysql5/mysql/ /opt/local/lib/mysql

4.コンパイルをします
  ./configure --prefix=/opt/local LDFLAGS=-L/opt/local/lib CPPFLAGS=-I/opt/local/include --with-mysql=/var/tmp/mysql-5.1.39 CFLAGS="-I/opt/local/include/mysql5/mysql -I/opt/local/include" CPPFLAGS="-I/opt/local/include/mysql5/mysql -I/opt/local/include"

5.makeとmake installする
  ※make install するときはsudoを付けないとパーミッションのエラーになるので忘れずに。

6.MySQLへインストールしたSQLを取込みます
  mysql5 -u root -p < support-files/install.sql

7.perlのテストスクリプトを実行する
  perl run_tests.pl
  ※それらしく実行されるはずです。

8.ちゃんとインストールされているか確認
  show plugins

9.インストールができて喜ぶw



linuxのサーバにインストールする場合ならばrpmも用意されており、ここまで苦労することはないはずです。
Q4Mの実装編も含みでご紹介させていただきたかったのですが、いろいろな問題に悩まされた関係上、次回に持ち越させてください。
次回をお楽しみにしていただけましたら幸いでございます。
本当にごめんなさい ><;

symfonyでExcelファイルを作成するプラグイン

みなさん、こんにちは。
毎年ちょっと遅めの夏休み時期に入ろうとしている、笹亀です。

前回のブログと同様でsymfonyプラグインのご紹介です。
今回は、symfonyExcelを出力するプラグインのsfPhpExcelPluginを使ってみました。
sfPhpExcelPluginは、PHPExcel出力するライブラリの一つであるPHPExcelをsymfony用にプラグイン化したものです。
実際のインストールから基本的なExcelファイルを出力する方法についてご紹介したいと思います。

まずは、プラグインのインストール方法です。
前回と同様ですが、1行で簡単にインストールできますねw


php symfony plugin-install http://plugins.symfony-project.org/sfPhpExcelPlugin


※私の実行環境は、symfonyの1.0ですが、1.1や1.2などをご使用している場合は、インストール方法が若干違うのでご注意ください。
 (HomeのInstallationタブを参照)

インストールした後は実装なのですが、私自身がPHPExcelを使ったこともなかったため、実際にどのような使用すればよいか調べながら使いました。
参考にした箇所をメモで残しておきます。参考になればと思います。
・PHPExcelのドキュメントを参照する
・「plugins/sfPhpExcelPlugin/examples/」内に格納されているサンプルファイルをチェックする
・細かいメソッドや設定については、PHPExcelのソースを見る

英語が苦手なもので、ドキュメントを読むのには少々苦労しました^^;

次に、実際にsymfonyのアクションでの使用例を記述します。
下記は、Excelファイルを生成してダウンロードさせるアクションです。
 ※スクリプトが見えにくい状態になっておりますので、ご注意ください。


public function executeTestExecelOutput()
{
  //このプラグインでは「Strict Standards」のエラーが出るためエラーレベルを調整する
  error_reporting(E_ALL);
  
  //オブジェクト生成
  $objPHPExcel = new sfPhpExcel();

  //Excelファイルのファイル情報をセット(MacのExcelでは確認できませんでした
  $objPHPExcel->getProperties()->setCreator("笹亀弘");
  $objPHPExcel->getProperties()->setLastModifiedBy("笹亀弘");
  $objPHPExcel->getProperties()->setTitle("Excel");
  $objPHPExcel->getProperties()->setSubject("テストExcel");
  $objPHPExcel->getProperties()->setDescription("ExcelファイルをPHPで出力するテストです");
  $objPHPExcel->getProperties()->setKeywords("office Execel php");
  $objPHPExcel->getProperties()->setCategory("カテゴリをセット");

  //使用するシートを指定し、シートタイトルをセットする
  $objPHPExcel->getActiveSheet()->setTitle('Simple');
  $objPHPExcel->setActiveSheetIndex(0);
  
  //デフォルトのスタイルにフォントとフォントサイズを設定(ここでは「MSゴシック」を指定)
  $objPHPExcel->getActiveSheet()->getDefaultStyle()->getFont()->setName('MS Gothic');
  $objPHPExcel->getActiveSheet()->getDefaultStyle()->getFont()->setSize(8);

  //ページの種類とページサイズと印刷エリアの指定(うまく動いているか微妙でした
  $objPHPExcel->getActiveSheet()->getPageSetup()->setOrientation(PHPExcel_Worksheet_PageSetup::ORIENTATION_DEFAULT);
  $objPHPExcel->getActiveSheet()->getPageSetup()->setPaperSize(PHPExcel_Worksheet_PageSetup::PAPERSIZE_A4);
  $objPHPExcel->getActiveSheet()->getPageSetup()->setPrintArea('A1:T58');
  
  //Cellに値をセット(エクセルの関数も使用できます
  $objPHPExcel->getActiveSheet()->setCellValue('A1', '15');
  $objPHPExcel->getActiveSheet()->setCellValue('A2', '58');
  $objPHPExcel->getActiveSheet()->setCellValue('A3', '=SUM(A1:A2)');
  $objPHPExcel->getActiveSheet()->setCellValue('B1', '日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語日本語');

  //Cellの行サイズをセット
  $objPHPExcel->getActiveSheet()->getRowDimension('1')->setRowHeight(40);
  $objPHPExcel->getActiveSheet()->getRowDimension('2')->setRowHeight(100);

  //Cellの列サイズをセット(列サイズの数値と同じ数値をセットしてもサイズが違うみたいです
  $objPHPExcel->getActiveSheet()->getColumnDimension('A')->setWidth(16);
  $objPHPExcel->getActiveSheet()->getColumnDimension('C')->setWidth(16);
  
  //B1のCellのスタイルをセットする
  // ・文字をセンタリングしてセル内で折り返す
  // ・セル内を塗りつぶす
  // ・上下左右を枠線で囲む
  $objPHPExcel->getActiveSheet()->getStyle('B1')->applyFromArray(
     array(
      'alignment' => array(
        'horizontal' => PHPExcel_Style_Alignment::HORIZONTAL_CENTER,
        'vertical' => PHPExcel_Style_Alignment::HORIZONTAL_CENTER,
        'wrap' => true,
     ),
      'borders' => array(
        'top'     => array('style' => PHPExcel_Style_Border::BORDER_THIN),
        'bottom'  => array('style' => PHPExcel_Style_Border::BORDER_THIN),
        'left'    => array('style' => PHPExcel_Style_Border::BORDER_THIN),
        'right'   => array('style' => PHPExcel_Style_Border::BORDER_THIN)
      ),
      'fill' => array(
        'type'       => PHPExcel_Style_Fill::FILL_SOLID,
        'rotation'   => 90,
        'startcolor' => array('argb' => 'CACC33FF'),
        'endcolor'   => array('argb' => 'FFFFFFFF')
      )
      )
  );
  
  //B1セルのスタイルをコピーしてB2〜B4まで適応させる
  $objPHPExcel->getActiveSheet()->duplicateStyle($objPHPExcel->getActiveSheet()->getStyle('B1'), 'B2:B4');
  
  //B2〜B4までをセルを結合する
  $objPHPExcel->getActiveSheet()->mergeCells('B2:B4');
  
  //シート内に画像を貼ることもできます(C2セルの位置から画像を貼る場合
  $objDrawing = new PHPExcel_Worksheet_Drawing();
  $objDrawing->setWorksheet($objPHPExcel->getActiveSheet());
  $objDrawing->setName('Paid');
  $objDrawing->setDescription('Paid');
  $objDrawing->setPath('/tmp/macos.gif');
  $objDrawing->setCoordinates('C2');
  $objDrawing->setOffsetX(20);
  $objDrawing->setOffsetY(10);
  $objDrawing->setRotation(25);

  //Office 2007またはExcel5形式で出力(CSVやHTMLやPDFなどでも出力可能
  //$output = new PHPExcel_Writer_Excel2007($objPHPExcel);
  $output = new PHPExcel_Writer_Excel5($objPHPExcel);
  $output->save('/tmp/test.xls');
  
  //保存したファイルを読み込みダウンロードさせるヘッダー情報を出力
  $data = fread(fopen('/tmp/test.xls', "r"), filesize('/tmp/test.xls'));
  $response = $this->getContext()->getResponse();
  $response->setHttpHeader('Pragma', '');
  $response->setHttpHeader('Cache-Control', '');
  $response->setHttpHeader('Content-Type', 'application/vnd.ms-excel');
  $response->setHttpHeader('Content-Disposition', 'attachment; filename="test.xls"');
  $response->setContent($data);

  //テンプレートを表示しないようにする
  return sfView::NONE;
  
}


実行すると、ダウンロード画面が表示されます。


ダウンロードしたファイルをExcelで開いて内容を確認します。


このように、symfonyでも簡単にExcelを作成することができます。


上記のようにExcelを簡単に作成できるのはとてもいいのですが、簡単なExcelでもスタイルや画像の張り込みなどを行うとスクリプトが長文になってしまい、スクリプトが汚くなってしまうことです。
privateメソッドなどを有効に活用してスタイルを別に定義しておくなど、工夫すると若干見やすくなります。

個人的にいただけないのが、一度ファイルに出力してからそのファイルを読み込むという二度手間みたいな処理をしないといけないのが気持ちが悪いです。

いいライブラリな分、不便な点も目立ちますね^^;
便利な所と不便な所を理解して、上手に活用していきたいものです。

このブログは、下記のサイトを参考にさせていただきました。
PHPExcel
 http://www.codeplex.com/PHPExcel
sfPhpExcelPlugin
 http://www.symfony-project.org/plugins/sfPhpExcelPlugin
symfony usersページ
 http://groups.google.com/group/symfony-users/browse_thread/thread/c7f28113284a491a

symfonyのjpMailPlugin(メール送信プラグイン)の使い方のまとめ

皆さん、こんにちは。
この季節になると鼻がムズムズする季節外れの花粉症持ちの笹亀です。

symfonyでメールを送信する場合、sfMailクラスが標準で実装されていますが、
それだけですと、日本語でのメール送信する場合は非常に苦労します。
今回は、いろいろ試行錯誤しながらたどり着いたjpMailPluginの使い方について
自分のメモの意味も含めて、まとめてみたいと思います。

まずは、プラグインのインストール方法です。(いまさら必要ないかもしれませんがw


symfony plugin-install http://develop.ddo.jp/symfony/jpMailPlugin-0.0.2.tgz


プラグインのインストールは、簡単ですねw

まずは基本的な使い方で、メールを送りたいアクションに日本語メールを送るような処理を加える方法です。


public function executeRegist() {
----
----その他の処理
----
//メールを送信する処理
  mb_internal_encoding("UTF-8"); # UTF-8の場合
  $mail = new jpMail();
  $mail->setFrom($from); # From:
  $mail->setSender($returnpath); # Return-Path:
  $mail->addReplyTo($replyto);  # Reply-to:
  $mail->setSubject('メール送信タイトルをセットする');
  $mail->setBody('メールの本文をここにセットします');
  $mail->addAddress($to); # To
  $mail->addAddress($to1); # Toは複数のアドレスをセットできます
  $mail->send();
----
----その他の処理
----

}

HTMLメールもこのプラグインで送信できます。
下記の例は、HTMLメールとテキストメールを同時に送信する方法です。


  mb_internal_encoding("UTF-8"); # UTF-8の場合
  $mail = new jpMail();
  $mail->setFrom($from); # From:
  $mail->setSender($returnpath); # Return-Path:
  $mail->addReplyTo($replyto);  # Reply-to:
  $mail->setSubject('メール送信タイトルをセットする');
  $mail->setContentType('text/html');
  $mail->setBody('<h3>メールの本文をここにセットします</h3>');
  $mail->setContentType('text/plain');
  $mail->setAltBody('メールの本文をここにセットします');
  $mail->addAddress($to); # To
  $mail->send();



覚えておくと便利なメソッドと便利な使い方のご紹介です。

個人的に頻繁に使ったメソッドです。


$mail->addCc($cc);

メールアドレスをセットすることで、CCとして送信できます



$mail->addBcc($bcc);

メールアドレスをセットすることで、BCCとして送信できます

次に、テンプレートを本文として使用する方法もあります。
テンプレートを使用してメール送信するということはメール送信をそれぞれのアクション内で定義するのではなく、ひとつのアクションにまとめることで実装後の管理をしやすくすることができます。こちらは、おやぢ組さんのページで詳しく解説されておりますので、こちらを参照ください。
上記サイトに記載されている「flash変数を使用した制御」なども大変便利な使い方です。

メールのテンプレートもsetTemplateでテンプレートを指定することで、ひとつの箇所にまとめておくと、テンプレートの修正も容易に行えます。
下記のように、会員登録後のメール送信やその他のメール送信のテンプレートをまとめることで一括管理することができます。


class memberActions extends sfActions {

  executeMemberRegist() {
  ----
  ----その他の処理
  ----
    //会員のオブジェクトをFlash変数にセットして、メール送信アクションを呼び出す
    $this->setFlash('member', $this->member);
    $raw_email = $this->getPresentationFor('mail', 'memberRegist', 'jpMail');
  ----
  ----その他の処理
  ----
  }

  executeMemberUnsub() {
  ----
  ----その他の処理
  ----
    //会員のオブジェクトをFlash変数にセットして、メール送信アクションを呼び出す
   $this->setFlash('member', $this->member);
    $raw_email = $this->getPresentationFor('mail', 'memberUnsub', 'jpMail');
  ----
  ----その他の処理
  ----
  }
}




class mailActions extends sfActions
{
 //会員登録の場合のメール処理
  public function executememberRegist()
  {
    //メンバーオブジェクトを取得する
    $member = $this->getFlash('member');
    $this->forward404Unless($member);

    $this->member = $member;

    mb_internal_encoding('UTF-8');

    $mail = new jpMail();
    $mail->setFrom($from);
    $mail->setSender($replyto);
    $mail->addReplyTo($returnpath);

    $mail->addAddress($member->getEmail());
    $mail->setSubject($member_title);

    $this->mail = $mail;

    $this->setFlash('member', null);
    //会員登録用のテンプレートを呼び出す
    $this->setTemplate(’’<プロジェクトルート>/libs/mailtemplates/memberRegistSuccess.class.php');
  }

 //会員退会の場合のメール処理
  public function executememberUnsub()
  {
    //メンバーオブジェクトを取得する
    $member = $this->getFlash('member');
    $this->forward404Unless($member);

    $this->member = $member;

    mb_internal_encoding('UTF-8');

    $mail = new jpMail();
    $mail->setFrom($from);
    $mail->setSender($replyto);
    $mail->addReplyTo($returnpath);

    $mail->addAddress($member->getEmail());
    $mail->setSubject($member_title);

    $this->mail = $mail;

    $this->setFlash('member', null);
    //退会用のテンプレートを呼び出す
    $this->setTemplate(’’<プロジェクトルート>/libs/mailtemplates/memberUnsubSuccess.class.php');
  }
}


このようにjpMailPluginは便利な使い方がたくさんあります。
どのようなときでもそうですが、メールを送る処理などは実装後のメンテナンスのことを考えると、このようにまとめられる所はまとめておくのがベストでしょう。どの言語でも必ず1度はハマってしまうのがメール関連の問題です。symfonyでのメール送信の情報としてお役にたてれば幸いです。

最後に「jpMailPlugin」を使用する際に注意があります。
私がご紹介したプラグインを使用しているときにですが、「$mail->setSubject()」で日本語で長いタイトルをセットしてメールを送信をすると、下記のようにタイトル内に「¥n」が付加されてしまう問題が起こりました。
「メール送信テストテスト\nテストテスト」

プラグインの中身を見てデバックをしてみましたが、mb_encode_mimeheader部分でバグが起きているみたいです。
52行目の変換で「\n」を「'」でくくっています。これが悪さをしているみたいです。


return mb_encode_mimeheader($value, $this->getCharset(), 'B', '\n');


このくくりを「"」のくくりに修正するとメールのタイトルのバグは解消できます。


return mb_encode_mimeheader($value, $this->getCharset(), 'B', "\n");


ご使用の際には上記についてお気をつけ下さい。

※現在の最新バージョン0.0.3では、上記のバグについてご対応いただけております。
brtriver様、誠にありがとうございました。