RoundCubeのlabelの一部を上書きする

RoundCubeの各項目の名称を、書き換えるような事が何度かありました。
(受信箱や送信済みの名称について、古いメーラと合わせたいとか)
そういう時は、各言語のlabels.incやmessages.incを編集する必要が有りましたが、
1.0以降は、上書きする機能を提供しているようです。

この投稿の続きを読む »

Facebooktwitterlinkedintumblrmail

baserCMSを3.0.2から3.0.7へ。ところでカテゴリ名ってまだ簡単に取れないんです?

muraveです。どもども。

時間ができたので自社サイトのbaserCMSを3.0.2から3.0.7へアップデートしました。今朝。

残念ながら順調にはいかなかったので自分のためにもメモを残しておこうと思います。
ところでカテゴリ名ってまだ簡単に取れないんです?(前振り)

■ アップデート処理を実行出来なかった件
スマートURLをオンにした状態ではアップデート処理を実行できませんでした

ギニャーーーー!

WordPress(ココ)と同居していることもあって.htaccessにいろいろと設定を追加しているせいかPOST先のページにアクセスできなかった模様。
スマートURLについて手作業でオフ、.htaccessを調整して実行しました。

/app/config/install.php の次の行をコメントアウト。

//Configure::write('App.baseUrl', '');

/app/webroot/.htaccess の一部をコメントアウト

RewriteEngine on
RewriteBase /app/webroot
#RewriteCond %{REQUEST_FILENAME} !-d
#RewriteCond %{REQUEST_FILENAME} !-f
#RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]

もちろん実行後、手作業で元に戻しました。

■ レイアウトの中でカテゴリ名を取得していたページが全滅
どうかんがえてもイリーガルなことをやっていたので文句はいえないのですが、参りました。

イリーガルなことってのはこういうこと。

//カテゴリーのタイトルを取得
$product_record = $this->BcBaser->PageCategory->find('first',array(
      'fields' => array('title'),
      'conditions' => array('name' => $product_category),
      'recursive' => -1,
));
$product_title = $product_record['PageCategory']['title'];

3.0.7のコードを調べた所 $this->BcBaser を $this->Page に変えたら行けそうな気がしたのですが、気のせいだったのでさらにイリーガル感が増してしまいました。

//カテゴリーのタイトルを取得
$page_category = new PageCategory(false, null, 'baser');
$product_record = $page_category->find('first',array(
      'fields' => array('title'),
      'conditions' => array('name' => $product_category),
      'recursive' => -1,
));
$product_title = $product_record['PageCategory']['title'];

本当はこんなことしたくないんです! カテゴリ名って簡単に取れるようになってるんでしょうか?

Facebooktwitterlinkedintumblrmail

CI ツール勉強会@福岡に参加した

お初にお目にかかります、3 月から入社した shun と申します。
未熟者なりに記事を書いていきますので、多少なりとも役に立つことがあればと思います。

では本題、今回参加した CI ツール勉強会@福岡についてです。

この投稿の続きを読む »

Facebooktwitterlinkedintumblrmail

Git リモートリポジトリをガツンと巻き戻す

かなり悩んで時間がかかったのでメモを残しておきます。
前提条件として、基本、Git Flowベースで作業しています。

不幸なことにガンガン開発を進めていたdevelopブランチをmasterブランチの状態まで巻き戻すことに。masterブランチにはhotfixが入ってたりもします。

調べて調べて、悩んで、相談して、調べて、以下のような手順で作業することにしました。

hotfix前のdevelopまで戻す。

$ git reset --hard 巻き戻すコミットのハッシュ

リモートのdevelopリポジトリを同じ所まで巻き戻す。

$ git push -f origin HEAD:develop

そして、hotfixについてはmasterから対象のコミットをチェリーピックでマージしてリモートリポジトリにpush(Souce Treeでやりました)。

そして、開発者諸氏にdevelopブランチをリモートから取り直すように連絡(忘れずに!)

