英语原文:http://wiki.rubyonrails.com/rails/pages/PhpBB+Integration
虽然 rForum 是个有前途的项目,但是至今还没有一个使用Ruby写的尽善尽美的论坛程序。所以,对于很多需要论坛模块的Rails项目来说,phpBb 多半是首选。本文讲述如何在Rails程序中整合phpBB2,以在你的网站中实现单一用户登录。实际的实现其实很简单。
首先,我假设你的phpBB2和你的Rails程序运行在同一个数据库上。并且你采用”phpbb_”数据表名前缀。
在phpBB数据表结构中有两个最需要注意的表:@phpbb_users@ 和 phpbb_sessions
首先,创建一个用户的数据模型
rails script/generate model User
然后,在 app/model/user.rb 中加入以下语句
class User < ActiveRecord::Base
set_primary_key "user_id"
set_table_name "phpbb_users"
def is_online?
tmp = User.find_by_sql( "SELECT session_user_id, session_logged_in FROM phpbb_sessions WHERE session_user_id=#{self.user_id}" )[0]
if self.user_allow_viewonline == 1 && self.user_id != -1 && !tmp.nil?
return true
else
return false
end
end
end
这样一来,我们告诉 Active Record? 我们的数据模式是在 phpbb_users 表基础上,并且使用 user_id 作为关键字段。 我们定义了方法 is_online? 是一个用于验证用户是否已经登录的数据库查询。(只有当用户登录,并且不隐身的时候,才返回 true。 在phpBB中 id == -1 意味是一个匿名用户)。
现在, phpBB 在你登录以后,在你的电脑上保存 cookie。所以可以使用 Rails 提供的 “cookies” 哈希来读取 phpBB的cookie。
这样的话,我们就可以识别当前登录的用户了
在 @app/controllers/application.rb@,写入:
class ApplicationController < ActionController::Base
def get_user_by_sid( )
sid = cookies["phpbb2mysql_sid"]
return nil if sid.nil?
User.find_by_sql( [ "SELECT phpbb_users.* FROM phpbb_sessions,phpbb_users WHERE session_id = ? AND phpbb_sessions.session_user_id = phpbb_users.user_id", sid ] )[0]
end
end
这个方法通过读取 phpbb 的 session 数据 而返回一个 User 对象。 如果这个用户退出登录 phpBB,那么他也退出 Rails 程序。
从而在所有控制器和页面视图中,当你需要检查一个用户是否登录的时候,只需要执行 get_user_by_sid(), 如果返回结果非空,那么就是这个用户的数据 User 对象。
有了这些以后,你就可以在你的程序中创建任何同 phpbb 用户之间的关联。这个用户只需要登录一次,然后你就可以访问他们所有的phpbb中的属性(email, AIM, 头像 等等),和其他所有你的 Rails User 对象指定的数据。
如果你希望用户登录 phpBb以后不是直接进入论坛,而是去你指定的页面,那么你可以以下的 php 脚本
<table border=0>
<tr><td colspan=2 align=left>
<B>Please Login</b>
</td>
</tr>
<tr>
<td>
Username:<form action="/forums/login.php" method="post" name="login_form" target=_top>
</td>
<td valign=top>
<input type="text" name="username">
</td>
</tr>
<tr>
<td>Password:</td>
<td>
<input type="password" name="password">
</td>
<tr>
<td></td>
<td align=center>
<input type=hidden name="redirect" value=".."><input type="submit" value="Login" name="login"></form>
</td>
</tr>
<tr>
<td></td>
<td align=center>
<a href="http://www.yourdomain.com/forums/profile.php?mode=register" target=_top><span style="font-size: 0.8em">Register</span></a>
</td>
</tr>
</table>
注意,以上脚本中的
<input type=hidden name="redirect" value="..">
中的值是你希望用户登录后访问的路径 (是相对你的论坛的路径)。
如果你希望让用户直接在你的程序中退出登录,那么你可以使用以下代码
<form action="/forums/login.php?logout=true" method="post" name="login_form" target=_top> <input type=hidden name="redirect" value="..">(<a href="#" onClick="javascript:document.login_form.submit()">logout</a> </form>
以下代码是我如何让用户在rails程序中向论坛发帖子。
首先,添加一些新数据模型:
# app/model/phpbb_forum.rb
class PhpbbForum < ActiveRecord::Base
set_primary_key :forum_id
end
# app/model/phpbb_post.rb
class PhpbbPost< ActiveRecord::Base
set_primary_key :post_id
end
# app/model/phpbb_posts_text.rb
class PhpbbPostsText < ActiveRecord::Base
set_primary_key :posts_text_id
set_table_name :phpbb_posts_text
end
# app/model/phpbb_topic.rb
class PhpbbTopic < ActiveRecord::Base
set_primary_key :topic_id
belongs_to :user, :foreign_key => 'topic_poster'
end
然后在你的程序中加入以下代码(我放在 app/controllers/application.rb 从而所有的控制器都可以使用)
def post_topic( forum_id, user_id, subject="test subject", content="test content" )
forum = PhpbbForum.find( forum_id )
return if( forum.nil? || user_id.nil? || forum_id.nil? || subject.nil? || content.nil? )
return unless validate_admin_with_hidden_action()
# Apply post
# Update forums table with new posts count, new topic count, and forum_last_post_id
forum.forum_posts += 1
forum.forum_topics += 1
# Update user post count
user.user_posts += 1
user.save
# Update posts with new topic_id,
topic = PhpbbTopic.create
post = PhpbbPost.create
post.topic_id = topic.id
post.forum_id = forum.id
post.poster_id = user.id
post.post_time = Time.now.to_f.to_i
post.poster_id = user_id
post.save
# Create new topic
topic.forum_id = forum.id
topic.topic_title = subject
topic.topic_poster = user_id
topic.topic_time = Time.now.to_f.to_i
topic.topic_first_post_id = post.id
topic.topic_last_post_id = post.id
topic.save
forum.forum_last_post_id = post.id
forum.save
# Create new posts text
post_text = PhpbbPostsText.new
post_text.post_id = post.id
post_text.bbcode_uid = "3edc9030fc"
post_text.post_subject = subject
post_text.post_text = content
post_text.save
return topic.id
end
end
以上代码没有被测试过,仅用来表达实现的想法。它们应该被更好地封装到数据模型中去,并且被完整测试。
还有一些可能有用的东西
首先,在数据模型中:
class PhpbbForum < ActiveRecord::Base
set_primary_key :forum_id
has_many :phpbb_topics, :foreign_key => 'forum_id'
end
class PhpbbPost< ActiveRecord::Base
set_primary_key :post_id
belongs_to :phpbb_topic, :foreign_key => 'topic_id'
belongs_to :phpbb_forum, :foreign_key => 'forum_id'
belongs_to :user, :foreign_key => 'poster_id'
has_one :phpbb_posts_text, :foreign_key => 'post_id'
def text
self.phpbb_posts_text.text
end
end
class PhpbbPostsText < ActiveRecord::Base
set_primary_key :post_id
set_table_name :phpbb_posts_text
belongs_to :phpbb_posts, :foreign_key=>'post_id'
def text
self[:post_text]
end
end
class PhpbbSession < ActiveRecord::Base
set_primary_key :session_id
belongs_to :user, :foreign_key => 'session_user_id'
end
class PhpbbTopic < ActiveRecord::Base
set_primary_key :topic_id
belongs_to :user, :foreign_key => 'topic_poster'
has_many :phpbb_posts
def last_post
PhpbbPost.find( self.topic_last_post_id )
end
end
然后有一个类似以下的用户数据模型
class User < ActiveRecord::Base
set_primary_key "user_id"
set_table_name :phpbb_users
has_many :phpbb_posts, :foreign_key => 'poster_id', :order=>'post_time DESC'
has_one :phpbb_session, :foreign_key=>'session_user_id'
def is_online?
tmp = User.find_by_sql( "SELECT session_user_id, session_logged_in FROM phpbb_sessions WHERE session_user_id=#{self.user_id}" )[0]
if self.user_allow_viewonline == 1 && self.user_id != -1 && !tmp.nil?
return true
else
return false
end
end
def sid
return self.phpbb_session.nil? ? -1 : self.phpbb_session.session_id
end
# Grab latest post for this user
def latest_post
self.phpbb_posts.first
end
end
现在你很方便的做:
Post.find( 1 ).text Post.find( 1 ).userPost.find( 1 ).topicPost.find( 1 ).forumUser.find( 2 ).last_post.text