simple_forum 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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>