加えて、このプロジェクトでは開発用サーバーにdevelopリポジトリから

$ git pull -rebase

でデプロイしているので、開発用サーバーのdevelopリポジトリもローカルと同じコマンドでリセットしてデプロイしなおしました。

Facebooktwitterlinkedintumblrmail

iOSのクラッシュログにシンボル情報を追加する

Appleの審査でリジェクトされた際に、「.clash」や「.ips」といった形でクラッシュログが添付されていることがあります。
中身を確認すると以下のような内容になっていますが、このままだとコードのどの部分でクラッシュしたのかが分かりません。

〜一部抜粋〜
Thread 5 Crashed:
1  CoreFoundation                   0x24936b62 0x24831000 + 135152
2  Foundation                       0x255e49f5 0x25575000 + 21531
3  SampleApp                        0x000341d2 0xb000 + 55521
4  libdispatch.dylib                0x32a107b6 0x32a0f000 + 1235

そこで、「symbolicatecrash」というツールを利用してログにシンボル情報を追加することで、クラッシュ箇所を特定出来るようにします。

必要なファイル

  • クラッシュログファイル
  • appファイル
  • dSYMファイル

appファイル及びdSYMファイルはクラッシュログを出力したものと同じものを使う必要があります。
私は以下の方法で取得しました。

  1. XcodeのOrganizerを開く
  2. 該当ビルド上で右クリックし、「Show In Finder」を選択
  3. Finderで選択されているファイル上で右クリックし、「パッケージの内容を表示」を選択
  4. 「dSYMs」ディレクトリ内に「.app.dSYM」が「Products/Applications」内に「.app」があります

symbolicatecrashの実行

実行環境
・OS X 10.9.5
・Xcode6.1

「symbolicatecrash」は私の環境では以下のパスにありました。

/Applications/Xcode.app/Contents/SharedFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/symbolicatecrash

長過ぎるのでシンボリックリンクやエイリアスを作成しておくと便利です。

あとは下記コマンドを実行する事で、クラッシュログにシンボル情報を追加したログファイルを作成できます。

    # このコマンドを実行しないと「Error: "DEVELOPER_DIR" is not defined at symbolicatecrash line 60.」といったエラーメッセージが表示されます。
    export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"

    symbolicatecrash クラッシュログファイル dSYMファイル appファイル > crash.log

作成されたcrash.logを確認してみるとシンボル情報が追加され、 SampleController.m の121行目辺りが怪しいことが分かるようになりました。

〜一部抜粋〜
Thread 5 Crashed:
1  CoreFoundation                   0x24936b62 +[NSException raise:format:] + 108
2  Foundation                       0x255e49f5 +[NSJSONSerialization JSONObjectWithData:options:error:] + 62
3  SampleApp                        0x000341d2 __35-[SampleController startSample:] (SampleController.m:121)
4  libdispatch.dylib                0x32a107b6 _dispatch_call_block_and_release + 8
Facebooktwitterlinkedintumblrmail

IonicでCrosswalk

IonicがCrosswalkに対応しました。
Crosswalk comes to Ionic

Crosswalkを組み込んでbuildを行うと、アプリ内にレンダリングエンジンを持つことができます。
通常、WebViewをレンダリングするのに、端末のデフォルトのレンダリングエンジンが使われます。しかし、OSのバージョンごとにレンダリングエンジンの振る舞いが微妙に違います。また、古いOSだと最新のものと比べてレンダリング速度が遅いです。
Crosswalkは、高速なレンダリングエンジンBlinkを搭載しており、それらの問題点を改善できます。
ただし、Crosswalkが対応しているのはandroid4.x以降だけです。iOSおよび古いandroidは未対応です。

既存のandroidアプリにCrosswalkを組み込んでみました。

IonicにCrosswalkを組み込むのは、すごく簡単です。
Ionicのプロジェクトディレクトリ直下で以下を実行するのみです。

