radiant-forum-extension 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (146) hide show
  1. data/README.md +155 -0
  2. data/Rakefile +139 -0
  3. data/VERSION +1 -0
  4. data/app/controllers/admin/forums_controller.rb +11 -0
  5. data/app/controllers/forums_controller.rb +34 -0
  6. data/app/controllers/posts_controller.rb +238 -0
  7. data/app/controllers/topics_controller.rb +78 -0
  8. data/app/models/forum.rb +52 -0
  9. data/app/models/moderatorship.rb +5 -0
  10. data/app/models/post.rb +122 -0
  11. data/app/models/post_attachment.rb +73 -0
  12. data/app/models/topic.rb +162 -0
  13. data/app/views/admin/forums/_form.html.haml +25 -0
  14. data/app/views/admin/forums/edit.html.haml +5 -0
  15. data/app/views/admin/forums/index.html.haml +76 -0
  16. data/app/views/admin/forums/moderate.html.haml +2 -0
  17. data/app/views/admin/forums/new.html.haml +5 -0
  18. data/app/views/admin/forums/remove.html.haml +22 -0
  19. data/app/views/admin/pages/_edit_commentability.html.haml +9 -0
  20. data/app/views/admin/reader_settings/_forum.html.haml +4 -0
  21. data/app/views/admin/sites/_choose_forum_layout.html.haml +7 -0
  22. data/app/views/forums/_forum.html.haml +22 -0
  23. data/app/views/forums/index.html.haml +44 -0
  24. data/app/views/forums/show.html.haml +38 -0
  25. data/app/views/forums/show.rss.builder +13 -0
  26. data/app/views/layouts/feed.rss.builder +5 -0
  27. data/app/views/monitorships/create.rjs.erb +1 -0
  28. data/app/views/monitorships/destroy.rjs.erb +1 -0
  29. data/app/views/monitorships/index.html.erb +35 -0
  30. data/app/views/pages/_comment.html.haml +10 -0
  31. data/app/views/pages/_comments.html.haml +15 -0
  32. data/app/views/posts/_attachment.html.haml +2 -0
  33. data/app/views/posts/_form.html.haml +6 -0
  34. data/app/views/posts/_latest.html.haml +17 -0
  35. data/app/views/posts/_post.html.haml +67 -0
  36. data/app/views/posts/_post.rss.builder +13 -0
  37. data/app/views/posts/_reply.html.haml +36 -0
  38. data/app/views/posts/_search.html.haml +28 -0
  39. data/app/views/posts/_uploader.html.haml +16 -0
  40. data/app/views/posts/edit.html.haml +20 -0
  41. data/app/views/posts/index.html.haml +33 -0
  42. data/app/views/posts/index.rss.builder +14 -0
  43. data/app/views/posts/new.html.haml +14 -0
  44. data/app/views/posts/preview.html.haml +32 -0
  45. data/app/views/posts/search.html.haml +54 -0
  46. data/app/views/posts/search.rss.builder +14 -0
  47. data/app/views/posts/show.html.haml +10 -0
  48. data/app/views/reader_notifier/post.rhtml +14 -0
  49. data/app/views/readers/_extra_controls.html.haml +5 -0
  50. data/app/views/readers/show_with_posts.html.haml +47 -0
  51. data/app/views/topics/_form.html.haml +26 -0
  52. data/app/views/topics/_help.html.haml +8 -0
  53. data/app/views/topics/_locked.html.haml +2 -0
  54. data/app/views/topics/_topic.html.haml +35 -0
  55. data/app/views/topics/_topic.rss.builder +9 -0
  56. data/app/views/topics/comments.html.haml +5 -0
  57. data/app/views/topics/edit.html.haml +26 -0
  58. data/app/views/topics/index.html.haml +41 -0
  59. data/app/views/topics/index.rss.builder +14 -0
  60. data/app/views/topics/new.html.haml +40 -0
  61. data/app/views/topics/show.html.haml +45 -0
  62. data/app/views/topics/show.rss.builder +14 -0
  63. data/config/routes.rb +16 -0
  64. data/db/migrate/001_create_forum_tables.rb +64 -0
  65. data/db/migrate/002_pages_commentable.rb +16 -0
  66. data/db/migrate/003_extend_sites.rb +13 -0
  67. data/db/migrate/004_sample_layout.rb +41 -0
  68. data/db/migrate/005_post_attachments.rb +19 -0
  69. data/db/migrate/006_user_relations.rb +20 -0
  70. data/db/migrate/007_reader_ownership.rb +35 -0
  71. data/db/migrate/008_first_posts.rb +13 -0
  72. data/db/migrate/20090824111005_import_helpers.rb +15 -0
  73. data/forum_extension.rb +44 -0
  74. data/lib/forum_admin_ui.rb +37 -0
  75. data/lib/forum_helper.rb +109 -0
  76. data/lib/forum_page.rb +45 -0
  77. data/lib/forum_reader.rb +9 -0
  78. data/lib/forum_reader_notifier.rb +9 -0
  79. data/lib/forum_readers_controller.rb +15 -0
  80. data/lib/forum_red_cloth3.rb +10 -0
  81. data/lib/forum_red_cloth4.rb +21 -0
  82. data/lib/forum_site.rb +12 -0
  83. data/lib/forum_tags.rb +285 -0
  84. data/lib/tasks/radiant_forum_extension_tasks.rake +137 -0
  85. data/pkg/radiant-forum-extension-0.5.0.gem +0 -0
  86. data/public/images/admin/forum.png +0 -0
  87. data/public/images/admin/new-forum.png +0 -0
  88. data/public/images/admin/nominus.png +0 -0
  89. data/public/images/admin/noremove.png +0 -0
  90. data/public/images/emoticons/angry.gif +0 -0
  91. data/public/images/emoticons/bigsmile.gif +0 -0
  92. data/public/images/emoticons/confused.gif +0 -0
  93. data/public/images/emoticons/cool.gif +0 -0
  94. data/public/images/emoticons/cry.gif +0 -0
  95. data/public/images/emoticons/devil.gif +0 -0
  96. data/public/images/emoticons/neutral.gif +0 -0
  97. data/public/images/emoticons/sad.gif +0 -0
  98. data/public/images/emoticons/shamed.gif +0 -0
  99. data/public/images/emoticons/shocked.gif +0 -0
  100. data/public/images/emoticons/smile.gif +0 -0
  101. data/public/images/emoticons/surprised.gif +0 -0
  102. data/public/images/emoticons/tongue.gif +0 -0
  103. data/public/images/emoticons/wink.gif +0 -0
  104. data/public/images/forum/attachment.png +0 -0
  105. data/public/images/forum/attachment_link.png +0 -0
  106. data/public/images/forum/attachment_over.png +0 -0
  107. data/public/images/forum/chk_off.png +0 -0
  108. data/public/images/forum/chk_on.png +0 -0
  109. data/public/images/forum/feed_14.png +0 -0
  110. data/public/images/forum/feed_28.png +0 -0
  111. data/public/images/forum/post_14.png +0 -0
  112. data/public/images/forum/post_14_over.png +0 -0
  113. data/public/images/forum/rdo_off.png +0 -0
  114. data/public/images/forum/rdo_on.png +0 -0
  115. data/public/images/forum/wait_16_grey.gif +0 -0
  116. data/public/javascripts/platform/forum.js +175 -0
  117. data/public/javascripts/platform/remotecontent.js +89 -0
  118. data/public/stylesheets/admin/forum.css +87 -0
  119. data/public/stylesheets/platform/forum.css +70 -0
  120. data/radiant-forum-extension.gemspec +215 -0
  121. data/spec/controllers/admin/forums_controller_spec.rb +54 -0
  122. data/spec/controllers/forums_controller_spec.rb +42 -0
  123. data/spec/controllers/posts_controller_spec.rb +310 -0
  124. data/spec/controllers/topics_controller_spec.rb +133 -0
  125. data/spec/datasets/forum_layouts_dataset.rb +15 -0
  126. data/spec/datasets/forum_pages_dataset.rb +11 -0
  127. data/spec/datasets/forum_readers_dataset.rb +55 -0
  128. data/spec/datasets/forum_sites_dataset.rb +10 -0
  129. data/spec/datasets/forums_dataset.rb +28 -0
  130. data/spec/datasets/posts_dataset.rb +31 -0
  131. data/spec/datasets/topics_dataset.rb +37 -0
  132. data/spec/lib/forum_admin_ui_spec.rb +35 -0
  133. data/spec/lib/forum_page_spec.rb +63 -0
  134. data/spec/lib/forum_reader_notification_spec.rb +9 -0
  135. data/spec/lib/forum_reader_spec.rb +13 -0
  136. data/spec/lib/forum_site_spec.rb +39 -0
  137. data/spec/models/forum_spec.rb +48 -0
  138. data/spec/models/post_spec.rb +91 -0
  139. data/spec/models/topic_spec.rb +105 -0
  140. data/spec/spec.opts +6 -0
  141. data/spec/spec_helper.rb +44 -0
  142. data/vendor/plugins/acts_as_list/README +23 -0
  143. data/vendor/plugins/acts_as_list/init.rb +3 -0
  144. data/vendor/plugins/acts_as_list/lib/active_record/acts/list.rb +256 -0
  145. data/vendor/plugins/acts_as_list/test/list_test.rb +332 -0
  146. 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
@@ -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
@@ -0,0 +1,5 @@
1
+ class Moderatorship < ActiveRecord::Base
2
+ belongs_to :forum
3
+ belongs_to :user
4
+ before_create { |r| count(:all, :conditions => ['forum_id = ? and user_id = ?', r.forum_id, r.user_id]).zero? }
5
+ end
@@ -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
@@ -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