CakePHPのACLを使って権限を管理していると、権限がないアクションへのリンクを隠したくなることがあるので私はこんな感じにしてます。
※注: ユーザーロールによるACLを対象に書いてますので個々のユーザーによるACLだとそのままでは動きません

HtmlHelper::link()を叩く度にACLで権限チェックをしていると大変よろしくないので、まずは以下のようなComponentのinit()を叩いてログインユーザーの権限情報を整理してセッションに格納します。

class PermissionComponent extends Object {
  public $components = array('Session', 'Auth', 'Acl');
  private $C = null;

  public function startup(&$controller) {
    $this->C = &$controller;
  }

  public function init() {
    $this->clear();

    $aro = $this->Acl->Aro->find('first', array(
      'conditions' => array(
        'Aro.model' => 'Role',
        'Aro.foreign_key' => $this->Auth->user('role_id'),
      ),
    ));

    $permissions = $this->Acl->Aro->Permission->find('all', array(
      'conditions' => array(
        'Permission.aro_id' => $aro['Aro']['id'],
      ),
    ));

    foreach ($permissions as $p) {
      if ($p['Permission']['_create'] == 1 && $p['Permission']['_read'] == 1 &&
        $p['Permission']['_update'] == 1 && $p['Permission']['_delete'] == 1) {
        $allow = true;
      } else {
        $allow = false;
      }

      if (!empty($p['Aco']['alias']) && $p['Aco']['alias'] == 'controllers') {
        $this->Session->write('Auth.Permissions.controllers', $allow);
      } elseif(!empty($p['Aco']['parent_id'])) {
        $parent = $this->Acl->Aco->findById($p['Aco']['parent_id']);
        $key = 'Auth.Permissions.' . $parent['Aco']['alias'] . '.' . $p['Aco']['alias'];
        $this->Session->write($key, $allow);
      }
    }
  }

  public function clear() {
    $this->Session->delete('Auth.Permissions');
  }
}

このPermissionComponent::init()は、ユーザーがログインした時点で実行するようにしています。AuthComponentのデフォルト設定であれば以下のようになるはずです。

class UsersController extends AppController {
  public $components = array('Auth', 'Permission');

  public function login() {
    $user = $this->Auth->user();
    if ($user) {
      $this->Permission->init();
      $this->redirect($this->Auth->loginRedirect);
    }
  }

  public function logout() {
    $this->redirect($this->Auth->logout());
  }
}

次に、セッションに格納した権限情報をViewからチェックする為のHelperを作ります。
このPermissionHelper::check()は、ログインユーザーが引数に取ったURLへアクセスする権限を持つ場合にtrueを返します。

class PermissionHelper extends AppHelper {
  public $helpers = array('Session');

  public function check($path){
    if(!is_array($path)) {
      $path = Router::parse($path);
    }
    if(empty($path['controller'])) {
      $path['controller'] = $this->params['controller'];
    }
    if(empty($path['action'])) {
      $path['action'] = 'index';
    }

    if(!empty($path['prefix']) && $path['prefix'] == 'admin') {
      return true;
    }

    $allkey = 'Auth.Permissions.controllers';
    if($this->Session->check($allkey) && $this->Session->read($allkey) === true){
      return true;
    }

    $key = 'Auth.Permissions.' . Inflector::camelize($path['controller']) . '.' . $path['action'];
    if($this->Session->check($key) && $this->Session->read($key) === true){
      return true;
    }

    return false;
  }
}

しかし、これでいちいちチェックしてからHtmlHelper::link()も呼ぶのは非常にめんどくさいので、このPermissionHelperを内部で使うようにHtmlHelperを継承したMyHtmlHelperを作ります。

class MyHtmlHelper extends HtmlHelper {
  public $helpers = array('Permission');

  public function link($title, $url = null, $options = array(), $confirmMessage = false) {
    $options = array_merge(array(
      'escape' => false,
    ), $options);
    $url = ($url !== null) ? $url : $title;

    if($this->Permission->check($url)) {
      return parent::link($title, $url, $options, $confirmMessage);;
    }
    return null;
  }
}

このMyHtmlHelper::link()はリンクを出力する前にPermissionHelperを用いて権限チェックを行い、アクセス権限がある場合のみリンクを出力します。

使い方は通常のHtmlHelperと何ら変わりありません。
しかし、わざわざViewで$this->MyHtml->link();などと打つのは大変煩わしいので、@hiromi2424のHackPluginを貰ってきてAppControllerで以下のようにするとviewを変更する必要がなくなるのでとても幸せになれます。

class AppController extends Controller {
  public $helpers = array('Hack.alias' => array('Html' => 'MyHtml'));
}

全然関係ない宣伝ですが、今年もPHPMatsuriの準備が本格化してきてます。
PHP Matsuri 2011
今年はPHP5.4, Symfony2, CakePHP2.0, CodeIgniter2.0とPHP界隈が盛り上がってますし、PHP以外にもTitanium mobileやTDD関連のセッション、ワークショップも開催予定です。
協賛企業様も絶賛募集中ですのでぜひ一度お気軽にご連絡ください。

Cake2.0ももうすぐRCですし私もアクティビティAGEAGEで参ります。

 

開発サーバー再構築したらruby+mongoidなアプリが軒並み動かなくなったので。
Ubuntu Server 10.04 LTSにaptから入れるmongodbは1.2.2なんだけど、今rubygemsで最新のmongoid2.0.2はmongodb1.6.0以降にしか対応してないので動くわけがない。
mongodbにはDebian/Ubuntu用の公式aptリポジトリがあるのでそれを使うと簡単です。

