Categories
open all | close allTags
CSRF | Aptana | rake | カテゴリ | テスト | Migration | フォーム | モデル | 抽象化 | OpenID | nextlink | デュアル・コア | アクセス制御 | タグ | パソコン | 認証 | 名称 | スキンエンジン | RESTful | プラグインSearch
力づくで解決
あまり,ここでばかり時間を費やしたくないので,コメントフォームを作ったときと同様,CSRF対策用のタグを手動で埋め込むことでとりあえず先に進むことにしました。ただ,さすがにフォームを作ったり,保存したりといったところは楽です。Railsのリソース・ルーティングにしたがってやっておくだけでほとんどはRailsが面倒見てくれる感じ。慣れたら,管理画面作るのはすいすいできそうです。というわけで,すごくいい加減(例えばデフォルトカテゴリはカテゴリIDを直接入れないといけない)ですが,ブログ設定をFoodynの管理画面で編集できるようになりました。
余談ですが「企業で使えるオープンソースCMS一挙12種類解説」という記事,なかなか参考になります。ステージ管理とか時限発行とか,考えていなかったなあと。時限発行は未来の日付で投稿された記事に対してpingなどを送る機能と同じような形で実現できそうな気がします。有効期限が切れた記事をドラフト扱いにするのか,遠い未来の投稿扱いにするのか,別のフラグを立てるのかといったところがちょっと悩ましいですが。
ステージ管理はどうやったらできるだろう。
またもやInvalidAuthenticcityTokenでエラー
とりあえずブログ設定のフォームなど,作ってみたりしているのですが,保存しようとするとInvalidAuthenticityTokenエラーが出てしまいます。普通にフォーム作っているだけなんだけどなあ。なぜだろう。
というわけでまたも停滞中。ちょっといらいら。
状況変わらず
Simple Localiaztionのバージョンを上げてみたのですが,状況は変わりません。動いていた部分も動かないのでちょっと困惑。バージョンが上がって大分変わっているみたいだし。とりあえずフォーラムに質問を投げてみたけどどうなることか。ここを放っておいて先に進むのは気が進まないのだけど,あまりにここで停滞しているのも何なので,そろそろ先に進むことを考えます。
言語ファイル
最近,どうも夜集中力が続かないのですが,このところ何をしているのかというと,管理画面用の言語ファイルを設定しようとして,意外に苦戦しています。設定はあっているようなのに,どうして動かないんだろう。プラグインのサイトのドキュメントを見ようとしたらApacheのエラーで落ちていたりとか。ちょっといらいら。初めての管理画面(笑)
とりあえず前言撤回で,RESTfulで用意されているもので大体なんとかできそうな感じがしてきました。deleteのユーザー・インタフェースなど,気になるところもありますが,まずは標準機能で実装していきたいと思います。で,一応今の管理画面。(管理画面)
URLは全部ではないですが,ちゃんとしたものを生成しています。
ここではメモとして,map.namespace付きのprefix付き,ネスティッド・リソースという“テンコ盛り”状態でのルーティングについて書いておきます。
map.namespace(:admin) do |admin|
admin.root :controller =>'admin'
admin.connect ':memberid', :controller =>'Admin'
admin.resources :blogs, :path_prefix=>'admin/:memberid' do |blog|
blog.resources :items
blog.resources :comments
blog.resources :categories
end
admin.resources :plugins, :path_prefix=>'memberid'
endというのがルーティング部分(まだ完全ではありません。必要なとこしか作ってないので)。ここでルートのコントローラを指定することで,アドミン用のコントローラはadminディレクトリの下から選ばれるようになります。アドミンへのアクセスは全部認証付きであり,認証されたユーザー名がmemberidになります。したがって'admin/:memberid'が管理画面のルートです。
その下のresourcesの部分が今日の骨子。ブログ周りのリソース定義をしています。RESTfulなルーティングを使うためにはresourcesが必要です。ここでちょっと注意が要るのがpath_prefixのところ,ここは本来':memberid'だけでいいはずなのですが,それだとURLを生成したときにadminが抜けてしまうバグ?があります。そこでわざわざこれを入れています。
以下でネストされたリソースを加えています。
URLを生成するときはリソースを指定する必要があるのですが,この場合,例えばアイテムの一覧を表示したかったら「admin_blog_items_url(@memberid, blog)」といったリソースをurl_forやlink_toなどのメソッドに渡します。ここで@memberidがユーザー名ですが,これをパラメータに入れるのも一つの肝。プレフィクスを指定しているからなのでしょうけれども,そのあたりの説明はドキュメントになく,試行錯誤で見つけました。なお,admin_blog_items_urlというメソッド名は「rake routes」でルート定義の一覧を出すと分かります。例えば今の部分は
admin_blog_items GET /admin/:memberid/blogs/:blog_id/items {:controller=>"admin/items", :action=>"index"}
といった形で記述されています。これで,今度はAdmin::ItemsControllerのindexメソッドを用意すればいいことが分かります。
これで後はスピードアップするかな?
RESTfulって
基本的にはWebサービスを想定しているから,クライアント機能はサービス内に用意されていないんですねえ,考えてみれば当たり前ですが。管理画面を作るときは結局すべて何かしら画面を通してから処理することになるので,その分のアクションを追加してあげないといけません。結局,map.resourcesなどを使っても楽になっているのかいないのかちょっと微妙な気も…
ルーティングの設定を楽にしたりカスタマイズしやすくする方法については,後でもう一回考えた方がいいかなと思っています。
補遺:前半で書いたことは半ば撤回しました。
権限を調べる部分の実装
まず,Memberのオブジェクトからはhas_rightというメソッドで権限を持っているかどうかを調べます。
# context is either a Blog, or an Item
def has_right rightname, context
#search role first.
context.has_right rightname, self
endrightnameが権限の名前,例えば"browse"などになります。contextはコメントにもあるように,どのコンテキストで権限を判断するかと言うこと。ブログあるいはアイテムがcontextです。で,これは実際にはコンテキストに問い合わせて調べます。つまり,BlogクラスとItemクラスにはどちらもhas_rightというメソッドがあってそれが呼び出されます。これが昨日書いたダック・タイピング,すなわち同じメソッドを持っていたら同じものとして使える,ということになります。
Blogクラスのこの部分の実装はこう。
def role_of member
if !member
return self.guest_role
else
t = Team.find(:first, :conditions=>["tblog=? and tmember=?", self.id, member.id])
if t then
return t.role
else
return self.user_role
end
end
end
def has_right rightname, member
role = self.role_of member
return role.attributes[rightname]
end
下のhas_rightがメインで,ロールに対してフィールド名で取得するようにしています。実はこれだと関係ないフィールドまで取れてしまいますが,そこはガチガチにしなくてもいいだろうと判断しています。上のrole_ofでは,ログインしていない場合(memberがnil),ログインしているけどチームに入っていない場合,チームに入っている場合に分けてRoleを返します。前二つの場合はBlogのテーブル内のフィールド情報から取り出します。guest_roleやuser_roleはクラスの上の方でbelongs_toとして定義しています。
一方Itemクラスは
def role_of member
role = self.blog.role_of member
itemrole = ItemRole.find(:first, :conditions=>["item_id=? and role_id=?",self.id, role.id])
if itemrole then
return itemrole
else
return role
end
end
def has_right rightname, member
role = self.role_of member
return role.attributes[rightname]
end
has_rightの部分は実は同じですが,ここでもダック・タイピングもどきのことをしています。アイテムで独自の権限を決める場合はその情報はItemRoleというクラスに入っていますが,その場合もRoleクラスと同じようにattributes[rightname]で結果を取り出しています。role_ofの方は先ほどのBlogのrole_ofでRoleを取り出し,ItemRoleが存在すればそっち,しなければ取ってきたRoleを返します。
ダック・タイピング
アイテムでもブログでも区別しないで権限を調べられるように,両者に同じメソッドを作ってアクセスするようにしています。これもダック・タイピングと呼んでいいのかな?先日デザインパターンの本を読んだのですが,なかなかおもしろかった半面,記述がJavaだったため,Rubyだとどうなるのだろうと気になるところもありました。Javaではインタフェースを中心にデザインパターンを構築することが多いようですが,Rubyだとインタフェースという機能はありません。その代わりにmoduleを使ってミックスインしたり,今回のように特に宣言なく同じAPIを用意して呼び出すといったことができます。こういった特質により,デザインパターンの実装も変わってくると思います。
Web上にはまつもとさんが書かれたものも含め,いくつかRubyによるデザインパターンの実装がありますが,できたら体系だって本として読みたいところ。
アクセス制御補遺
ちょっとめんどくさいのがゲストの扱いです。まず,ログイン処理するところで,ゲストの場合,ゲストというユーザーにするか,それともユーザーなしにするかという選択肢があります。また,各ブログでゲストに与える権限(Role)を決められるようにしないといけません。考え方としてはゲストというユーザーが必ずTeamに入っていて,そこで権限を設定するというのが一つのやり方。もう一つはblogのテーブルにゲスト・ロールのIDを入れるフィールドを追加する方法。
前者の方が形としてはきれいで,クエリーも1種類で済みそうなのがメリット。ただし,Teamにゲストが入るので,そこだけ特別扱いしないと何か変なことになってしまう恐れがないとも言えません。また,必ずゲストがTeamに入るよう,制御するのもちょっと面倒かも。後者はそういったルールを作らなくて済むのがメリットですが,クエリーはちょっと面倒になるかも。これだとユーザーなしのままで進められというメリットもあります。
ログイン・メンバーだけど,ブログのチームに入っていない場合にどういう扱いにするかという問題もあります。全員チームに入れた上で権限を決めるというやり方はありますが,人数が多くなるとチームのテーブルがn×nのオーダーで膨れ上がります。この場合もブログにログインしているけどチームでない場合の権限へのIDを入れておくのがやりやすそう。
当初はここでシステムロールから権限を決めるつもりだったのですが,そうするとブログごとに設定が変えられない,あるいはブログごとにちがうシステムロールを作らないといけいないことになるので,これまたちょっと面倒なことになります。
パターンが多くてこんがらがりそう。
管理画面設計中
とはいっても,まだやっているのはフリーの管理画面デザインをRailsのヴューに当てはめているだけ。中身はまだ空です。それとは関係ないけど一つメモ。Foodyn CMSは複数ユーザーで安全に運用できることを目指していますが,スキンの扱いで一つ考えなければいけないところがありました。例えば,あるユーザーがスキンを選んでカスタマイズする場合,それは他のユーザーには波及しないようにしないといけません。つまり,ブログごとにスキンの複製を作ってからカスタマイズすることになります。また,こういったスキンはブログを消去した際に一緒に削除しないといけません。
Foodynでは,スキン・エンジンごとにduplicateを使うかどうかを設定できるようにしようと思っています。まだその辺の処理は組み込んでいませんが,一応覚書として。