threddedDANIEL 0.14.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (367) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +744 -0
  4. data/app/assets/images/favicons/README.md +3 -0
  5. data/app/assets/images/favicons/amazon.png +0 -0
  6. data/app/assets/images/favicons/github.png +0 -0
  7. data/app/assets/images/favicons/google_branding/logo_calendar_128px.png +0 -0
  8. data/app/assets/images/favicons/google_branding/logo_docs_48px.png +0 -0
  9. data/app/assets/images/favicons/google_branding/logo_drive_48px.png +0 -0
  10. data/app/assets/images/favicons/google_branding/logo_forms_48px.png +0 -0
  11. data/app/assets/images/favicons/google_branding/logo_sheets_48px.png +0 -0
  12. data/app/assets/images/favicons/google_branding/logo_slides_48px.png +0 -0
  13. data/app/assets/images/favicons/stackexchange.png +0 -0
  14. data/app/assets/images/favicons/twitter.png +0 -0
  15. data/app/assets/images/favicons/wikipedia.png +0 -0
  16. data/app/assets/images/thredded/breadcrumb-chevron.svg +1 -0
  17. data/app/assets/images/thredded/follow.svg +1 -0
  18. data/app/assets/images/thredded/lock.svg +1 -0
  19. data/app/assets/images/thredded/moderation.svg +1 -0
  20. data/app/assets/images/thredded/private-messages.svg +1 -0
  21. data/app/assets/images/thredded/settings.svg +1 -0
  22. data/app/assets/images/thredded/three-dot-menu.svg +3 -0
  23. data/app/assets/images/thredded/unfollow.svg +1 -0
  24. data/app/assets/javascripts/thredded.es6 +2 -0
  25. data/app/assets/javascripts/thredded/components/currently_online.es6 +32 -0
  26. data/app/assets/javascripts/thredded/components/flash_messages.es6 +9 -0
  27. data/app/assets/javascripts/thredded/components/mention_autocompletion.es6 +40 -0
  28. data/app/assets/javascripts/thredded/components/post_form.es6 +37 -0
  29. data/app/assets/javascripts/thredded/components/preview_area.es6 +56 -0
  30. data/app/assets/javascripts/thredded/components/quote_post.es6 +49 -0
  31. data/app/assets/javascripts/thredded/components/time_stamps.es6 +25 -0
  32. data/app/assets/javascripts/thredded/components/topic_form.es6 +94 -0
  33. data/app/assets/javascripts/thredded/components/topics.es6 +46 -0
  34. data/app/assets/javascripts/thredded/components/turboforms.es6 +25 -0
  35. data/app/assets/javascripts/thredded/components/user_preferences_form.es6 +66 -0
  36. data/app/assets/javascripts/thredded/components/user_textcomplete.es6 +50 -0
  37. data/app/assets/javascripts/thredded/components/users_select.es6 +122 -0
  38. data/app/assets/javascripts/thredded/core/csrf_tokens.es6 +9 -0
  39. data/app/assets/javascripts/thredded/core/debounce.es6 +32 -0
  40. data/app/assets/javascripts/thredded/core/escape_html.es6 +7 -0
  41. data/app/assets/javascripts/thredded/core/hide_soft_keyboard.es6 +7 -0
  42. data/app/assets/javascripts/thredded/core/on_page_load.es6 +54 -0
  43. data/app/assets/javascripts/thredded/core/serialize_form.es6 +9 -0
  44. data/app/assets/javascripts/thredded/core/thredded.es6 +1 -0
  45. data/app/assets/javascripts/thredded/dependencies.js +8 -0
  46. data/app/assets/javascripts/thredded/dependencies/autosize.js +1 -0
  47. data/app/assets/javascripts/thredded/dependencies/textcomplete.js +1 -0
  48. data/app/assets/javascripts/thredded/dependencies/timeago.js +1 -0
  49. data/app/assets/javascripts/thredded/dependencies/ujs.js +1 -0
  50. data/app/assets/javascripts/thredded/thredded.es6 +2 -0
  51. data/app/assets/stylesheets/thredded.scss +3 -0
  52. data/app/assets/stylesheets/thredded/_base.scss +13 -0
  53. data/app/assets/stylesheets/thredded/_dependencies.scss +0 -0
  54. data/app/assets/stylesheets/thredded/_email.scss +51 -0
  55. data/app/assets/stylesheets/thredded/_thredded.scss +33 -0
  56. data/app/assets/stylesheets/thredded/base/_alerts.scss +44 -0
  57. data/app/assets/stylesheets/thredded/base/_buttons.scss +73 -0
  58. data/app/assets/stylesheets/thredded/base/_dropdown-menu.scss +83 -0
  59. data/app/assets/stylesheets/thredded/base/_forms.scss +84 -0
  60. data/app/assets/stylesheets/thredded/base/_grid.scss +58 -0
  61. data/app/assets/stylesheets/thredded/base/_lists.scss +31 -0
  62. data/app/assets/stylesheets/thredded/base/_nav.scss +67 -0
  63. data/app/assets/stylesheets/thredded/base/_tables.scss +17 -0
  64. data/app/assets/stylesheets/thredded/base/_typography.scss +72 -0
  65. data/app/assets/stylesheets/thredded/base/_variables.scss +141 -0
  66. data/app/assets/stylesheets/thredded/components/_alerts.scss +19 -0
  67. data/app/assets/stylesheets/thredded/components/_base.scss +25 -0
  68. data/app/assets/stylesheets/thredded/components/_currently-online.scss +54 -0
  69. data/app/assets/stylesheets/thredded/components/_empty.scss +11 -0
  70. data/app/assets/stylesheets/thredded/components/_flash-message.scss +19 -0
  71. data/app/assets/stylesheets/thredded/components/_following.scss +14 -0
  72. data/app/assets/stylesheets/thredded/components/_form-list.scss +39 -0
  73. data/app/assets/stylesheets/thredded/components/_icons.scss +3 -0
  74. data/app/assets/stylesheets/thredded/components/_main-section.scss +9 -0
  75. data/app/assets/stylesheets/thredded/components/_mention-autocomplete.scss +57 -0
  76. data/app/assets/stylesheets/thredded/components/_messageboard.scss +155 -0
  77. data/app/assets/stylesheets/thredded/components/_onebox.scss +275 -0
  78. data/app/assets/stylesheets/thredded/components/_pagination.scss +35 -0
  79. data/app/assets/stylesheets/thredded/components/_post-form.scss +27 -0
  80. data/app/assets/stylesheets/thredded/components/_post.scss +138 -0
  81. data/app/assets/stylesheets/thredded/components/_preferences.scss +19 -0
  82. data/app/assets/stylesheets/thredded/components/_preview_area.scss +11 -0
  83. data/app/assets/stylesheets/thredded/components/_topic-delete.scss +8 -0
  84. data/app/assets/stylesheets/thredded/components/_topic-header.scss +111 -0
  85. data/app/assets/stylesheets/thredded/components/_topics.scss +186 -0
  86. data/app/assets/stylesheets/thredded/layout/_main-container.scss +16 -0
  87. data/app/assets/stylesheets/thredded/layout/_main-navigation.scss +79 -0
  88. data/app/assets/stylesheets/thredded/layout/_moderation.scss +97 -0
  89. data/app/assets/stylesheets/thredded/layout/_navigation.scss +87 -0
  90. data/app/assets/stylesheets/thredded/layout/_search-navigation.scss +126 -0
  91. data/app/assets/stylesheets/thredded/layout/_user-navigation.scss +20 -0
  92. data/app/assets/stylesheets/thredded/layout/_user.scss +10 -0
  93. data/app/assets/stylesheets/thredded/utilities/_is-compact.scss +26 -0
  94. data/app/assets/stylesheets/thredded/utilities/_is-expanded.scss +16 -0
  95. data/app/commands/thredded/at_notification_extractor.rb +23 -0
  96. data/app/commands/thredded/autofollow_users.rb +62 -0
  97. data/app/commands/thredded/create_messageboard.rb +42 -0
  98. data/app/commands/thredded/mark_all_read.rb +22 -0
  99. data/app/commands/thredded/moderate_post.rb +48 -0
  100. data/app/commands/thredded/notify_following_users.rb +78 -0
  101. data/app/commands/thredded/notify_private_topic_users.rb +34 -0
  102. data/app/controllers/concerns/thredded/new_post_params.rb +21 -0
  103. data/app/controllers/concerns/thredded/new_private_post_params.rb +21 -0
  104. data/app/controllers/concerns/thredded/new_private_topic_params.rb +25 -0
  105. data/app/controllers/concerns/thredded/new_topic_params.rb +19 -0
  106. data/app/controllers/concerns/thredded/render_preview.rb +16 -0
  107. data/app/controllers/thredded/application_controller.rb +145 -0
  108. data/app/controllers/thredded/autocomplete_users_controller.rb +52 -0
  109. data/app/controllers/thredded/messageboard_groups_controller.rb +31 -0
  110. data/app/controllers/thredded/messageboards_controller.rb +53 -0
  111. data/app/controllers/thredded/moderation_controller.rb +103 -0
  112. data/app/controllers/thredded/post_permalinks_controller.rb +11 -0
  113. data/app/controllers/thredded/post_previews_controller.rb +29 -0
  114. data/app/controllers/thredded/posts_controller.rb +97 -0
  115. data/app/controllers/thredded/preferences_controller.rb +42 -0
  116. data/app/controllers/thredded/private_post_permalinks_controller.rb +12 -0
  117. data/app/controllers/thredded/private_post_previews_controller.rb +29 -0
  118. data/app/controllers/thredded/private_posts_controller.rb +96 -0
  119. data/app/controllers/thredded/private_topic_previews_controller.rb +15 -0
  120. data/app/controllers/thredded/private_topics_controller.rb +102 -0
  121. data/app/controllers/thredded/read_states_controller.rb +13 -0
  122. data/app/controllers/thredded/theme_previews_controller.rb +34 -0
  123. data/app/controllers/thredded/topic_previews_controller.rb +15 -0
  124. data/app/controllers/thredded/topics_controller.rb +205 -0
  125. data/app/forms/thredded/edit_topic_form.rb +51 -0
  126. data/app/forms/thredded/post_form.rb +54 -0
  127. data/app/forms/thredded/private_post_form.rb +50 -0
  128. data/app/forms/thredded/private_topic_form.rb +160 -0
  129. data/app/forms/thredded/topic_form.rb +95 -0
  130. data/app/forms/thredded/user_preferences_form.rb +110 -0
  131. data/app/helpers/thredded/application_helper.rb +129 -0
  132. data/app/helpers/thredded/nav_helper.rb +42 -0
  133. data/app/helpers/thredded/render_helper.rb +15 -0
  134. data/app/helpers/thredded/urls_helper.rb +134 -0
  135. data/app/jobs/thredded/activity_updater_job.rb +21 -0
  136. data/app/jobs/thredded/auto_follow_and_notify_job.rb +14 -0
  137. data/app/jobs/thredded/notify_private_topic_users_job.rb +12 -0
  138. data/app/mailer_previews/thredded/base_mailer_preview.rb +118 -0
  139. data/app/mailer_previews/thredded/post_mailer_preview.rb +13 -0
  140. data/app/mailer_previews/thredded/private_topic_mailer_preview.rb +13 -0
  141. data/app/mailers/thredded/base_mailer.rb +18 -0
  142. data/app/mailers/thredded/post_mailer.rb +21 -0
  143. data/app/mailers/thredded/private_topic_mailer.rb +21 -0
  144. data/app/models/concerns/thredded/content_moderation_state.rb +67 -0
  145. data/app/models/concerns/thredded/friendly_id_reserved_words_and_pagination.rb +17 -0
  146. data/app/models/concerns/thredded/moderation_state.rb +14 -0
  147. data/app/models/concerns/thredded/notifier_preference.rb +19 -0
  148. data/app/models/concerns/thredded/post_common.rb +76 -0
  149. data/app/models/concerns/thredded/search_parser.rb +41 -0
  150. data/app/models/concerns/thredded/topic_common.rb +94 -0
  151. data/app/models/concerns/thredded/topics_search.rb +67 -0
  152. data/app/models/concerns/thredded/user_topic_read_state_common.rb +42 -0
  153. data/app/models/thredded/category.rb +18 -0
  154. data/app/models/thredded/messageboard.rb +120 -0
  155. data/app/models/thredded/messageboard_group.rb +19 -0
  156. data/app/models/thredded/messageboard_notifications_for_followed_topics.rb +30 -0
  157. data/app/models/thredded/messageboard_user.rb +14 -0
  158. data/app/models/thredded/notifications_for_followed_topics.rb +25 -0
  159. data/app/models/thredded/notifications_for_private_topics.rb +22 -0
  160. data/app/models/thredded/null_user.rb +51 -0
  161. data/app/models/thredded/null_user_topic_read_state.rb +17 -0
  162. data/app/models/thredded/post.rb +77 -0
  163. data/app/models/thredded/post_moderation_record.rb +56 -0
  164. data/app/models/thredded/private_post.rb +63 -0
  165. data/app/models/thredded/private_topic.rb +89 -0
  166. data/app/models/thredded/private_user.rb +8 -0
  167. data/app/models/thredded/stats.rb +25 -0
  168. data/app/models/thredded/topic.rb +179 -0
  169. data/app/models/thredded/topic_category.rb +8 -0
  170. data/app/models/thredded/user_detail.rb +27 -0
  171. data/app/models/thredded/user_extender.rb +75 -0
  172. data/app/models/thredded/user_messageboard_preference.rb +45 -0
  173. data/app/models/thredded/user_permissions/admin/if_admin_column_true.rb +14 -0
  174. data/app/models/thredded/user_permissions/admin/none.rb +14 -0
  175. data/app/models/thredded/user_permissions/message/readers_of_writeable_boards.rb +15 -0
  176. data/app/models/thredded/user_permissions/moderate/if_moderator_column_true.rb +16 -0
  177. data/app/models/thredded/user_permissions/moderate/none.rb +16 -0
  178. data/app/models/thredded/user_permissions/read/all.rb +27 -0
  179. data/app/models/thredded/user_permissions/write/all.rb +16 -0
  180. data/app/models/thredded/user_permissions/write/none.rb +16 -0
  181. data/app/models/thredded/user_post_notification.rb +29 -0
  182. data/app/models/thredded/user_preference.rb +26 -0
  183. data/app/models/thredded/user_private_topic_read_state.rb +13 -0
  184. data/app/models/thredded/user_topic_follow.rb +34 -0
  185. data/app/models/thredded/user_topic_read_state.rb +13 -0
  186. data/app/notifiers/thredded/email_notifier.rb +25 -0
  187. data/app/policies/thredded/messageboard_group_policy.rb +16 -0
  188. data/app/policies/thredded/messageboard_policy.rb +49 -0
  189. data/app/policies/thredded/post_policy.rb +64 -0
  190. data/app/policies/thredded/private_post_policy.rb +38 -0
  191. data/app/policies/thredded/private_topic_policy.rb +24 -0
  192. data/app/policies/thredded/topic_policy.rb +49 -0
  193. data/app/view_hooks/thredded/all_view_hooks.rb +125 -0
  194. data/app/view_models/thredded/base_topic_view.rb +43 -0
  195. data/app/view_models/thredded/messageboard_group_view.rb +27 -0
  196. data/app/view_models/thredded/post_view.rb +89 -0
  197. data/app/view_models/thredded/posts_page_view.rb +27 -0
  198. data/app/view_models/thredded/private_topic_view.rb +20 -0
  199. data/app/view_models/thredded/private_topics_page_view.rb +31 -0
  200. data/app/view_models/thredded/topic_email_view.rb +18 -0
  201. data/app/view_models/thredded/topic_posts_page_view.rb +17 -0
  202. data/app/view_models/thredded/topic_view.rb +68 -0
  203. data/app/view_models/thredded/topics_page_view.rb +38 -0
  204. data/app/views/layouts/thredded/application.html.erb +18 -0
  205. data/app/views/thredded/categories/_category.html.erb +1 -0
  206. data/app/views/thredded/error_pages/forbidden.html.erb +6 -0
  207. data/app/views/thredded/error_pages/not_found.html.erb +6 -0
  208. data/app/views/thredded/kaminari/_first_page.html.erb +11 -0
  209. data/app/views/thredded/kaminari/_gap.html.erb +8 -0
  210. data/app/views/thredded/kaminari/_last_page.html.erb +11 -0
  211. data/app/views/thredded/kaminari/_next_page.html.erb +11 -0
  212. data/app/views/thredded/kaminari/_page.html.erb +12 -0
  213. data/app/views/thredded/kaminari/_paginator.html.erb +23 -0
  214. data/app/views/thredded/kaminari/_prev_page.html.erb +11 -0
  215. data/app/views/thredded/messageboard_groups/new.html.erb +28 -0
  216. data/app/views/thredded/messageboards/_form.html.erb +30 -0
  217. data/app/views/thredded/messageboards/_messageboard.html.erb +20 -0
  218. data/app/views/thredded/messageboards/_messageboard_meta.html.erb +13 -0
  219. data/app/views/thredded/messageboards/edit.html.erb +15 -0
  220. data/app/views/thredded/messageboards/index.html.erb +34 -0
  221. data/app/views/thredded/messageboards/new.html.erb +15 -0
  222. data/app/views/thredded/moderation/_nav.html.erb +19 -0
  223. data/app/views/thredded/moderation/_post.html.erb +19 -0
  224. data/app/views/thredded/moderation/_post_moderation_actions.html.erb +12 -0
  225. data/app/views/thredded/moderation/_post_moderation_record.html.erb +46 -0
  226. data/app/views/thredded/moderation/_user_moderation_state.html.erb +3 -0
  227. data/app/views/thredded/moderation/_user_post.html.erb +12 -0
  228. data/app/views/thredded/moderation/_users_search_form.html.erb +13 -0
  229. data/app/views/thredded/moderation/activity.html.erb +20 -0
  230. data/app/views/thredded/moderation/history.html.erb +13 -0
  231. data/app/views/thredded/moderation/pending.html.erb +24 -0
  232. data/app/views/thredded/moderation/user.html.erb +54 -0
  233. data/app/views/thredded/moderation/users.html.erb +41 -0
  234. data/app/views/thredded/post_mailer/post_notification.html.erb +26 -0
  235. data/app/views/thredded/post_mailer/post_notification.text.erb +14 -0
  236. data/app/views/thredded/post_previews/preview.html.erb +1 -0
  237. data/app/views/thredded/post_previews/update.html.erb +1 -0
  238. data/app/views/thredded/posts/_content.html.erb +1 -0
  239. data/app/views/thredded/posts/_form.html.erb +5 -0
  240. data/app/views/thredded/posts/_post.html.erb +13 -0
  241. data/app/views/thredded/posts/_user.html.erb +3 -0
  242. data/app/views/thredded/posts/edit.html.erb +16 -0
  243. data/app/views/thredded/posts/new.html.erb +15 -0
  244. data/app/views/thredded/posts_common/_actions.html.erb +28 -0
  245. data/app/views/thredded/posts_common/_content.html.erb +3 -0
  246. data/app/views/thredded/posts_common/_form.html.erb +22 -0
  247. data/app/views/thredded/posts_common/_header.html.erb +8 -0
  248. data/app/views/thredded/posts_common/_header_with_topic.html.erb +15 -0
  249. data/app/views/thredded/posts_common/_header_with_user_and_topic.html.erb +18 -0
  250. data/app/views/thredded/posts_common/actions/_delete.html.erb +4 -0
  251. data/app/views/thredded/posts_common/actions/_edit.html.erb +3 -0
  252. data/app/views/thredded/posts_common/actions/_mark_as_unread.html.erb +2 -0
  253. data/app/views/thredded/posts_common/actions/_quote.html.erb +4 -0
  254. data/app/views/thredded/posts_common/form/_after_content.html.erb +8 -0
  255. data/app/views/thredded/posts_common/form/_before_content.html.erb +8 -0
  256. data/app/views/thredded/posts_common/form/_content.html.erb +7 -0
  257. data/app/views/thredded/posts_common/form/_content_field.html.erb +8 -0
  258. data/app/views/thredded/posts_common/form/_preview_area.html.erb +16 -0
  259. data/app/views/thredded/preferences/_form.html.erb +95 -0
  260. data/app/views/thredded/preferences/_messageboards_nav.html.erb +8 -0
  261. data/app/views/thredded/preferences/_messageboards_nav_item.html.erb +2 -0
  262. data/app/views/thredded/preferences/edit.html.erb +20 -0
  263. data/app/views/thredded/private_post_previews/preview.html.erb +1 -0
  264. data/app/views/thredded/private_post_previews/update.html.erb +1 -0
  265. data/app/views/thredded/private_posts/_content.html.erb +1 -0
  266. data/app/views/thredded/private_posts/_form.html.erb +6 -0
  267. data/app/views/thredded/private_posts/_private_post.html.erb +6 -0
  268. data/app/views/thredded/private_posts/edit.html.erb +16 -0
  269. data/app/views/thredded/private_posts/new.html.erb +11 -0
  270. data/app/views/thredded/private_topic_mailer/message_notification.html.erb +26 -0
  271. data/app/views/thredded/private_topic_mailer/message_notification.text.erb +15 -0
  272. data/app/views/thredded/private_topic_previews/preview.html.erb +1 -0
  273. data/app/views/thredded/private_topics/_breadcrumbs.html.erb +4 -0
  274. data/app/views/thredded/private_topics/_form.html.erb +39 -0
  275. data/app/views/thredded/private_topics/_header.html.erb +17 -0
  276. data/app/views/thredded/private_topics/_no_private_topics.html.erb +6 -0
  277. data/app/views/thredded/private_topics/_private_topic.html.erb +23 -0
  278. data/app/views/thredded/private_topics/edit.html.erb +35 -0
  279. data/app/views/thredded/private_topics/header/_participant.html.erb +1 -0
  280. data/app/views/thredded/private_topics/index.html.erb +32 -0
  281. data/app/views/thredded/private_topics/new.html.erb +11 -0
  282. data/app/views/thredded/private_topics/private_topic/_participant.html.erb +1 -0
  283. data/app/views/thredded/private_topics/show.html.erb +28 -0
  284. data/app/views/thredded/search/_form.html.erb +13 -0
  285. data/app/views/thredded/shared/_breadcrumbs.html.erb +6 -0
  286. data/app/views/thredded/shared/_content_moderation_blocked_state.html.erb +8 -0
  287. data/app/views/thredded/shared/_currently_online.html.erb +6 -0
  288. data/app/views/thredded/shared/_flash_messages.html.erb +7 -0
  289. data/app/views/thredded/shared/_header.html.erb +3 -0
  290. data/app/views/thredded/shared/_nav.html.erb +19 -0
  291. data/app/views/thredded/shared/_page.html.erb +15 -0
  292. data/app/views/thredded/shared/currently_online/_header.html.erb +5 -0
  293. data/app/views/thredded/shared/currently_online/_user_list.html.erb +3 -0
  294. data/app/views/thredded/shared/currently_online/_user_list_item.html.erb +6 -0
  295. data/app/views/thredded/shared/nav/_moderation.html.erb +14 -0
  296. data/app/views/thredded/shared/nav/_notification_preferences.html.erb +8 -0
  297. data/app/views/thredded/shared/nav/_private_topics.html.erb +12 -0
  298. data/app/views/thredded/shared/nav/_standalone.html.erb +12 -0
  299. data/app/views/thredded/shared/nav/_standalone_profile.html.erb +3 -0
  300. data/app/views/thredded/shared/preview.html.erb +10 -0
  301. data/app/views/thredded/theme_previews/_section_title.html.erb +3 -0
  302. data/app/views/thredded/theme_previews/show.html.erb +99 -0
  303. data/app/views/thredded/topic_previews/preview.html.erb +1 -0
  304. data/app/views/thredded/topics/_followers.html.erb +12 -0
  305. data/app/views/thredded/topics/_form.html.erb +32 -0
  306. data/app/views/thredded/topics/_header.html.erb +32 -0
  307. data/app/views/thredded/topics/_sticky_topics_divider.html.erb +1 -0
  308. data/app/views/thredded/topics/_topic.html.erb +47 -0
  309. data/app/views/thredded/topics/_topic_form_admin_options.html.erb +12 -0
  310. data/app/views/thredded/topics/edit.html.erb +50 -0
  311. data/app/views/thredded/topics/index.html.erb +35 -0
  312. data/app/views/thredded/topics/new.html.erb +12 -0
  313. data/app/views/thredded/topics/search.html.erb +39 -0
  314. data/app/views/thredded/topics/show.html.erb +46 -0
  315. data/app/views/thredded/users/_link.html.erb +14 -0
  316. data/app/views/thredded/users/_post.html.erb +6 -0
  317. data/app/views/thredded/users/_posts.html.erb +7 -0
  318. data/bin/rails +6 -0
  319. data/config/i18n-tasks.yml +16 -0
  320. data/config/locales/de.yml +257 -0
  321. data/config/locales/en.yml +253 -0
  322. data/config/locales/es.yml +257 -0
  323. data/config/locales/fr.yml +255 -0
  324. data/config/locales/it.yml +257 -0
  325. data/config/locales/pl.yml +257 -0
  326. data/config/locales/pt-BR.yml +258 -0
  327. data/config/locales/ru.yml +255 -0
  328. data/config/locales/zh-CN.yml +246 -0
  329. data/config/routes.rb +87 -0
  330. data/db/migrate/20160329231848_create_thredded.rb +257 -0
  331. data/db/seeds.rb +4 -0
  332. data/db/upgrade_migrations/20160611094616_upgrade_v0_5_to_v0_6.rb +28 -0
  333. data/db/upgrade_migrations/20160723012349_upgrade_v0_6_to_v0_7.rb +46 -0
  334. data/db/upgrade_migrations/20161019150201_upgrade_v0_7_to_v0_8.rb +34 -0
  335. data/db/upgrade_migrations/20161113161801_upgrade_v0_8_to_v0_9.rb +60 -0
  336. data/db/upgrade_migrations/20170125033319_upgrade_v0_9_to_v0_10.rb +36 -0
  337. data/db/upgrade_migrations/20170312131417_upgrade_thredded_v0_10_to_v0_11.rb +23 -0
  338. data/db/upgrade_migrations/20170420163138_upgrade_thredded_v0_11_to_v0_12.rb +28 -0
  339. data/db/upgrade_migrations/20170811090735_upgrade_thredded_v0_13_to_v_014.rb +21 -0
  340. data/lib/generators/thredded/install/USAGE +8 -0
  341. data/lib/generators/thredded/install/install_generator.rb +20 -0
  342. data/lib/generators/thredded/install/templates/initializer.rb +160 -0
  343. data/lib/tasks/thredded_tasks.rake +14 -0
  344. data/lib/thredded.rb +190 -0
  345. data/lib/thredded/base_migration.rb +14 -0
  346. data/lib/thredded/base_notifier.rb +28 -0
  347. data/lib/thredded/collection_to_strings_with_cache_renderer.rb +86 -0
  348. data/lib/thredded/content_formatter.rb +129 -0
  349. data/lib/thredded/database_seeder.rb +290 -0
  350. data/lib/thredded/db_tools.rb +103 -0
  351. data/lib/thredded/email_transformer.rb +22 -0
  352. data/lib/thredded/email_transformer/base.rb +47 -0
  353. data/lib/thredded/email_transformer/onebox.rb +21 -0
  354. data/lib/thredded/engine.rb +28 -0
  355. data/lib/thredded/errors.rb +68 -0
  356. data/lib/thredded/formatting_demo_content.rb +30 -0
  357. data/lib/thredded/html_pipeline/at_mention_filter.rb +78 -0
  358. data/lib/thredded/html_pipeline/autolink_filter.rb +15 -0
  359. data/lib/thredded/html_pipeline/kramdown_filter.rb +39 -0
  360. data/lib/thredded/html_pipeline/onebox_filter.rb +143 -0
  361. data/lib/thredded/html_pipeline/wrap_iframes_filter.rb +13 -0
  362. data/lib/thredded/version.rb +5 -0
  363. data/lib/thredded/view_hooks/config.rb +37 -0
  364. data/lib/thredded/view_hooks/renderer.rb +30 -0
  365. data/vendor/assets/javascripts/autosize.min.js +6 -0
  366. data/vendor/assets/javascripts/textcomplete.min.js +2 -0
  367. metadata +1035 -0
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thredded
4
+ # A controller for managing {PrivatePost}s.
5
+ class PrivatePostsController < Thredded::ApplicationController
6
+ include ActionView::RecordIdentifier
7
+ include NewPrivatePostParams
8
+
9
+ helper_method :topic
10
+ after_action :update_user_activity
11
+
12
+ after_action :verify_authorized
13
+
14
+ def new
15
+ @post_form = Thredded::PrivatePostForm.new(
16
+ user: thredded_current_user, topic: parent_topic, post_params: new_private_post_params
17
+ )
18
+ authorize_creating @post_form.post
19
+ end
20
+
21
+ def create
22
+ @post_form = Thredded::PrivatePostForm.new(
23
+ user: thredded_current_user, topic: parent_topic, post_params: new_private_post_params
24
+ )
25
+ authorize_creating @post_form.post
26
+ if @post_form.save
27
+ redirect_to post_path(@post_form.post, user: thredded_current_user)
28
+ else
29
+ render :new
30
+ end
31
+ end
32
+
33
+ def edit
34
+ @post_form = Thredded::PrivatePostForm.for_persisted(post)
35
+ authorize @post_form.post, :update?
36
+ return redirect_to(canonical_topic_params) unless params_match?(canonical_topic_params)
37
+ render
38
+ end
39
+
40
+ def update
41
+ authorize post, :update?
42
+ post.update(new_private_post_params)
43
+
44
+ redirect_to post_path(post, user: thredded_current_user)
45
+ end
46
+
47
+ def destroy
48
+ authorize post, :destroy?
49
+ post.destroy!
50
+
51
+ redirect_back fallback_location: topic_url(topic),
52
+ notice: I18n.t('thredded.posts.deleted_notice')
53
+ end
54
+
55
+ def mark_as_unread
56
+ authorize post, :read?
57
+ page = post.page
58
+ post.mark_as_unread(thredded_current_user, page)
59
+ after_mark_as_unread # customization hook
60
+ end
61
+
62
+ def quote
63
+ authorize_reading post
64
+ render plain: Thredded::ContentFormatter.quote_content(post.content)
65
+ end
66
+
67
+ private
68
+
69
+ def canonical_topic_params
70
+ { private_topic_id: topic.slug }
71
+ end
72
+
73
+ def after_mark_as_unread
74
+ redirect_to private_topics_path
75
+ end
76
+
77
+ def topic
78
+ post.postable
79
+ end
80
+
81
+ def parent_topic
82
+ Thredded::PrivateTopic
83
+ .includes(:private_users)
84
+ .friendly
85
+ .find(params[:private_topic_id])
86
+ end
87
+
88
+ def post
89
+ @post ||= Thredded::PrivatePost.find(params[:id])
90
+ end
91
+
92
+ def current_page
93
+ params[:page].nil? ? 1 : params[:page].to_i
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thredded
4
+ class PrivateTopicPreviewsController < Thredded::ApplicationController
5
+ include Thredded::NewPrivateTopicParams
6
+ include Thredded::RenderPreview
7
+
8
+ def preview
9
+ form = Thredded::PrivateTopicForm.new(new_private_topic_params)
10
+ @private_post = form.post
11
+ @private_post.postable = form.private_topic
12
+ render_preview
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thredded
4
+ class PrivateTopicsController < Thredded::ApplicationController
5
+ include Thredded::NewPrivateTopicParams
6
+ include Thredded::NewPrivatePostParams
7
+
8
+ before_action :thredded_require_login!
9
+
10
+ def index
11
+ page_scope = Thredded::PrivateTopic
12
+ .distinct
13
+ .for_user(thredded_current_user)
14
+ .order_recently_posted_first
15
+ .page(params[:page])
16
+ return redirect_to(last_page_params(page_scope)) if page_beyond_last?(page_scope)
17
+ @private_topics = Thredded::PrivateTopicsPageView.new(thredded_current_user, page_scope)
18
+
19
+ Thredded::PrivateTopicForm.new(user: thredded_current_user).tap do |form|
20
+ @new_private_topic = form if policy(form.private_topic).create?
21
+ end
22
+ end
23
+
24
+ def show
25
+ authorize private_topic, :read?
26
+ return redirect_to(canonical_topic_params) unless params_match?(canonical_topic_params)
27
+
28
+ page_scope = private_topic
29
+ .posts
30
+ .includes(:user)
31
+ .order_oldest_first
32
+ .page(current_page)
33
+ return redirect_to(last_page_params(page_scope)) if page_beyond_last?(page_scope)
34
+ @posts = Thredded::TopicPostsPageView.new(thredded_current_user, private_topic, page_scope)
35
+
36
+ if thredded_signed_in?
37
+ Thredded::UserPrivateTopicReadState.touch!(
38
+ thredded_current_user.id, private_topic.id, page_scope.last, current_page
39
+ )
40
+ end
41
+
42
+ @new_post = Thredded::PrivatePostForm.new(
43
+ user: thredded_current_user, topic: private_topic, post_params: new_private_post_params
44
+ )
45
+ end
46
+
47
+ def new
48
+ @private_topic = Thredded::PrivateTopicForm.new(new_private_topic_params)
49
+ authorize_creating @private_topic.private_topic
50
+ end
51
+
52
+ def create
53
+ @private_topic = Thredded::PrivateTopicForm.new(new_private_topic_params)
54
+ if @private_topic.save
55
+ redirect_to @private_topic.private_topic
56
+ else
57
+ render :new
58
+ end
59
+ end
60
+
61
+ def edit
62
+ authorize private_topic, :update?
63
+ return redirect_to(canonical_topic_params) unless params_match?(canonical_topic_params)
64
+ render
65
+ end
66
+
67
+ def update
68
+ authorize private_topic, :update?
69
+ if private_topic.update(private_topic_params)
70
+ redirect_to private_topic_url(private_topic),
71
+ notice: t('thredded.private_topics.updated_notice')
72
+ else
73
+ render :edit
74
+ end
75
+ end
76
+
77
+ private
78
+
79
+ def canonical_topic_params
80
+ { id: private_topic.slug }
81
+ end
82
+
83
+ def current_page
84
+ (params[:page] || 1).to_i
85
+ end
86
+
87
+ # Returns the `@private_topic` instance variable.
88
+ # If `@private_topic` is not set, it first sets it to the topic with the slug or ID given by `params[:id]`.
89
+ #
90
+ # @return [Thredded::PrivateTopic]
91
+ # @raise [Thredded::Errors::PrivateTopicNotFound] if the topic with the given slug does not exist.
92
+ def private_topic
93
+ @private_topic ||= Thredded::PrivateTopic.friendly_find!(params[:id])
94
+ end
95
+
96
+ def private_topic_params
97
+ params
98
+ .require(:private_topic)
99
+ .permit(:title)
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thredded
4
+ class ReadStatesController < Thredded::ApplicationController
5
+ before_action :thredded_require_login!
6
+
7
+ def update
8
+ Thredded::MarkAllRead.run(thredded_current_user) if thredded_signed_in?
9
+
10
+ redirect_to request.referer
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thredded
4
+ class ThemePreviewsController < Thredded::ApplicationController
5
+ def show # rubocop:disable Metrics/MethodLength
6
+ @messageboard = Thredded::Messageboard.first
7
+ fail Thredded::Errors::DatabaseEmpty unless @messageboard
8
+ @user = if thredded_current_user.thredded_anonymous?
9
+ Thredded.user_class.new(id: 1334, name: 'joe', email: 'joe@example.com')
10
+ else
11
+ thredded_current_user
12
+ end
13
+ @messageboards = Thredded::Messageboard.all
14
+ @topics = Thredded::TopicsPageView.new(@user, @messageboard.topics.page(1).limit(3))
15
+ @private_topics = Thredded::PrivateTopicsPageView.new(@user, @user.thredded_private_topics.page(1).limit(3))
16
+ topic = Thredded::Topic.new(messageboard: @messageboard, title: 'Hello', slug: 'hello', user: @user)
17
+ @topic = Thredded::TopicView.from_user(topic, @user)
18
+ @posts = Thredded::TopicPostsPageView.new(@user, topic, topic.posts.page(1).limit(3))
19
+ @post = topic.posts.build(id: 1337, postable: topic, content: 'Hello world', user: @user)
20
+ @post_form = Thredded::PostForm.for_persisted(@post)
21
+ @new_post = Thredded::PostForm.new(user: @user, topic: topic)
22
+ @new_topic = Thredded::TopicForm.new(user: @user, messageboard: @messageboard)
23
+ @new_private_topic = Thredded::PrivateTopicForm.new(user: @user)
24
+ private_topic = Thredded::PrivateTopic.new(id: 17, title: 'Hello', user: @user, last_user: @user, users: [@user])
25
+ @private_topic = Thredded::PrivateTopicView.from_user(private_topic, @user)
26
+ @private_posts = Thredded::TopicPostsPageView.new(@user, private_topic, private_topic.posts.page(1).limit(3))
27
+ @private_post = private_topic.posts.build(
28
+ id: 1337, postable: private_topic, content: 'A private hello world', user: @user
29
+ )
30
+ @private_post_form = Thredded::PrivatePostForm.for_persisted(@private_post)
31
+ @preferences = Thredded::UserPreferencesForm.new(user: @user, messageboard: @messageboard)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thredded
4
+ class TopicPreviewsController < Thredded::ApplicationController
5
+ include Thredded::NewTopicParams
6
+ include Thredded::RenderPreview
7
+
8
+ def preview
9
+ form = Thredded::TopicForm.new(new_topic_params)
10
+ @post = form.post
11
+ @post.postable = form.topic
12
+ render_preview
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,205 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thredded
4
+ class TopicsController < Thredded::ApplicationController # rubocop:disable Metrics/ClassLength
5
+ include Thredded::NewTopicParams
6
+ include Thredded::NewPostParams
7
+
8
+ before_action :thredded_require_login!,
9
+ only: %i[edit new update create destroy follow unfollow]
10
+
11
+ before_action :use_topic_messageboard,
12
+ only: %i[show edit update destroy follow unfollow]
13
+
14
+ after_action :update_user_activity
15
+
16
+ after_action :verify_authorized, except: %i[search]
17
+ after_action :verify_policy_scoped, except: %i[show new create edit update destroy follow unfollow]
18
+
19
+ def index
20
+ authorize_reading messageboard
21
+ unless params_match?(canonical_messageboard_params)
22
+ skip_policy_scope
23
+ return redirect_to(canonical_messageboard_params)
24
+ end
25
+
26
+ page_scope = policy_scope(messageboard.topics)
27
+ .order_sticky_first.order_recently_posted_first
28
+ .page(current_page)
29
+ return redirect_to(last_page_params(page_scope)) if page_beyond_last?(page_scope)
30
+ @topics = Thredded::TopicsPageView.new(thredded_current_user, page_scope)
31
+ Thredded::TopicForm.new(messageboard: messageboard, user: thredded_current_user).tap do |form|
32
+ @new_topic = form if policy(form.topic).create?
33
+ end
34
+ end
35
+
36
+ def show
37
+ authorize topic, :read?
38
+ return redirect_to(canonical_topic_params) unless params_match?(canonical_topic_params)
39
+ page_scope = policy_scope(topic.posts)
40
+ .order_oldest_first
41
+ .includes(:user, :messageboard, :postable)
42
+ .page(current_page)
43
+ return redirect_to(last_page_params(page_scope)) if page_beyond_last?(page_scope)
44
+ @posts = Thredded::TopicPostsPageView.new(thredded_current_user, topic, page_scope)
45
+
46
+ if thredded_signed_in?
47
+ Thredded::UserTopicReadState.touch!(
48
+ thredded_current_user.id, topic.id, page_scope.last, current_page
49
+ )
50
+ end
51
+
52
+ @new_post = Thredded::PostForm.new(user: thredded_current_user, topic: topic, post_params: new_post_params)
53
+ end
54
+
55
+ def search
56
+ in_messageboard = params.key?(:messageboard_id)
57
+ if in_messageboard
58
+ authorize_reading messageboard
59
+ unless params_match?(canonical_messageboard_params)
60
+ skip_policy_scope
61
+ return redirect_to(canonical_messageboard_params)
62
+ end
63
+ end
64
+ @query = params[:q].to_s
65
+ topics_scope = policy_scope(
66
+ if in_messageboard
67
+ messageboard.topics
68
+ else
69
+ Thredded::Topic.where(messageboard_id: policy_scope(Thredded::Messageboard.all).pluck(:id))
70
+ end
71
+ )
72
+ page_scope = topics_scope
73
+ .search_query(@query)
74
+ .order_recently_posted_first
75
+ .includes(:categories, :last_user, :user)
76
+ .page(current_page)
77
+ return redirect_to(last_page_params(page_scope)) if page_beyond_last?(page_scope)
78
+ @topics = Thredded::TopicsPageView.new(thredded_current_user, page_scope)
79
+ end
80
+
81
+ def new
82
+ @new_topic = Thredded::TopicForm.new(new_topic_params)
83
+ authorize_creating @new_topic.topic
84
+ return redirect_to(canonical_messageboard_params) unless params_match?(canonical_messageboard_params)
85
+ render
86
+ end
87
+
88
+ def category
89
+ authorize_reading messageboard
90
+ @category = messageboard.categories.friendly.find(params[:category_id])
91
+ @topics = Thredded::TopicsPageView.new(
92
+ thredded_current_user,
93
+ policy_scope(@category.topics)
94
+ .unstuck
95
+ .order_recently_posted_first
96
+ .page(current_page)
97
+ )
98
+ render :index
99
+ end
100
+
101
+ def create
102
+ @new_topic = Thredded::TopicForm.new(new_topic_params)
103
+ authorize_creating @new_topic.topic
104
+ if @new_topic.save
105
+ redirect_to messageboard_topics_path(messageboard)
106
+ else
107
+ render :new
108
+ end
109
+ end
110
+
111
+ def edit
112
+ authorize topic, :update?
113
+ return redirect_to(canonical_topic_params) unless params_match?(canonical_topic_params)
114
+ @edit_topic = Thredded::EditTopicForm.new(user: thredded_current_user, topic: topic)
115
+ end
116
+
117
+ def update
118
+ topic.assign_attributes(topic_params_for_update)
119
+ authorize topic, :update?
120
+ if topic.messageboard_id_changed?
121
+ # Work around the association not being reset.
122
+ # TODO: report issue to Rails. Looks like a regression of:
123
+ # https://rails.lighthouseapp.com/projects/8994/tickets/2989
124
+ topic.messageboard = Thredded::Messageboard.find(topic.messageboard_id)
125
+
126
+ authorize topic.messageboard, :post?
127
+ end
128
+ @edit_topic = Thredded::EditTopicForm.new(user: thredded_current_user, topic: topic)
129
+ if @edit_topic.save
130
+ redirect_to messageboard_topic_url(topic.messageboard, topic),
131
+ notice: t('thredded.topics.updated_notice')
132
+ else
133
+ render :edit
134
+ end
135
+ end
136
+
137
+ def destroy
138
+ authorize topic, :destroy?
139
+ topic.destroy!
140
+ redirect_to messageboard_topics_path(messageboard),
141
+ notice: t('thredded.topics.deleted_notice')
142
+ end
143
+
144
+ def follow
145
+ authorize topic, :read?
146
+ Thredded::UserTopicFollow.create_unless_exists(thredded_current_user.id, topic.id)
147
+ follow_change_response(following: true)
148
+ end
149
+
150
+ def unfollow
151
+ authorize topic, :read?
152
+ Thredded::UserTopicFollow.find_by(topic_id: topic.id, user_id: thredded_current_user.id).try(:destroy)
153
+ follow_change_response(following: false)
154
+ end
155
+
156
+ private
157
+
158
+ def canonical_messageboard_params
159
+ { messageboard_id: messageboard.slug }
160
+ end
161
+
162
+ def canonical_topic_params
163
+ { messageboard_id: messageboard.slug, id: topic.slug }
164
+ end
165
+
166
+ def follow_change_response(following:)
167
+ notice = following ? t('thredded.topics.followed_notice') : t('thredded.topics.unfollowed_notice')
168
+ respond_to do |format|
169
+ format.html { redirect_to messageboard_topic_url(messageboard, topic), notice: notice }
170
+ format.json { render(json: { follow: following }) }
171
+ end
172
+ end
173
+
174
+ # Returns the `@topic` instance variable.
175
+ # If `@topic` is not set, it first sets it to the topic with the slug or ID given by `params[:id]`.
176
+ #
177
+ # @return [Thredded::Topic]
178
+ # @raise [Thredded::Errors::TopicNotFound] if the topic with the given slug does not exist.
179
+ def topic
180
+ @topic ||= Thredded::Topic.friendly_find!(params[:id])
181
+ end
182
+
183
+ # Use the topic's messageboard instead of the one specified in the URL,
184
+ # to account for `params[:messageboard_id]` pointing to the wrong messageboard
185
+ def use_topic_messageboard
186
+ @messageboard = topic.messageboard
187
+ end
188
+
189
+ def topic_params
190
+ params
191
+ .require(:topic)
192
+ .permit(:title, :locked, :sticky, category_ids: [])
193
+ end
194
+
195
+ def topic_params_for_update
196
+ params
197
+ .require(:topic)
198
+ .permit(:title, :locked, :sticky, :messageboard_id, category_ids: [])
199
+ end
200
+
201
+ def current_page
202
+ (params[:page] || 1).to_i
203
+ end
204
+ end
205
+ end