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,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thredded
4
+ class EditTopicForm
5
+ include ActiveModel::Model
6
+
7
+ delegate :id, :title, :category_ids, :locked, :sticky, :messageboard, :messageboard_id, :valid?,
8
+ to: :@topic
9
+
10
+ # @param user [Thredded.user_class]
11
+ # @param topic [Thredded::Topic]
12
+ def initialize(user:, topic:)
13
+ @user = user
14
+ @topic = topic
15
+ end
16
+
17
+ def self.model_name
18
+ Thredded::Topic.model_name
19
+ end
20
+
21
+ def category_options
22
+ @topic.messageboard.categories.map { |cat| [cat.name, cat.id] }
23
+ end
24
+
25
+ def messageboard_options
26
+ @user.thredded_can_write_messageboards.map { |messageboard| [messageboard.name, messageboard.id] }
27
+ end
28
+
29
+ def save
30
+ return false unless valid?
31
+ @topic.save!
32
+ true
33
+ end
34
+
35
+ def persisted?
36
+ true
37
+ end
38
+
39
+ def path
40
+ Thredded::UrlsHelper.messageboard_topic_path(@topic.messageboard, @topic)
41
+ end
42
+
43
+ def edit_path
44
+ Thredded::UrlsHelper.edit_messageboard_topic_path(@topic.messageboard, @topic)
45
+ end
46
+
47
+ def messageboard_path
48
+ Thredded::UrlsHelper.messageboard_topics_path(@topic.messageboard)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thredded
4
+ class PostForm
5
+ attr_reader :post, :topic
6
+ delegate :id,
7
+ :persisted?,
8
+ :content,
9
+ :content=,
10
+ to: :@post
11
+
12
+ # @param user [Thredded.user_class]
13
+ # @param topic [Topic]
14
+ # @param post [PrivatePost]
15
+ # @param post_params [Hash]
16
+ def initialize(user:, topic:, post: nil, post_params: {})
17
+ @messageboard = topic.messageboard
18
+ @topic = topic
19
+ @post = post ? post : topic.posts.build
20
+ user ||= Thredded::NullUser.new
21
+
22
+ if post_params.include?(:quote_post)
23
+ post_params[:content] =
24
+ Thredded::ContentFormatter.quote_content(post_params.delete(:quote_post).content)
25
+ end
26
+ @post.attributes = post_params.merge(
27
+ user: (user unless user.thredded_anonymous?),
28
+ messageboard: topic.messageboard
29
+ )
30
+ end
31
+
32
+ def self.for_persisted(post)
33
+ new(user: post.user, topic: post.postable, post: post)
34
+ end
35
+
36
+ def submit_path
37
+ Thredded::UrlsHelper.url_for([@messageboard, @topic, @post, only_path: true])
38
+ end
39
+
40
+ def preview_path
41
+ if @post.persisted?
42
+ Thredded::UrlsHelper.messageboard_topic_post_preview_path(@messageboard, @topic, @post)
43
+ else
44
+ Thredded::UrlsHelper.preview_new_messageboard_topic_post_path(@messageboard, @topic)
45
+ end
46
+ end
47
+
48
+ def save
49
+ return false unless @post.valid?
50
+ @post.save!
51
+ true
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thredded
4
+ class PrivatePostForm
5
+ attr_reader :post, :topic
6
+ delegate :id,
7
+ :persisted?,
8
+ :content,
9
+ :content=,
10
+ to: :@post
11
+
12
+ # @param user [Thredded.user_class]
13
+ # @param topic [PrivateTopic]
14
+ # @param post [Post]
15
+ # @param post_params [Hash]
16
+ def initialize(user:, topic:, post: nil, post_params: {})
17
+ @topic = topic
18
+ @post = post ? post : topic.posts.build
19
+ user ||= Thredded::NullUser.new
20
+
21
+ if post_params.include?(:quote_post)
22
+ post_params[:content] =
23
+ Thredded::ContentFormatter.quote_content(post_params.delete(:quote_post).content)
24
+ end
25
+ @post.attributes = post_params.merge(user: (user unless user.thredded_anonymous?))
26
+ end
27
+
28
+ def self.for_persisted(post)
29
+ new(user: post.user, topic: post.postable, post: post)
30
+ end
31
+
32
+ def submit_path
33
+ Thredded::UrlsHelper.url_for([@topic, @post, only_path: true])
34
+ end
35
+
36
+ def preview_path
37
+ if @post.persisted?
38
+ Thredded::UrlsHelper.private_topic_private_post_preview_path(@topic, @post)
39
+ else
40
+ Thredded::UrlsHelper.preview_new_private_topic_private_post_path(@topic)
41
+ end
42
+ end
43
+
44
+ def save
45
+ return false unless @post.valid?
46
+ @post.save!
47
+ true
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thredded
4
+ class PrivateTopicForm # rubocop:disable Metrics/ClassLength
5
+ include ActiveModel::Model
6
+
7
+ delegate :id,
8
+ to: :private_topic
9
+
10
+ attr_accessor \
11
+ :title,
12
+ :category_ids,
13
+ :user_ids,
14
+ :locked,
15
+ :sticky,
16
+ :content,
17
+ :private_topic
18
+
19
+ attr_reader :user, :params
20
+ attr_writer :user_names
21
+
22
+ validate :validate_children
23
+
24
+ def initialize(params = {})
25
+ @params = params
26
+ @title = params[:title]
27
+ @category_ids = params[:category_ids] || []
28
+ @user_ids = params[:user_ids] || []
29
+ @user = params[:user] || fail('user is required')
30
+ @locked = params[:locked]
31
+ @sticky = params[:sticky]
32
+ @content = params[:content]
33
+ @user_names = params[:user_names]
34
+ end
35
+
36
+ def self.model_name
37
+ Thredded::PrivateTopic.model_name
38
+ end
39
+
40
+ def save
41
+ @user_ids ||= []
42
+ @user_ids += Thredded.user_class.where(Thredded.user_name_column => parse_names(user_names)).pluck(:id)
43
+
44
+ return false unless valid?
45
+
46
+ ActiveRecord::Base.transaction do
47
+ private_topic.save!
48
+ post.save!
49
+ end
50
+ true
51
+ end
52
+
53
+ def private_topic
54
+ @private_topic ||= Thredded::PrivateTopic.new(
55
+ title: title,
56
+ users: private_users,
57
+ user: non_null_user,
58
+ last_user: non_null_user
59
+ )
60
+ end
61
+
62
+ def post
63
+ @post ||= private_topic.posts.build(
64
+ content: content,
65
+ user: non_null_user
66
+ )
67
+ end
68
+
69
+ def submit_path
70
+ Thredded::UrlsHelper.url_for([private_topic, only_path: true])
71
+ end
72
+
73
+ def preview_path
74
+ Thredded::UrlsHelper.preview_new_private_topic_path
75
+ end
76
+
77
+ def user_names
78
+ @user_names ||= Thredded.user_class.where(id: user_ids).pluck(Thredded.user_name_column).map do |name|
79
+ if name.include?(',')
80
+ "\"#{name}\""
81
+ else
82
+ name
83
+ end
84
+ end.join(', ')
85
+ end
86
+
87
+ private
88
+
89
+ def topic_categories
90
+ if category_ids
91
+ ids = category_ids.reject(&:empty?)
92
+ Thredded::Category.where(id: ids)
93
+ else
94
+ []
95
+ end
96
+ end
97
+
98
+ def private_users
99
+ Thredded.user_class.where(id: normalized_user_ids)
100
+ end
101
+
102
+ def private_user_ids
103
+ private_users.map(&:id)
104
+ end
105
+
106
+ def normalized_user_ids
107
+ user_ids
108
+ .map(&:to_s)
109
+ .reject(&:empty?)
110
+ .push(user.id.to_s)
111
+ .uniq
112
+ end
113
+
114
+ # @return [Thredded.user_class, nil] return a user or nil if the user is a NullUser
115
+ # This is necessary because assigning a NullUser to an ActiveRecord association results in an exception.
116
+ def non_null_user
117
+ @user unless @user.thredded_anonymous?
118
+ end
119
+
120
+ def validate_children
121
+ promote_errors(private_topic.errors) if private_topic.invalid?
122
+ promote_errors(post.errors) if post.invalid?
123
+ end
124
+
125
+ def promote_errors(child_errors)
126
+ child_errors.each do |attribute, message|
127
+ errors.add(attribute, message)
128
+ end
129
+ end
130
+
131
+ def parse_names(text) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
132
+ result = []
133
+ current = String.new
134
+ in_name = in_quoted = false
135
+ text.each_char do |char|
136
+ case char
137
+ when '"'
138
+ in_quoted = !in_quoted
139
+ when ' '
140
+ current << char if in_name
141
+ when ','
142
+ if in_quoted
143
+ current << char
144
+ else
145
+ in_name = false
146
+ unless current.empty?
147
+ result << current.dup
148
+ current.clear
149
+ end
150
+ end
151
+ else
152
+ in_name = true
153
+ current << char
154
+ end
155
+ end
156
+ result << current unless current.empty?
157
+ result
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thredded
4
+ class TopicForm
5
+ include ActiveModel::Model
6
+
7
+ attr_accessor :title, :category_ids, :locked, :sticky, :content, :topic
8
+ attr_reader :user, :messageboard
9
+
10
+ validate :validate_children
11
+
12
+ def initialize(params = {})
13
+ @title = params[:title]
14
+ @category_ids = params[:category_ids]
15
+ @locked = params[:locked] || false
16
+ @sticky = params[:sticky] || false
17
+ @content = params[:content]
18
+ @user = params[:user] || fail('user is required')
19
+ @messageboard = params[:messageboard]
20
+ end
21
+
22
+ def self.model_name
23
+ Thredded::Topic.model_name
24
+ end
25
+
26
+ def category_options
27
+ messageboard.categories.map { |cat| [cat.name, cat.id] }
28
+ end
29
+
30
+ def save
31
+ return false unless valid?
32
+
33
+ ActiveRecord::Base.transaction do
34
+ topic.save!
35
+ post.save!
36
+ Thredded::UserTopicReadState.read_on_first_post!(user, topic) if topic.previous_changes.include?(:id)
37
+ end
38
+ true
39
+ end
40
+
41
+ def topic
42
+ @topic ||= messageboard.topics.build(
43
+ title: title,
44
+ locked: locked,
45
+ sticky: sticky,
46
+ user: non_null_user,
47
+ categories: topic_categories,
48
+ )
49
+ end
50
+
51
+ def post
52
+ @post ||= topic.posts.build(
53
+ content: content,
54
+ user: non_null_user,
55
+ messageboard: messageboard
56
+ )
57
+ end
58
+
59
+ def submit_path
60
+ Thredded::UrlsHelper.url_for([messageboard, topic, only_path: true])
61
+ end
62
+
63
+ def preview_path
64
+ Thredded::UrlsHelper.preview_new_messageboard_topic_path(messageboard)
65
+ end
66
+
67
+ private
68
+
69
+ # @return [Thredded.user_class, nil] return a user or nil if the user is a NullUser
70
+ # This is necessary because assigning a NullUser to an ActiveRecord association results in an exception.
71
+ def non_null_user
72
+ @user unless @user.thredded_anonymous?
73
+ end
74
+
75
+ def topic_categories
76
+ if category_ids
77
+ ids = category_ids.reject(&:empty?)
78
+ Thredded::Category.where(id: ids)
79
+ else
80
+ []
81
+ end
82
+ end
83
+
84
+ def validate_children
85
+ promote_errors(topic.errors) if topic.invalid?
86
+ promote_errors(post.errors) if post.invalid?
87
+ end
88
+
89
+ def promote_errors(child_errors)
90
+ child_errors.each do |attribute, message|
91
+ errors.add(attribute, message)
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thredded
4
+ class UserPreferencesForm
5
+ include ActiveModel::Model
6
+
7
+ # @return [Thredded::Messageboard, nil]
8
+ attr_reader :messageboard
9
+
10
+ validate :validate_children
11
+
12
+ delegate :follow_topics_on_mention, :follow_topics_on_mention=,
13
+ :auto_follow_topics, :auto_follow_topics=,
14
+ :messageboard_notifications_for_followed_topics_attributes=,
15
+ :notifications_for_followed_topics_attributes=,
16
+ :notifications_for_private_topics_attributes=,
17
+ to: :user_preference
18
+
19
+ delegate :follow_topics_on_mention, :follow_topics_on_mention=,
20
+ :auto_follow_topics, :auto_follow_topics=,
21
+ to: :user_messageboard_preference,
22
+ prefix: :messageboard
23
+
24
+ # @param user [Thredded.user_class]
25
+ # @param messageboard [Thredded::Messageboard, nil]
26
+ # @param messageboards [ActiveRecord::Relation<Thredded::Messageboard>]
27
+ def initialize(user:, messageboard: nil, messageboards: nil, params: {})
28
+ @user = user
29
+ @messageboard = messageboard
30
+ @messageboards = messageboards
31
+ super(params)
32
+ end
33
+
34
+ # @return [Boolean]
35
+ def save
36
+ return false unless valid?
37
+ Thredded::UserPreference.transaction do
38
+ user_preference.save!
39
+
40
+ # Update all of the messageboards' auto_follow_topics if the global preference has changed.
41
+ if user_preference.previous_changes.include?('auto_follow_topics')
42
+ Thredded::UserMessageboardPreference.where(user: @user)
43
+ .update_all(auto_follow_topics: user_preference.auto_follow_topics)
44
+ user_messageboard_preference.auto_follow_topics_will_change! if messageboard
45
+ end
46
+
47
+ user_messageboard_preference.save! if messageboard
48
+ end
49
+ true
50
+ end
51
+
52
+ def messageboard_groups
53
+ @messageboard_groups ||= Thredded::MessageboardGroupView.grouped(@messageboards)
54
+ end
55
+
56
+ def notifications_for_private_topics
57
+ for_every_notifier(user_preference.notifications_for_private_topics)
58
+ end
59
+
60
+ def notifications_for_followed_topics
61
+ for_every_notifier(user_preference.notifications_for_followed_topics)
62
+ end
63
+
64
+ def messageboard_notifications_for_followed_topics
65
+ return nil unless messageboard
66
+ for_every_notifier(user_preference.messageboard_notifications_for_followed_topics.for_messageboard(messageboard))
67
+ end
68
+
69
+ def for_every_notifier(prefs)
70
+ Thredded.notifiers.map do |notifier|
71
+ prefs.find { |n| n.notifier_key == notifier.key } || prefs.build(notifier_key: notifier.key)
72
+ end
73
+ end
74
+
75
+ def update_path
76
+ if @messageboard
77
+ Thredded::UrlsHelper.messageboard_preferences_path(@messageboard)
78
+ else
79
+ Thredded::UrlsHelper.global_preferences_path
80
+ end
81
+ end
82
+
83
+ private
84
+
85
+ # @return [Thredded::UserPreference]
86
+ def user_preference
87
+ @user_preference ||= @user.thredded_user_preference
88
+ end
89
+
90
+ # @return [Thredded::UserMessageboardPreference, nil]
91
+ def user_messageboard_preference
92
+ return nil unless @messageboard
93
+ @user_messageboard_preference ||=
94
+ user_preference.messageboard_preferences.find_or_initialize_by(messageboard_id: @messageboard.id)
95
+ end
96
+
97
+ def validate_children
98
+ promote_errors(user_preference.errors) if user_preference.invalid?
99
+ if messageboard && user_messageboard_preference.invalid? # rubocop:disable Style/GuardClause
100
+ promote_errors(user_messageboard_preference.errors, :messageboard)
101
+ end
102
+ end
103
+
104
+ def promote_errors(child_errors, prefix = nil)
105
+ child_errors.each do |attribute, message|
106
+ errors.add([prefix, attribute].compact.join('_'), message)
107
+ end
108
+ end
109
+ end
110
+ end