ionic browser add crosswalk

ただこれだと、古いバージョンのCrosswalkが組み込まれてしまったので、バージョンを指定して以下のように実行しました。

ionic browser add crosswalk@10.39.235.15

注意すべきなのは、これを実行するとplatformsディレクトリ以下を大きく書き換えてしまいます。あらかじめ、プロジェクトディレクトリのバックアップを取っておくとか、バージョン管理で元に戻せるようにしておく必要があります。

で、build&installします。

ionic run android

作成されたapkファイルを見ると、Crosswalk非搭載の時はapkファイルが8MBだったのに、40MBもありました。

BUILD_MULTIPLE_APKS=true ionic build android

とやると、x86とARMそれぞれのapkが生成され、サイズは25MB程度でした。

アプリを実行します。実行端末はXPERIA Z1(android4.4.2)です。
読み込みに時間がかかるので、起動が若干遅くなっています。

他、気づいた点

  • 今まで<input type=date>にあった以下の不具合がなくなった

    • 値をセットしているにもかかわらず、datepickerのデフォルト値が今日の日付
    • datepickerの枠外をクリックし表示を消した後、再度datepickerを表示できない
  • スプラッシュスクリーンが出なくなった
  • 今までファイルのアップロードができていたのに出来なくなった(何でだろう)

結構、変わっています。
速度的には、どうだろう・・・。少なくとも劇的には速くなってないですね。

Facebooktwitterlinkedintumblrmail

Socket.IO:マルチプロセスのnode.jsで、Roomに入室したクライアント一覧を取得する

チャット等で、Roomに誰が入室しているのかを知る際の方法です。
Roomに入室したクライアント(socket id)一覧の取得は、socket.io 1.0以降では以下で取得できます。

io.nsps[yourNamespace].adapter.rooms[roomName]

ただしnode.jsをマルチプロセスで実行している場合、自プロセス以外のクライアント一覧は取得できないです。
そのためRedisのKVSを使い、以下のようにしました。

1.Roomに入室:
 RedisのKVSに、各プロセス毎に、各ルームに入室したクライアント(socket id)一覧をセット

redisClient = redis.createClient(redisPort, redisIp, {})
process_name = localIp + 'p' + port # プロセス名:プロセスのIPとポート番号を結合した文字列

#
# socketをroomにjoinし、且つRedisにsetする
#
join = (socket, room)->;
  socket.join room    # socketをroomにjoin
  key = room + ':' + process_name

  value = []
  if io.of('/').adapter.rooms[room]?
    for socketId of io.of('/').adapter.rooms[room]
      value.push socketId
  redisClient.set key, JSON.stringify(value) # JSONの文字列として値をセット
  
 
roomName = 'works#' + work_id
join socket, roomName

下記のような感じでRedisにセットされます。

redis 127.0.0.1:6379[1]> keys *
1) "users#558:10.0.2.15p1338"
2) "works#1016:10.0.2.15p1337"
3) "works#1016:10.0.2.15p1338"
4) "users#10:10.0.2.15p1337"
redis 127.0.0.1:6379[1]> get works#1016:10.0.2.15p1337
"[\"1ZFRYWVeyrMPaiqOAAAD\"]"
redis 127.0.0.1:6379[1]> get works#1016:10.0.2.15p1338
"[\"vuturxDpPBL2ZHuLAAAK\"]"

2.クライアント一覧を取得:
 RedisのKVSからワイルドカードを使ったkeysコマンドで該当するキーをすべて取得後、mgetコマンドで値を取得

#
# roomIDからsocket.idのリストを取得し、コールバック関数実行
#
clients = (room, cb, opts) ->
  key = room + ':*'
  redisClient.keys key, (err, replies)->
    redisClient.mget replies, (err, vals)->
      socketIds = []
      for val in vals
        socketIds = socketIds.concat JSON.parse val
      cb(socketIds, opts)


