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,700 @@
1
+ require "securerandom"
2
+ require "shellwords"
3
+
4
+ rails_den_spec = Gem.loaded_specs["rails_den"]
5
+ rails_den_source = rails_den_spec&.full_gem_path
6
+
7
+ gem "rails_den", "~> #{RailsDen::VERSION}"
8
+
9
+
10
+ application <<~RUBY
11
+ config.react.sprockets_strategy = false
12
+ RUBY
13
+
14
+ after_bundle do
15
+ bin_rails_path = "bin/rails"
16
+ bin_rails = File.read(bin_rails_path)
17
+
18
+ rails_den_server_default = <<~'RAILS_DEN_SERVER'
19
+ rails_den_server_command = %w[server s].include?(ARGV.first)
20
+
21
+ rails_den_environment_explicit =
22
+ ENV["RAILS_ENV"] ||
23
+ ENV["RACK_ENV"] ||
24
+ ARGV.any? { |argument|
25
+ argument == "-e" ||
26
+ argument == "--environment" ||
27
+ argument.start_with?("--environment=")
28
+ }
29
+
30
+ if rails_den_server_command && !rails_den_environment_explicit
31
+ ENV["RAILS_ENV"] = "production"
32
+ end
33
+
34
+ RAILS_DEN_SERVER
35
+
36
+ unless bin_rails.include?('ENV["RAILS_ENV"] = "production"')
37
+ bin_rails.sub!(
38
+ "#!/usr/bin/env ruby\n",
39
+ "#!/usr/bin/env ruby\n\n#{rails_den_server_default}"
40
+ )
41
+
42
+ File.write(bin_rails_path, bin_rails)
43
+ end
44
+
45
+ generate "authentication"
46
+ generate "simple_form:install"
47
+
48
+ generate "migration",
49
+ "AddUsernameToUsers",
50
+ "username:string"
51
+
52
+ username_migration =
53
+ Dir["db/migrate/*_add_username_to_users.rb"].max
54
+
55
+ File.write(
56
+ username_migration,
57
+ <<~RUBY
58
+ class AddUsernameToUsers < ActiveRecord::Migration[8.1]
59
+ def change
60
+ add_column :users,
61
+ :username,
62
+ :string,
63
+ null: false
64
+
65
+ add_index :users,
66
+ :username,
67
+ unique: true
68
+ end
69
+ end
70
+ RUBY
71
+ )
72
+
73
+ remove_file "app/models/user.rb"
74
+
75
+ file "app/models/user.rb", <<~RUBY
76
+ class User < ApplicationRecord
77
+ has_secure_password
78
+ has_many :sessions, dependent: :destroy
79
+
80
+ attr_accessor :current_password
81
+
82
+ normalizes :email_address,
83
+ with: ->(email) {
84
+ email.strip.downcase
85
+ }
86
+
87
+ normalizes :username,
88
+ with: ->(username) {
89
+ username.strip.downcase
90
+ }
91
+
92
+ validates :username,
93
+ presence: true,
94
+ uniqueness: {
95
+ case_sensitive: false
96
+ },
97
+ length: {
98
+ in: 3..30
99
+ },
100
+ format: {
101
+ with: /\\A[a-z0-9_]+\\z/,
102
+ message: "may contain only letters, numbers, and underscores"
103
+ }
104
+ end
105
+ RUBY
106
+
107
+ file "app/controllers/rails_den_controller.rb", <<~RUBY
108
+ class RailsDenController < ApplicationController
109
+ allow_unauthenticated_access
110
+ end
111
+ RUBY
112
+
113
+ remove_file "app/controllers/sessions_controller.rb"
114
+
115
+ file "app/controllers/sessions_controller.rb", <<~RUBY
116
+ class SessionsController < ApplicationController
117
+ allow_unauthenticated_access only: %i[new create]
118
+
119
+ rate_limit to: 10,
120
+ within: 3.minutes,
121
+ only: :create,
122
+ with: -> {
123
+ redirect_to new_session_path,
124
+ alert: "Try again later."
125
+ }
126
+
127
+ layout "rails_den/application"
128
+
129
+ def new
130
+ render "rails_den/sessions/new"
131
+ end
132
+
133
+ def create
134
+ credentials = params.expect(
135
+ session: [
136
+ :email_address,
137
+ :password
138
+ ]
139
+ )
140
+
141
+ if user = User.authenticate_by(credentials)
142
+ state = RailsDen::RestrictionResolver.account_state(user: user)
143
+
144
+ if state == "active"
145
+ start_new_session_for(user)
146
+ redirect_to after_authentication_url
147
+ elsif state == "suspended"
148
+ restriction = RailsDen::RestrictionResolver.active_restrictions(user: user)
149
+ .select { |item| item.kind == "suspension" }
150
+ .max_by(&:ends_at)
151
+
152
+ redirect_to new_session_path,
153
+ alert: "Your community access is suspended until \#{I18n.l(restriction.ends_at, format: :long)}."
154
+ else
155
+ redirect_to new_session_path,
156
+ alert: "Your community access has been banned."
157
+ end
158
+ else
159
+ redirect_to new_session_path,
160
+ alert: "Try another email address or password."
161
+ end
162
+ end
163
+
164
+ def destroy
165
+ terminate_session
166
+ redirect_to new_session_path, status: :see_other
167
+ end
168
+ end
169
+ RUBY
170
+
171
+ file "app/controllers/registrations_controller.rb", <<~RUBY
172
+ class RegistrationsController < ApplicationController
173
+ allow_unauthenticated_access only: %i[new create]
174
+
175
+ before_action :ensure_registration_enabled, only: %i[new create]
176
+
177
+ layout "rails_den/application"
178
+
179
+ def new
180
+ @user = User.new
181
+
182
+ render "rails_den/registrations/new"
183
+ end
184
+
185
+ def create
186
+ @user = User.new(registration_params)
187
+
188
+ if save_registration
189
+ start_new_session_for(@user)
190
+ redirect_to "/rails_den"
191
+ else
192
+ render "rails_den/registrations/new",
193
+ status: :unprocessable_entity
194
+ end
195
+ end
196
+
197
+ def edit
198
+ @user = Current.user
199
+
200
+ render "rails_den/registrations/edit"
201
+ end
202
+
203
+ def update
204
+ @user = Current.user
205
+ @user.current_password = params.dig(:user, :current_password)
206
+
207
+ unless @user.authenticate(@user.current_password.to_s)
208
+ @user.errors.add(:base, "Current password is incorrect.")
209
+
210
+ return render "rails_den/registrations/edit",
211
+ status: :unprocessable_entity
212
+ end
213
+
214
+ attributes = account_params.to_h.symbolize_keys
215
+
216
+ if attributes[:password].blank?
217
+ attributes.delete(:password)
218
+ attributes.delete(:password_confirmation)
219
+ end
220
+
221
+ if @user.update(attributes)
222
+ destroy_other_sessions if attributes.key?(:password)
223
+
224
+ redirect_to edit_registration_path,
225
+ notice: "Your account has been updated."
226
+ else
227
+ render "rails_den/registristrations/edit",
228
+ status: :unprocessable_entity
229
+ end
230
+ end
231
+
232
+ private
233
+
234
+ def save_registration
235
+ User.transaction do
236
+ next false unless @user.save
237
+
238
+ if User.count == 1
239
+ RailsDen::Administrator.create!(
240
+ user: @user
241
+ )
242
+ end
243
+
244
+ true
245
+ end
246
+ end
247
+
248
+ def registration_params
249
+ params.require(:user).permit(
250
+ :username,
251
+ :email_address,
252
+ :password,
253
+ :password_confirmation
254
+ )
255
+ end
256
+
257
+ def account_params
258
+ params.require(:user).permit(
259
+ :username,
260
+ :email_address,
261
+ :password,
262
+ :password_confirmation
263
+ )
264
+ end
265
+
266
+ def destroy_other_sessions
267
+ @user.sessions.where.not(id: Current.session.id).destroy_all
268
+ end
269
+
270
+ def ensure_registration_enabled
271
+ head :not_found unless RailsDen.config.registration_enabled
272
+ end
273
+ end
274
+ RUBY
275
+
276
+ remove_file "app/controllers/passwords_controller.rb"
277
+
278
+ file "app/controllers/passwords_controller.rb", <<~RUBY
279
+ class PasswordsController < ApplicationController
280
+ allow_unauthenticated_access
281
+
282
+ before_action :set_user_by_token,
283
+ only: %i[edit update]
284
+
285
+ rate_limit to: 10,
286
+ within: 3.minutes,
287
+ only: :create,
288
+ with: -> {
289
+ redirect_to new_password_path,
290
+ alert: "Try again later."
291
+ }
292
+
293
+ layout "rails_den/application"
294
+
295
+ def new
296
+ render "rails_den/passwords/new"
297
+ end
298
+
299
+ def create
300
+ email_address = password_reset_params[:email_address]
301
+
302
+ if user = User.find_by(email_address: email_address)
303
+ PasswordsMailer.reset(user).deliver_later
304
+ end
305
+
306
+ redirect_to new_session_path,
307
+ notice: "Password reset instructions sent (if user with that email address exists)."
308
+ end
309
+
310
+ def edit
311
+ render "rails_den/passwords/edit"
312
+ end
313
+
314
+ def update
315
+ if @user.update(password_params)
316
+ @user.sessions.destroy_all
317
+
318
+ redirect_to new_session_path,
319
+ notice: "Password has been reset."
320
+ else
321
+ redirect_to edit_password_path(params[:token]),
322
+ alert: "Passwords did not match."
323
+ end
324
+ end
325
+
326
+ private
327
+
328
+ def password_reset_params
329
+ params.require(:password_reset).permit(
330
+ :email_address
331
+ )
332
+ end
333
+
334
+ def password_params
335
+ params.require(:password).permit(
336
+ :password,
337
+ :password_confirmation
338
+ )
339
+ end
340
+
341
+ def set_user_by_token
342
+ @user = User.find_by_password_reset_token!(
343
+ params[:token]
344
+ )
345
+ rescue ActiveSupport::MessageVerifier::InvalidSignature
346
+ redirect_to new_password_path,
347
+ alert: "Password reset link is invalid or has expired."
348
+ end
349
+ end
350
+ RUBY
351
+
352
+ remove_file "app/mailers/passwords_mailer.rb"
353
+
354
+ file "app/mailers/passwords_mailer.rb", <<~RUBY
355
+ class PasswordsMailer < ApplicationMailer
356
+ def reset(user)
357
+ @user = user
358
+
359
+ mail(
360
+ subject: "Reset your password",
361
+ to: user.email_address,
362
+ template_path: "rails_den/passwords_mailer",
363
+ template_name: "reset"
364
+ )
365
+ end
366
+ end
367
+ RUBY
368
+
369
+ remove_file "app/views/sessions/new.html.erb"
370
+ remove_file "app/views/passwords/new.html.erb"
371
+ remove_file "app/views/passwords/edit.html.erb"
372
+ remove_file "app/views/passwords_mailer/reset.html.erb"
373
+ remove_file "app/views/passwords_mailer/reset.text.erb"
374
+
375
+ rails_den_stylesheet =
376
+ File.join(
377
+ rails_den_source,
378
+ "app/assets/stylesheets/rails_den/application.css"
379
+ )
380
+
381
+ file(
382
+ "app/assets/stylesheets/rails_den/application.css",
383
+ File.read(rails_den_stylesheet)
384
+ )
385
+
386
+ route <<~RUBY
387
+ root to: redirect("/rails_den")
388
+
389
+ resource :registration, only: %i[new create edit update]
390
+
391
+ mount RailsDen::Engine => "/rails_den"
392
+ RUBY
393
+
394
+ initializer "rails_den.rb", <<~RUBY
395
+ RailsDen.configure do |config|
396
+ config.community_name = "RailsDen"
397
+ config.user_class = "User"
398
+ config.user_display_name_method = :username
399
+ config.user_mention_name_method = :username
400
+
401
+ config.user_mention_resolver = ->(normalized_name) {
402
+ User.find_by(username: normalized_name)
403
+ }
404
+
405
+ config.user_mention_candidates_resolver = ->(prefix, limit) {
406
+ escaped_prefix = ActiveRecord::Base.sanitize_sql_like(prefix)
407
+
408
+ User.where("username LIKE ?", "\#{escaped_prefix}%")
409
+ .order(:username)
410
+ .limit(limit)
411
+ }
412
+
413
+ config.current_user_resolver = -> {
414
+ if respond_to?(:authenticated?, true)
415
+ authenticated? ? Current.user : nil
416
+ elsif (session_id = cookies.signed[:session_id])
417
+ Session.find_by(id: session_id)&.user
418
+ end
419
+ }
420
+
421
+ config.authentication_handler = -> {
422
+ session[:return_to_after_authenticating] = request.url
423
+ redirect_to main_app.new_session_path
424
+ }
425
+
426
+ config.presence_redis_url = "redis://127.0.0.1:6379/0"
427
+
428
+ config.parent_controller = "RailsDenController"
429
+ end
430
+ RUBY
431
+
432
+ dockerfile_path = "Dockerfile"
433
+ dockerfile = File.read(dockerfile_path)
434
+
435
+ base_packages =
436
+ "apt-get install --no-install-recommends -y curl libjemalloc2 libvips postgresql-client"
437
+
438
+ unless dockerfile.include?(base_packages)
439
+ raise "RailsDen standalone generation failed because the generated Dockerfile base package list was not recognized."
440
+ end
441
+
442
+ dockerfile.sub!(base_packages) do
443
+ "#{base_packages} redis-server"
444
+ end
445
+
446
+ default_command =
447
+ 'CMD ["./bin/thrust", "./bin/rails", "server"]'
448
+
449
+ unless dockerfile.include?(default_command)
450
+ raise "RailsDen standalone generation failed because the generated Dockerfile server command was not recognized."
451
+ end
452
+
453
+ dockerfile.sub!(default_command) do
454
+ 'CMD ["sh", "-c", "redis-server --bind 127.0.0.1 --protected-mode yes --save \'\' --appendonly no --daemonize no --dir /tmp & exec ./bin/thrust ./bin/rails server"]'
455
+ end
456
+
457
+ File.write(dockerfile_path, dockerfile)
458
+
459
+ app_name =
460
+ File.basename(destination_root)
461
+ .downcase
462
+ .gsub(/[^a-z0-9_]/, "_")
463
+
464
+ database_role = app_name
465
+ database_password = SecureRandom.base64(36).tr("/+", "AZ")
466
+
467
+ production_databases = [
468
+ "#{app_name}_production",
469
+ "#{app_name}_production_cache",
470
+ "#{app_name}_production_queue",
471
+ "#{app_name}_production_cable"
472
+ ]
473
+
474
+ password_file =
475
+ File.join(destination_root, "config", "rails_den_database_password")
476
+
477
+ File.write(password_file, "#{database_password}\n")
478
+ File.chmod(0o600, password_file)
479
+
480
+ append_file ".gitignore", <<~TEXT
481
+
482
+ # RailsDen production database password
483
+ /config/rails_den_database_password
484
+ TEXT
485
+
486
+ database_yml = File.read("config/database.yml")
487
+
488
+ unless database_yml.include?(" host: 127.0.0.1\n")
489
+ database_yml.sub!(
490
+ " primary: &primary_production\n <<: *default\n",
491
+ " primary: &primary_production\n <<: *default\n host: 127.0.0.1\n"
492
+ )
493
+ end
494
+
495
+ environment_variable =
496
+ "#{app_name.upcase}_DATABASE_PASSWORD"
497
+
498
+ database_yml.gsub!(
499
+ %(<%= ENV["#{environment_variable}"] %>),
500
+ %(<%= File.read(Rails.root.join("config/rails_den_database_password")).strip %>)
501
+ )
502
+
503
+ File.write("config/database.yml", database_yml)
504
+
505
+ escaped_role =
506
+ database_role.gsub("'", "''")
507
+
508
+ escaped_password =
509
+ database_password.gsub("'", "''")
510
+
511
+ role_sql = <<~SQL
512
+ DO
513
+ $do$
514
+ BEGIN
515
+ IF NOT EXISTS (
516
+ SELECT FROM pg_catalog.pg_roles
517
+ WHERE rolname = '#{escaped_role}'
518
+ ) THEN
519
+ CREATE ROLE "#{database_role}"
520
+ LOGIN
521
+ PASSWORD '#{escaped_password}';
522
+ ELSE
523
+ ALTER ROLE "#{database_role}"
524
+ WITH LOGIN
525
+ PASSWORD '#{escaped_password}';
526
+ END IF;
527
+ END
528
+ $do$;
529
+ SQL
530
+
531
+ unless system(
532
+ "sudo",
533
+ "-u",
534
+ "postgres",
535
+ "psql",
536
+ "-v",
537
+ "ON_ERROR_STOP=1",
538
+ "-d",
539
+ "postgres",
540
+ "-c",
541
+ role_sql
542
+ )
543
+ raise "RailsDen standalone generation failed while creating the PostgreSQL production role."
544
+ end
545
+
546
+ production_databases.each do |database|
547
+ database_exists =
548
+ system(
549
+ "sudo",
550
+ "-u",
551
+ "postgres",
552
+ "psql",
553
+ "-d",
554
+ "postgres",
555
+ "-tAc",
556
+ "SELECT 1 FROM pg_database WHERE datname='#{database}'"
557
+ )
558
+
559
+ unless database_exists
560
+ raise "RailsDen standalone generation failed while checking PostgreSQL database #{database}."
561
+ end
562
+
563
+ existing_database =
564
+ `sudo -u postgres psql -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname='#{database}'"`.strip
565
+
566
+ if existing_database != "1"
567
+ unless system(
568
+ "sudo",
569
+ "-u",
570
+ "postgres",
571
+ "createdb",
572
+ "--owner=#{database_role}",
573
+ database
574
+ )
575
+ raise "RailsDen standalone generation failed while creating PostgreSQL database #{database}."
576
+ end
577
+ end
578
+ end
579
+
580
+ unless system("bin/rails", "active_storage:install")
581
+ raise "RailsDen standalone generation failed while installing Active Storage."
582
+ end
583
+
584
+ unless system("bin/rails", "rails_den:install:migrations")
585
+ raise "RailsDen standalone generation failed while installing RailsDen migrations."
586
+ end
587
+
588
+ unless system(
589
+ { "RAILS_ENV" => "production" },
590
+ "bin/rails",
591
+ "db:prepare"
592
+ )
593
+ raise "RailsDen standalone generation failed while preparing the production databases."
594
+ end
595
+
596
+ unless system(
597
+ {
598
+ "RAILS_ENV" => "production",
599
+ "SECRET_KEY_BASE_DUMMY" => "1"
600
+ },
601
+ "bin/rails",
602
+ "assets:precompile"
603
+ )
604
+ raise "RailsDen standalone generation failed while precompiling production assets."
605
+ end
606
+
607
+ unless system(
608
+ {
609
+ "RAILS_ENV" => "production",
610
+ "SECRET_KEY_BASE_DUMMY" => "1"
611
+ },
612
+ "bin/rails",
613
+ "runner",
614
+ "abort 'RailsDen production boot failed' unless Rails.env.production?"
615
+ )
616
+ raise "RailsDen standalone generation failed while verifying the production application."
617
+ end
618
+
619
+ say
620
+ say "============================================================"
621
+ say
622
+ say "RailsDen production application is ready!"
623
+ say
624
+ say "PostgreSQL role:"
625
+ say
626
+ say " #{database_role}"
627
+ say
628
+ say "Production databases:"
629
+ production_databases.each do |database|
630
+ say
631
+ say " #{database}"
632
+ end
633
+ say
634
+ say "RailsDen has:"
635
+ say
636
+ say " created the PostgreSQL application role"
637
+ say " created the production databases"
638
+ say " migrated the production databases"
639
+ say " precompiled production assets"
640
+ say " successfully booted Rails in production"
641
+ say
642
+ say "The database password is stored in:"
643
+ say
644
+ say " config/rails_den_database_password"
645
+ say
646
+ say "The password file is mode 0600 and excluded from Git."
647
+ say
648
+ say "Users sign in with their email address and are publicly"
649
+ say "identified by username."
650
+ say
651
+ say "The first registered user becomes a RailsDen administrator."
652
+ say
653
+ say "Start RailsDen in production with:"
654
+ say
655
+ say " RAILS_ENV=production bin/rails server"
656
+ say
657
+ say "CONFIGURATION"
658
+ say
659
+ say "RailsDen's main configuration file is:"
660
+ say
661
+ say " config/initializers/rails_den.rb"
662
+ say
663
+ say "RailsDen is mounted in:"
664
+ say
665
+ say " config/routes.rb"
666
+ say
667
+ say "The generated application mounts RailsDen at /rails_den."
668
+ say
669
+ say "CUSTOMIZATION"
670
+ say
671
+ say "RailsDen's authentication views are built in and already active."
672
+ say "You do NOT need to install them unless you want to customize them."
673
+ say
674
+ say "To copy the RailsDen views into this application, run:"
675
+ say
676
+ say " rails_den install:views"
677
+ say
678
+ say "The copied files will appear under app/views and override"
679
+ say "RailsDen's packaged defaults."
680
+ say
681
+ say "RailsDen's stylesheet has been copied to:"
682
+ say
683
+ say " app/assets/stylesheets/rails_den/application.css"
684
+ say
685
+ say "Edit that file to customize the appearance of your community."
686
+ say
687
+ say "MIGRATING FROM DISCOURSE?"
688
+ say
689
+ say "1. Create and download an official Discourse backup."
690
+ say
691
+ say "2. From this RailsDen application directory, run:"
692
+ say
693
+ say " bin/rails rails_den:import:discourse /path/to/discourse-backup.tar.gz"
694
+ say
695
+ say "The Discourse importer requires PostgreSQL."
696
+ say "Imported users must reset their password before first sign-in."
697
+ say
698
+ say "============================================================"
699
+ say
700
+ end