rails_den 2.0.0

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 (528) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +652 -0
  3. data/README.md +74 -0
  4. data/Rakefile +6 -0
  5. data/app/assets/javascript/rails_den/attachments.js +733 -0
  6. data/app/assets/javascript/rails_den/bookmarks.js +58 -0
  7. data/app/assets/javascript/rails_den/cable.js +18 -0
  8. data/app/assets/javascript/rails_den/chat.js +3008 -0
  9. data/app/assets/javascript/rails_den/chat_3d_management.js +1230 -0
  10. data/app/assets/javascript/rails_den/composer.js +1785 -0
  11. data/app/assets/javascript/rails_den/content.js +1337 -0
  12. data/app/assets/javascript/rails_den/emoji_catalog.js +136 -0
  13. data/app/assets/javascript/rails_den/licenses/react-LICENSE.txt +21 -0
  14. data/app/assets/javascript/rails_den/licenses/react-dom-LICENSE.txt +21 -0
  15. data/app/assets/javascript/rails_den/licenses/scheduler-LICENSE.txt +21 -0
  16. data/app/assets/javascript/rails_den/licenses/three-LICENSE.txt +21 -0
  17. data/app/assets/javascript/rails_den/members.js +187 -0
  18. data/app/assets/javascript/rails_den/notifications.js +211 -0
  19. data/app/assets/javascript/rails_den/presence.js +757 -0
  20. data/app/assets/javascript/rails_den/private_messages.js +92 -0
  21. data/app/assets/javascript/rails_den/react_runtime.js +43 -0
  22. data/app/assets/javascript/rails_den/reactions.js +139 -0
  23. data/app/assets/javascript/rails_den/spatial.js +4338 -0
  24. data/app/assets/stylesheets/rails_den/advertising.css +91 -0
  25. data/app/assets/stylesheets/rails_den/application.css +1724 -0
  26. data/app/assets/stylesheets/rails_den/attachments.css +255 -0
  27. data/app/assets/stylesheets/rails_den/board_presence.css +38 -0
  28. data/app/assets/stylesheets/rails_den/bookmarks.css +36 -0
  29. data/app/assets/stylesheets/rails_den/chat.css +1555 -0
  30. data/app/assets/stylesheets/rails_den/chat_3d.css +1283 -0
  31. data/app/assets/stylesheets/rails_den/chat_3d_management.css +294 -0
  32. data/app/assets/stylesheets/rails_den/chat_reactions.css +79 -0
  33. data/app/assets/stylesheets/rails_den/community_presence.css +51 -0
  34. data/app/assets/stylesheets/rails_den/composer.css +358 -0
  35. data/app/assets/stylesheets/rails_den/emoji_catalog.css +33 -0
  36. data/app/assets/stylesheets/rails_den/member_profiles.css +662 -0
  37. data/app/assets/stylesheets/rails_den/moderation.css +561 -0
  38. data/app/assets/stylesheets/rails_den/notifications.css +222 -0
  39. data/app/assets/stylesheets/rails_den/onebox.css +126 -0
  40. data/app/assets/stylesheets/rails_den/post_links.css +37 -0
  41. data/app/assets/stylesheets/rails_den/post_presence.css +47 -0
  42. data/app/assets/stylesheets/rails_den/presence.css +96 -0
  43. data/app/assets/stylesheets/rails_den/presence_directory.css +217 -0
  44. data/app/assets/stylesheets/rails_den/presence_live.css +15 -0
  45. data/app/assets/stylesheets/rails_den/private_messages.css +41 -0
  46. data/app/assets/stylesheets/rails_den/reactions.css +53 -0
  47. data/app/assets/stylesheets/rails_den/search.css +100 -0
  48. data/app/assets/stylesheets/rails_den/tags.css +136 -0
  49. data/app/channels/rails_den/chat_spatial_channel.rb +203 -0
  50. data/app/channels/rails_den/chat_spatial_occupancy_channel.rb +69 -0
  51. data/app/channels/rails_den/chat_stream_channel.rb +108 -0
  52. data/app/channels/rails_den/notifications_channel.rb +30 -0
  53. data/app/channels/rails_den/presence_channel.rb +205 -0
  54. data/app/controllers/admin/rails_den/administrators_controller.rb +26 -0
  55. data/app/controllers/admin/rails_den/advertising_management_controller.rb +337 -0
  56. data/app/controllers/admin/rails_den/application_controller.rb +116 -0
  57. data/app/controllers/admin/rails_den/badges_controller.rb +106 -0
  58. data/app/controllers/admin/rails_den/board_access_controller.rb +143 -0
  59. data/app/controllers/admin/rails_den/boards_controller.rb +89 -0
  60. data/app/controllers/admin/rails_den/categories_controller.rb +89 -0
  61. data/app/controllers/admin/rails_den/member_badge_awards_controller.rb +92 -0
  62. data/app/controllers/admin/rails_den/members_controller.rb +495 -0
  63. data/app/controllers/admin/rails_den/plugins_controller.rb +25 -0
  64. data/app/controllers/admin/rails_den/profile_fields_controller.rb +104 -0
  65. data/app/controllers/admin/rails_den/reaction_types_controller.rb +127 -0
  66. data/app/controllers/admin/rails_den/report_reasons_controller.rb +15 -0
  67. data/app/controllers/admin/rails_den/roles_controller.rb +190 -0
  68. data/app/controllers/admin/rails_den/tags_controller.rb +11 -0
  69. data/app/controllers/admin/rails_den/themes_controller.rb +60 -0
  70. data/app/controllers/admin/rails_den/trust_levels_controller.rb +97 -0
  71. data/app/controllers/rails_den/advertising_controller.rb +14 -0
  72. data/app/controllers/rails_den/application_controller.rb +218 -0
  73. data/app/controllers/rails_den/attachments_controller.rb +128 -0
  74. data/app/controllers/rails_den/board_follows_controller.rb +119 -0
  75. data/app/controllers/rails_den/board_read_states_controller.rb +36 -0
  76. data/app/controllers/rails_den/boards_controller.rb +103 -0
  77. data/app/controllers/rails_den/bookmarks_controller.rb +91 -0
  78. data/app/controllers/rails_den/chat_channel_memberships_controller.rb +193 -0
  79. data/app/controllers/rails_den/chat_channel_read_states_controller.rb +46 -0
  80. data/app/controllers/rails_den/chat_channels_controller.rb +556 -0
  81. data/app/controllers/rails_den/chat_message_pins_controller.rb +78 -0
  82. data/app/controllers/rails_den/chat_messages_controller.rb +166 -0
  83. data/app/controllers/rails_den/chat_reaction_types_controller.rb +24 -0
  84. data/app/controllers/rails_den/community_read_states_controller.rb +12 -0
  85. data/app/controllers/rails_den/home_controller.rb +35 -0
  86. data/app/controllers/rails_den/member_profiles_controller.rb +286 -0
  87. data/app/controllers/rails_den/member_reports_controller.rb +22 -0
  88. data/app/controllers/rails_den/members_controller.rb +42 -0
  89. data/app/controllers/rails_den/mention_candidates_controller.rb +14 -0
  90. data/app/controllers/rails_den/moderation/audit_events_controller.rb +14 -0
  91. data/app/controllers/rails_den/moderation/member_histories_controller.rb +37 -0
  92. data/app/controllers/rails_den/moderation/member_notes_controller.rb +26 -0
  93. data/app/controllers/rails_den/moderation/report_notes_controller.rb +27 -0
  94. data/app/controllers/rails_den/moderation/reports_controller.rb +97 -0
  95. data/app/controllers/rails_den/moderation/restrictions_controller.rb +71 -0
  96. data/app/controllers/rails_den/moderation/topics_controller.rb +61 -0
  97. data/app/controllers/rails_den/moderation/warnings_controller.rb +50 -0
  98. data/app/controllers/rails_den/notification_preferences_controller.rb +116 -0
  99. data/app/controllers/rails_den/notifications_controller.rb +67 -0
  100. data/app/controllers/rails_den/post_bookmarks_controller.rb +29 -0
  101. data/app/controllers/rails_den/post_drafts_controller.rb +125 -0
  102. data/app/controllers/rails_den/post_reports_controller.rb +31 -0
  103. data/app/controllers/rails_den/posts_controller.rb +318 -0
  104. data/app/controllers/rails_den/presence_controller.rb +12 -0
  105. data/app/controllers/rails_den/presence_snapshots_controller.rb +83 -0
  106. data/app/controllers/rails_den/previews_controller.rb +10 -0
  107. data/app/controllers/rails_den/private_conversations_controller.rb +120 -0
  108. data/app/controllers/rails_den/private_messages_controller.rb +45 -0
  109. data/app/controllers/rails_den/reactions_controller.rb +124 -0
  110. data/app/controllers/rails_den/reports_controller.rb +63 -0
  111. data/app/controllers/rails_den/search_controller.rb +64 -0
  112. data/app/controllers/rails_den/tags_controller.rb +70 -0
  113. data/app/controllers/rails_den/topic_bookmarks_controller.rb +35 -0
  114. data/app/controllers/rails_den/topic_follows_controller.rb +117 -0
  115. data/app/controllers/rails_den/topic_read_positions_controller.rb +40 -0
  116. data/app/controllers/rails_den/topic_reports_controller.rb +18 -0
  117. data/app/controllers/rails_den/topics_controller.rb +583 -0
  118. data/app/dashboards/rails_den/administrator_dashboard.rb +51 -0
  119. data/app/dashboards/rails_den/board_dashboard.rb +57 -0
  120. data/app/dashboards/rails_den/category_dashboard.rb +50 -0
  121. data/app/dashboards/rails_den/profile_field_dashboard.rb +53 -0
  122. data/app/dashboards/rails_den/reaction_type_dashboard.rb +26 -0
  123. data/app/dashboards/rails_den/report_reason_dashboard.rb +57 -0
  124. data/app/dashboards/rails_den/tag_dashboard.rb +59 -0
  125. data/app/helpers/rails_den/application_helper.rb +39 -0
  126. data/app/helpers/rails_den/boards_helper.rb +4 -0
  127. data/app/helpers/rails_den/moderation/reports_helper.rb +42 -0
  128. data/app/helpers/rails_den/notifications_helper.rb +4 -0
  129. data/app/helpers/rails_den/posts_helper.rb +4 -0
  130. data/app/helpers/rails_den/topics_helper.rb +4 -0
  131. data/app/jobs/rails_den/application_job.rb +4 -0
  132. data/app/jobs/rails_den/expire_user_restriction_job.rb +9 -0
  133. data/app/jobs/rails_den/member_profile_stats_refresh_job.rb +48 -0
  134. data/app/jobs/rails_den/reconcile_user_restrictions_job.rb +9 -0
  135. data/app/jobs/rails_den/resolve_onebox_entry_job.rb +74 -0
  136. data/app/mailers/rails_den/application_mailer.rb +6 -0
  137. data/app/models/rails_den/administrator.rb +18 -0
  138. data/app/models/rails_den/advertising_configuration.rb +14 -0
  139. data/app/models/rails_den/application_record.rb +5 -0
  140. data/app/models/rails_den/badge.rb +142 -0
  141. data/app/models/rails_den/badge_award.rb +87 -0
  142. data/app/models/rails_den/board.rb +129 -0
  143. data/app/models/rails_den/board_follow.rb +18 -0
  144. data/app/models/rails_den/board_read_state.rb +60 -0
  145. data/app/models/rails_den/board_role_assignment.rb +45 -0
  146. data/app/models/rails_den/board_role_permission.rb +31 -0
  147. data/app/models/rails_den/board_role_requirement.rb +16 -0
  148. data/app/models/rails_den/board_sponsorship.rb +21 -0
  149. data/app/models/rails_den/bookmark.rb +31 -0
  150. data/app/models/rails_den/category.rb +81 -0
  151. data/app/models/rails_den/category_role_requirement.rb +16 -0
  152. data/app/models/rails_den/category_sponsorship.rb +21 -0
  153. data/app/models/rails_den/chat_channel.rb +44 -0
  154. data/app/models/rails_den/chat_channel_membership.rb +21 -0
  155. data/app/models/rails_den/chat_channel_read_state.rb +100 -0
  156. data/app/models/rails_den/chat_message.rb +37 -0
  157. data/app/models/rails_den/community_read_state.rb +52 -0
  158. data/app/models/rails_den/community_sponsorship.rb +15 -0
  159. data/app/models/rails_den/member_profile.rb +113 -0
  160. data/app/models/rails_den/mention.rb +20 -0
  161. data/app/models/rails_den/moderation_event.rb +16 -0
  162. data/app/models/rails_den/moderation_note.rb +11 -0
  163. data/app/models/rails_den/notification.rb +33 -0
  164. data/app/models/rails_den/notification_event.rb +40 -0
  165. data/app/models/rails_den/notification_mute.rb +9 -0
  166. data/app/models/rails_den/onebox_entry.rb +214 -0
  167. data/app/models/rails_den/post.rb +396 -0
  168. data/app/models/rails_den/post_draft.rb +63 -0
  169. data/app/models/rails_den/post_revision.rb +10 -0
  170. data/app/models/rails_den/private_conversation.rb +25 -0
  171. data/app/models/rails_den/private_conversation_participant.rb +16 -0
  172. data/app/models/rails_den/private_message.rb +35 -0
  173. data/app/models/rails_den/private_message_recipient.rb +44 -0
  174. data/app/models/rails_den/profile_field.rb +150 -0
  175. data/app/models/rails_den/profile_field_value.rb +136 -0
  176. data/app/models/rails_den/reaction.rb +56 -0
  177. data/app/models/rails_den/reaction_type.rb +23 -0
  178. data/app/models/rails_den/reader_visit.rb +61 -0
  179. data/app/models/rails_den/report.rb +127 -0
  180. data/app/models/rails_den/report_reason.rb +52 -0
  181. data/app/models/rails_den/reputation_event.rb +92 -0
  182. data/app/models/rails_den/role.rb +87 -0
  183. data/app/models/rails_den/role_assignment.rb +18 -0
  184. data/app/models/rails_den/role_permission.rb +41 -0
  185. data/app/models/rails_den/sluggable.rb +102 -0
  186. data/app/models/rails_den/sponsor.rb +20 -0
  187. data/app/models/rails_den/sponsorship.rb +87 -0
  188. data/app/models/rails_den/tag.rb +61 -0
  189. data/app/models/rails_den/theme_selection.rb +4 -0
  190. data/app/models/rails_den/topic.rb +307 -0
  191. data/app/models/rails_den/topic_follow.rb +18 -0
  192. data/app/models/rails_den/topic_read_state.rb +76 -0
  193. data/app/models/rails_den/topic_role_requirement.rb +16 -0
  194. data/app/models/rails_den/topic_tag.rb +16 -0
  195. data/app/models/rails_den/trust_level.rb +62 -0
  196. data/app/models/rails_den/trust_level_achievement.rb +21 -0
  197. data/app/models/rails_den/user_restriction.rb +54 -0
  198. data/app/models/rails_den/user_warning.rb +10 -0
  199. data/app/presenters/rails_den/member_profile_presenter.rb +285 -0
  200. data/app/queries/rails_den/topic_new_post_counts.rb +153 -0
  201. data/app/queries/rails_den/topic_unread_counts.rb +137 -0
  202. data/app/services/rails_den/board_following.rb +91 -0
  203. data/app/services/rails_den/bookmark_list.rb +109 -0
  204. data/app/services/rails_den/bookmark_state.rb +17 -0
  205. data/app/services/rails_den/built_in_role_provisioner.rb +98 -0
  206. data/app/services/rails_den/chat/broadcaster.rb +81 -0
  207. data/app/services/rails_den/chat/message_payload.rb +26 -0
  208. data/app/services/rails_den/chat/presence_snapshot.rb +269 -0
  209. data/app/services/rails_den/chat/spatial_broadcaster.rb +135 -0
  210. data/app/services/rails_den/chat/spatial_occupancy_snapshot.rb +35 -0
  211. data/app/services/rails_den/chat/spatial_registry.rb +682 -0
  212. data/app/services/rails_den/chat/spatial_room.rb +81 -0
  213. data/app/services/rails_den/chat/spatial_snapshot.rb +261 -0
  214. data/app/services/rails_den/chat/spatial_update.rb +243 -0
  215. data/app/services/rails_den/chat/subscription_token.rb +28 -0
  216. data/app/services/rails_den/chat/unread_counts.rb +79 -0
  217. data/app/services/rails_den/chat/whisper_delivery.rb +287 -0
  218. data/app/services/rails_den/chat_channel_access.rb +97 -0
  219. data/app/services/rails_den/default_report_reason_provisioner.rb +30 -0
  220. data/app/services/rails_den/emoji_catalog.rb +101 -0
  221. data/app/services/rails_den/gamification/activity_recorder.rb +69 -0
  222. data/app/services/rails_den/gamification/badge_evaluator.rb +55 -0
  223. data/app/services/rails_den/gamification/current_trust_level.rb +21 -0
  224. data/app/services/rails_den/gamification/metrics.rb +93 -0
  225. data/app/services/rails_den/gamification/reputation_ledger.rb +261 -0
  226. data/app/services/rails_den/gamification/trust_level_evaluator.rb +78 -0
  227. data/app/services/rails_den/global_moderation_policy.rb +32 -0
  228. data/app/services/rails_den/member_profile_bio_renderer.rb +28 -0
  229. data/app/services/rails_den/mentions/autocomplete.rb +69 -0
  230. data/app/services/rails_den/mentions/candidate.rb +19 -0
  231. data/app/services/rails_den/mentions/parser.rb +185 -0
  232. data/app/services/rails_den/mentions/reconciler.rb +46 -0
  233. data/app/services/rails_den/mentions/renderer.rb +165 -0
  234. data/app/services/rails_den/moderation_access.rb +42 -0
  235. data/app/services/rails_den/moderation_audit.rb +38 -0
  236. data/app/services/rails_den/moderation_event_scope.rb +12 -0
  237. data/app/services/rails_den/moderation_notification_delivery.rb +23 -0
  238. data/app/services/rails_den/notification_events/emitter.rb +226 -0
  239. data/app/services/rails_den/notification_preferences.rb +169 -0
  240. data/app/services/rails_den/notifications/broadcaster.rb +72 -0
  241. data/app/services/rails_den/notifications/materializer.rb +284 -0
  242. data/app/services/rails_den/notifications/subscription_token.rb +28 -0
  243. data/app/services/rails_den/permission_resolver.rb +252 -0
  244. data/app/services/rails_den/presence/board_summary.rb +131 -0
  245. data/app/services/rails_den/presence/broadcaster.rb +193 -0
  246. data/app/services/rails_den/presence/chat_room_registry.rb +263 -0
  247. data/app/services/rails_den/presence/community_summary.rb +166 -0
  248. data/app/services/rails_den/presence/directory.rb +499 -0
  249. data/app/services/rails_den/presence/last_seen.rb +23 -0
  250. data/app/services/rails_den/presence/page_map.rb +202 -0
  251. data/app/services/rails_den/presence/registry.rb +805 -0
  252. data/app/services/rails_den/presence/subscription_token.rb +28 -0
  253. data/app/services/rails_den/presence/visibility.rb +95 -0
  254. data/app/services/rails_den/private_messages/mailbox.rb +108 -0
  255. data/app/services/rails_den/private_messages/read_marker.rb +25 -0
  256. data/app/services/rails_den/private_messages/sender.rb +113 -0
  257. data/app/services/rails_den/private_messages/unread_count.rb +22 -0
  258. data/app/services/rails_den/reaction_summary.rb +54 -0
  259. data/app/services/rails_den/reaction_type_importer.rb +91 -0
  260. data/app/services/rails_den/reaction_type_search.rb +34 -0
  261. data/app/services/rails_den/readable_community_content.rb +156 -0
  262. data/app/services/rails_den/report_assignment.rb +31 -0
  263. data/app/services/rails_den/report_creator.rb +96 -0
  264. data/app/services/rails_den/report_delivery.rb +39 -0
  265. data/app/services/rails_den/report_disposition.rb +25 -0
  266. data/app/services/rails_den/report_recipients.rb +89 -0
  267. data/app/services/rails_den/report_review_policy.rb +66 -0
  268. data/app/services/rails_den/report_review_scope.rb +49 -0
  269. data/app/services/rails_den/restriction_resolver.rb +28 -0
  270. data/app/services/rails_den/role_resolver.rb +66 -0
  271. data/app/services/rails_den/search/adapters/basic.rb +35 -0
  272. data/app/services/rails_den/search/adapters/postgresql.rb +73 -0
  273. data/app/services/rails_den/search/adapters.rb +21 -0
  274. data/app/services/rails_den/search/query.rb +358 -0
  275. data/app/services/rails_den/topic_following.rb +91 -0
  276. data/app/services/rails_den/topic_merger.rb +27 -0
  277. data/app/services/rails_den/topic_moderation.rb +57 -0
  278. data/app/services/rails_den/topic_splitter.rb +36 -0
  279. data/app/services/rails_den/user_restriction_manager.rb +84 -0
  280. data/app/services/rails_den/user_warning_issuer.rb +19 -0
  281. data/app/views/admin/rails_den/advertising_management/_assignment_form.html.erb +340 -0
  282. data/app/views/admin/rails_den/advertising_management/_errors.html.erb +10 -0
  283. data/app/views/admin/rails_den/advertising_management/show.html.erb +324 -0
  284. data/app/views/admin/rails_den/badges/_form.html.erb +191 -0
  285. data/app/views/admin/rails_den/badges/edit.html.erb +14 -0
  286. data/app/views/admin/rails_den/badges/index.html.erb +98 -0
  287. data/app/views/admin/rails_den/badges/new.html.erb +14 -0
  288. data/app/views/admin/rails_den/board_access/show.html.erb +51 -0
  289. data/app/views/admin/rails_den/boards/_form.html.erb +5 -0
  290. data/app/views/admin/rails_den/categories/_form.html.erb +4 -0
  291. data/app/views/admin/rails_den/members/index.html.erb +121 -0
  292. data/app/views/admin/rails_den/members/show.html.erb +398 -0
  293. data/app/views/admin/rails_den/plugins/index.html.erb +49 -0
  294. data/app/views/admin/rails_den/profile_fields/_form.html.erb +169 -0
  295. data/app/views/admin/rails_den/reaction_types/_catalog_search.html.erb +22 -0
  296. data/app/views/admin/rails_den/reaction_types/_errors.html.erb +3 -0
  297. data/app/views/admin/rails_den/reaction_types/index.html.erb +66 -0
  298. data/app/views/admin/rails_den/reaction_types/new.html.erb +23 -0
  299. data/app/views/admin/rails_den/reaction_types/selected.html.erb +15 -0
  300. data/app/views/admin/rails_den/roles/_form.html.erb +64 -0
  301. data/app/views/admin/rails_den/roles/edit.html.erb +18 -0
  302. data/app/views/admin/rails_den/roles/index.html.erb +49 -0
  303. data/app/views/admin/rails_den/roles/new.html.erb +6 -0
  304. data/app/views/admin/rails_den/shared/_access_form.html.erb +59 -0
  305. data/app/views/admin/rails_den/themes/index.html.erb +129 -0
  306. data/app/views/admin/rails_den/trust_levels/_form.html.erb +80 -0
  307. data/app/views/admin/rails_den/trust_levels/edit.html.erb +14 -0
  308. data/app/views/admin/rails_den/trust_levels/index.html.erb +86 -0
  309. data/app/views/admin/rails_den/trust_levels/new.html.erb +14 -0
  310. data/app/views/administrate/application/_index_header.html.erb +68 -0
  311. data/app/views/administrate/application/new.html.erb +42 -0
  312. data/app/views/administrate/application/show.html.erb +89 -0
  313. data/app/views/layouts/administrate/application/_navigation.html.erb +61 -0
  314. data/app/views/layouts/administrate/application.html.erb +40 -0
  315. data/app/views/layouts/rails_den/_navbar.html.erb +293 -0
  316. data/app/views/layouts/rails_den/application.html.erb +198 -0
  317. data/app/views/rails_den/advertising/show.html.erb +13 -0
  318. data/app/views/rails_den/boards/_follow_button.html.erb +45 -0
  319. data/app/views/rails_den/boards/show.html.erb +150 -0
  320. data/app/views/rails_den/bookmarks/_post_control.html.erb +12 -0
  321. data/app/views/rails_den/bookmarks/_topic_control.html.erb +12 -0
  322. data/app/views/rails_den/bookmarks/index.html.erb +78 -0
  323. data/app/views/rails_den/chat_channels/_form.html.erb +174 -0
  324. data/app/views/rails_den/chat_channels/edit.html.erb +51 -0
  325. data/app/views/rails_den/chat_channels/index.html.erb +402 -0
  326. data/app/views/rails_den/chat_channels/new.html.erb +31 -0
  327. data/app/views/rails_den/chat_channels/show.html.erb +476 -0
  328. data/app/views/rails_den/home/index.html.erb +142 -0
  329. data/app/views/rails_den/member_profiles/_avatar.html.erb +18 -0
  330. data/app/views/rails_den/member_profiles/_custom_fields.html.erb +39 -0
  331. data/app/views/rails_den/member_profiles/_followed_boards.html.erb +15 -0
  332. data/app/views/rails_den/member_profiles/_followed_topics.html.erb +15 -0
  333. data/app/views/rails_den/member_profiles/_header.html.erb +103 -0
  334. data/app/views/rails_den/member_profiles/_recent_posts.html.erb +19 -0
  335. data/app/views/rails_den/member_profiles/_recent_topics.html.erb +15 -0
  336. data/app/views/rails_den/member_profiles/_stats.html.erb +11 -0
  337. data/app/views/rails_den/member_profiles/edit.html.erb +265 -0
  338. data/app/views/rails_den/member_profiles/show.html.erb +97 -0
  339. data/app/views/rails_den/member_reports/new.html.erb +1 -0
  340. data/app/views/rails_den/moderation/audit_events/index.html.erb +11 -0
  341. data/app/views/rails_den/moderation/member_histories/show.html.erb +314 -0
  342. data/app/views/rails_den/moderation/reports/index.html.erb +47 -0
  343. data/app/views/rails_den/moderation/reports/show.html.erb +344 -0
  344. data/app/views/rails_den/moderation/restrictions/new.html.erb +14 -0
  345. data/app/views/rails_den/moderation/topics/show.html.erb +14 -0
  346. data/app/views/rails_den/moderation/warnings/new.html.erb +10 -0
  347. data/app/views/rails_den/notifications/_empty_state.html.erb +13 -0
  348. data/app/views/rails_den/notifications/index.html.erb +308 -0
  349. data/app/views/rails_den/passwords/edit.html.erb +55 -0
  350. data/app/views/rails_den/passwords/new.html.erb +49 -0
  351. data/app/views/rails_den/passwords_mailer/reset.html.erb +22 -0
  352. data/app/views/rails_den/passwords_mailer/reset.text.erb +14 -0
  353. data/app/views/rails_den/post_reports/new.html.erb +1 -0
  354. data/app/views/rails_den/posts/_attachments.html.erb +94 -0
  355. data/app/views/rails_den/posts/edit.html.erb +95 -0
  356. data/app/views/rails_den/posts/history.html.erb +123 -0
  357. data/app/views/rails_den/presence/_directory_body.html.erb +155 -0
  358. data/app/views/rails_den/presence/_summary.html.erb +26 -0
  359. data/app/views/rails_den/presence/index.html.erb +29 -0
  360. data/app/views/rails_den/private_conversations/_composer.html.erb +28 -0
  361. data/app/views/rails_den/private_conversations/_conversation_row.html.erb +19 -0
  362. data/app/views/rails_den/private_conversations/_mailbox_nav.html.erb +7 -0
  363. data/app/views/rails_den/private_conversations/_message.html.erb +4 -0
  364. data/app/views/rails_den/private_conversations/_pagination.html.erb +6 -0
  365. data/app/views/rails_den/private_conversations/index.html.erb +11 -0
  366. data/app/views/rails_den/private_conversations/new.html.erb +5 -0
  367. data/app/views/rails_den/private_conversations/sent.html.erb +11 -0
  368. data/app/views/rails_den/private_conversations/show.html.erb +23 -0
  369. data/app/views/rails_den/reactions/_picker_options.html.erb +14 -0
  370. data/app/views/rails_den/reactions/_post_reactions.html.erb +87 -0
  371. data/app/views/rails_den/reactions/_reactor_list.html.erb +16 -0
  372. data/app/views/rails_den/reactions/index.html.erb +34 -0
  373. data/app/views/rails_den/reactions/picker.html.erb +14 -0
  374. data/app/views/rails_den/registrations/edit.html.erb +95 -0
  375. data/app/views/rails_den/registrations/new.html.erb +77 -0
  376. data/app/views/rails_den/reports/_form.html.erb +54 -0
  377. data/app/views/rails_den/search/index.html.erb +235 -0
  378. data/app/views/rails_den/sessions/new.html.erb +64 -0
  379. data/app/views/rails_den/shared/_ad_network_placement.html.erb +5 -0
  380. data/app/views/rails_den/shared/_composer.html.erb +266 -0
  381. data/app/views/rails_den/shared/_sponsor_placement.html.erb +44 -0
  382. data/app/views/rails_den/tags/show.html.erb +91 -0
  383. data/app/views/rails_den/topic_reports/new.html.erb +1 -0
  384. data/app/views/rails_den/topics/_author_badges.html.erb +15 -0
  385. data/app/views/rails_den/topics/_follow_button.html.erb +45 -0
  386. data/app/views/rails_den/topics/_summary.html.erb +121 -0
  387. data/app/views/rails_den/topics/_tags.html.erb +47 -0
  388. data/app/views/rails_den/topics/new.html.erb +181 -0
  389. data/app/views/rails_den/topics/show.html.erb +430 -0
  390. data/config/locales/rails_den.en.yml +65 -0
  391. data/config/routes.rb +442 -0
  392. data/db/migrate/20260820181527_create_rails_den_administrators.rb +12 -0
  393. data/db/migrate/20260820205806_create_rails_den_categories.rb +16 -0
  394. data/db/migrate/20260820212200_create_rails_den_boards.rb +23 -0
  395. data/db/migrate/20260820234758_create_rails_den_topics.rb +24 -0
  396. data/db/migrate/20260821001937_create_rails_den_posts.rb +11 -0
  397. data/db/migrate/20260822004306_add_soft_deletion_to_rails_den_posts.rb +9 -0
  398. data/db/migrate/20260823183944_create_rails_den_onebox_entries.rb +65 -0
  399. data/db/migrate/20260824025007_add_reply_to_to_rails_den_posts.rb +8 -0
  400. data/db/migrate/20260824092300_create_rails_den_post_revisions.rb +22 -0
  401. data/db/migrate/20260824103358_create_rails_den_post_drafts.rb +36 -0
  402. data/db/migrate/20260824142522_create_rails_den_topic_read_states.rb +32 -0
  403. data/db/migrate/20260824214941_create_rails_den_reader_visits.rb +16 -0
  404. data/db/migrate/20260824225733_create_rails_den_board_read_states.rb +34 -0
  405. data/db/migrate/20260824232113_create_rails_den_community_read_states.rb +26 -0
  406. data/db/migrate/20260825121109_create_rails_den_topic_follows.rb +36 -0
  407. data/db/migrate/20260825232623_create_rails_den_board_follows.rb +36 -0
  408. data/db/migrate/20260826003155_create_rails_den_mentions.rb +27 -0
  409. data/db/migrate/20260826080109_create_rails_den_member_profiles.rb +32 -0
  410. data/db/migrate/20260826092936_add_quoted_post_to_rails_den_posts_and_post_drafts.rb +17 -0
  411. data/db/migrate/20260826101755_create_rails_den_notification_events.rb +19 -0
  412. data/db/migrate/20260826121710_create_rails_den_notifications.rb +28 -0
  413. data/db/migrate/20260826140846_create_rails_den_notification_mutes.rb +25 -0
  414. data/db/migrate/20260826175426_create_rails_den_tags.rb +31 -0
  415. data/db/migrate/20260826175428_create_rails_den_topic_tags.rb +26 -0
  416. data/db/migrate/20260827021133_create_rails_den_roles.rb +16 -0
  417. data/db/migrate/20260827021135_create_rails_den_role_assignments.rb +22 -0
  418. data/db/migrate/20260827031134_create_rails_den_role_permissions.rb +28 -0
  419. data/db/migrate/20260827031136_create_rails_den_board_role_permissions.rb +35 -0
  420. data/db/migrate/20260827033116_provision_rails_den_built_in_roles_and_permissions.rb +108 -0
  421. data/db/migrate/20260827040515_create_rails_den_board_role_assignments.rb +33 -0
  422. data/db/migrate/20260827041710_create_rails_den_board_role_requirements.rb +27 -0
  423. data/db/migrate/20260827063901_create_rails_den_category_role_requirements.rb +23 -0
  424. data/db/migrate/20260827063909_create_rails_den_topic_role_requirements.rb +23 -0
  425. data/db/migrate/20260827203747_create_rails_den_report_reasons.rb +19 -0
  426. data/db/migrate/20260827203752_create_rails_den_reports.rb +24 -0
  427. data/db/migrate/20260827203807_provision_rails_den_default_report_reasons.rb +32 -0
  428. data/db/migrate/20260828030352_create_rails_den_moderation_events.rb +26 -0
  429. data/db/migrate/20260828030354_create_rails_den_moderation_notes.rb +13 -0
  430. data/db/migrate/20260828030357_create_rails_den_user_warnings.rb +17 -0
  431. data/db/migrate/20260828030359_create_rails_den_user_restrictions.rb +30 -0
  432. data/db/migrate/20260828030404_add_moderation_fields_to_rails_den_reports.rb +55 -0
  433. data/db/migrate/20260828030407_add_moderation_fields_to_rails_den_topics.rb +14 -0
  434. data/db/migrate/20260828031955_add_opening_post_to_rails_den_topics.rb +25 -0
  435. data/db/migrate/20260828083552_create_rails_den_reaction_types.rb +15 -0
  436. data/db/migrate/20260828083554_create_rails_den_reactions.rb +20 -0
  437. data/db/migrate/20260828083556_provision_rails_den_default_reaction_type.rb +18 -0
  438. data/db/migrate/20260828151420_create_rails_den_bookmarks.rb +20 -0
  439. data/db/migrate/20260828165545_create_rails_den_profile_fields.rb +37 -0
  440. data/db/migrate/20260828165547_create_rails_den_profile_field_values.rb +31 -0
  441. data/db/migrate/20260828191644_add_presence_fields_to_rails_den_member_profiles.rb +22 -0
  442. data/db/migrate/20260829003137_create_rails_den_private_conversations.rb +13 -0
  443. data/db/migrate/20260829003139_create_rails_den_private_conversation_participants.rb +35 -0
  444. data/db/migrate/20260829003141_create_rails_den_private_messages.rb +19 -0
  445. data/db/migrate/20260829003143_create_rails_den_private_message_recipients.rb +26 -0
  446. data/db/migrate/20260829003435_provision_rails_den_message_capability_defaults.rb +39 -0
  447. data/db/migrate/20260829013557_create_rails_den_chat_channels.rb +17 -0
  448. data/db/migrate/20260829014413_create_rails_den_chat_channel_memberships.rb +26 -0
  449. data/db/migrate/20260829014523_create_rails_den_chat_messages.rb +27 -0
  450. data/db/migrate/20260830155021_create_rails_den_chat_channel_read_states.rb +37 -0
  451. data/db/migrate/20260830155023_add_pinned_to_rails_den_chat_messages.rb +16 -0
  452. data/db/migrate/20260830185149_create_rails_den_badges.rb +44 -0
  453. data/db/migrate/20260830185151_create_rails_den_badge_awards.rb +43 -0
  454. data/db/migrate/20260830190053_create_rails_den_reputation_events.rb +52 -0
  455. data/db/migrate/20260830190055_create_rails_den_trust_levels.rb +52 -0
  456. data/db/migrate/20260830190057_create_rails_den_trust_level_achievements.rb +39 -0
  457. data/db/migrate/20260830190059_add_reputation_points_to_rails_den_member_profiles.rb +17 -0
  458. data/db/migrate/20260831120000_create_rails_den_advertising_and_sponsorships.rb +55 -0
  459. data/db/migrate/20260831120001_add_description_to_rails_den_sponsors.rb +5 -0
  460. data/db/migrate/20260831120002_add_advertising_page_to_rails_den_advertising_configurations.rb +7 -0
  461. data/db/migrate/20260831120003_add_destination_url_to_rails_den_sponsors.rb +5 -0
  462. data/db/migrate/20260831120004_move_destination_url_to_rails_den_sponsorships.rb +138 -0
  463. data/db/migrate/20260831204539_create_rails_den_theme_selections.rb +9 -0
  464. data/exe/rails_den +5 -0
  465. data/lib/generators/rails_den/install/install_generator.rb +42 -0
  466. data/lib/generators/rails_den/install/templates/rails_den.rb +75 -0
  467. data/lib/generators/rails_den/install/views/views_generator.rb +56 -0
  468. data/lib/rails/commands/rails_den/import/discourse/discourse_command.rb +48 -0
  469. data/lib/rails_den/cli.rb +174 -0
  470. data/lib/rails_den/configuration.rb +173 -0
  471. data/lib/rails_den/engine.rb +10 -0
  472. data/lib/rails_den/extensions/descriptor.rb +138 -0
  473. data/lib/rails_den/extensions/registry.rb +65 -0
  474. data/lib/rails_den/extensions.rb +49 -0
  475. data/lib/rails_den/formatters/markdown.rb +30 -0
  476. data/lib/rails_den/formatters/plain_text.rb +18 -0
  477. data/lib/rails_den/formatting.rb +130 -0
  478. data/lib/rails_den/gamification.rb +77 -0
  479. data/lib/rails_den/import/discourse/backup.rb +169 -0
  480. data/lib/rails_den/import/discourse/finalizer.rb +617 -0
  481. data/lib/rails_den/import/discourse/importer.rb +266 -0
  482. data/lib/rails_den/import/discourse/source_database.rb +320 -0
  483. data/lib/rails_den/import/discourse/staff_roles.rb +173 -0
  484. data/lib/rails_den/import/discourse/transformer.rb +940 -0
  485. data/lib/rails_den/import/discourse.rb +9 -0
  486. data/lib/rails_den/licensing/client.rb +128 -0
  487. data/lib/rails_den/licensing/result.rb +57 -0
  488. data/lib/rails_den/licensing.rb +28 -0
  489. data/lib/rails_den/metadata.rb +343 -0
  490. data/lib/rails_den/onebox/address_guard.rb +108 -0
  491. data/lib/rails_den/onebox/body_renderer.rb +135 -0
  492. data/lib/rails_den/onebox/embed_policy.rb +179 -0
  493. data/lib/rails_den/onebox/errors.rb +24 -0
  494. data/lib/rails_den/onebox/extractor.rb +130 -0
  495. data/lib/rails_den/onebox/fetcher.rb +199 -0
  496. data/lib/rails_den/onebox/http_transport.rb +164 -0
  497. data/lib/rails_den/onebox/metadata.rb +343 -0
  498. data/lib/rails_den/onebox/oembed/client.rb +46 -0
  499. data/lib/rails_den/onebox/oembed/discovery.rb +296 -0
  500. data/lib/rails_den/onebox/oembed/response.rb +375 -0
  501. data/lib/rails_den/onebox/oembed.rb +18 -0
  502. data/lib/rails_den/onebox/providers/base.rb +23 -0
  503. data/lib/rails_den/onebox/providers/bluesky.rb +402 -0
  504. data/lib/rails_den/onebox/providers/generic.rb +310 -0
  505. data/lib/rails_den/onebox/providers/giphy.rb +281 -0
  506. data/lib/rails_den/onebox/providers/github.rb +300 -0
  507. data/lib/rails_den/onebox/providers/mastodon.rb +505 -0
  508. data/lib/rails_den/onebox/providers/soundcloud.rb +199 -0
  509. data/lib/rails_den/onebox/providers/spotify.rb +196 -0
  510. data/lib/rails_den/onebox/providers/tenor.rb +296 -0
  511. data/lib/rails_den/onebox/providers/tiktok.rb +292 -0
  512. data/lib/rails_den/onebox/providers/twitch.rb +414 -0
  513. data/lib/rails_den/onebox/providers/vimeo.rb +295 -0
  514. data/lib/rails_den/onebox/providers/youtube.rb +201 -0
  515. data/lib/rails_den/onebox/registry.rb +41 -0
  516. data/lib/rails_den/onebox/renderer.rb +403 -0
  517. data/lib/rails_den/onebox/result.rb +86 -0
  518. data/lib/rails_den/onebox/synchronizer.rb +161 -0
  519. data/lib/rails_den/onebox/url.rb +35 -0
  520. data/lib/rails_den/onebox.rb +176 -0
  521. data/lib/rails_den/presence.rb +162 -0
  522. data/lib/rails_den/standalone_template.rb +700 -0
  523. data/lib/rails_den/themes.rb +80 -0
  524. data/lib/rails_den/version.rb +3 -0
  525. data/lib/rails_den/view_installer.rb +129 -0
  526. data/lib/rails_den.rb +113 -0
  527. data/lib/tasks/rails_den_tasks.rake +4 -0
  528. metadata +735 -0