Ubuntu and Debian packages – MongoDB

手順は以下の通り。
もともと入ってるmongodbはアンインストールしておきましょう。

$ sudo su -
# apt-key adv --keyserver keyserver.ubuntu.com --recv 7F0CEB10
# echo "deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen" > /etc/apt/source-list.d/10gen.list
# aptitude update
# aptitude install mongodb-10gen

最後にインストールできたか確認する。
2011年7月27日現在だと以下のようになるはず。

$ mongod --version
db version v1.8.2, pdfile version 4.5

Rails3.1RC4でassetsがRoutingError(404 Not Found)になったら

 Rails  コメントは受け付けていません。
7月 262011
 

Rails3.1が楽しすぎて調子にノッてたらちょいハマリしたので。
Rails3.1RC4とSprockets2.0.0.beta.11以降の相性が悪いらしく、これらのバージョンを使うとassets/以下のcssやjsが全てRoutingErrorで404 Not Foundになってしまいます。
sprocketsのissueには上がっていないので不確実ですが、bundle update等してしまうと発症する模様です。
幸いSprockets2.0.0.beta.10以前なら問題なく動きますのでGemfileを以下のようにすれば解決します。

# RAILS_ROOT/Gemfile
gem 'sprockets', '= 2.0.0.beta.10'

Rails3.1楽しすぎるのでみんなやるといいと思う。

 

エンジニアのみなさんはもうLionにアップグレードしましたよね。
私も発売日の夜にアップグレードしました。

OSXのターミナルアプリであるiTerm2も、Lion発売日の翌日にバージョンが上がり、Lionの新しいフルスクリーンモードに対応しました。
このフルスクリーンモードは、iTerm2の場合左右に余分なスペースができてしまったり、デュアルディスプレイを生かせなかったりと非常に不快です。

30秒ほど途方に暮れてたんですが、普通にPreferences > General > Window > Use Lion-style Fullscreen windowsのチェックを外したら直るので困ってる人は試してみるとよいと思う。

 

こんな記事があったのでついカッとなって書いた。今は反省している。

Mac OS X Lion:これは僕らが期待した未来じゃない : ギズモード・ジャパン

この筆者が期待した未来はもう1世代先っぽい。
もちろん私もこの筆者と同じ未来を期待してるけど、これは”MacOS”なので。

「これはiOSではない、次世代OSでもない、だがMacOS至上最強」

ジョブズがこの新OSに”MacOS Lion”と名付けたのは、そんなメッセージを込めたんじゃないかと妄想してる。私がMacOSとして至上最強であると思うのは、以下に挙げるような機能が追加されたことによる。

フルスクリーンアプリケーション

そのまんま。フルスクリーン非対応なアプリもフルスクリーン化してくれるなら非常に嬉しい。

再開

アプリケーションを閉じても、次に起動したときに前回の状態のまま開くらしい。
クライアントが送りつけてきたExcel方眼紙とか見るときにすごく助かりそう。

オートセーブ

ファイルを自動保存する機能らしい。
timemachineだけでもだいぶ助かってるんだから更に助かると思う。

バージョン

ファイルの変更履歴を世代毎に管理できるらしい。
プログラマにはおなじみの言葉だけど、ExcelやPowerpointなんかのバイナリファイルでも内容の履歴を比較できたりするなら泣くほど嬉しいかも。

AirDrop

近くにいる他のMacにドラッグ&ドロップでファイルを共有できる機能らしい。
今だと隣の人にもわざわざメールやskypeで送ってるわけで、まぁ便利になりますよきっと。

ほら、地味ではあるが確実に「痒いところに手が届く」機能が実装されている。これだけの機能を3000円以下で、Mac App Storeからオンラインでインストール出来るなら確実に買いだと思う。

今後の展開

・UIについて
アプリを切り替える度にジェスチャーによる動作が変わるなんてUIは大変ストレスフルなのでそこが一番の課題。これについては正式版で改善されてるかもしれないけどあまり使わないのでどっちでもいい。

・iOSとの統合について
次期OSでiOSとの統合を実現すると思う。

・MacOSについて
Lionが最後な気がするけど特に根拠は無い。
きっとiOSとの統合を果たした次期OSには「MacOS」という名前は付かないんじゃないかな。
百獣の王”Lion”が最後だなんてかっこいいじゃまいか。

 

AuthComponent関係ないけど。
CakePHPだとcore.phpの内容に応じてsessionの設定などをいろいろやってるので、
php.iniなんかでcookieの有効期限を0にするだけだとうまく動かない。

独自の設定を追加するには、core.phpに書いあるとおり以下の方法を使うといいらしい。

To define a custom session handler, save it at /app/config/<name>.php.
Set the value of ‘Session.save’ to to utilize it in CakePHP.

app/config/[Session.saveの値].phpを作ったらいいよだって。

というわけで実際にやってみる。
my_sessionの部分は適宜置き換えてください。

app/config/core.php line 127:

// ここを
Configure::write('Session.save', 'php');
// こう
Configure::write('Session.save', 'my_session');
?>

app/config/my_session.php

if (empty($_SESSION)) {
  session_set_cookie_params(0);
}

ちなみに、この my_session.php には他にもini_setなどでphpのセッション設定などいろいろできる。

© 2011 sanojimaru.com Suffusion theme by Sayontan Sinha