threddedDANIEL 0.14.5

Sign up to get free protection for your applications and to get access to all the features.
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,25 @@
1
+ //= require thredded/core/on_page_load
2
+ //= require thredded/core/serialize_form
3
+
4
+ // Submit GET forms with turbolinks
5
+ (() => {
6
+ const Thredded = window.Thredded;
7
+ const Turbolinks = window.Turbolinks;
8
+
9
+ Thredded.onPageLoad(() => {
10
+ if (!Turbolinks || !Turbolinks.supported) return;
11
+ Array.prototype.forEach.call(document.querySelectorAll('[data-thredded-turboform]'), (form) => {
12
+ form.addEventListener('submit', handleSubmit);
13
+ });
14
+ });
15
+
16
+ const handleSubmit = (evt) => {
17
+ evt.preventDefault();
18
+ const form = evt.currentTarget;
19
+ Turbolinks.visit(form.action + (form.action.indexOf('?') === -1 ? '?' : '&') + Thredded.serializeForm(form));
20
+
21
+ // On mobile the soft keyboard doesn't won't go away after the submit since we're submitting with
22
+ // Turbolinks. Hide it:
23
+ Thredded.hideSoftKeyboard();
24
+ };
25
+ })();
@@ -0,0 +1,66 @@
1
+ //= require thredded/core/on_page_load
2
+
3
+ // Reflects the logic of user preference settings by enabling/disabling certain inputs.
4
+ (() => {
5
+ const Thredded = window.Thredded;
6
+
7
+ const COMPONENT_SELECTOR = '[data-thredded-user-preferences-form]';
8
+ const BOUND_MESSAGEBOARD_NAME = 'data-thredded-bound-messageboard-pref';
9
+ const UPDATE_ON_CHANGE_NAME = 'data-thredded-update-checkbox-on-change';
10
+
11
+ class MessageboardPreferenceBinding {
12
+ constructor(form, genericCheckboxName, messageboardCheckboxName) {
13
+ this.messageboardCheckbox = form.querySelector(`[type="checkbox"][name="${messageboardCheckboxName}"]`);
14
+ if (!this.messageboardCheckbox) {
15
+ return;
16
+ }
17
+ this.messageboardCheckbox.addEventListener('change', () => {
18
+ this.rememberMessageboardChecked();
19
+ });
20
+ this.rememberMessageboardChecked();
21
+
22
+ this.genericCheckbox = form.querySelector(`[type="checkbox"][name="${genericCheckboxName}"]`);
23
+ this.genericCheckbox.addEventListener('change', () => {
24
+ this.updateMessageboardCheckbox();
25
+ });
26
+ this.updateMessageboardCheckbox();
27
+ }
28
+
29
+ rememberMessageboardChecked() {
30
+ this.messageboardCheckedWas = this.messageboardCheckbox.checked;
31
+ }
32
+
33
+ updateMessageboardCheckbox() {
34
+ const enabled = this.genericCheckbox.checked;
35
+ this.messageboardCheckbox.disabled = !enabled;
36
+ this.messageboardCheckbox.checked = enabled ? this.messageboardCheckedWas : false;
37
+ }
38
+ }
39
+
40
+ class UpdateOnChange {
41
+ constructor(form, sourceElement, targetName) {
42
+ const target = form.querySelector(`[type="checkbox"][name="${targetName}"]`);
43
+ if (!target) return;
44
+ sourceElement.addEventListener('change', () => {
45
+ target.checked = sourceElement.checked;
46
+ });
47
+ }
48
+ }
49
+
50
+ class UserPreferencesForm {
51
+ constructor(form) {
52
+ Array.prototype.forEach.call(form.querySelectorAll(`input[${BOUND_MESSAGEBOARD_NAME}]`), (element) => {
53
+ new MessageboardPreferenceBinding(form, element.name, element.getAttribute(BOUND_MESSAGEBOARD_NAME));
54
+ });
55
+ Array.prototype.forEach.call(form.querySelectorAll(`input[${UPDATE_ON_CHANGE_NAME}]`), (element) => {
56
+ new UpdateOnChange(form, element, element.getAttribute(UPDATE_ON_CHANGE_NAME));
57
+ });
58
+ }
59
+ }
60
+
61
+ Thredded.onPageLoad(() => {
62
+ Array.prototype.forEach.call(document.querySelectorAll(COMPONENT_SELECTOR), (form) => {
63
+ new UserPreferencesForm(form);
64
+ });
65
+ });
66
+ })();
@@ -0,0 +1,50 @@
1
+ //= require thredded/core/thredded
2
+ //= require thredded/core/escape_html
3
+
4
+ (() => {
5
+ const Thredded = window.Thredded;
6
+
7
+ Thredded.UserTextcomplete = {
8
+ DROPDOWN_CLASS_NAME: 'thredded--textcomplete-dropdown',
9
+
10
+ formatUser({avatar_url, name, display_name}) {
11
+ return "<div class='thredded--textcomplete-user-result'>" +
12
+ `<img class='thredded--textcomplete-user-result__avatar' src='${Thredded.escapeHtml(avatar_url)}' >` +
13
+ `<span class='thredded--textcomplete-user-result__name'>${Thredded.escapeHtml(name)}</span>` +
14
+ (name !== display_name && display_name ?
15
+ `<span class='thredded--textcomplete-user-result__display_name'>${Thredded.escapeHtml(display_name)}</span>` :
16
+ '') +
17
+ '</div>';
18
+ },
19
+
20
+ searchFn({url, autocompleteMinLength}) {
21
+ return function search(term, callback, match) {
22
+ if (term.length < autocompleteMinLength) {
23
+ callback([]);
24
+ return;
25
+ }
26
+ const request = new XMLHttpRequest();
27
+ request.open('GET', `${url}?q=${term}`, /* async */ true);
28
+ request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
29
+ request.onload = () => {
30
+ // Ignore errors
31
+ if (request.status < 200 || request.status >= 400) {
32
+ callback([]);
33
+ return;
34
+ }
35
+ callback(JSON.parse(request.responseText).results.map(({avatar_url, id, display_name, name}) => {
36
+ return {avatar_url, id, name, display_name, match};
37
+ }));
38
+ };
39
+ request.send();
40
+ }
41
+ }
42
+ };
43
+
44
+ document.addEventListener('turbolinks:before-cache', () => {
45
+ Array.prototype.forEach.call(
46
+ document.getElementsByClassName(Thredded.UserTextcomplete.DROPDOWN_CLASS_NAME), (node) => {
47
+ node.parentNode.removeChild(node);
48
+ });
49
+ });
50
+ })();
@@ -0,0 +1,122 @@
1
+ //= require thredded/core/on_page_load
2
+ //= require thredded/components/user_textcomplete
3
+ //= require thredded/dependencies/autosize
4
+
5
+ (() => {
6
+ const Thredded = window.Thredded;
7
+ const autosize = window.autosize;
8
+
9
+ const COMPONENT_SELECTOR = '[data-thredded-users-select]';
10
+
11
+ Thredded.UsersSelect = {
12
+ DROPDOWN_MAX_COUNT: 6,
13
+ };
14
+
15
+ function parseNames(text) {
16
+ const result = [];
17
+ let current = [];
18
+ let currentIndex = 0;
19
+ let inQuoted = false;
20
+ let inName = false;
21
+ for (let i = 0; i < text.length; ++i) {
22
+ const char = text.charAt(i);
23
+ switch (char) {
24
+ case '"':
25
+ inQuoted = !inQuoted;
26
+ break;
27
+ case ' ':
28
+ if (inName) current.push(char);
29
+ break;
30
+ case ',':
31
+ if (inQuoted) {
32
+ current.push(char);
33
+ } else {
34
+ inName = false;
35
+ if (current.length) {
36
+ result.push({name: current.join(''), index: currentIndex});
37
+ current.length = 0;
38
+ }
39
+ }
40
+ break;
41
+ default:
42
+ if (!inName) currentIndex = i;
43
+ inName = true;
44
+ current.push(char);
45
+ }
46
+ }
47
+ if (current.length) result.push({name: current.join(''), index: currentIndex});
48
+ return result;
49
+ }
50
+
51
+ const initUsersSelect = (textarea) => {
52
+ autosize(textarea);
53
+ // Prevent multiple lines
54
+ textarea.addEventListener('keypress', (evt) => {
55
+ if (evt.keyCode === 13 || evt.keyCode === 10) {
56
+ evt.preventDefault()
57
+ }
58
+ });
59
+ const editor = new Textcomplete.editors.Textarea(textarea);
60
+ const textcomplete = new Textcomplete(editor, {
61
+ dropdown: {
62
+ className: Thredded.UserTextcomplete.DROPDOWN_CLASS_NAME,
63
+ maxCount: Thredded.UsersSelect.DROPDOWN_MAX_COUNT,
64
+ },
65
+ });
66
+
67
+ const searchFn = Thredded.UserTextcomplete.searchFn({
68
+ url: textarea.getAttribute('data-autocomplete-url'),
69
+ autocompleteMinLength: parseInt(textarea.getAttribute('data-autocomplete-min-length'), 10)
70
+ });
71
+ textcomplete.on('rendered', function() {
72
+ if (textcomplete.dropdown.items.length) {
73
+ textcomplete.dropdown.items[0].activate();
74
+ }
75
+ });
76
+ textcomplete.register([{
77
+ index: 0,
78
+ match: (text) => {
79
+ const names = parseNames(text);
80
+ if (names.length) {
81
+ const {name, index} = names[names.length - 1];
82
+ const matchData = [name];
83
+ matchData.index = index;
84
+ return matchData;
85
+ } else {
86
+ return null;
87
+ }
88
+ },
89
+ search (term, callback, match) {
90
+ searchFn(term, function(results) {
91
+ const names = parseNames(textarea.value).map(({name}) => name);
92
+ callback(results.filter((result) => names.indexOf(result.name) === -1));
93
+ }, match);
94
+ },
95
+ template: Thredded.UserTextcomplete.formatUser,
96
+ replace ({name}) {
97
+ if (/,/.test(name)) {
98
+ return `"${name}", `
99
+ } else {
100
+ return `${name}, `
101
+ }
102
+ }
103
+ }]);
104
+ };
105
+
106
+ function destroyUsersSelect(textarea) {
107
+ autosize.destroy(textarea);
108
+ }
109
+
110
+ window.Thredded.onPageLoad(() => {
111
+ Array.prototype.forEach.call(document.querySelectorAll(COMPONENT_SELECTOR), (node) => {
112
+ initUsersSelect(node);
113
+ });
114
+ });
115
+
116
+ document.addEventListener('turbolinks:before-cache', () => {
117
+ Array.prototype.forEach.call(document.querySelectorAll(COMPONENT_SELECTOR), (node) => {
118
+ destroyUsersSelect(node);
119
+ });
120
+ });
121
+
122
+ })();
@@ -0,0 +1,9 @@
1
+ //= require thredded/core/on_page_load
2
+
3
+ window.Thredded.onPageLoad(() => {
4
+ if ('Rails' in window) {
5
+ window.Rails.refreshCSRFTokens();
6
+ } else if ('jQuery' in window && 'rails' in window.jQuery) {
7
+ window.jQuery.rails.refreshCSRFTokens();
8
+ }
9
+ });
@@ -0,0 +1,32 @@
1
+ //= require thredded/core/thredded
2
+
3
+ /**
4
+ * Return a function, that, as long as it continues to be invoked, will
5
+ * not be triggered. The function will be called after it stops being
6
+ * called for `wait` milliseconds. If `immediate` is passed, trigger the
7
+ * function on the leading edge, instead of the trailing.
8
+ * Based on https://john-dugan.com/javascript-debounce/.
9
+ *
10
+ * @param {Function} func
11
+ * @param {Number} wait in milliseconds
12
+ * @param {Boolean} immediate
13
+ * @returns {Function}
14
+ */
15
+ window.Thredded.debounce = function(func, wait, immediate) {
16
+ let timeoutId = null;
17
+ return function() {
18
+ const context = this, args = arguments;
19
+ const later = function() {
20
+ timeoutId = null;
21
+ if (!immediate) {
22
+ func.apply(context, args);
23
+ }
24
+ };
25
+ const callNow = immediate && !timeoutId;
26
+ clearTimeout(timeoutId);
27
+ timeoutId = setTimeout(later, wait || 200);
28
+ if (callNow) {
29
+ func.apply(context, args);
30
+ }
31
+ };
32
+ };
@@ -0,0 +1,7 @@
1
+ //= require thredded/core/thredded
2
+
3
+ window.Thredded.escapeHtml = function(text) {
4
+ const node = document.createElement('div');
5
+ node.textContent = text;
6
+ return node.innerHTML;
7
+ };
@@ -0,0 +1,7 @@
1
+ //= require thredded/core/thredded
2
+
3
+ window.Thredded.hideSoftKeyboard = () => {
4
+ const activeElement = document.activeElement;
5
+ if (!activeElement || !activeElement.blur) return;
6
+ activeElement.blur();
7
+ };
@@ -0,0 +1,54 @@
1
+ //= require thredded/core/thredded
2
+
3
+ (() => {
4
+ const isTurbolinks = 'Turbolinks' in window && window.Turbolinks.supported;
5
+ const isTurbolinks5 = isTurbolinks && 'clearCache' in window.Turbolinks;
6
+
7
+ let onPageLoadFiredOnce = false;
8
+ const pageLoadCallbacks = [];
9
+ const triggerOnPageLoad = () => {
10
+ pageLoadCallbacks.forEach((callback) => {
11
+ callback();
12
+ });
13
+ onPageLoadFiredOnce = true;
14
+ };
15
+
16
+ // Fires the callback on DOMContentLoaded or a Turbolinks page load.
17
+ // If called from an async script on the first page load, and the DOMContentLoad event
18
+ // has already fired, will execute the callback immediately.
19
+ window.Thredded.onPageLoad = (callback) => {
20
+ pageLoadCallbacks.push(callback);
21
+ // With async script loading, a callback may be added after the DOMContentLoaded event has already triggered.
22
+ // This means we will receive neither a DOMContentLoaded event, nor a turbolinks:load event on Turbolinks 5.
23
+ if (!onPageLoadFiredOnce && window.Thredded.DOMContentLoadedFired) {
24
+ callback();
25
+ }
26
+ };
27
+
28
+ if (isTurbolinks5) {
29
+ // In Turbolinks 5.0.1, turbolinks:load may have already fired (before DOMContentLoaded).
30
+ // If so, add our own DOMContentLoaded listener:
31
+ // See: https://github.com/turbolinks/turbolinks/commit/69d353ea73d10ee6b25c2866fc5706879ba403e3
32
+ if (window.Turbolinks.controller.lastRenderedLocation) {
33
+ document.addEventListener('DOMContentLoaded', () => {
34
+ triggerOnPageLoad();
35
+ });
36
+ }
37
+ document.addEventListener('turbolinks:load', () => {
38
+ triggerOnPageLoad();
39
+ });
40
+ } else {
41
+ // Turbolinks Classic (with or without jQuery.Turbolinks), or no Turbolinks:
42
+ if (!window.Thredded.DOMContentLoadedFired) {
43
+ document.addEventListener('DOMContentLoaded', () => {
44
+ triggerOnPageLoad();
45
+ });
46
+ }
47
+ if (isTurbolinks) {
48
+ document.addEventListener('page:load', () => {
49
+ triggerOnPageLoad();
50
+ })
51
+ }
52
+ }
53
+ })();
54
+
@@ -0,0 +1,9 @@
1
+ //= require thredded/core/thredded
2
+
3
+ window.Thredded.serializeForm = (form) => {
4
+ // Can't use new FormData(form).entries() because it's not supported on any IE
5
+ // The below is not a full replacement, but enough for Thredded's purposes.
6
+ return Array.prototype.map.call(form.querySelectorAll('[name]'), (e) => {
7
+ return `${encodeURIComponent(e.name)}=${encodeURIComponent(e.value)}`;
8
+ }).join('&');
9
+ };
@@ -0,0 +1 @@
1
+ window.Thredded = window.Thredded || {};
@@ -0,0 +1,8 @@
1
+ //= require thredded/dependencies/timeago
2
+ //= require thredded/dependencies/ujs
3
+ //= require thredded/dependencies/autosize
4
+ //= require thredded/dependencies/textcomplete
5
+
6
+ // We are not currently using any features that require the Babel polyfill
7
+ // Enable this if we do:
8
+ //- require babel/polyfill
@@ -0,0 +1 @@
1
+ //= require autosize.min
@@ -0,0 +1 @@
1
+ //= require textcomplete.min
@@ -0,0 +1 @@
1
+ //= require timeago
@@ -0,0 +1 @@
1
+ //= require rails-ujs
@@ -0,0 +1,2 @@
1
+ //= require_tree ./core
2
+ //= require_tree ./components
@@ -0,0 +1,3 @@
1
+ @import "thredded/dependencies";
2
+ @import "thredded/thredded";
3
+
@@ -0,0 +1,13 @@
1
+ // This Sass package defines Thredded variables, mixins, and placeholders.
2
+
3
+ @import "base/variables";
4
+ @import "base/grid";
5
+
6
+ @import "base/alerts";
7
+ @import "base/buttons";
8
+ @import "base/dropdown-menu";
9
+ @import "base/forms";
10
+ @import "base/lists";
11
+ @import "base/nav";
12
+ @import "base/tables";
13
+ @import "base/typography";
@@ -0,0 +1,51 @@
1
+ @import "thredded/base";
2
+ @import "thredded/components/post";
3
+ @import "thredded/components/onebox";
4
+
5
+ .thredded--email {
6
+ font-family: $thredded-base-font-family;
7
+ color: $thredded-text-color;
8
+ -webkit-font-smoothing: antialiased;
9
+ font-size: 16px;
10
+ line-height: 1.5;
11
+
12
+ a {
13
+ @extend %thredded--link;
14
+ }
15
+ hr {
16
+ @extend %thredded--hr;
17
+ }
18
+ }
19
+
20
+ .thredded--email-post {
21
+ margin: 0;
22
+
23
+ &--author {
24
+ margin-bottom: 2px;
25
+ }
26
+
27
+ .thredded--post--content {
28
+ font-size: inherit;
29
+ margin: 0 0 0.75rem;
30
+ padding-left: 0;
31
+
32
+ .onebox-wrapper-table {
33
+ width: 100%;
34
+ td {
35
+ border: $thredded-base-border;
36
+ text-align: left;
37
+ padding: $thredded-small-spacing;
38
+ > aside.onebox {
39
+ margin-bottom: 0;
40
+ padding: 0;
41
+ border: 0;
42
+ box-shadow: none;
43
+ }
44
+ }
45
+ }
46
+
47
+ aside.onebox {
48
+ margin-bottom: $thredded-small-spacing;
49
+ }
50
+ }
51
+ }