roomName = 'works#' + work_id
clients roomName, (socketIds)->
  console.log 'Roomに入室しているクライアント一覧:' + socketIds.join(',')

3.Roomから退室:
 RedisのKVSの値を変更または削除

#
# socketをroomからleaveし、且つredisにsetする
#
leave = (socket, room)->
  socket.leave room
  key = room + ':' + process_name

  value = []
  if io.of('/').adapter.rooms[room]?
    for socketId of io.of('/').adapter.rooms[room]
      value.push socketId
  if value.length > 0
    redisClient.set key, JSON.stringify(value)
  else
    redisClient.del key

roomName = 'works#' + work_id
leave socket, roomName

一応これで実現可能ですが、クライアント一覧取得するためだけにredisのKVSを使うというのは、無駄にリソースを使っている感じであまりいい方法ではないように思います。

また、以下のような感じでプロセス終了時にredisのデータを全て削除する必要があります。

for signal in ['SIGINT', 'SIGHUP', 'SIGTERM']
  process.on signal, ->
    key = '*:' + process_name
    redisClient.keys key, (err, replies)->
      async.each replies, (reply)->
        redisClient.del reply
      process.exit 1
      return

もっといい方法があれば、誰が教えて頂けたらと思います。

Facebooktwitterlinkedintumblrmail

RoundCube1.0で追加されていたCanned Responses

定型文的な使い方を想定していると思われる、Canned Responsesと言う機能が1.0辺りで追加されたようです。メール作成画面の署名の隣に回答が見えると思います。

canned_response01

※言語を英語に設定しているとResponsesと表示されます。

この投稿の続きを読む »

Facebooktwitterlinkedintumblrmail

Android-ProgressDialogFragmentというライブラリを作りました。

1.Android-ProgressDialogFragmentというライブラリを作りました。
https://github.com/YuichiUchida/Android-ProgressDialogFragment
 ロード中はこういう感じになります。
1

2.今まではこういう感じに作っていました。
2
ListFragmentなどがこういう実装になっていますね。

この方式は下の画像のように2つの画面を重ね
ロード中にはコンテンツ部分を消し、プログレス画面を表示し、
ロードが終るとコンテンツを表示し、プログレス画面を消すという方式になっています。
3

長所としては、できあがった奇麗な画面だけを表示できることですが
たくさんの画面に使うと動作が重く感じるようになってきます。

このライブラリは、やり方はほとんど同じですが
コンテンツ部分を常に表示し、プログレス画面だけを制御します。

3.使い方
Gradleに下記のように追記します。

support-v4ライブラリを使う場合

dependencies {
    compile 'me.yuichi0301:pdfragment:1.1.+'
}

ライブラリを使用しない場合

dependencies {
    compile 'me.yuichi0301:pdfragment-native:1.1.+'
}

あとはProgressDialogFragment継承して使用します。

public class SampleFragment extends ProgressDialogFragment {
    // your code of fragment
}

ActionBarSherlockを使う場合

dependencies {
    compile 'com.android.support:support-v4:21.0.3'
    compile('me.yuichi0301:sherlockpdfragment:1.1.+') {
        exclude module: 'support-v4'
    }
}
public class SampleFragment extends SherlockProgressDialogFragment {
    // your code of fragment
}
Facebooktwitterlinkedintumblrmail

Mac基本のキ:アプリを32ビットモードで起動する

仕事始めの朝に食らいまいした。あけましておめでとうございます。

今回のように64ビットアプリでトラブルにあったときに32ビットモードで起動すると回避出来たりすることがありますので覚えておくといいことがあるかもしれません。

そして忘れてずっと32ビットモードで暮らしてしまうかもしれません。使えないよりはいいですけどね。

設定方法はつぶやいてるとおりですが、わかりやすいようにキャプチャとってみました。

contextMenu

右クリックして「情報を見る」

Thunderbird_app_の情報

そして「32ビットモードで開く」にチェック。

Facebooktwitterlinkedintumblrmail