dm_forum 4.2.1.5

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.
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