dm_forum 4.2.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (185) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +23 -0
  3. data/README.md +33 -0
  4. data/Rakefile +30 -0
  5. data/app/assets/javascripts/dm_forum/admin.js +20 -0
  6. data/app/assets/javascripts/dm_forum/application.js +16 -0
  7. data/app/assets/javascripts/dm_forum/dm_forum.js +3 -0
  8. data/app/assets/stylesheets/dm_forum/admin.css +2 -0
  9. data/app/assets/stylesheets/dm_forum/application.css +12 -0
  10. data/app/controllers/dm_forum/admin/admin_controller.rb +13 -0
  11. data/app/controllers/dm_forum/admin/dashboard_controller.rb +12 -0
  12. data/app/controllers/dm_forum/admin/forum_categories_controller.rb +80 -0
  13. data/app/controllers/dm_forum/admin/forum_sites_controller.rb +40 -0
  14. data/app/controllers/dm_forum/admin/forums_controller.rb +113 -0
  15. data/app/controllers/dm_forum/application_controller.rb +25 -0
  16. data/app/controllers/dm_forum/forum_comments_controller.rb +88 -0
  17. data/app/controllers/dm_forum/forum_topics_controller.rb +93 -0
  18. data/app/controllers/dm_forum/forums_controller.rb +45 -0
  19. data/app/datatables/forum_user_datatable.rb +79 -0
  20. data/app/helpers/dm_forum/application_helper.rb +5 -0
  21. data/app/helpers/dm_forum/forum_helper.rb +109 -0
  22. data/app/mailers/forum_notification_mailer.rb +27 -0
  23. data/app/models/dm_forum/concerns/ability.rb +50 -0
  24. data/app/models/dm_forum/concerns/user.rb +37 -0
  25. data/app/models/dm_forum/permitted_params.rb +38 -0
  26. data/app/models/forum.rb +74 -0
  27. data/app/models/forum_category.rb +14 -0
  28. data/app/models/forum_comment.rb +93 -0
  29. data/app/models/forum_site.rb +27 -0
  30. data/app/models/forum_topic.rb +146 -0
  31. data/app/presenters/forum_comment_presenter.rb +8 -0
  32. data/app/presenters/forum_common_presenter.rb +31 -0
  33. data/app/presenters/forum_presenter.rb +14 -0
  34. data/app/views/dm_forum/admin/dashboard/_widget_forum_comments.html.erb +38 -0
  35. data/app/views/dm_forum/admin/dashboard/widget_forum_comments.js.erb +1 -0
  36. data/app/views/dm_forum/admin/forum_categories/_content_toolbar.html.erb +5 -0
  37. data/app/views/dm_forum/admin/forum_categories/_form.html.erb +18 -0
  38. data/app/views/dm_forum/admin/forum_categories/edit.html.erb +3 -0
  39. data/app/views/dm_forum/admin/forum_categories/index.html.erb +25 -0
  40. data/app/views/dm_forum/admin/forum_categories/new.html.erb +3 -0
  41. data/app/views/dm_forum/admin/forum_categories/show.html.erb +41 -0
  42. data/app/views/dm_forum/admin/forum_sites/_form.html.erb +18 -0
  43. data/app/views/dm_forum/admin/forum_sites/edit.html.erb +2 -0
  44. data/app/views/dm_forum/admin/forum_sites/show.html.erb +12 -0
  45. data/app/views/dm_forum/admin/forums/_content_toolbar.html.erb +5 -0
  46. data/app/views/dm_forum/admin/forums/_form.html.erb +24 -0
  47. data/app/views/dm_forum/admin/forums/edit.html.erb +2 -0
  48. data/app/views/dm_forum/admin/forums/new.html.erb +2 -0
  49. data/app/views/dm_forum/admin/forums/show.html.erb +68 -0
  50. data/app/views/dm_forum/forum_comments/_formatting.html.erb +12 -0
  51. data/app/views/dm_forum/forum_comments/edit.html.erb +15 -0
  52. data/app/views/dm_forum/forum_topics/_comment_item.html.erb +26 -0
  53. data/app/views/dm_forum/forum_topics/_comments_list.html.erb +63 -0
  54. data/app/views/dm_forum/forum_topics/_follow_button.html.erb +3 -0
  55. data/app/views/dm_forum/forum_topics/_form.html.erb +24 -0
  56. data/app/views/dm_forum/forum_topics/edit.html.erb +13 -0
  57. data/app/views/dm_forum/forum_topics/new.html.erb +13 -0
  58. data/app/views/dm_forum/forum_topics/show.html.erb +49 -0
  59. data/app/views/dm_forum/forum_topics/toggle_follow.js.coffee +5 -0
  60. data/app/views/dm_forum/forums/_category_side_list.html.erb +28 -0
  61. data/app/views/dm_forum/forums/_forum_header.html.erb +14 -0
  62. data/app/views/dm_forum/forums/_no_forums.html.erb +1 -0
  63. data/app/views/dm_forum/forums/_topics_list.html.erb +86 -0
  64. data/app/views/dm_forum/forums/categories.html.erb +32 -0
  65. data/app/views/dm_forum/forums/list.html.erb +10 -0
  66. data/app/views/dm_forum/forums/show.html.erb +39 -0
  67. data/app/views/layouts/email_templates/forum_notification.html.erb +15 -0
  68. data/app/views/layouts/email_templates/forum_notification.text.erb +16 -0
  69. data/config/initializers/dm_forum.rb +0 -0
  70. data/config/locales/fms.cs.yml +104 -0
  71. data/config/locales/fms.de.yml +104 -0
  72. data/config/locales/fms.en.yml +104 -0
  73. data/config/locales/fms.fi.yml +104 -0
  74. data/config/locales/fms.ja.yml +104 -0
  75. data/config/routes.rb +43 -0
  76. data/db/migrate/20130103151149_create_forums.rb +72 -0
  77. data/db/migrate/20130424125513_add_public_forum.rb +13 -0
  78. data/db/migrate/20130430084925_add_forum_category.rb +7 -0
  79. data/db/migrate/20140201112134_delete_monitorships.rb +17 -0
  80. data/db/migrate/20140303121325_add_requires_subscription_forum.rb +5 -0
  81. data/db/migrate/20140423113420_add_forum_owner.rb +6 -0
  82. data/db/migrate/20160821150125_index_foreign_keys_in_fms_forum_sites.rb +5 -0
  83. data/db/migrate/20160821150126_index_foreign_keys_in_fms_forum_topics.rb +9 -0
  84. data/db/migrate/20160821150127_index_foreign_keys_in_fms_forums.rb +7 -0
  85. data/lib/dm_forum/engine.rb +9 -0
  86. data/lib/dm_forum/version.rb +3 -0
  87. data/lib/dm_forum.rb +4 -0
  88. data/lib/tasks/dm_forum_tasks.rake +4 -0
  89. data/spec/dummy/README.rdoc +28 -0
  90. data/spec/dummy/Rakefile +6 -0
  91. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  92. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  93. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  94. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  95. data/spec/dummy/app/models/ability.rb +12 -0
  96. data/spec/dummy/app/models/user.rb +6 -0
  97. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  98. data/spec/dummy/bin/bundle +3 -0
  99. data/spec/dummy/bin/rails +4 -0
  100. data/spec/dummy/bin/rake +4 -0
  101. data/spec/dummy/config/application.rb +26 -0
  102. data/spec/dummy/config/boot.rb +5 -0
  103. data/spec/dummy/config/database.yml +25 -0
  104. data/spec/dummy/config/environment.rb +5 -0
  105. data/spec/dummy/config/environments/development.rb +37 -0
  106. data/spec/dummy/config/environments/production.rb +82 -0
  107. data/spec/dummy/config/environments/test.rb +39 -0
  108. data/spec/dummy/config/initializers/assets.rb +8 -0
  109. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  110. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  111. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  112. data/spec/dummy/config/initializers/inflections.rb +16 -0
  113. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  114. data/spec/dummy/config/initializers/session_store.rb +3 -0
  115. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  116. data/spec/dummy/config/locales/en.yml +23 -0
  117. data/spec/dummy/config/routes.rb +17 -0
  118. data/spec/dummy/config/secrets.yml +22 -0
  119. data/spec/dummy/config.ru +4 -0
  120. data/spec/dummy/db/development.sqlite3 +0 -0
  121. data/spec/dummy/db/migrate/20141114170927_add_globalize_countries.dm_core.rb +50 -0
  122. data/spec/dummy/db/migrate/20141114170928_devise_create_users.dm_core.rb +46 -0
  123. data/spec/dummy/db/migrate/20141114170929_add_user_fields.dm_core.rb +14 -0
  124. data/spec/dummy/db/migrate/20141114170930_rolify_create_roles.dm_core.rb +20 -0
  125. data/spec/dummy/db/migrate/20141114170931_add_last_access.dm_core.rb +10 -0
  126. data/spec/dummy/db/migrate/20141114170932_create_versions.dm_core.rb +19 -0
  127. data/spec/dummy/db/migrate/20141114170933_add_object_changes_column_to_versions.dm_core.rb +12 -0
  128. data/spec/dummy/db/migrate/20141114170934_create_dm_core_accounts.dm_core.rb +13 -0
  129. data/spec/dummy/db/migrate/20141114170935_add_account_to_users.dm_core.rb +9 -0
  130. data/spec/dummy/db/migrate/20141114170936_create_preferences.dm_core.rb +13 -0
  131. data/spec/dummy/db/migrate/20141114170937_create_comments.dm_core.rb +22 -0
  132. data/spec/dummy/db/migrate/20141114170938_add_activity.dm_core.rb +21 -0
  133. data/spec/dummy/db/migrate/20141114170939_add_type_to_comments.dm_core.rb +9 -0
  134. data/spec/dummy/db/migrate/20141114170940_add_category.dm_core.rb +28 -0
  135. data/spec/dummy/db/migrate/20141114170941_create_email_table.dm_core.rb +26 -0
  136. data/spec/dummy/db/migrate/20141114170942_add_user_profile.dm_core.rb +46 -0
  137. data/spec/dummy/db/migrate/20141114170943_add_profile_email.dm_core.rb +14 -0
  138. data/spec/dummy/db/migrate/20141114170944_create_payment_history.dm_core.rb +37 -0
  139. data/spec/dummy/db/migrate/20141114170945_change_anchor_field.dm_core.rb +10 -0
  140. data/spec/dummy/db/migrate/20141114170946_create_user_site_profile.dm_core.rb +27 -0
  141. data/spec/dummy/db/migrate/20141114170947_add_avatar.dm_core.rb +12 -0
  142. data/spec/dummy/db/migrate/20141114170948_add_notify_to_payment_history.dm_core.rb +8 -0
  143. data/spec/dummy/db/migrate/20141114170949_acts_as_votable_migration.dm_core.rb +28 -0
  144. data/spec/dummy/db/migrate/20141114170950_add_user_site_profile_uuid.dm_core.rb +19 -0
  145. data/spec/dummy/db/migrate/20141114170951_add_invoice_id.dm_core.rb +7 -0
  146. data/spec/dummy/db/migrate/20141114170952_acts_as_follower_migration.dm_core.rb +18 -0
  147. data/spec/dummy/db/migrate/20141114170953_rename_invoice_id.dm_core.rb +12 -0
  148. data/spec/dummy/db/migrate/20141114170954_add_core_addresses.dm_core.rb +18 -0
  149. data/spec/dummy/db/migrate/20141114170955_papertrail_increase_column.dm_core.rb +9 -0
  150. data/spec/dummy/db/migrate/20141114170956_acts_as_taggable_on_migration.dm_core.rb +32 -0
  151. data/spec/dummy/db/migrate/20141114170957_add_missing_unique_indices.dm_core.rb +23 -0
  152. data/spec/dummy/db/migrate/20141114170958_add_taggings_counter_cache_to_tags.dm_core.rb +16 -0
  153. data/spec/dummy/db/migrate/20141114170959_create_custom_fields.dm_core.rb +40 -0
  154. data/spec/dummy/db/migrate/20141114170960_add_missing_taggable_index.dm_core.rb +11 -0
  155. data/spec/dummy/db/migrate/20141119112030_create_cms.dm_cms.rb +92 -0
  156. data/spec/dummy/db/migrate/20141119112031_add_account_to_cms.dm_cms.rb +11 -0
  157. data/spec/dummy/db/migrate/20141119112032_create_blog.dm_cms.rb +62 -0
  158. data/spec/dummy/db/migrate/20141119112033_add_notification_sent.dm_cms.rb +6 -0
  159. data/spec/dummy/db/migrate/20141119112034_add_blog_comment.dm_cms.rb +8 -0
  160. data/spec/dummy/db/migrate/20141119112035_add_blog_image.dm_cms.rb +6 -0
  161. data/spec/dummy/db/migrate/20141119112036_rename_snippet_slug.dm_cms.rb +14 -0
  162. data/spec/dummy/db/migrate/20141119112037_add_requires_subscription_blog.dm_cms.rb +8 -0
  163. data/spec/dummy/db/migrate/20141119112038_add_pages_ranked_model.dm_cms.rb +23 -0
  164. data/spec/dummy/db/migrate/20141119112039_add_blog_owner.dm_cms.rb +7 -0
  165. data/spec/dummy/db/migrate/20141119112040_create_media_files.dm_cms.rb +30 -0
  166. data/spec/dummy/db/migrate/20141119112041_add_cmspage_summary.dm_cms.rb +7 -0
  167. data/spec/dummy/db/migrate/20141119112042_add_blog_image_email_header.dm_cms.rb +6 -0
  168. data/spec/dummy/db/migrate/20141119112043_add_header_image.dm_cms.rb +10 -0
  169. data/spec/dummy/db/migrate/20160128144312_add_favored_locale.dm_core.rb +17 -0
  170. data/spec/dummy/db/migrate/20160128144313_update_papertrail_v4.dm_core.rb +41 -0
  171. data/spec/dummy/db/schema.rb +628 -0
  172. data/spec/dummy/public/404.html +67 -0
  173. data/spec/dummy/public/422.html +67 -0
  174. data/spec/dummy/public/500.html +66 -0
  175. data/spec/dummy/public/favicon.ico +0 -0
  176. data/spec/factories/accounts.rb +9 -0
  177. data/spec/factories/user_profiles.rb +10 -0
  178. data/spec/factories/users.rb +19 -0
  179. data/spec/models/forum_spec.rb +9 -0
  180. data/spec/rails_helper.rb +70 -0
  181. data/spec/spec_helper.rb +85 -0
  182. data/spec/support/accounts.rb +17 -0
  183. data/spec/support/devise.rb +44 -0
  184. data/spec/support/fix_locale.rb +57 -0
  185. metadata +337 -0
