radiant-forum-extension 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +155 -0
- data/Rakefile +139 -0
- data/VERSION +1 -0
- data/app/controllers/admin/forums_controller.rb +11 -0
- data/app/controllers/forums_controller.rb +34 -0
- data/app/controllers/posts_controller.rb +238 -0
- data/app/controllers/topics_controller.rb +78 -0
- data/app/models/forum.rb +52 -0
- data/app/models/moderatorship.rb +5 -0
- data/app/models/post.rb +122 -0
- data/app/models/post_attachment.rb +73 -0
- data/app/models/topic.rb +162 -0
- data/app/views/admin/forums/_form.html.haml +25 -0
- data/app/views/admin/forums/edit.html.haml +5 -0
- data/app/views/admin/forums/index.html.haml +76 -0
- data/app/views/admin/forums/moderate.html.haml +2 -0
- data/app/views/admin/forums/new.html.haml +5 -0
- data/app/views/admin/forums/remove.html.haml +22 -0
- data/app/views/admin/pages/_edit_commentability.html.haml +9 -0
- data/app/views/admin/reader_settings/_forum.html.haml +4 -0
- data/app/views/admin/sites/_choose_forum_layout.html.haml +7 -0
- data/app/views/forums/_forum.html.haml +22 -0
- data/app/views/forums/index.html.haml +44 -0
- data/app/views/forums/show.html.haml +38 -0
- data/app/views/forums/show.rss.builder +13 -0
- data/app/views/layouts/feed.rss.builder +5 -0
- data/app/views/monitorships/create.rjs.erb +1 -0
- data/app/views/monitorships/destroy.rjs.erb +1 -0
- data/app/views/monitorships/index.html.erb +35 -0
- data/app/views/pages/_comment.html.haml +10 -0
- data/app/views/pages/_comments.html.haml +15 -0
- data/app/views/posts/_attachment.html.haml +2 -0
- data/app/views/posts/_form.html.haml +6 -0
- data/app/views/posts/_latest.html.haml +17 -0
- data/app/views/posts/_post.html.haml +67 -0
- data/app/views/posts/_post.rss.builder +13 -0
- data/app/views/posts/_reply.html.haml +36 -0
- data/app/views/posts/_search.html.haml +28 -0
- data/app/views/posts/_uploader.html.haml +16 -0
- data/app/views/posts/edit.html.haml +20 -0
- data/app/views/posts/index.html.haml +33 -0
- data/app/views/posts/index.rss.builder +14 -0
- data/app/views/posts/new.html.haml +14 -0
- data/app/views/posts/preview.html.haml +32 -0
- data/app/views/posts/search.html.haml +54 -0
- data/app/views/posts/search.rss.builder +14 -0
- data/app/views/posts/show.html.haml +10 -0
- data/app/views/reader_notifier/post.rhtml +14 -0
- data/app/views/readers/_extra_controls.html.haml +5 -0
- data/app/views/readers/show_with_posts.html.haml +47 -0
- data/app/views/topics/_form.html.haml +26 -0
- data/app/views/topics/_help.html.haml +8 -0
- data/app/views/topics/_locked.html.haml +2 -0
- data/app/views/topics/_topic.html.haml +35 -0
- data/app/views/topics/_topic.rss.builder +9 -0
- data/app/views/topics/comments.html.haml +5 -0
- data/app/views/topics/edit.html.haml +26 -0
- data/app/views/topics/index.html.haml +41 -0
- data/app/views/topics/index.rss.builder +14 -0
- data/app/views/topics/new.html.haml +40 -0
- data/app/views/topics/show.html.haml +45 -0
- data/app/views/topics/show.rss.builder +14 -0
- data/config/routes.rb +16 -0
- data/db/migrate/001_create_forum_tables.rb +64 -0
- data/db/migrate/002_pages_commentable.rb +16 -0
- data/db/migrate/003_extend_sites.rb +13 -0
- data/db/migrate/004_sample_layout.rb +41 -0
- data/db/migrate/005_post_attachments.rb +19 -0
- data/db/migrate/006_user_relations.rb +20 -0
- data/db/migrate/007_reader_ownership.rb +35 -0
- data/db/migrate/008_first_posts.rb +13 -0
- data/db/migrate/20090824111005_import_helpers.rb +15 -0
- data/forum_extension.rb +44 -0
- data/lib/forum_admin_ui.rb +37 -0
- data/lib/forum_helper.rb +109 -0
- data/lib/forum_page.rb +45 -0
- data/lib/forum_reader.rb +9 -0
- data/lib/forum_reader_notifier.rb +9 -0
- data/lib/forum_readers_controller.rb +15 -0
- data/lib/forum_red_cloth3.rb +10 -0
- data/lib/forum_red_cloth4.rb +21 -0
- data/lib/forum_site.rb +12 -0
- data/lib/forum_tags.rb +285 -0
- data/lib/tasks/radiant_forum_extension_tasks.rake +137 -0
- data/pkg/radiant-forum-extension-0.5.0.gem +0 -0
- data/public/images/admin/forum.png +0 -0
- data/public/images/admin/new-forum.png +0 -0
- data/public/images/admin/nominus.png +0 -0
- data/public/images/admin/noremove.png +0 -0
- data/public/images/emoticons/angry.gif +0 -0
- data/public/images/emoticons/bigsmile.gif +0 -0
- data/public/images/emoticons/confused.gif +0 -0
- data/public/images/emoticons/cool.gif +0 -0
- data/public/images/emoticons/cry.gif +0 -0
- data/public/images/emoticons/devil.gif +0 -0
- data/public/images/emoticons/neutral.gif +0 -0
- data/public/images/emoticons/sad.gif +0 -0
- data/public/images/emoticons/shamed.gif +0 -0
- data/public/images/emoticons/shocked.gif +0 -0
- data/public/images/emoticons/smile.gif +0 -0
- data/public/images/emoticons/surprised.gif +0 -0
- data/public/images/emoticons/tongue.gif +0 -0
- data/public/images/emoticons/wink.gif +0 -0
- data/public/images/forum/attachment.png +0 -0
- data/public/images/forum/attachment_link.png +0 -0
- data/public/images/forum/attachment_over.png +0 -0
- data/public/images/forum/chk_off.png +0 -0
- data/public/images/forum/chk_on.png +0 -0
- data/public/images/forum/feed_14.png +0 -0
- data/public/images/forum/feed_28.png +0 -0
- data/public/images/forum/post_14.png +0 -0
- data/public/images/forum/post_14_over.png +0 -0
- data/public/images/forum/rdo_off.png +0 -0
- data/public/images/forum/rdo_on.png +0 -0
- data/public/images/forum/wait_16_grey.gif +0 -0
- data/public/javascripts/platform/forum.js +175 -0
- data/public/javascripts/platform/remotecontent.js +89 -0
- data/public/stylesheets/admin/forum.css +87 -0
- data/public/stylesheets/platform/forum.css +70 -0
- data/radiant-forum-extension.gemspec +215 -0
- data/spec/controllers/admin/forums_controller_spec.rb +54 -0
- data/spec/controllers/forums_controller_spec.rb +42 -0
- data/spec/controllers/posts_controller_spec.rb +310 -0
- data/spec/controllers/topics_controller_spec.rb +133 -0
- data/spec/datasets/forum_layouts_dataset.rb +15 -0
- data/spec/datasets/forum_pages_dataset.rb +11 -0
- data/spec/datasets/forum_readers_dataset.rb +55 -0
- data/spec/datasets/forum_sites_dataset.rb +10 -0
- data/spec/datasets/forums_dataset.rb +28 -0
- data/spec/datasets/posts_dataset.rb +31 -0
- data/spec/datasets/topics_dataset.rb +37 -0
- data/spec/lib/forum_admin_ui_spec.rb +35 -0
- data/spec/lib/forum_page_spec.rb +63 -0
- data/spec/lib/forum_reader_notification_spec.rb +9 -0
- data/spec/lib/forum_reader_spec.rb +13 -0
- data/spec/lib/forum_site_spec.rb +39 -0
- data/spec/models/forum_spec.rb +48 -0
- data/spec/models/post_spec.rb +91 -0
- data/spec/models/topic_spec.rb +105 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +44 -0
- data/vendor/plugins/acts_as_list/README +23 -0
- data/vendor/plugins/acts_as_list/init.rb +3 -0
- data/vendor/plugins/acts_as_list/lib/active_record/acts/list.rb +256 -0
- data/vendor/plugins/acts_as_list/test/list_test.rb +332 -0
- metadata +287 -0
@@ -0,0 +1,78 @@
|
|
1
|
+
class TopicsController < ReaderActionController
|
2
|
+
|
3
|
+
before_filter :find_forum_and_topic, :except => :index
|
4
|
+
before_filter :require_activated_reader, :except => [:index, :show]
|
5
|
+
|
6
|
+
radiant_layout { |controller| controller.layout_for(:forum) }
|
7
|
+
|
8
|
+
def index
|
9
|
+
params[:per_page] ||= 20
|
10
|
+
@topics = Topic.visible.paginate(:all, :order => "topics.sticky desc, topics.replied_at desc", :page => params[:page] || 1, :per_page => params[:per_page], :include => [:forum, :reader])
|
11
|
+
render_page_or_feed
|
12
|
+
end
|
13
|
+
|
14
|
+
def new
|
15
|
+
@topic = Topic.new
|
16
|
+
@topic.first_post = @topic.posts.build
|
17
|
+
@topic.forum = @forum || Forum.find_by_name(Radiant::Config['forum.default_forum'])
|
18
|
+
end
|
19
|
+
|
20
|
+
def show
|
21
|
+
if @page && request.format == 'text/html'
|
22
|
+
redirect_to "#{@page.url}#forum"
|
23
|
+
return false
|
24
|
+
end
|
25
|
+
@topic.hit! unless current_reader and @topic.reader == current_reader
|
26
|
+
store_location((request.format == 'text/html') ? request.request_uri : request.referer)
|
27
|
+
|
28
|
+
params[:per_page] ||= 20
|
29
|
+
params[:page] = 1 if params[:page] == 'first'
|
30
|
+
params[:page] = (@topic.posts.count.to_f / params[:per_page].to_f).ceil if params[:page] == 'last'
|
31
|
+
@posts = Post.paginate_by_topic_id(@topic.id, :page => params[:page], :per_page => params[:per_page], :include => :reader, :order => 'posts.created_at asc')
|
32
|
+
render_page_or_feed(@page ? 'comments' : 'show')
|
33
|
+
end
|
34
|
+
|
35
|
+
def create
|
36
|
+
# post creation is handled by a before_create in the topic model
|
37
|
+
# and then calls back to set initial reply data in the topic
|
38
|
+
@forum = Forum.find(params[:topic][:forum_id]) if params[:topic][:forum_id]
|
39
|
+
@topic = @forum.topics.create!(params[:topic])
|
40
|
+
@topic.first_post.save_attachments(params[:files]) unless @topic.page && !Radiant::Config['forum.comments_have_attachments']
|
41
|
+
|
42
|
+
respond_to do |format|
|
43
|
+
format.html { redirect_to forum_topic_path(@forum, @topic) }
|
44
|
+
end
|
45
|
+
rescue ActiveRecord::RecordInvalid => invalid
|
46
|
+
flash[:error] = "Sorry: #{invalid}. Please check the form"
|
47
|
+
respond_to do |format|
|
48
|
+
format.html { render :action => 'new' }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def update
|
53
|
+
# post update is handled by a before_update in the topic model
|
54
|
+
@topic.attributes = params[:topic]
|
55
|
+
@topic.save!
|
56
|
+
respond_to do |format|
|
57
|
+
format.html { redirect_to forum_topic_path(@forum, @topic) }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def destroy
|
62
|
+
@topic.destroy
|
63
|
+
flash[:notice] = "Topic '{name}' was deleted."[:topic_deleted_message, CGI::escapeHTML(@topic.name)]
|
64
|
+
respond_to do |format|
|
65
|
+
format.html { redirect_to forum_path(@forum) }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
protected
|
70
|
+
|
71
|
+
def find_forum_and_topic
|
72
|
+
@topic = Topic.find(params[:id]) if params[:id]
|
73
|
+
@page = @topic.page if @topic
|
74
|
+
@forum = @topic.forum if @topic
|
75
|
+
@forum ||= Forum.find(params[:forum_id]) if params[:forum_id]
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
data/app/models/forum.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
class Forum < ActiveRecord::Base
|
2
|
+
is_site_scoped if defined? ActiveRecord::SiteNotFound
|
3
|
+
default_scope :order => 'name ASC'
|
4
|
+
belongs_to :created_by, :class_name => 'User'
|
5
|
+
belongs_to :updated_by, :class_name => 'User'
|
6
|
+
validates_presence_of :name
|
7
|
+
named_scope :visible, {}
|
8
|
+
|
9
|
+
has_many :topics, :order => 'sticky desc, replied_at desc', :dependent => :destroy do
|
10
|
+
def first
|
11
|
+
@first_topic ||= find(:first)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
has_many :recent_topics, :class_name => 'Topic', :order => 'replied_at desc' do
|
16
|
+
def first
|
17
|
+
@first_recent_topic ||= find(:first)
|
18
|
+
end
|
19
|
+
def latest
|
20
|
+
@latest_topics ||= find(:all, :limit => 5)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
has_many :posts, :order => 'posts.created_at desc' do
|
25
|
+
def last
|
26
|
+
@last_post ||= find(:first, :include => :reader)
|
27
|
+
end
|
28
|
+
def latest
|
29
|
+
@latest_posts ||= find(:all, :limit => 5)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.find_or_create_comments_forum
|
34
|
+
@comments_forum = self.find_by_for_comments(true) || self.create(
|
35
|
+
:name => 'Page Comments',
|
36
|
+
:description => 'This forum automatically gathers up all comments on pages. You can reply here or on the page.',
|
37
|
+
:created_by => User.find_by_admin(true),
|
38
|
+
:position => 999,
|
39
|
+
:created_at => Time.now,
|
40
|
+
:for_comments => true
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
def dom_id
|
45
|
+
"forum_#{self.id}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def visible_to?(reader=nil)
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
data/app/models/post.rb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'sanitize'
|
2
|
+
class Post < ActiveRecord::Base
|
3
|
+
|
4
|
+
is_site_scoped if defined? ActiveRecord::SiteNotFound
|
5
|
+
|
6
|
+
belongs_to :forum, :counter_cache => true
|
7
|
+
belongs_to :reader, :counter_cache => true
|
8
|
+
belongs_to :topic, :counter_cache => true
|
9
|
+
belongs_to :created_by, :class_name => 'User'
|
10
|
+
belongs_to :updated_by, :class_name => 'User'
|
11
|
+
has_many :attachments, :class_name => 'PostAttachment', :order => :position, :dependent => :destroy
|
12
|
+
|
13
|
+
attr_writer :name
|
14
|
+
|
15
|
+
before_validation :set_reader
|
16
|
+
before_create :set_forum
|
17
|
+
after_create :update_topic_reply_data
|
18
|
+
after_destroy :revert_topic_reply_data
|
19
|
+
|
20
|
+
validates_presence_of :reader, :topic, :body
|
21
|
+
|
22
|
+
named_scope :visible, {}
|
23
|
+
named_scope :latest, lambda { |count|
|
24
|
+
{
|
25
|
+
:order => 'created_at DESC',
|
26
|
+
:limit => count
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
def topic_page
|
31
|
+
self.topic.page_for(self)
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_xml(options = {})
|
35
|
+
options[:except] ||= []
|
36
|
+
options[:except] << :topic_title << :forum_name
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
def dom_id
|
41
|
+
"post_#{self.id}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def first?
|
45
|
+
self.topic.first_post == self
|
46
|
+
end
|
47
|
+
|
48
|
+
def has_replies?
|
49
|
+
self.topic.last_post != self
|
50
|
+
end
|
51
|
+
|
52
|
+
def is_comment?
|
53
|
+
!self.topic.page.nil?
|
54
|
+
end
|
55
|
+
|
56
|
+
def page
|
57
|
+
return self.topic.page if is_comment?
|
58
|
+
end
|
59
|
+
|
60
|
+
def editable_interval
|
61
|
+
Radiant::Config['forum.editable_period'].to_i.minutes if Radiant::Config['forum.editable_period']
|
62
|
+
end
|
63
|
+
|
64
|
+
def still_editable_for
|
65
|
+
if editable_interval && still_editable?
|
66
|
+
self.created_at + editable_interval - Time.now
|
67
|
+
else
|
68
|
+
0
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def still_editable?
|
73
|
+
!editable_interval || Time.now - self.created_at < editable_interval
|
74
|
+
end
|
75
|
+
|
76
|
+
def editable_by?(reader=nil)
|
77
|
+
return false unless reader
|
78
|
+
still_editable? && reader && (reader.id == reader_id)
|
79
|
+
end
|
80
|
+
|
81
|
+
def visible_to?(reader=nil)
|
82
|
+
topic.forum.visible_to?(reader)
|
83
|
+
end
|
84
|
+
|
85
|
+
# we shouldn't have formatting in here, but page comments need to be rendered from a radius tag
|
86
|
+
|
87
|
+
def body_html
|
88
|
+
if body
|
89
|
+
html = RedCloth.new(body, [ :hard_breaks, :filter_html ]).to_html(:textile, :smilies)
|
90
|
+
Sanitize.clean(html, Sanitize::Config::RELAXED)
|
91
|
+
else
|
92
|
+
""
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def date_html
|
97
|
+
self.created_at.to_s
|
98
|
+
end
|
99
|
+
|
100
|
+
def save_attachments(files=nil)
|
101
|
+
files.collect {|file| self.attachments.create(:file => file) unless file.blank? } if files
|
102
|
+
end
|
103
|
+
|
104
|
+
protected
|
105
|
+
|
106
|
+
def set_reader
|
107
|
+
self.reader ||= Reader.current
|
108
|
+
end
|
109
|
+
|
110
|
+
def set_forum
|
111
|
+
self.forum ||= self.topic.forum
|
112
|
+
end
|
113
|
+
|
114
|
+
def update_topic_reply_data
|
115
|
+
self.topic.refresh_reply_data(self)
|
116
|
+
end
|
117
|
+
|
118
|
+
def revert_topic_reply_data
|
119
|
+
self.topic.refresh_reply_data
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
class PostAttachment < ActiveRecord::Base
|
2
|
+
|
3
|
+
@@image_content_types = ['image/jpeg', 'image/pjpeg', 'image/gif', 'image/png', 'image/x-png', 'image/jpg']
|
4
|
+
cattr_reader :image_content_types
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def image?(content_type)
|
8
|
+
image_content_types.include?(content_type)
|
9
|
+
end
|
10
|
+
|
11
|
+
def thumbnail_sizes
|
12
|
+
if Radiant::Config.table_exists? && Radiant::Config["assets.additional_thumbnails"]
|
13
|
+
thumbnails = Radiant::Config["assets.additional_thumbnails"].split(', ').collect{|s| s.split('=')}.inject({}) {|ha, (k, v)| ha[k.to_sym] = v; ha}
|
14
|
+
else
|
15
|
+
thumbnails = {}
|
16
|
+
end
|
17
|
+
thumbnails.merge({
|
18
|
+
:icon => ['24x24#', :png],
|
19
|
+
:thumbnail => ['100x100>', :png]
|
20
|
+
})
|
21
|
+
end
|
22
|
+
|
23
|
+
def thumbnail_names
|
24
|
+
thumbnail_sizes.keys
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
belongs_to :post
|
29
|
+
belongs_to :reader
|
30
|
+
acts_as_list :scope => :post_id
|
31
|
+
has_attached_file :file,
|
32
|
+
:styles => thumbnail_sizes,
|
33
|
+
:whiny_thumbnails => false,
|
34
|
+
:url => "/:class/:id/:basename:no_original_style.:extension",
|
35
|
+
:path => ":rails_root/public/:class/:id/:basename:no_original_style.:extension"
|
36
|
+
|
37
|
+
attr_protected :file_file_name, :file_content_type, :file_file_size
|
38
|
+
validates_attachment_presence :file, :message => "You must choose a file to upload!"
|
39
|
+
validates_attachment_content_type :file, :content_type => Radiant::Config["forum.attachment_content_types"].split(', ') if Radiant::Config.table_exists? && Radiant::Config["forum.attachment_content_types"]
|
40
|
+
validates_attachment_size :file, :less_than => Radiant::Config["forum.max_attachment_size"].to_i.megabytes if Radiant::Config.table_exists? && Radiant::Config["forum.max_attachment_size"]
|
41
|
+
|
42
|
+
def image?
|
43
|
+
self.class.image?(file_content_type)
|
44
|
+
end
|
45
|
+
|
46
|
+
def filename
|
47
|
+
file_file_name
|
48
|
+
end
|
49
|
+
|
50
|
+
def basename
|
51
|
+
File.basename(filename, ".*") if filename
|
52
|
+
end
|
53
|
+
|
54
|
+
def extension
|
55
|
+
filename.split('.').last.downcase if filename
|
56
|
+
end
|
57
|
+
|
58
|
+
def icon
|
59
|
+
iconpath = Radiant::Config.table_exists? && Radiant::Config['forum.icon_path'] ? Radiant::Config['forum.icon_path'] : '/images/forum/icons'
|
60
|
+
if image?
|
61
|
+
return file.url(:icon)
|
62
|
+
else
|
63
|
+
icon = File.join(RAILS_ROOT, 'public', iconpath, "#{extension}.png")
|
64
|
+
logger.warn "!! looking for #{icon}"
|
65
|
+
if File.exists? icon
|
66
|
+
"#{iconpath}/#{extension}.png"
|
67
|
+
else
|
68
|
+
"#{iconpath}/attachment.png"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
data/app/models/topic.rb
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
class Topic < ActiveRecord::Base
|
2
|
+
is_site_scoped if defined? ActiveRecord::SiteNotFound
|
3
|
+
|
4
|
+
belongs_to :forum, :counter_cache => true
|
5
|
+
belongs_to :page
|
6
|
+
belongs_to :reader
|
7
|
+
belongs_to :created_by, :class_name => 'User'
|
8
|
+
belongs_to :updated_by, :class_name => 'User'
|
9
|
+
|
10
|
+
belongs_to :first_post, :class_name => 'Post', :include => :reader # aka topic.body. should not change
|
11
|
+
belongs_to :last_post, :class_name => 'Post', :include => :reader # this is just for display efficiency.
|
12
|
+
belongs_to :replied_by, :class_name => 'Reader' # this too.
|
13
|
+
has_many :posts, :order => 'posts.created_at', :include => :reader, :dependent => :destroy do
|
14
|
+
def last
|
15
|
+
@last_post ||= find(:last)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
validates_presence_of :forum, :reader, :name
|
20
|
+
validates_uniqueness_of :old_id, :allow_nil => true
|
21
|
+
|
22
|
+
before_validation :set_reader
|
23
|
+
before_create :set_defaults
|
24
|
+
before_update :check_for_changing_forums
|
25
|
+
before_validation_on_create :post_valid?
|
26
|
+
after_create :save_post
|
27
|
+
before_update :update_post
|
28
|
+
|
29
|
+
attr_accessor :body
|
30
|
+
|
31
|
+
named_scope :visible, {}
|
32
|
+
named_scope :latest, lambda { |count|
|
33
|
+
{
|
34
|
+
:order => 'replied_at DESC',
|
35
|
+
:limit => count
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
def voice_count
|
40
|
+
posts.count(:select => "DISTINCT reader_id")
|
41
|
+
end
|
42
|
+
|
43
|
+
def voices
|
44
|
+
# TODO - move into sql
|
45
|
+
posts.map { |p| p.reader }.uniq
|
46
|
+
end
|
47
|
+
|
48
|
+
def hit!
|
49
|
+
self.class.increment_counter :hits, id
|
50
|
+
end
|
51
|
+
|
52
|
+
def sticky?() sticky == 1 end
|
53
|
+
|
54
|
+
def views() hits end
|
55
|
+
|
56
|
+
def paged?() posts_count > posts_per_page end
|
57
|
+
|
58
|
+
def posts_per_page
|
59
|
+
ppp = Radiant::Config['forum.posts_per_page'] || 25
|
60
|
+
ppp.to_i.to_f
|
61
|
+
end
|
62
|
+
|
63
|
+
def last_page
|
64
|
+
(posts_count.to_f/posts_per_page).ceil.to_i
|
65
|
+
end
|
66
|
+
|
67
|
+
def page_for(post)
|
68
|
+
return nil unless post.topic == self
|
69
|
+
return 1 unless paged?
|
70
|
+
(posts.index(post)/posts_per_page).ceil.to_i
|
71
|
+
end
|
72
|
+
|
73
|
+
def editable_by?(user)
|
74
|
+
reader && (reader.id == reader_id)
|
75
|
+
end
|
76
|
+
|
77
|
+
def has_posts?
|
78
|
+
posts_count > (page ? 0 : 1)
|
79
|
+
end
|
80
|
+
|
81
|
+
def refresh_reply_data(post=nil)
|
82
|
+
if !post && posts.empty? # ie. the post has just been deleted and there are no others
|
83
|
+
self.destroy
|
84
|
+
elsif has_posts?
|
85
|
+
post ||= posts.last
|
86
|
+
self.last_post = post
|
87
|
+
self.replied_by = post.reader
|
88
|
+
self.replied_at = post.created_at
|
89
|
+
self.save!
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def last_post_with_fetch
|
94
|
+
if lp = last_post_without_fetch
|
95
|
+
lp
|
96
|
+
elsif has_posts? && lp = posts.last
|
97
|
+
update_attribute(:last_post_id, lp.id)
|
98
|
+
lp
|
99
|
+
end
|
100
|
+
end
|
101
|
+
alias_method_chain :last_post, :fetch
|
102
|
+
|
103
|
+
def dom_id
|
104
|
+
"topic_#{self.id}"
|
105
|
+
end
|
106
|
+
|
107
|
+
def visible_to?(reader=nil)
|
108
|
+
forum.visible_to?(reader)
|
109
|
+
end
|
110
|
+
|
111
|
+
protected
|
112
|
+
|
113
|
+
def set_reader
|
114
|
+
self.reader ||= Reader.current
|
115
|
+
end
|
116
|
+
|
117
|
+
def set_defaults
|
118
|
+
self.sticky ||= 0
|
119
|
+
self.locked ||= 0
|
120
|
+
end
|
121
|
+
|
122
|
+
def check_for_changing_forums
|
123
|
+
old = Topic.find(id)
|
124
|
+
if old.forum_id != forum_id
|
125
|
+
set_posts_forum_id
|
126
|
+
Forum.update_all ["posts_count = posts_count - ?", posts_count], ["id = ?", old.forum_id]
|
127
|
+
Forum.update_all ["posts_count = posts_count + ?", posts_count], ["id = ?", forum_id]
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def set_posts_forum_id
|
132
|
+
Post.update_all ['forum_id = ?', forum_id], ['topic_id = ?', id]
|
133
|
+
end
|
134
|
+
|
135
|
+
def post_valid?
|
136
|
+
post = Post.new(:body => self.body, :reader => self.reader, :topic => self)
|
137
|
+
unless post.valid?
|
138
|
+
self.errors.add(:body, post.errors.on(:body))
|
139
|
+
self.errors.add(:reader, post.errors.on(:reader))
|
140
|
+
raise ActiveRecord::RecordInvalid.new(self)
|
141
|
+
end
|
142
|
+
true
|
143
|
+
end
|
144
|
+
|
145
|
+
def save_post
|
146
|
+
self.first_post = self.posts.create!(:body => self.body, :created_at => self.created_at, :reader => self.reader)
|
147
|
+
self.replied_at ||= Time.now();
|
148
|
+
self.save(false)
|
149
|
+
end
|
150
|
+
|
151
|
+
def update_post
|
152
|
+
post = self.first_post
|
153
|
+
if !self.body.nil? && self.body != post.body
|
154
|
+
post.body = self.body
|
155
|
+
post.save!
|
156
|
+
end
|
157
|
+
rescue ActiveRecord::RecordInvalid => ow
|
158
|
+
self.errors.add(:body, post.errors.on(:body))
|
159
|
+
raise
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|