simple_forum 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,69 @@
1
+ module SimpleForum
2
+ class Forum < ::ActiveRecord::Base
3
+
4
+ set_table_name 'simple_forum_forums' #should work table_name_prefix in SimpleForum module but it's not!'
5
+
6
+ #acts_as_nested_set
7
+
8
+ has_many :topics,
9
+ :order => "#{SimpleForum::Topic.quoted_table_name}.last_updated_at DESC",
10
+ :dependent => :destroy,
11
+ :class_name => "SimpleForum::Topic"
12
+
13
+ belongs_to :recent_topic,
14
+ :class_name => 'SimpleForum::Topic'
15
+
16
+ has_many :posts,
17
+ :order => "#{SimpleForum::Post.quoted_table_name}.created_at DESC",
18
+ :class_name => 'SimpleForum::Post'
19
+
20
+ belongs_to :recent_post,
21
+ :class_name => 'SimpleForum::Post'
22
+
23
+ belongs_to :category,
24
+ :class_name => 'SimpleForum::Category'
25
+
26
+ has_many :moderatorships,
27
+ :class_name => 'SimpleForum::Moderatorship'
28
+
29
+ has_many :moderators,
30
+ :through => :moderatorships,
31
+ :source => :user
32
+
33
+ scope :default_order, order("#{quoted_table_name}.position ASC")
34
+
35
+ validates :name, :presence => true
36
+ validates :position, :presence => true, :numericality => {:only_integer => true, :allow_nil => true}
37
+
38
+ attr_accessible :name, :body, :parent_id, :position, :moderator_ids, :category_id
39
+
40
+ if respond_to?(:has_friendly_id)
41
+ has_friendly_id :name, :use_slug => true, :approximate_ascii => true
42
+ else
43
+ def to_param
44
+ "#{id}-#{name.to_s.parameterize}"
45
+ end
46
+ end
47
+
48
+ def recent_activity?(user)
49
+ SimpleForum::UserActivity.new(user).recent_activity?(self)
50
+ end
51
+
52
+ def bang_recent_activity(user)
53
+ SimpleForum::UserActivity.new(user).bang(self)
54
+ end
55
+
56
+ def moderated_by?(user)
57
+ return false unless user
58
+ @moderated_by_cache ||= {}
59
+ if @moderated_by_cache.has_key?(user.id)
60
+ @moderated_by_cache[user.id]
61
+ else
62
+ @moderated_by_cache[user.id] = moderators.include?(user)
63
+ end
64
+ end
65
+
66
+ alias_method :is_moderator?, :moderated_by?
67
+
68
+ end
69
+ end
@@ -0,0 +1,18 @@
1
+ module SimpleForum
2
+ class Moderatorship < ::ActiveRecord::Base
3
+
4
+ set_table_name 'simple_forum_moderatorships' #should work table_name_prefix in SimpleForum module but it's not!'
5
+
6
+ belongs_to :forum,
7
+ :class_name => "SimpleForum::Forum"
8
+
9
+ belongs_to :user,
10
+ :class_name => instance_eval(&AbstractAuth.invoke(:user_class)).name
11
+
12
+ validates :forum, :user, :presence => true
13
+ validates :user_id, :uniqueness => {:scope => :forum_id, :allow_nil => true}
14
+
15
+ attr_accessible :forum_id, :user_id
16
+
17
+ end
18
+ end
@@ -0,0 +1,105 @@
1
+ module SimpleForum
2
+ class Post < ::ActiveRecord::Base
3
+
4
+ set_table_name 'simple_forum_posts' #should work table_name_prefix in SimpleForum module but it's not!'
5
+
6
+ belongs_to :user,
7
+ :class_name => instance_eval(&AbstractAuth.invoke(:user_class)).name
8
+
9
+ belongs_to :edited_by,
10
+ :class_name => instance_eval(&AbstractAuth.invoke(:user_class)).name
11
+
12
+ belongs_to :deleted_by,
13
+ :class_name => instance_eval(&AbstractAuth.invoke(:user_class)).name
14
+
15
+ belongs_to :topic,
16
+ :counter_cache => true,
17
+ :class_name => "SimpleForum::Topic"
18
+
19
+ belongs_to :forum,
20
+ :counter_cache => true,
21
+ :class_name => "SimpleForum::Forum"
22
+
23
+ before_validation :set_forum_id, :on => :create
24
+
25
+ after_create :update_cached_fields
26
+ after_destroy :update_cached_fields
27
+
28
+ scope :recent, order("#{quoted_table_name}.created_at DESC")
29
+
30
+ attr_accessible :body
31
+ validates :topic, :forum, :user, :presence => true
32
+ validates :body, :presence => true
33
+
34
+ validate :topic_must_not_be_closed, :on => :create
35
+
36
+ def topic_must_not_be_closed
37
+ errors.add(:base, I18n.t('simple_forum.errors.topic_is_close', :default => "Topic is closed.")) if topic && topic.is_closed?
38
+ end
39
+
40
+ def on_page
41
+ before_count = topic.posts.where(["#{SimpleForum::Post.quoted_table_name}.created_at<?", created_at]).size
42
+ [((before_count + 1).to_f / SimpleForum::Post.per_page).ceil.to_i, 1].max
43
+ end
44
+
45
+ def self.per_page
46
+ 15
47
+ end
48
+
49
+ def output
50
+ body.to_s.bbcode_to_html
51
+ end
52
+
53
+ def output_without_tags
54
+ HTML::FullSanitizer.new.sanitize(output.gsub(/\<fieldset\>\<legend\>.*\<\/legend\>\<blockquote\>(.|\n)*\<\/blockquote\>/, ''))
55
+ end
56
+
57
+ def editable_by?(user, is_moderator=false)
58
+ return false if new_record?
59
+ return false if is_deleted?
60
+ is_moderator = forum.is_moderator?(user) if is_moderator.nil?
61
+ return true if is_moderator
62
+ created_at > SimpleForum.minutes_for_edit_post.minutes.ago && user == self.user
63
+ end
64
+
65
+ def deletable_by?(user, is_moderator=false)
66
+ return false if new_record?
67
+ return false if is_deleted?
68
+ is_moderator = forum.is_moderator?(user) if is_moderator.nil?
69
+ return true if is_moderator
70
+ created_at > SimpleForum.minutes_for_delete_post.minutes.ago && user == self.user
71
+ end
72
+
73
+ def is_deleted
74
+ !!deleted_at
75
+ end
76
+
77
+ alias_method :is_deleted?, :is_deleted
78
+
79
+ def is_edited
80
+ !!edited_at
81
+ end
82
+
83
+ alias_method :is_edited?, :is_edited
84
+
85
+ def mark_as_deleted_by(user)
86
+ return false unless deletable_by?(user, nil)
87
+ self.deleted_at = Time.now
88
+ self.deleted_by = user
89
+ self.save
90
+ end
91
+
92
+ protected
93
+
94
+ def update_cached_fields
95
+ topic.update_cached_post_fields(self) if topic
96
+ if user && user.respond_to?(:forum_posts_count)
97
+ user.class.update_all({:forum_posts_count => SimpleForum::Post.where(:user_id => user.id).count}, {:id => user.id})
98
+ end
99
+ end
100
+
101
+ def set_forum_id
102
+ self.forum = topic.forum if topic
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,134 @@
1
+ module SimpleForum
2
+ class Topic < ::ActiveRecord::Base
3
+
4
+ set_table_name 'simple_forum_topics' #should work table_name_prefix in SimpleForum module but it's not!'
5
+
6
+ belongs_to :user, :class_name => instance_eval(&AbstractAuth.invoke(:user_class)).name
7
+
8
+ belongs_to :forum,
9
+ :class_name => "SimpleForum::Forum", :counter_cache => true
10
+
11
+ has_many :posts,
12
+ :order => "#{SimpleForum::Post.quoted_table_name}.created_at ASC",
13
+ :class_name => "SimpleForum::Post",
14
+ :dependent => :delete_all
15
+
16
+ belongs_to :recent_post,
17
+ :class_name => "SimpleForum::Post"
18
+
19
+ has_one :last_post,
20
+ :order => "#{SimpleForum::Post.table_name}.created_at DESC",
21
+ :class_name => "SimpleForum::Post"
22
+
23
+ has_one :first_post,
24
+ :order => "#{SimpleForum::Post.quoted_table_name}.created_at ASC",
25
+ :class_name => "SimpleForum::Post"
26
+
27
+
28
+ validates :title, :forum, :presence => true
29
+ validates :user, :presence => true, :on => :create
30
+ validates :body, :presence => true, :on => :create
31
+ validate :forum_must_be_topicable, :on => :create
32
+
33
+ def forum_must_be_topicable
34
+ errors.add(:base, t('simple_forum.validations.forum_must_be_topicable')) if forum && !forum.is_topicable?
35
+ end
36
+
37
+ before_destroy :decrement_posts_counter_cache_for_forum
38
+
39
+ before_validation :set_default_attributes, :on => :create
40
+ after_create :create_initial_post
41
+
42
+ attr_accessor :body
43
+ attr_accessible :title, :body
44
+
45
+ def update_cached_post_fields(post)
46
+ if remaining_post = post.frozen? ? last_post : post
47
+ self.class.update_all({:last_updated_at => remaining_post.created_at,
48
+ :recent_post_id => remaining_post.id,
49
+ # :posts_count => posts.size
50
+ }, {:id => id})
51
+ forum.class.update_all({:recent_post_id => remaining_post.id}, {:id => forum.id})
52
+ else
53
+ destroy
54
+ end
55
+ end
56
+
57
+ def paged?
58
+ posts.size > SimpleForum::Post.per_page
59
+ end
60
+
61
+ def last_page
62
+ @last_page ||= [(posts.size.to_f / SimpleForum::Post.per_page).ceil.to_i, 1].max
63
+ end
64
+
65
+ #return array with page numbers
66
+ # topic.page_numbers => [1, 2, 3, 4] #when pages count is 4
67
+ # topic.page_numbers => [1, 2, 3, 4, 5] #when pages count is 5
68
+ # topic.page_numbers => [1, nil, 3, 4, 5, 6] #when pages count is 6
69
+ # topic.page_numbers => [1, nil, 4, 5, 6, 7] #when pages count is 7
70
+ def page_numbers(max=5)
71
+ if last_page > max
72
+ [1] + [nil] + ((last_page-max+2)..last_page).to_a
73
+ else
74
+ (1..last_page).to_a
75
+ end
76
+ end
77
+
78
+ if respond_to?(:has_friendly_id)
79
+ has_friendly_id :title, :use_slug => true, :approximate_ascii => true
80
+ else
81
+ def to_param
82
+ "#{id}-#{title.to_s.parameterize}"
83
+ end
84
+ end
85
+
86
+ def author?(u)
87
+ user == u
88
+ end
89
+
90
+ def is_open
91
+ !is_closed?
92
+ end
93
+
94
+ alias_method :is_open?, :is_open
95
+
96
+ def open!
97
+ update_attribute(:is_closed, false)
98
+ end
99
+
100
+ def close!
101
+ update_attribute(:is_closed, true)
102
+ end
103
+
104
+ def recent_activity?(user)
105
+ SimpleForum::UserActivity.new(user).recent_activity?(self)
106
+ end
107
+
108
+ def bang_recent_activity(user)
109
+ SimpleForum::UserActivity.new(user).bang(self)
110
+ end
111
+
112
+ def increment_views_count
113
+ self.class.increment_counter(:views_count, self)
114
+ end
115
+
116
+ private
117
+
118
+ def decrement_posts_counter_cache_for_forum
119
+ forum.class.update_counters(forum.id, :posts_count => (-1) * posts.size) if forum
120
+ end
121
+
122
+ def set_default_attributes
123
+ self.last_updated_at ||= Time.now
124
+ end
125
+
126
+ def create_initial_post
127
+ p = self.posts.new(:body => @body) do |post|
128
+ post.user = user
129
+ end
130
+ p.save!
131
+ @body = nil
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,80 @@
1
+ require 'pstore'
2
+
3
+ class SimpleForum::UserActivity
4
+
5
+ PATH = Rails.root.join("file_store")
6
+ FILE = "simple_forum_activity"
7
+ FileUtils.mkdir_p(PATH)
8
+
9
+ attr_reader :user
10
+
11
+ def recent_activity?(object)
12
+ self[object] > Time.now
13
+ return false unless user.try(:id)
14
+ if object.is_a?(SimpleForum::Forum)
15
+ if recent_post = object.recent_post
16
+ recent_post.created_at > self[object]
17
+ else
18
+ false
19
+ end
20
+ else #SimpleForum::Topic
21
+ if object.last_updated_at
22
+ object.last_updated_at > self[object]
23
+ else
24
+ false
25
+ end
26
+ end
27
+ end
28
+
29
+ def initialize(user)
30
+ @user = user
31
+ end
32
+
33
+ def self.store
34
+ @@store ||= PStore.new(File.join(PATH, FILE))
35
+ end
36
+
37
+ def store
38
+ self.class.store
39
+ end
40
+
41
+ def [](object, default=Time.now)
42
+ ret = nil
43
+ store.transaction(true) do
44
+ user_hash = store[user.id] || {}
45
+ object_hash = user_hash[key_for_object(object)] || {}
46
+ ret = object_hash[object.id]
47
+ end if user.try(:id)
48
+ ret || default
49
+ end
50
+
51
+ def bang(object)
52
+ store.transaction do
53
+ user_hash = store[user.id] || {}
54
+ object_hash = (user_hash[key_for_object(object)] ||= {})
55
+ object_hash[object.id] = Time.now
56
+ store[user.id] = user_hash
57
+ end if user.try(:id)
58
+ nil
59
+ end
60
+
61
+ def destroy
62
+ store.transaction do
63
+ store.delete(user.id)
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ def key_for_object(object)
70
+ case object
71
+ when SimpleForum::Forum then
72
+ :f
73
+ when SimpleForum::Topic then
74
+ :t
75
+ else
76
+ object.model_name.cache_key
77
+ end
78
+ end
79
+
80
+ end
@@ -0,0 +1,50 @@
1
+ <!DOCTYPE html>
2
+ <html lang="<%= I18n.locale %>">
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
5
+ <title><%= yield(:title) || '' %></title>
6
+ <%= favicon_link_tag %>
7
+
8
+ <%= stylesheet_link_tag('/simple_forum_engine/stylesheets/simple_forum') %>
9
+
10
+ <%= yield :stylesheets %>
11
+
12
+ <%= javascript_include_tag('https://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js') %>
13
+ <%= javascript_include_tag('rails') %>
14
+
15
+ <%= yield :javascripts %>
16
+
17
+ <%= yield :head %>
18
+
19
+ <%= csrf_meta_tag %>
20
+ </head>
21
+ <body>
22
+
23
+ <p class="logo">
24
+ <%= link_to image_tag('http://galdomedia.pl/images/logo.gif', :alt => 'Galdomedia'), "http://galdomedia.pl", :title => "Galdomedia" %>
25
+ </p>
26
+
27
+ <% if flash.present? %>
28
+ <div id="flash_messages">
29
+ <% flash.each do |key, val| %>
30
+ <%= content_tag :div, val, :class => key %>
31
+ <% end %>
32
+ </div>
33
+ <% end %>
34
+
35
+ <h1>Simple Forum</h1>
36
+
37
+ <div id="left">
38
+ <%= yield(:left_sidebar) %>
39
+ </div>
40
+
41
+ <div id="right">
42
+
43
+ </div>
44
+
45
+ <div id="content">
46
+ <%= yield %>
47
+ </div>
48
+
49
+ </body>
50
+ </html>