@@ -0,0 +1,79 @@
1
+ class ForumUserDatatable
2
+ include ActionView::Helpers::TagHelper
3
+
4
+ delegate :params, :link_to, :image_tag, :number_to_currency, :time_ago_in_words, to: :@view
5
+ delegate :url_helpers, to: 'DmForum::Engine.routes'
6
+
7
+ #------------------------------------------------------------------------------
8
+ def initialize(view, forum)
9
+ @view = view
10
+ @forum = forum
11
+ end
12
+
13
+ #------------------------------------------------------------------------------
14
+ def as_json(options = {})
15
+ {
16
+ sEcho: params[:sEcho].to_i,
17
+ iTotalRecords: User.count,
18
+ iTotalDisplayRecords: users.total_entries,
19
+ aaData: data
20
+ }
21
+ end
22
+
23
+ private
24
+
25
+ #------------------------------------------------------------------------------
26
+ def data
27
+ users.map do |user|
28
+ [
29
+ action(user),
30
+ user.country.nil? ? 'n/a' : user.country.english_name,
31
+ ]
32
+ end
33
+ end
34
+
35
+ #------------------------------------------------------------------------------
36
+ def users
37
+ @users ||= fetch_users
38
+ end
39
+
40
+ #------------------------------------------------------------------------------
41
+ def fetch_users
42
+ users = User.includes(:user_profile => [ :country ] ).references(:user_profile).order("#{sort_column} #{sort_direction}")
43
+ users = users.page(page).per_page(per_page)
44
+ if params[:sSearch].present?
45
+ users = users.where("LOWER(user_profiles.first_name) like :search OR LOWER(user_profiles.last_name) like :search OR LOWER(users.email) like :search", search: "%#{params[:sSearch]}%".downcase)
46
+ end
47
+ users
48
+ end
49
+
50
+ #------------------------------------------------------------------------------
51
+ def page
52
+ params[:iDisplayStart].to_i/per_page + 1
53
+ end
54
+
55
+ #------------------------------------------------------------------------------
56
+ def per_page
57
+ params[:iDisplayLength].to_i > 0 ? params[:iDisplayLength].to_i : 50
58
+ end
59
+
60
+ #------------------------------------------------------------------------------
61
+ def sort_column
62
+ columns = ["LOWER(user_profiles.first_name) #{sort_direction}, LOWER(user_profiles.last_name)", 'globalize_countries.english_name']
63
+ columns[params[:iSortCol_0].to_i]
64
+ end
65
+
66
+ #------------------------------------------------------------------------------
67
+ def sort_direction
68
+ params[:sSortDir_0] == "desc" ? "desc" : "asc"
69
+ end
70
+
71
+ #------------------------------------------------------------------------------
72
+ def action(user)
73
+ if @forum.member? user
74
+ icons(:checkmark) + "  ".html_safe + user.full_name
75
+ else
76
+ link_to(icons(:add), url_helpers.forum_add_member_admin_forum_path(@forum, :locale => DmCore::Language.locale, :user_id => user.id), :title => 'Add Access') + "  ".html_safe + user.full_name
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,5 @@
1
+ module DmForum
2
+ module ApplicationHelper
3
+ include ForumHelper
4
+ end
5
+ end
@@ -0,0 +1,109 @@
1
+ module DmForum
2
+ module ForumHelper
3
+
4
+ #------------------------------------------------------------------------------
5
+ def topic_title_link(topic, options)
6
+ if topic.title =~ /^\[([^\]]{1,15})\]((\s+)\w+.*)/
7
+ "<span class='flag'>#{$1}</span>" +
8
+ link_to($2.strip, forum_forum_topic_path(@forum, topic), options)
9
+ else
10
+ link_to(topic.title, forum_forum_topic_path(@forum, topic), options)
11
+ end
12
+ end
13
+
14
+ #------------------------------------------------------------------------------
15
+ def modify_history(function, name, path, params_hash=nil)
16
+ state = "null"
17
+ query_string = "?"
18
+ if params_hash
19
+ state = params_hash.to_json
20
+ params_hash.each { |k,v| query_string << "#{k}=#{v}&" }
21
+ end
22
+ query_string = query_string.chop
23
+ %{history.#{function}(#{state}, '#{j(name)}', '#{j(path+query_string)}');}.html_safe
24
+ end
25
+
26
+ #------------------------------------------------------------------------------
27
+ def edited_on_tag(post)
28
+ if (post.updated_at - post.created_at > 5.minutes)
29
+ %{<span class='date'>#{I18n.t 'fms.post_edited', :when => time_ago_in_words(post.updated_at)}</span>}.html_safe
30
+ end
31
+ end
32
+
33
+ # used to know if a topic has changed since we read it last
34
+ #------------------------------------------------------------------------------
35
+ def recent_topic_activity(topic)
36
+ return false unless user_signed_in?
37
+ return topic.last_updated_at > ((session[:forum_topics] ||= {})[topic.id] || last_active)
38
+ end
39
+
40
+ # used to know if a forum has changed since we read it last
41
+ #------------------------------------------------------------------------------
42
+ def recent_forum_activity(forum)
43
+ return false unless user_signed_in? && forum.recent_topic
44
+ return forum.recent_topic.last_updated_at > ((session[:forums] ||= {})[forum.id] || last_active)
45
+ end
46
+
47
+ #------------------------------------------------------------------------------
48
+ def last_active
49
+ session[:last_active] ||= Time.now.utc
50
+ end
51
+
52
+ # Ripe for optimization
53
+ #------------------------------------------------------------------------------
54
+ def voice_count
55
+ pluralize ForumSite.site.forum_topics.to_a.sum { |t| t.voice_count }, 'voice'
56
+ end
57
+
58
+ #------------------------------------------------------------------------------
59
+ def feed_icon_tag(title, url)
60
+ # (@feed_icons ||= []) << { :url => url, :title => title }
61
+ # link_to image_tag('feed-icon.png', :size => '14x14', :alt => "Subscribe to #{title}"), url
62
+ end
63
+
64
+ #------------------------------------------------------------------------------
65
+ def forum_crumbs(forum = nil)
66
+ seperator = "<span class='arrow'><i class='icon-angle-right'></i></span>".html_safe
67
+ out = "".html_safe
68
+ out += link_to(I18n.t('fms.forums'), forum_root_path) + seperator
69
+ if forum
70
+ out += link_to(forum.name, forum_path(forum))
71
+ page = session[:forum_page] ? session[:forum_page][forum.id] : nil
72
+ if page and page != 1
73
+ out += "<small style='color:#ccc'>(".html_safe
74
+ out += link_to(I18n.t('fms.page_nr'), forum_path(:id => forum, :page => page))
75
+ out += ")</small>".html_safe
76
+ end
77
+ out += seperator
78
+ end
79
+ return out
80
+ end
81
+
82
+ #------------------------------------------------------------------------------
83
+ def forum_topic_icon(forum_topic)
84
+ if recent_topic_activity(forum_topic)
85
+ style = "style='color:green'"
86
+ recent_txt = I18n.t('fms.views_forums.recent_activity')
87
+ else
88
+ stlye = ''
89
+ recent_txt = I18n.t('fms.views_forums.no_recent_activity')
90
+ end
91
+ if forum_topic.locked?
92
+ "<i class='icon-lock icon-large' #{style} title='#{I18n.t('fms.views_forums.comma_locked_topic')}'></i>".html_safe
93
+ else
94
+ "<i class='icon-comments icon-large' #{style} title='#{recent_txt}'></i>".html_safe
95
+ end
96
+ end
97
+
98
+ #------------------------------------------------------------------------------
99
+ def forum_comment_user_state(forum_comment)
100
+ if forum_comment.user.is_admin?
101
+ I18n.t 'fms.user_is_administrator', :default => 'Administator'
102
+ elsif can?(:moderate, forum_comment.forum_topic.forum)
103
+ I18n.t 'fms.user_is_moderator', :default => 'Moderator'
104
+ elsif forum_comment.user.suspended?
105
+ forum_comment.user.state
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,27 @@
1
+ class ForumNotificationMailer < DmCore::SiteMailer
2
+
3
+ helper DmCore::LiquidHelper
4
+ helper DmCore::UrlHelper
5
+ helper DmCore::AccountHelper
6
+
7
+ #------------------------------------------------------------------------------
8
+ def follower_notification(user, topic, comment_list)
9
+ account = topic.account
10
+ @subject = "Commets: #{topic.title}"
11
+ @recipients = user.email
12
+ @topic = topic
13
+ @comment_list = comment_list
14
+ @topic_link = dm_forum.forum_forum_topic_url(topic.forum.slug, topic.slug, locale: I18n.locale)
15
+ @forum_link = dm_forum.forum_show_url(topic.forum.slug, locale: I18n.locale)
16
+
17
+ headers = { "Return-Path" => account.preferred_smtp_from_email }
18
+ mail( from: account.preferred_smtp_from_email,
19
+ reply_to: account.preferred_smtp_from_email,
20
+ to: @recipients, subject: @subject,
21
+ theme: account.account_prefix) do |format|
22
+ format.text { render "layouts/email_templates/forum_notification.text.erb" }
23
+ format.html { render "layouts/email_templates/forum_notification.html.erb" }
24
+ end
25
+ end
26
+
27
+ end
@@ -0,0 +1,50 @@
1
+ # Wrap forum specific CanCan rules. Should be included in the main app's
2
+ # Ability class.
3
+ # NOTE: When checking abilities, don't check for Class level abilities,
4
+ # unless you don't care about the instance level. For example, don't
5
+ # use both styles
6
+ # can? :moderate, Forum
7
+ # can? :moderate, @forum
8
+ # In this case, if you need to check the class level, then use specific
9
+ # current_user.has_role? :moderator, Forum
10
+ #------------------------------------------------------------------------------
11
+
12
+ module DmForum
13
+ module Concerns
14
+ module Ability
15
+ def dm_forum_abilities(user)
16
+ if user
17
+ #--- Admin
18
+ if user.has_role?(:forum_manager)
19
+ can :manage_forums, :all
20
+ can :access_admin, :all
21
+ end
22
+
23
+ #--- Forum
24
+ can(:read, Forum) { |forum| forum.can_be_read_by?(user) }
25
+ can(:reply, Forum) { |forum| forum.can_be_replied_by?(user) }
26
+ can :moderate, Forum, :id => Forum.published.with_role(:moderator, user).map(&:id)
27
+
28
+ #--- Comment
29
+ can :edit, ForumComment, :user_id => user.id
30
+ else
31
+ #--- can only read/see public forums when not logged in
32
+ can(:read, Forum) { |forum| forum.can_be_read_by?(user) }
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ #------------------------------------------------------------------------------
40
+ # The abilities get basically compiled. So if you use
41
+ #
42
+ # can :moderate, Forum, :id => Forum.with_role(:moderator, user).map(&:id)
43
+ #
44
+ # this will execute the Forum.with_role query once during Ability.new. However
45
+ #
46
+ # can :moderate, Forum do |forum|
47
+ # user.has_role? :moderator, forum
48
+ # end
49
+ #
50
+ # this will execute the has_role? block on each call to can?
@@ -0,0 +1,37 @@
1
+ # Extends the User model with hooks for the Forum
2
+ #------------------------------------------------------------------------------
3
+ module DmForum
4
+ module Concerns
5
+ module User
6
+ extend ActiveSupport::Concern
7
+
8
+ # 'included do' causes the included code to be evaluated in the
9
+ # conext where it is included (post.rb), rather than be
10
+ # executed in the module's context (blorgh/concerns/models/post).
11
+ #------------------------------------------------------------------------------
12
+ included do
13
+
14
+ has_many :forum_comments, {:as => :commentable, :dependent => :delete_all}
15
+ has_many :forum_topics, -> { order("#{ForumTopic.table_name}.created_at desc") }
16
+
17
+ #------------------------------------------------------------------------------
18
+ def available_forums
19
+ @available_forums ||= site.ordered_forums - forums
20
+ end
21
+ end
22
+
23
+ #------------------------------------------------------------------------------
24
+ module ClassMethods
25
+ end
26
+
27
+ end
28
+ end
29
+ end
30
+
31
+ # {todo} non-working attempts to include this automatically if the forum engine is being used,
32
+ # so that it doesn't have to be added to the application's user.rb file by hand
33
+ #------------------------------------------------------------------------------
34
+ # class User
35
+ # include DmForum::Concerns::User
36
+ # end
37
+ # User.send :include, DmForum::Concerns::User
@@ -0,0 +1,38 @@
1
+ module DmForum
2
+ module PermittedParams
3
+
4
+ #------------------------------------------------------------------------------
5
+ def forum_site_params
6
+ params.require(:forum_site).permit! if can? :manage_forums, :all
7
+ end
8
+
9
+ #------------------------------------------------------------------------------
10
+ def forum_category_params
11
+ params.require(:forum_category).permit! if can? :manage_forums, :all
12
+ end
13
+
14
+ #------------------------------------------------------------------------------
15
+ def forum_params
16
+ params.require(:forum).permit! if can? :manage_forums, :all
17
+ end
18
+
19
+ #------------------------------------------------------------------------------
20
+ def forum_topic_params
21
+ if can? :manage_forums, :all
22
+ params.require(:forum_topic).permit!
23
+ else
24
+ params.require(:forum_topic).permit(:title, :body)
25
+ end
26
+ end
27
+
28
+ #------------------------------------------------------------------------------
29
+ def forum_comment_params
30
+ if can? :manage_forums, :all
31
+ params.require(:forum_comment).permit!
32
+ else
33
+ params.require(:forum_comment).permit(:title, :body)
34
+ end
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,74 @@
1
+ class Forum < ActiveRecord::Base
2
+ include DmCore::Concerns::PublicPrivate
3
+
4
+ self.table_name = 'fms_forums'
5
+
6
+ # --- FriendlyId
7
+ extend FriendlyId
8
+ friendly_id :name, use: :scoped, scope: :account_id
9
+ validates_presence_of :slug
10
+ before_save :normalize_slug
11
+
12
+ resourcify
13
+
14
+ include RankedModel
15
+ ranks :row_order, :with_same => :forum_category_id
16
+
17
+ belongs_to :forum_site
18
+ belongs_to :forum_category, :class_name => 'ForumCategory'
19
+ has_many :forum_topics, -> { order('sticky desc, last_updated_at desc') }, :dependent => :destroy
20
+ belongs_to :owner, :polymorphic => true
21
+
22
+ # this is used to see if a forum is "fresh"... we can't use topics because it puts
23
+ # stickies first even if they are not the most recently modified
24
+ has_many :recent_topics, -> { includes(:user).order('last_updated_at DESC') }, :class_name => 'ForumTopic'
25
+ has_one :recent_topic, -> { order('last_updated_at DESC') }, :class_name => 'ForumTopic'
26
+
27
+ default_scope { where(account_id: Account.current.id).order(:row_order) }
28
+ scope :published, -> { where(:published => true) }
29
+
30
+ # --- validations
31
+ validates_presence_of :name
32
+
33
+ # If user set slug sepcifically, we need to make sure it's been normalized
34
+ #------------------------------------------------------------------------------
35
+ def normalize_slug
36
+ self.slug = normalize_friendly_id(self.slug)
37
+ end
38
+
39
+ # regenerate slug if it's blank
40
+ #------------------------------------------------------------------------------
41
+ def should_generate_new_friendly_id?
42
+ self.slug.blank?
43
+ end
44
+
45
+ # use babosa gem (to_slug) to allow better handling of multi-language slugs
46
+ #------------------------------------------------------------------------------
47
+ def normalize_friendly_id(text)
48
+ text.to_s.to_slug.normalize.to_s
49
+ end
50
+
51
+ #------------------------------------------------------------------------------
52
+ def followed_topics(user)
53
+ user.following.following_by_type('ForumTopic').where(forum_id: self.id)
54
+ end
55
+
56
+ #------------------------------------------------------------------------------
57
+ def to_s
58
+ name
59
+ end
60
+
61
+ # Send comment notifications to any followers
62
+ #------------------------------------------------------------------------------
63
+ def self.notify_followers(start_time, end_time = Time.now)
64
+ comments = Comment.where(commentable_type: 'ForumTopic', created_at: start_time..end_time)
65
+ comments_by_topic = comments.group_by {|i| i.commentable_id }
66
+ comments_by_topic.each do |topic_id, topic_comments|
67
+ forum_topic = ForumTopic.find(topic_id)
68
+ followers = forum_topic.followers
69
+ followers.each do |follower|
70
+ email = ForumNotificationMailer.follower_notification(follower.user, forum_topic, topic_comments).deliver_now
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,14 @@
1
+ class ForumCategory < Category
2
+
3
+ has_many :forums, :dependent => :destroy
4
+
5
+ # Are any of the forums readable by this user? One positive is all need...
6
+ #------------------------------------------------------------------------------
7
+ def any_readable_forums?(user)
8
+ if user && user.is_admin?
9
+ true
10
+ else
11
+ forums.any? { |f| f.can_be_read_by?(user) }
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,93 @@
1
+ class ForumComment < Comment
2
+
3
+ #--- counter cache is set in the Comment model
4
+ belongs_to :forum_topic, :polymorphic => true, :foreign_key => :commentable_id, :foreign_type => :commentable_type
5
+
6
+ #--- update counter cache in forum
7
+ after_create :increment_forum_counter_cache
8
+ after_destroy :decrement_forum_counter_cache
9
+
10
+ default_scope { where(account_id: Account.current.id) }
11
+
12
+ validates_presence_of :user_id, :body
13
+ validate :topic_is_not_locked
14
+
15
+ after_create :update_cached_fields
16
+ after_update :update_cached_fields
17
+ after_destroy :update_cached_fields
18
+
19
+ self.per_page = 10
20
+
21
+ # For a forum topic, we have one root comment, which is the body of the topic.
22
+ # All other comments are either children or siblings
23
+ #------------------------------------------------------------------------------
24
+ def self.create_comment(forum_topic, body, user, parent_comment = nil)
25
+ unless parent_comment
26
+ #--- if it's the first comment, make it the root, otherwise it's a child
27
+ parent_comment = (forum_topic.forum_comments.empty? ? nil : forum_topic.forum_comments[0].root)
28
+ end
29
+ forum_topic.forum_comments.build(:body => body).tap do |comment|
30
+ comment.user = user
31
+ comment.parent = parent_comment
32
+ comment.save
33
+ end
34
+ end
35
+
36
+ #------------------------------------------------------------------------------
37
+ def self.search(query, options = {})
38
+ #--- (Beast) had to change the other join string since it conflicts when we bring parents in
39
+ options[:conditions] ||= ["LOWER(#{ForumComment.table_name}.body) LIKE ?", "%#{query}%"] unless query.blank?
40
+ options[:select] ||= "#{ForumComment.table_name}.*, #{ForumTopic.table_name}.title as topic_title, f.name as forum_name"
41
+ options[:joins] ||= "inner join #{ForumTopic.table_name} on #{ForumComment.table_name}.topic_id = #{ForumTopic.table_name}.id " +
42
+ "inner join #{Forum.table_name} as f on #{ForumTopic.table_name}.forum_id = f.id"
43
+ options[:order] ||= "#{ForumComment.table_name}.created_at DESC"
44
+ options[:count] ||= {:select => "#{ForumComment.table_name}.id"}
45
+ paginate options
46
+ end
47
+
48
+ #------------------------------------------------------------------------------
49
+ def self.search_monitored(user_id, query, options = {})
50
+ #--- (Beast) Same as above, but select only posts in topics monitored by the given user
51
+ # [todo]
52
+ # options[:conditions] ||= ["LOWER(#{ForumComment.table_name}.body) LIKE ?", "%#{query}%"] unless query.blank?
53
+ # options[:select] ||= "#{ForumComment.table_name}.*, #{ForumTopic.table_name}.title as topic_title, f.name as forum_name"
54
+ # options[:joins] ||= "inner join #{ForumTopic.table_name} on #{ForumComment.table_name}.topic_id = #{ForumTopic.table_name}.id " +
55
+ # "inner join #{Forum.table_name} as f on #{ForumTopic.table_name}.forum_id = f.id " +
56
+ # "inner join #{Monitorship.table_name} as m on #{ForumComment.table_name}.topic_id = m.topic_id AND " +
57
+ # "m.user_id = #{user_id} AND m.active != 0"
58
+ # options[:order] ||= "#{ForumComment.table_name}.created_at DESC"
59
+ # options[:count] ||= {:select => "#{ForumComment.table_name}.id"}
60
+ # paginate options
61
+ end
62
+
63
+ #------------------------------------------------------------------------------
64
+ def forum_name
65
+ forum_topic.forum.name
66
+ end
67
+
68
+ protected
69
+
70
+ #------------------------------------------------------------------------------
71
+ def update_cached_fields
72
+ forum_topic.update_cached_comment_fields(self) unless forum_topic.nil?
73
+ end
74
+
75
+ #------------------------------------------------------------------------------
76
+ def topic_is_not_locked
77
+ errors.add(:base, "Topic is locked") if forum_topic && forum_topic.locked? && forum_topic.comments_count > 0
78
+ end
79
+
80
+ private
81
+
82
+ # Since we can't have a belongs_to to the specific forum, update the comment
83
+ # counter cache by hand
84
+ #------------------------------------------------------------------------------
85
+ def increment_forum_counter_cache
86
+ Forum.increment_counter( 'comments_count', self.forum_topic.forum.id )
87
+ end
88
+
89
+ def decrement_forum_counter_cache
90
+ Forum.decrement_counter( 'comments_count', self.forum_topic.forum.id ) unless self.forum_topic.nil?
91
+ end
92
+
93
+ end
@@ -0,0 +1,27 @@
1
+ class ForumSite < ActiveRecord::Base
2
+ class UndefinedError < StandardError; end
3
+
4
+ self.table_name = 'fms_forum_sites'
5
+
6
+ has_many :forums, -> { where(is_public: true) }
7
+ has_many :all_forums, :class_name => 'Forum'
8
+ has_many :forum_topics, :through => :forums
9
+ has_many :forum_comments, :through => :forums
10
+
11
+ default_scope { where(account_id: Account.current.id) }
12
+
13
+ attr_readonly :comments_count, :forum_topics_count, :users_online
14
+
15
+
16
+ # There is only one forum site per account
17
+ #------------------------------------------------------------------------------
18
+ def self.site
19
+ ForumSite.first
20
+ end
21
+
22
+ #------------------------------------------------------------------------------
23
+ def ordered_forums
24
+ forums
25
+ end
26
+
27
+ end