@@ -0,0 +1,27 @@
1
+ module RailsDen
2
+ module Moderation
3
+ class ReportNotesController < RailsDen::ApplicationController
4
+ def create
5
+ report = RailsDen::Report.find(params[:report_id])
6
+ policy = RailsDen::ReportReviewPolicy.new(viewer: current_rails_den_user)
7
+ raise ActiveRecord::RecordNotFound unless policy.reviewable?(report)
8
+
9
+ note = nil
10
+ RailsDen::ModerationNote.transaction do
11
+ note = report.moderation_notes.create!(author: current_rails_den_user, body: params[:body])
12
+ RailsDen::ModerationAudit.record!(action: "moderation_note.created", subject: report,
13
+ actor: current_rails_den_user, report: report,
14
+ scope_board: report_board(report))
15
+ end
16
+ redirect_to moderation_report_path(report), status: :see_other, notice: "Private note added."
17
+ end
18
+
19
+ private
20
+
21
+ def report_board(report)
22
+ target = report.reportable
23
+ target.is_a?(RailsDen::Topic) ? target.board : target&.topic&.board
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,97 @@
1
+ module RailsDen
2
+ module Moderation
3
+ class ReportsController < RailsDen::ApplicationController
4
+ before_action :require_moderation_access!
5
+
6
+ def index
7
+ @reports = RailsDen::ReportReviewScope.call(
8
+ viewer: current_rails_den_user,
9
+ relation: RailsDen::Report.open
10
+ )
11
+ end
12
+
13
+ def history
14
+ relation = RailsDen::Report.history
15
+ relation = relation.where(status: params[:status]) if params[:status].in?(%w[resolved dismissed])
16
+ relation = relation.where(report_reason_id: params[:report_reason_id]) if params[:report_reason_id].present?
17
+ relation = relation.where(reportable_type: params[:reportable_type]) if params[:reportable_type].present?
18
+ @reports = RailsDen::ReportReviewScope.call(viewer: current_rails_den_user, relation: relation)
19
+ @reasons = RailsDen::ReportReason.ordered
20
+ render :index
21
+ end
22
+
23
+ def show
24
+ @report = RailsDen::Report.includes(
25
+ :report_reason,
26
+ :reporter,
27
+ :reportable
28
+ ).find(params[:id])
29
+
30
+ policy = RailsDen::ReportReviewPolicy.new(
31
+ viewer: current_rails_den_user
32
+ )
33
+
34
+ raise ActiveRecord::RecordNotFound unless policy.reviewable?(@report)
35
+
36
+ @live_target_available = policy.live_target_readable?(@report)
37
+ @notes = @report.moderation_notes.includes(:author).order(:created_at, :id)
38
+ @related_reports = RailsDen::ReportReviewScope.call(
39
+ viewer: current_rails_den_user,
40
+ relation: RailsDen::Report.open.where(subject_user: @report.subject_user).where.not(id: @report.id)
41
+ )
42
+ @subject_reports = RailsDen::ReportReviewScope.call(
43
+ viewer: current_rails_den_user,
44
+ relation: RailsDen::Report.where(subject_user: @report.subject_user)
45
+ )
46
+ @global_moderator = policy.global_moderator?
47
+ end
48
+
49
+
50
+ def claim
51
+ load_reviewable_report
52
+ RailsDen::ReportAssignment.call(report: @report, actor: current_rails_den_user,
53
+ assignee: current_rails_den_user)
54
+ redirect_to moderation_report_path(@report), status: :see_other, notice: "Report claimed."
55
+ end
56
+
57
+ def release
58
+ load_reviewable_report
59
+ RailsDen::ReportAssignment.call(report: @report, actor: current_rails_den_user, assignee: nil)
60
+ redirect_to moderation_report_path(@report), status: :see_other, notice: "Report released."
61
+ end
62
+
63
+ %i[resolve dismiss reopen].each do |transition|
64
+ define_method(transition) do
65
+ load_reviewable_report
66
+ status = { resolve: "resolved", dismiss: "dismissed", reopen: "open" }.fetch(transition)
67
+ RailsDen::ReportDisposition.call(report: @report, actor: current_rails_den_user,
68
+ status: status, note: params[:note])
69
+ if @report.errors.any?
70
+ show
71
+ render :show, status: :unprocessable_entity
72
+ else
73
+ redirect_to moderation_report_path(@report), status: :see_other,
74
+ notice: "Report #{status}."
75
+ end
76
+ end
77
+ end
78
+
79
+ private
80
+
81
+ def require_moderation_access!
82
+ return if RailsDen::ModerationAccess.allowed?(
83
+ user: current_rails_den_user
84
+ )
85
+
86
+ raise ActiveRecord::RecordNotFound
87
+ end
88
+
89
+ def load_reviewable_report
90
+ @report = RailsDen::Report.find(params[:id])
91
+ raise ActiveRecord::RecordNotFound unless RailsDen::ReportReviewPolicy.new(
92
+ viewer: current_rails_den_user
93
+ ).reviewable?(@report)
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,71 @@
1
+ module RailsDen
2
+ module Moderation
3
+ class RestrictionsController < RailsDen::ApplicationController
4
+ before_action :load_member, only: %i[new create]
5
+ before_action :authorize_member!, only: %i[new create]
6
+
7
+ def new
8
+ @restriction = RailsDen::UserRestriction.new(kind: params[:kind], source_report_id: params[:report_id])
9
+ @active_restrictions = RailsDen::RestrictionResolver.active_restrictions(user: @member)
10
+ end
11
+
12
+ def create
13
+ attributes = restriction_params
14
+ @restriction = RailsDen::UserRestrictionManager.create!(
15
+ user: @member, actor: current_rails_den_user, kind: attributes[:kind],
16
+ reason: attributes[:reason], internal_note: attributes[:internal_note],
17
+ ends_at: parsed_ends_at(attributes), indefinite: attributes[:indefinite] == "1",
18
+ source_report: report_from(attributes[:source_report_id])
19
+ )
20
+ redirect_to moderation_member_history_path(mention_name: RailsDen.user_mention_name(@member)),
21
+ status: :see_other, notice: "#{@restriction.kind.humanize} imposed."
22
+ rescue ActiveRecord::RecordInvalid => error
23
+ @restriction = error.record
24
+ @active_restrictions = RailsDen::RestrictionResolver.active_restrictions(user: @member)
25
+ render :new, status: :unprocessable_entity
26
+ end
27
+
28
+ def lift
29
+ @restriction = RailsDen::UserRestriction.find(params[:id])
30
+ RailsDen::UserRestrictionManager.lift!(restriction: @restriction,
31
+ actor: current_rails_den_user,
32
+ reason: params[:lift_reason])
33
+ redirect_to moderation_member_history_path(
34
+ mention_name: RailsDen.user_mention_name(@restriction.user)
35
+ ), status: :see_other, notice: "Restriction lifted."
36
+ end
37
+
38
+ private
39
+
40
+ def load_member
41
+ @member = RailsDen.resolve_user_mention(params[:mention_name].to_s.downcase) || raise(ActiveRecord::RecordNotFound)
42
+ end
43
+
44
+ def authorize_member!
45
+ raise ActiveRecord::RecordNotFound unless RailsDen::GlobalModerationPolicy.new(
46
+ actor: current_rails_den_user
47
+ ).may_sanction?(@member)
48
+ end
49
+
50
+ def restriction_params
51
+ params.fetch(:restriction, {}).permit(:kind, :reason, :internal_note, :duration_minutes,
52
+ :indefinite, :source_report_id)
53
+ end
54
+
55
+ def parsed_ends_at(attributes)
56
+ return if attributes[:indefinite] == "1"
57
+ minutes = Integer(attributes[:duration_minutes], exception: false)
58
+ minutes&.positive? ? Time.current + minutes.minutes : nil
59
+ end
60
+
61
+ def report_from(id)
62
+ return if id.blank?
63
+ report = RailsDen::Report.find(id)
64
+ raise ActiveRecord::RecordNotFound unless RailsDen::ReportReviewPolicy.new(
65
+ viewer: current_rails_den_user
66
+ ).reviewable?(report)
67
+ report
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,61 @@
1
+ module RailsDen
2
+ module Moderation
3
+ class TopicsController < RailsDen::ApplicationController
4
+ before_action :load_topic
5
+
6
+ def show
7
+ RailsDen::TopicModeration.authorize!(current_rails_den_user, @topic)
8
+ end
9
+
10
+ def pin = toggle(:pinned, true, "Topic pinned.")
11
+ def unpin = toggle(:pinned, false, "Topic unpinned.")
12
+ def lock = toggle(:locked, true, "Topic locked.")
13
+ def unlock = toggle(:locked, false, "Topic unlocked.")
14
+
15
+ def move
16
+ destination = RailsDen::Board.find(params[:board_id])
17
+ RailsDen::TopicModeration.move!(topic: @topic, destination: destination,
18
+ actor: current_rails_den_user)
19
+ redirect_to topic_path(@topic), status: :see_other, notice: "Topic moved."
20
+ end
21
+
22
+ def merge
23
+ destination = RailsDen::Topic.find(params[:destination_topic_id])
24
+ RailsDen::TopicMerger.call(source: @topic, destination: destination,
25
+ actor: current_rails_den_user)
26
+ redirect_to topic_path(destination), status: :see_other, notice: "Topics merged."
27
+ end
28
+
29
+ def split
30
+ starting_post = @topic.posts.find(params[:post_id])
31
+ destination = RailsDen::Board.find(params[:board_id])
32
+ new_topic = RailsDen::TopicSplitter.call(source: @topic, starting_post: starting_post,
33
+ destination_board: destination,
34
+ title: params[:title], actor: current_rails_den_user)
35
+ redirect_to topic_path(new_topic), status: :see_other, notice: "Topic split."
36
+ end
37
+
38
+ def destroy
39
+ RailsDen::TopicModeration.soft_delete!(topic: @topic, actor: current_rails_den_user)
40
+ redirect_to board_path(@topic.board), status: :see_other, notice: "Topic deleted."
41
+ end
42
+
43
+ def restore
44
+ RailsDen::TopicModeration.restore!(topic: @topic, actor: current_rails_den_user)
45
+ redirect_to topic_path(@topic), status: :see_other, notice: "Topic restored."
46
+ end
47
+
48
+ private
49
+
50
+ def load_topic
51
+ @topic = RailsDen::Topic.find(params[:id])
52
+ end
53
+
54
+ def toggle(attribute, value, notice)
55
+ RailsDen::TopicModeration.toggle!(topic: @topic, actor: current_rails_den_user,
56
+ attribute: attribute, value: value)
57
+ redirect_to topic_path(@topic), status: :see_other, notice: notice
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,50 @@
1
+ module RailsDen
2
+ module Moderation
3
+ class WarningsController < RailsDen::ApplicationController
4
+ before_action :load_member
5
+ before_action :authorize!
6
+
7
+ def new
8
+ @warning = RailsDen::UserWarning.new(report_id: params[:report_id])
9
+ end
10
+
11
+ def create
12
+ @warning = RailsDen::UserWarningIssuer.call(
13
+ user: @member, actor: current_rails_den_user,
14
+ message: warning_params[:message], internal_note: warning_params[:internal_note],
15
+ report: report_from_params
16
+ )
17
+ redirect_to moderation_member_history_path(mention_name: RailsDen.user_mention_name(@member)),
18
+ status: :see_other, notice: "Warning issued."
19
+ rescue ActiveRecord::RecordInvalid => error
20
+ @warning = error.record
21
+ render :new, status: :unprocessable_entity
22
+ end
23
+
24
+ private
25
+
26
+ def load_member
27
+ @member = RailsDen.resolve_user_mention(params[:mention_name].to_s.downcase) || raise(ActiveRecord::RecordNotFound)
28
+ end
29
+
30
+ def authorize!
31
+ raise ActiveRecord::RecordNotFound unless RailsDen::GlobalModerationPolicy.new(
32
+ actor: current_rails_den_user
33
+ ).may_sanction?(@member)
34
+ end
35
+
36
+ def warning_params
37
+ params.fetch(:warning, {}).permit(:message, :internal_note, :report_id)
38
+ end
39
+
40
+ def report_from_params
41
+ return if warning_params[:report_id].blank?
42
+ report = RailsDen::Report.find(warning_params[:report_id])
43
+ raise ActiveRecord::RecordNotFound unless RailsDen::ReportReviewPolicy.new(
44
+ viewer: current_rails_den_user
45
+ ).reviewable?(report)
46
+ report
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,116 @@
1
+ module RailsDen
2
+ class NotificationPreferencesController < ApplicationController
3
+ def update
4
+ notifiable = find_notifiable
5
+
6
+ result =
7
+ RailsDen::NotificationPreferences.set(
8
+ recipient: current_rails_den_user,
9
+ notifiable: notifiable,
10
+ level: preference_level
11
+ )
12
+
13
+ respond_to do |format|
14
+ format.html do
15
+ redirect_to destination_path(notifiable),
16
+ status: :see_other,
17
+ notice: "Notification preference updated."
18
+ end
19
+
20
+ format.json do
21
+ render json: {
22
+ level: result.level.to_s,
23
+ changed: result.changed,
24
+ notifiable_type:
25
+ notifiable.class.polymorphic_name,
26
+ notifiable_id: notifiable.id
27
+ }
28
+ end
29
+ end
30
+ rescue ArgumentError => error
31
+ respond_to do |format|
32
+ format.html do
33
+ head :unprocessable_entity
34
+ end
35
+
36
+ format.json do
37
+ render json: {
38
+ errors: [
39
+ error.message
40
+ ]
41
+ },
42
+ status: :unprocessable_entity
43
+ end
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def preference_level
50
+ params
51
+ .require(:notification_preference)
52
+ .fetch(:level)
53
+ end
54
+
55
+ def find_notifiable
56
+ if params[:topic_id]
57
+ find_readable_topic
58
+ elsif params[:board_id]
59
+ find_readable_board
60
+ else
61
+ raise ActiveRecord::RecordNotFound
62
+ end
63
+ end
64
+
65
+ def find_readable_topic
66
+ topic =
67
+ RailsDen::Topic.find(
68
+ params[:topic_id]
69
+ )
70
+
71
+ readable_content =
72
+ ReadableCommunityContent.new(
73
+ viewer: current_rails_den_user
74
+ )
75
+
76
+ unless readable_content.topic_readable?(
77
+ topic
78
+ )
79
+ raise ActiveRecord::RecordNotFound
80
+ end
81
+
82
+ topic
83
+ end
84
+
85
+ def find_readable_board
86
+ board =
87
+ RailsDen::Board.find(
88
+ params[:board_id]
89
+ )
90
+
91
+ readable_content =
92
+ ReadableCommunityContent.new(
93
+ viewer: current_rails_den_user
94
+ )
95
+
96
+ unless readable_content.board_readable?(
97
+ board
98
+ )
99
+ raise ActiveRecord::RecordNotFound
100
+ end
101
+
102
+ board
103
+ end
104
+
105
+ def destination_path(notifiable)
106
+ case notifiable
107
+ when RailsDen::Topic
108
+ topic_path(notifiable)
109
+ when RailsDen::Board
110
+ board_path(notifiable)
111
+ else
112
+ raise ActiveRecord::RecordNotFound
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,67 @@
1
+ module RailsDen
2
+ class NotificationsController < ApplicationController
3
+ def index
4
+ @notifications =
5
+ RailsDen::Notification
6
+ .includes(
7
+ notification_event: [
8
+ :actor,
9
+ :subject,
10
+ :target
11
+ ]
12
+ )
13
+ .where(
14
+ recipient: current_rails_den_user
15
+ )
16
+ .order(created_at: :desc)
17
+ .select do |notification|
18
+ readable_content.notification_readable?(
19
+ notification
20
+ )
21
+ end
22
+
23
+ mark_notifications_read
24
+ end
25
+
26
+ def destroy
27
+ notification =
28
+ RailsDen::Notification.find_by(
29
+ id: params[:id],
30
+ recipient: current_rails_den_user
31
+ )
32
+
33
+ notification&.destroy!
34
+
35
+ redirect_to notifications_path,
36
+ status: :see_other
37
+ end
38
+
39
+ private
40
+
41
+ def readable_content
42
+ @readable_content ||=
43
+ RailsDen::ReadableCommunityContent.new(
44
+ viewer: current_rails_den_user
45
+ )
46
+ end
47
+
48
+ def mark_notifications_read
49
+ updated =
50
+ RailsDen::Notification
51
+ .where(
52
+ recipient: current_rails_den_user,
53
+ read_at: nil
54
+ )
55
+ .update_all(
56
+ read_at: Time.current,
57
+ updated_at: Time.current
58
+ )
59
+
60
+ return unless updated.positive?
61
+
62
+ RailsDen::Notifications::Broadcaster.call(
63
+ recipient: current_rails_den_user
64
+ )
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,29 @@
1
+ module RailsDen
2
+ class PostBookmarksController < ApplicationController
3
+ def create
4
+ @topic = Topic.find(params[:topic_id])
5
+ readable = ReadableCommunityContent.new(viewer: current_rails_den_user)
6
+ raise ActiveRecord::RecordNotFound unless readable.topic_readable?(@topic)
7
+ @post = @topic.posts.where(deleted_at: nil).find(params[:post_id])
8
+ raise ActiveRecord::RecordNotFound unless readable.post_readable?(@post)
9
+
10
+ bookmark = Bookmark.create_or_find_by!(bookmarker: current_rails_den_user, bookmarkable: @post)
11
+ respond_to do |format|
12
+ format.html { redirect_to_post }
13
+ format.json { render json: { html: render_control(bookmark) } }
14
+ end
15
+ rescue ActiveRecord::RecordNotUnique
16
+ retry
17
+ end
18
+
19
+ private
20
+
21
+ def render_control(bookmark)
22
+ render_to_string(partial: "rails_den/bookmarks/post_control", formats: [:html], locals: { post: @post, bookmark: bookmark })
23
+ end
24
+
25
+ def redirect_to_post
26
+ redirect_to topic_path(@topic, page: @topic.posts_page_for(@post), anchor: "post-#{@post.id}")
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,125 @@
1
+ module RailsDen
2
+ class PostDraftsController < ApplicationController
3
+ before_action :require_rails_den_participation!, only: :update
4
+ before_action :find_readable_topic
5
+ before_action :require_reply_permission!,
6
+ only: :update
7
+ before_action :require_unlocked_topic!,
8
+ only: :update
9
+
10
+ def update
11
+ body =
12
+ draft_params[:body]
13
+ .to_s
14
+
15
+ if body.blank?
16
+ destroy_current_draft
17
+
18
+ return head :no_content
19
+ end
20
+
21
+ quoted_post =
22
+ find_quoted_post
23
+
24
+ draft =
25
+ PostDraft.find_or_initialize_by(
26
+ topic: @topic,
27
+ author: current_rails_den_user
28
+ )
29
+
30
+ draft.body = body
31
+ draft.quoted_post = quoted_post
32
+ draft.reply_to =
33
+ quoted_post || find_reply_to_post
34
+
35
+ if draft.save
36
+ head :no_content
37
+ else
38
+ render json: {
39
+ errors: draft.errors.full_messages
40
+ },
41
+ status: :unprocessable_entity
42
+ end
43
+ end
44
+
45
+ def destroy
46
+ destroy_current_draft
47
+
48
+ head :no_content
49
+ end
50
+
51
+ private
52
+
53
+ def find_readable_topic
54
+ @topic =
55
+ Topic.find(
56
+ params[:topic_id]
57
+ )
58
+
59
+ readable_content =
60
+ ReadableCommunityContent.new(
61
+ viewer: current_rails_den_user
62
+ )
63
+
64
+ unless readable_content.topic_readable?(
65
+ @topic
66
+ )
67
+ raise ActiveRecord::RecordNotFound
68
+ end
69
+ end
70
+
71
+ def require_reply_permission!
72
+ return if PermissionResolver.allowed?(
73
+ user: current_rails_den_user,
74
+ capability: :reply,
75
+ board: @topic.board
76
+ )
77
+
78
+ head :forbidden
79
+ end
80
+
81
+ def require_unlocked_topic!
82
+ return unless @topic.locked?
83
+
84
+ head :forbidden
85
+ end
86
+
87
+ def find_reply_to_post
88
+ reply_to_id =
89
+ draft_params[:reply_to_id]
90
+
91
+ return if reply_to_id.blank?
92
+
93
+ @topic.posts.find(reply_to_id)
94
+ end
95
+
96
+ def find_quoted_post
97
+ quoted_post_id =
98
+ draft_params[:quoted_post_id]
99
+
100
+ return if quoted_post_id.blank?
101
+
102
+ @topic
103
+ .posts
104
+ .where(deleted_at: nil)
105
+ .find(quoted_post_id)
106
+ end
107
+
108
+ def destroy_current_draft
109
+ PostDraft.find_by(
110
+ topic: @topic,
111
+ author: current_rails_den_user
112
+ )&.destroy!
113
+ end
114
+
115
+ def draft_params
116
+ params
117
+ .require(:rails_den_post_draft)
118
+ .permit(
119
+ :body,
120
+ :reply_to_id,
121
+ :quoted_post_id
122
+ )
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,31 @@
1
+ module RailsDen
2
+ class PostReportsController < ReportsController
3
+ private
4
+
5
+ def load_reportable!
6
+ topic = Topic.find(params[:topic_id])
7
+ readable = ReadableCommunityContent.new(viewer: current_rails_den_user)
8
+ raise ActiveRecord::RecordNotFound unless readable.topic_readable?(topic)
9
+
10
+ @reportable = readable.posts.where(topic: topic).find(params[:post_id])
11
+ end
12
+
13
+ def report_heading
14
+ "Report post by #{RailsDen.user_display_name(@reportable.author)}"
15
+ end
16
+
17
+ def report_subject = @reportable.body.to_s.truncate(120)
18
+
19
+ def report_submit_path
20
+ topic_post_report_path(@reportable.topic, @reportable)
21
+ end
22
+
23
+ def report_return_path
24
+ topic_path(
25
+ @reportable.topic,
26
+ page: @reportable.topic.posts_page_for(@reportable),
27
+ anchor: "post-#{@reportable.id}"
28
+ )
29
+ end
30
+ end
31
+ end