publify_core 9.0.0.pre1

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 (372) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README.rdoc +3 -0
  4. data/Rakefile +35 -0
  5. data/app/assets/fonts/open-sans-bold.woff +0 -0
  6. data/app/assets/fonts/open-sans.woff +0 -0
  7. data/app/assets/images/admin/loading.gif +0 -0
  8. data/app/assets/images/admin/typologo.gif +0 -0
  9. data/app/assets/images/calendar_date_select/calendar.gif +0 -0
  10. data/app/assets/images/close.gif +0 -0
  11. data/app/assets/images/closelabel.gif +0 -0
  12. data/app/assets/images/go.png +0 -0
  13. data/app/assets/images/loading.gif +0 -0
  14. data/app/assets/images/nextlabel.gif +0 -0
  15. data/app/assets/images/overlay.png +0 -0
  16. data/app/assets/images/powered.gif +0 -0
  17. data/app/assets/images/prevlabel.gif +0 -0
  18. data/app/assets/images/spinner-blue.gif +0 -0
  19. data/app/assets/images/spinner.gif +0 -0
  20. data/app/assets/images/thumb_blank.jpg +0 -0
  21. data/app/assets/javascripts/application.js +2 -0
  22. data/app/assets/javascripts/cookies.js +60 -0
  23. data/app/assets/javascripts/datetimepicker.js +1470 -0
  24. data/app/assets/javascripts/lang/da_DK.js +17 -0
  25. data/app/assets/javascripts/lang/default.js +21 -0
  26. data/app/assets/javascripts/lang/en_US.js +21 -0
  27. data/app/assets/javascripts/lang/fr_FR.js +21 -0
  28. data/app/assets/javascripts/lang/nl_NL.js +21 -0
  29. data/app/assets/javascripts/lang/zh_TW.js +17 -0
  30. data/app/assets/javascripts/lightbox.js +350 -0
  31. data/app/assets/javascripts/observe.js +28 -0
  32. data/app/assets/javascripts/publify.js +11 -0
  33. data/app/assets/javascripts/publify_admin.js +138 -0
  34. data/app/assets/javascripts/quicktags.js +440 -0
  35. data/app/assets/javascripts/set-timeago-lang.js +3 -0
  36. data/app/assets/javascripts/sidebar.js +28 -0
  37. data/app/assets/javascripts/spinnable.js +5 -0
  38. data/app/assets/javascripts/tagmanager.js +481 -0
  39. data/app/assets/javascripts/typeahead.js +1139 -0
  40. data/app/assets/javascripts/widearea.js +486 -0
  41. data/app/assets/stylesheets/accounts.css.scss +7 -0
  42. data/app/assets/stylesheets/administration_structure.css.scss +239 -0
  43. data/app/assets/stylesheets/coderay.css +135 -0
  44. data/app/assets/stylesheets/datetimepicker.css +306 -0
  45. data/app/assets/stylesheets/lightbox.css +63 -0
  46. data/app/assets/stylesheets/publify.css.scss +9 -0
  47. data/app/assets/stylesheets/publify_admin.css.scss +11 -0
  48. data/app/assets/stylesheets/rss.css +53 -0
  49. data/app/assets/stylesheets/sidebar_admin.css.scss +36 -0
  50. data/app/assets/stylesheets/tagmanager.css +102 -0
  51. data/app/assets/stylesheets/user-styles.css +29 -0
  52. data/app/assets/stylesheets/widearea.css +133 -0
  53. data/app/controllers/accounts_controller.rb +2 -0
  54. data/app/controllers/admin/base_controller.rb +41 -0
  55. data/app/controllers/admin/cache_controller.rb +33 -0
  56. data/app/controllers/admin/content_controller.rb +174 -0
  57. data/app/controllers/admin/dashboard_controller.rb +87 -0
  58. data/app/controllers/admin/feedback_controller.rb +159 -0
  59. data/app/controllers/admin/migrations_controller.rb +20 -0
  60. data/app/controllers/admin/notes_controller.rb +69 -0
  61. data/app/controllers/admin/pages_controller.rb +88 -0
  62. data/app/controllers/admin/post_types_controller.rb +56 -0
  63. data/app/controllers/admin/profiles_controller.rb +48 -0
  64. data/app/controllers/admin/redirects_controller.rb +47 -0
  65. data/app/controllers/admin/resources_controller.rb +30 -0
  66. data/app/controllers/admin/seo_controller.rb +45 -0
  67. data/app/controllers/admin/settings_controller.rb +53 -0
  68. data/app/controllers/admin/sidebar_controller.rb +66 -0
  69. data/app/controllers/admin/tags_controller.rb +53 -0
  70. data/app/controllers/admin/textfilters_controller.rb +6 -0
  71. data/app/controllers/admin/themes_controller.rb +37 -0
  72. data/app/controllers/admin/users_controller.rb +65 -0
  73. data/app/controllers/articles_controller.rb +205 -0
  74. data/app/controllers/authors_controller.rb +27 -0
  75. data/app/controllers/base_controller.rb +45 -0
  76. data/app/controllers/comments_controller.rb +69 -0
  77. data/app/controllers/content_controller.rb +31 -0
  78. data/app/controllers/feedback_controller.rb +47 -0
  79. data/app/controllers/notes_controller.rb +37 -0
  80. data/app/controllers/setup_controller.rb +62 -0
  81. data/app/controllers/tags_controller.rb +55 -0
  82. data/app/controllers/text_controller.rb +9 -0
  83. data/app/controllers/textfilter_controller.rb +3 -0
  84. data/app/controllers/theme_controller.rb +59 -0
  85. data/app/controllers/trackbacks_controller.rb +36 -0
  86. data/app/controllers/xml_controller.rb +70 -0
  87. data/app/helpers/admin/base_helper.rb +87 -0
  88. data/app/helpers/admin/feedback_helper.rb +42 -0
  89. data/app/helpers/articles_helper.rb +8 -0
  90. data/app/helpers/authors_helper.rb +39 -0
  91. data/app/helpers/base_helper.rb +246 -0
  92. data/app/helpers/blog_helper.rb +12 -0
  93. data/app/helpers/xml_helper.rb +16 -0
  94. data/app/mailers/notification_mailer.rb +38 -0
  95. data/app/models/ability.rb +52 -0
  96. data/app/models/archives_sidebar.rb +45 -0
  97. data/app/models/article/factory.rb +56 -0
  98. data/app/models/article/states.rb +178 -0
  99. data/app/models/article.rb +321 -0
  100. data/app/models/blog.rb +290 -0
  101. data/app/models/blog_sweeper.rb +86 -0
  102. data/app/models/comment.rb +53 -0
  103. data/app/models/config_manager.rb +81 -0
  104. data/app/models/content.rb +138 -0
  105. data/app/models/content_base.rb +95 -0
  106. data/app/models/feedback/states.rb +256 -0
  107. data/app/models/feedback.rb +225 -0
  108. data/app/models/meta_sidebar.rb +8 -0
  109. data/app/models/note.rb +144 -0
  110. data/app/models/page.rb +36 -0
  111. data/app/models/page_cache.rb +33 -0
  112. data/app/models/page_sidebar.rb +12 -0
  113. data/app/models/ping.rb +116 -0
  114. data/app/models/post_type.rb +15 -0
  115. data/app/models/redirect.rb +45 -0
  116. data/app/models/redirection.rb +4 -0
  117. data/app/models/resource.rb +28 -0
  118. data/app/models/search_sidebar.rb +7 -0
  119. data/app/models/sidebar.rb +138 -0
  120. data/app/models/static_sidebar.rb +20 -0
  121. data/app/models/tag.rb +63 -0
  122. data/app/models/tag_sidebar.rb +27 -0
  123. data/app/models/text_filter.rb +61 -0
  124. data/app/models/trackback.rb +58 -0
  125. data/app/models/trigger.rb +45 -0
  126. data/app/models/user.rb +148 -0
  127. data/app/services/migrator.rb +25 -0
  128. data/app/services/title_builder.rb +80 -0
  129. data/app/uploaders/resource_uploader.rb +30 -0
  130. data/app/views/accounts/confirm.html.erb +8 -0
  131. data/app/views/admin/cache/show.html.erb +18 -0
  132. data/app/views/admin/content/_article_list.html.erb +26 -0
  133. data/app/views/admin/content/_form.html.erb +165 -0
  134. data/app/views/admin/content/autosave.js.erb +5 -0
  135. data/app/views/admin/content/edit.html.erb +3 -0
  136. data/app/views/admin/content/index.html.erb +48 -0
  137. data/app/views/admin/content/index.js.erb +1 -0
  138. data/app/views/admin/content/new.html.erb +3 -0
  139. data/app/views/admin/dashboard/_comment.html.erb +18 -0
  140. data/app/views/admin/dashboard/_comments.html.erb +10 -0
  141. data/app/views/admin/dashboard/_drafts.html.erb +25 -0
  142. data/app/views/admin/dashboard/_inbound.html.erb +31 -0
  143. data/app/views/admin/dashboard/_overview.html.erb +23 -0
  144. data/app/views/admin/dashboard/_welcome.html.erb +28 -0
  145. data/app/views/admin/dashboard/index.html.erb +17 -0
  146. data/app/views/admin/feedback/_button.html.erb +19 -0
  147. data/app/views/admin/feedback/_feedback.html.erb +7 -0
  148. data/app/views/admin/feedback/_ham.html.erb +17 -0
  149. data/app/views/admin/feedback/_spam.html.erb +23 -0
  150. data/app/views/admin/feedback/article.html.erb +69 -0
  151. data/app/views/admin/feedback/edit.html.erb +48 -0
  152. data/app/views/admin/feedback/ham.js +1 -0
  153. data/app/views/admin/feedback/index.html.erb +53 -0
  154. data/app/views/admin/feedback/spam.js +1 -0
  155. data/app/views/admin/migrations/show.html.erb +39 -0
  156. data/app/views/admin/notes/_form.html.erb +37 -0
  157. data/app/views/admin/notes/_header.html.erb +6 -0
  158. data/app/views/admin/notes/_list.html.erb +13 -0
  159. data/app/views/admin/notes/_note.html.erb +14 -0
  160. data/app/views/admin/notes/edit.html.erb +11 -0
  161. data/app/views/admin/notes/index.html.erb +11 -0
  162. data/app/views/admin/notes/show.html.erb +14 -0
  163. data/app/views/admin/pages/_form.html.erb +101 -0
  164. data/app/views/admin/pages/_pages.html.erb +21 -0
  165. data/app/views/admin/pages/edit.html.erb +1 -0
  166. data/app/views/admin/pages/index.html.erb +17 -0
  167. data/app/views/admin/pages/new.html.erb +1 -0
  168. data/app/views/admin/post_types/_index_and_form.html.erb +65 -0
  169. data/app/views/admin/post_types/edit.html.erb +1 -0
  170. data/app/views/admin/post_types/index.html.erb +1 -0
  171. data/app/views/admin/profiles/index.html.erb +10 -0
  172. data/app/views/admin/redirects/_index_and_form.html.erb +68 -0
  173. data/app/views/admin/redirects/edit.html.erb +1 -0
  174. data/app/views/admin/redirects/index.html.erb +1 -0
  175. data/app/views/admin/resources/index.html.erb +68 -0
  176. data/app/views/admin/seo/_general.html.erb +123 -0
  177. data/app/views/admin/seo/_permalinks.html.erb +53 -0
  178. data/app/views/admin/seo/_titles.html.erb +210 -0
  179. data/app/views/admin/seo/show.html.erb +32 -0
  180. data/app/views/admin/settings/display.html.erb +110 -0
  181. data/app/views/admin/settings/feedback.html.erb +125 -0
  182. data/app/views/admin/settings/index.html.erb +73 -0
  183. data/app/views/admin/settings/write.html.erb +87 -0
  184. data/app/views/admin/shared/_edit.html.erb +4 -0
  185. data/app/views/admin/shared/_menu.html.erb +122 -0
  186. data/app/views/admin/shared/_twitter_alert.html.erb +3 -0
  187. data/app/views/admin/sidebar/_available.html.erb +6 -0
  188. data/app/views/admin/sidebar/_available.json.erb +6 -0
  189. data/app/views/admin/sidebar/_config.html.erb +27 -0
  190. data/app/views/admin/sidebar/_target.html.erb +9 -0
  191. data/app/views/admin/sidebar/_target_sidebar.html.erb +20 -0
  192. data/app/views/admin/sidebar/destroy.js.erb +1 -0
  193. data/app/views/admin/sidebar/index.html.erb +31 -0
  194. data/app/views/admin/sidebar/sortable.js.erb +3 -0
  195. data/app/views/admin/sidebar/update.js.erb +2 -0
  196. data/app/views/admin/tags/_index_and_form.html.erb +59 -0
  197. data/app/views/admin/tags/edit.html.erb +1 -0
  198. data/app/views/admin/tags/index.html.erb +1 -0
  199. data/app/views/admin/themes/index.html.erb +27 -0
  200. data/app/views/admin/users/_form.html.erb +215 -0
  201. data/app/views/admin/users/edit.html.erb +8 -0
  202. data/app/views/admin/users/index.html.erb +39 -0
  203. data/app/views/admin/users/new.html.erb +8 -0
  204. data/app/views/archives_sidebar/_content.html.erb +13 -0
  205. data/app/views/articles/_article.html.erb +9 -0
  206. data/app/views/articles/_article_collection.html.erb +8 -0
  207. data/app/views/articles/_article_content.html.erb +5 -0
  208. data/app/views/articles/_article_excerpt.html.erb +13 -0
  209. data/app/views/articles/_article_links.html.erb +10 -0
  210. data/app/views/articles/_comment.html.erb +1 -0
  211. data/app/views/articles/_comment_errors.html.erb +2 -0
  212. data/app/views/articles/_comment_form.html.erb +48 -0
  213. data/app/views/articles/_comment_list.html.erb +5 -0
  214. data/app/views/articles/_comment_preview.html.erb +4 -0
  215. data/app/views/articles/_full_article_content.html.erb +2 -0
  216. data/app/views/articles/_password_form.html.erb +10 -0
  217. data/app/views/articles/_protected_article_content.html.erb +6 -0
  218. data/app/views/articles/_trackback.html.erb +6 -0
  219. data/app/views/articles/archives.html.erb +25 -0
  220. data/app/views/articles/comment.js.erb +5 -0
  221. data/app/views/articles/comment_failed.js.erb +3 -0
  222. data/app/views/articles/error.html.erb +3 -0
  223. data/app/views/articles/feedback_atom_feed.atom.builder +8 -0
  224. data/app/views/articles/feedback_rss_feed.rss.builder +21 -0
  225. data/app/views/articles/index.html.erb +1 -0
  226. data/app/views/articles/index_atom_feed.atom.builder +8 -0
  227. data/app/views/articles/index_rss_feed.rss.builder +20 -0
  228. data/app/views/articles/live_search.html.erb +10 -0
  229. data/app/views/articles/read.html.erb +61 -0
  230. data/app/views/articles/search.html.erb +8 -0
  231. data/app/views/articles/trackback.xml.builder +5 -0
  232. data/app/views/articles/view_page.html.erb +3 -0
  233. data/app/views/authors/show.html.erb +40 -0
  234. data/app/views/authors/show_atom_feed.atom.builder +8 -0
  235. data/app/views/authors/show_rss_feed.rss.builder +20 -0
  236. data/app/views/comments/_comment.html.erb +16 -0
  237. data/app/views/comments/index.html.erb +1 -0
  238. data/app/views/comments/index_atom_feed.atom.builder +8 -0
  239. data/app/views/comments/index_rss_feed.rss.builder +20 -0
  240. data/app/views/comments/preview.html.erb +1 -0
  241. data/app/views/comments/preview.js.erb +3 -0
  242. data/app/views/devise/mailer/reset_password_instructions.html.erb +13 -0
  243. data/app/views/devise/passwords/edit.html.erb +28 -0
  244. data/app/views/devise/passwords/new.html.erb +20 -0
  245. data/app/views/devise/registrations/new.html.erb +36 -0
  246. data/app/views/devise/sessions/new.html.erb +32 -0
  247. data/app/views/devise/shared/_links.html.erb +15 -0
  248. data/app/views/errors/404.html.erb +2 -0
  249. data/app/views/layouts/accounts.html.erb +33 -0
  250. data/app/views/layouts/administration.html.erb +37 -0
  251. data/app/views/layouts/default.html.erb +32 -0
  252. data/app/views/layouts/editor.html.erb +31 -0
  253. data/app/views/meta_sidebar/_content.html.erb +8 -0
  254. data/app/views/notes/_note.html.erb +15 -0
  255. data/app/views/notes/error.html.erb +3 -0
  256. data/app/views/notes/index.html.erb +15 -0
  257. data/app/views/notes/show.html.erb +5 -0
  258. data/app/views/notes/show_in_reply.html.erb +16 -0
  259. data/app/views/notification_mailer/_mail_footer.html.erb +7 -0
  260. data/app/views/notification_mailer/_mail_header.html.erb +1 -0
  261. data/app/views/notification_mailer/article.html.erb +6 -0
  262. data/app/views/notification_mailer/comment.html.erb +11 -0
  263. data/app/views/notification_mailer/notif_user.html.erb +14 -0
  264. data/app/views/page_sidebar/_content.html.erb +12 -0
  265. data/app/views/search_sidebar/_content.html.erb +10 -0
  266. data/app/views/settings/done.html.erb +2 -0
  267. data/app/views/settings/install.html.erb +12 -0
  268. data/app/views/setup/index.html.erb +13 -0
  269. data/app/views/shared/_atom_header.atom.builder +6 -0
  270. data/app/views/shared/_atom_item_article.atom.builder +39 -0
  271. data/app/views/shared/_atom_item_comment.atom.builder +10 -0
  272. data/app/views/shared/_atom_item_trackback.atom.builder +9 -0
  273. data/app/views/shared/_flash.erb +10 -0
  274. data/app/views/shared/_page_header.html.erb +26 -0
  275. data/app/views/shared/_rss_item_article.rss.builder +35 -0
  276. data/app/views/shared/_rss_item_comment.rss.builder +8 -0
  277. data/app/views/shared/_rss_item_trackback.rss.builder +7 -0
  278. data/app/views/sidebar/_row.html.erb +1 -0
  279. data/app/views/sidebar/_sidebar.html.erb +5 -0
  280. data/app/views/sidebar/display_plugins.html.erb +5 -0
  281. data/app/views/sidebar/show.html.erb +1 -0
  282. data/app/views/static_sidebar/_content.html.erb +2 -0
  283. data/app/views/tag_sidebar/_content.html.erb +10 -0
  284. data/app/views/tags/index.html.erb +15 -0
  285. data/app/views/tags/show.html.erb +1 -0
  286. data/app/views/theme/static_view_test.html.erb +1 -0
  287. data/app/views/trackbacks/index_atom_feed.atom.builder +7 -0
  288. data/app/views/trackbacks/index_rss_feed.rss.builder +20 -0
  289. data/app/views/trackbacks/trackback.xml.builder +4 -0
  290. data/app/views/xml/_googlesitemap_item_article.googlesitemap.builder +5 -0
  291. data/app/views/xml/_googlesitemap_item_category.googlesitemap.builder +4 -0
  292. data/app/views/xml/_googlesitemap_item_page.googlesitemap.builder +4 -0
  293. data/app/views/xml/_googlesitemap_item_tag.googlesitemap.builder +4 -0
  294. data/app/views/xml/feed.googlesitemap.builder +7 -0
  295. data/app/views/xml/rsd.rsd.builder +8 -0
  296. data/config/i18n-tasks.yml +49 -0
  297. data/config/initializers/devise.rb +265 -0
  298. data/config/initializers/mime_types.rb +6 -0
  299. data/config/locales/da.yml +827 -0
  300. data/config/locales/de.yml +827 -0
  301. data/config/locales/en.yml +827 -0
  302. data/config/locales/es-MX.yml +827 -0
  303. data/config/locales/fr.yml +827 -0
  304. data/config/locales/he.yml +827 -0
  305. data/config/locales/it.yml +827 -0
  306. data/config/locales/ja.yml +827 -0
  307. data/config/locales/lt.yml +827 -0
  308. data/config/locales/nb-NO.yml +827 -0
  309. data/config/locales/nl.yml +827 -0
  310. data/config/locales/pl.yml +827 -0
  311. data/config/locales/pt-BR.yml +827 -0
  312. data/config/locales/ro.yml +827 -0
  313. data/config/locales/ru.yml +827 -0
  314. data/config/locales/sidebars.da.yml +20 -0
  315. data/config/locales/sidebars.de.yml +20 -0
  316. data/config/locales/sidebars.en.yml +20 -0
  317. data/config/locales/sidebars.es-MX.yml +20 -0
  318. data/config/locales/sidebars.fr.yml +20 -0
  319. data/config/locales/sidebars.he.yml +20 -0
  320. data/config/locales/sidebars.it.yml +20 -0
  321. data/config/locales/sidebars.ja.yml +20 -0
  322. data/config/locales/sidebars.lt.yml +20 -0
  323. data/config/locales/sidebars.nb-NO.yml +20 -0
  324. data/config/locales/sidebars.nl.yml +20 -0
  325. data/config/locales/sidebars.pl.yml +20 -0
  326. data/config/locales/sidebars.pt-BR.yml +20 -0
  327. data/config/locales/sidebars.ro.yml +20 -0
  328. data/config/locales/sidebars.ru.yml +20 -0
  329. data/config/locales/sidebars.zh-CN.yml +20 -0
  330. data/config/locales/sidebars.zh-TW.yml +20 -0
  331. data/config/locales/zh-CN.yml +827 -0
  332. data/config/locales/zh-TW.yml +827 -0
  333. data/config/routes.rb +177 -0
  334. data/db/migrate/113_initial_schema.rb +205 -0
  335. data/db/migrate/114_fixes_buggy_articles_and_notes.rb +52 -0
  336. data/db/migrate/115_drops_categories_for_tags.rb +34 -0
  337. data/db/migrate/20150207131657_add_missing_indexes.rb +19 -0
  338. data/db/migrate/20150807134129_simplify_redirect_relations.rb +38 -0
  339. data/db/migrate/20150808052637_add_blog_ids.rb +33 -0
  340. data/db/migrate/20150808191127_add_blog_id_to_redirects.rb +15 -0
  341. data/db/migrate/20150810094754_add_blog_id_to_tags.rb +15 -0
  342. data/db/migrate/20160108111120_add_devise_to_users.rb +53 -0
  343. data/db/migrate/20160108184201_move_last_connection_to_last_sign_in_at.rb +16 -0
  344. data/db/migrate/20160110094906_remove_profiles_rights.rb +14 -0
  345. data/db/migrate/20160605103918_replace_profile_id_with_string.rb +30 -0
  346. data/db/migrate/20160605154632_remove_profiles.rb +24 -0
  347. data/db/migrate/20160701061851_demand_blog_id_on_contents.rb +9 -0
  348. data/db/migrate/20160701062604_add_blog_id_to_resources.rb +28 -0
  349. data/db/seeds.rb +37 -0
  350. data/lib/email_notify.rb +26 -0
  351. data/lib/format.rb +17 -0
  352. data/lib/publify_core/engine.rb +23 -0
  353. data/lib/publify_core/lang.rb +5 -0
  354. data/lib/publify_core/version.rb +3 -0
  355. data/lib/publify_core.rb +56 -0
  356. data/lib/publify_guid.rb +9 -0
  357. data/lib/publify_plugins.rb +72 -0
  358. data/lib/publify_textfilter_markdown.rb +44 -0
  359. data/lib/publify_textfilter_none.rb +14 -0
  360. data/lib/publify_textfilter_smartypants.rb +14 -0
  361. data/lib/publify_textfilter_textile.rb +21 -0
  362. data/lib/publify_textfilter_twitterfilter.rb +33 -0
  363. data/lib/publify_time.rb +30 -0
  364. data/lib/sidebar_field.rb +115 -0
  365. data/lib/sidebar_registry.rb +33 -0
  366. data/lib/spam_protection.rb +101 -0
  367. data/lib/stateful.rb +106 -0
  368. data/lib/tasks/publify_core_tasks.rake +4 -0
  369. data/lib/text_filter_plugin.rb +182 -0
  370. data/lib/theme.rb +72 -0
  371. data/lib/transforms.rb +45 -0
  372. metadata +865 -0
@@ -0,0 +1,53 @@
1
+ require_dependency 'spam_protection'
2
+ require 'timeout'
3
+
4
+ class Comment < Feedback
5
+ belongs_to :user
6
+ content_fields :body
7
+ validates :author, :body, presence: true
8
+
9
+ after_save :send_notifications
10
+
11
+ attr_accessor :referrer, :permalink
12
+
13
+ scope :spam, -> { where(state: 'spam') }
14
+ scope :not_spam, -> { where("state != 'spam'") }
15
+ scope :presumed_spam, -> { where(state: 'presumed_spam') }
16
+ scope :presumed_ham, -> { where(state: 'presumed_ham') }
17
+ scope :ham, -> { where(state: 'ham') }
18
+ scope :unconfirmed, -> { where(state: %w(presumed_spam presumed_ham)) }
19
+
20
+ scope :last_published, -> { published.limit(5).order('created_at DESC') }
21
+
22
+ def notify_user_via_email(user)
23
+ EmailNotify.send_comment(self, user) if user.notify_via_email?
24
+ end
25
+
26
+ def interested_users
27
+ User.where(notify_on_comments: true)
28
+ end
29
+
30
+ def default_text_filter
31
+ TextFilter.find_or_default(blog.comment_text_filter)
32
+ end
33
+
34
+ def feed_title
35
+ "Comment on #{article.title} by #{author}"
36
+ end
37
+
38
+ protected
39
+
40
+ def article_allows_feedback?
41
+ return true if article.allow_comments?
42
+ errors.add(:article, 'Article is not open to comments')
43
+ false
44
+ end
45
+
46
+ def originator
47
+ author
48
+ end
49
+
50
+ def content_fields
51
+ [:body]
52
+ end
53
+ end
@@ -0,0 +1,81 @@
1
+ module ConfigManager
2
+ def self.append_features(base)
3
+ super
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+ def fields
9
+ @fields ||= Hash.new { Item.new }
10
+ end
11
+
12
+ def setting(name, type = :object, default = nil)
13
+ item = Item.new
14
+ item.name = name.to_s
15
+ item.ruby_type = type
16
+ item.default = default
17
+ fields[name.to_s] = item
18
+ add_setting_accessor(item)
19
+ end
20
+
21
+ def default_for(key)
22
+ fields[key.to_s].default
23
+ end
24
+
25
+ private
26
+
27
+ def add_setting_accessor(item)
28
+ add_setting_reader(item)
29
+ add_setting_writer(item)
30
+ end
31
+
32
+ def add_setting_reader(item)
33
+ send(:define_method, item.name) do
34
+ raw_value = settings[item.name]
35
+ raw_value.nil? ? item.default : raw_value
36
+ end
37
+ if item.ruby_type == :boolean
38
+ send(:define_method, item.name + '?') do
39
+ raw_value = settings[item.name]
40
+ raw_value.nil? ? item.default : raw_value
41
+ end
42
+ end
43
+ end
44
+
45
+ def add_setting_writer(item)
46
+ send(:define_method, "#{item.name}=") do |newvalue|
47
+ self.settings ||= {}
48
+ retval = settings[item.name] = canonicalize(item.name, newvalue)
49
+ retval
50
+ end
51
+ end
52
+ end
53
+
54
+ def canonicalize(key, value)
55
+ self.class.fields[key.to_s].canonicalize(value)
56
+ end
57
+
58
+ class Item
59
+ attr_accessor :name, :ruby_type, :default
60
+
61
+ def canonicalize(value)
62
+ case ruby_type
63
+ when :boolean
64
+ case value
65
+ when '0', 0, '', false, 'false', 'f', nil
66
+ false
67
+ else
68
+ true
69
+ end
70
+ when :integer
71
+ value.to_i
72
+ when :string
73
+ value.to_s
74
+ when :yaml
75
+ value.to_yaml
76
+ else
77
+ value
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,138 @@
1
+ require 'set'
2
+ require 'uri'
3
+
4
+ class Content < ActiveRecord::Base
5
+ include Stateful
6
+
7
+ include ContentBase
8
+
9
+ # TODO: Move these calls to ContentBase
10
+ after_save :invalidates_cache?
11
+ after_destroy ->(c) { c.invalidates_cache?(true) }
12
+
13
+ belongs_to :text_filter
14
+ belongs_to :user
15
+ belongs_to :blog
16
+
17
+ validates :blog, presence: true
18
+
19
+ has_one :redirect, dependent: :destroy
20
+
21
+ has_many :triggers, as: :pending_item, dependent: :delete_all
22
+
23
+ scope :user_id, ->(user_id) { where('user_id = ?', user_id) }
24
+ scope :published, -> { where(published: true, published_at: Time.at(0)..Time.now).order('published_at DESC') }
25
+ scope :published_at, ->(time_params) { published.where(published_at: PublifyTime.delta(*time_params)).order('published_at DESC') }
26
+ scope :not_published, -> { where('published = ?', false) }
27
+ scope :draft, -> { where('state = ?', 'draft') }
28
+ scope :no_draft, -> { where('state <> ?', 'draft').order('published_at DESC') }
29
+ scope :searchstring, lambda { |search_string|
30
+ tokens = search_string.split(' ').map { |c| "%#{c.downcase}%" }
31
+ where('state = ? AND ' + (['(LOWER(body) LIKE ? OR LOWER(extended) LIKE ? OR LOWER(title) LIKE ?)'] * tokens.size).join(' AND '),
32
+ 'published', *tokens.map { |token| [token] * 3 }.flatten)
33
+ }
34
+ scope :already_published, -> { where('published = ? AND published_at < ?', true, Time.now).order(default_order) }
35
+
36
+ scope :published_at_like, ->(date_at) { where(published_at: PublifyTime.delta_like(date_at)) }
37
+
38
+ serialize :whiteboard
39
+
40
+ def author=(user)
41
+ if user.respond_to?(:login)
42
+ self[:author] = user.login
43
+ self.user = user
44
+ elsif user.is_a?(String)
45
+ self[:author] = user
46
+ end
47
+ end
48
+
49
+ # Set the text filter for this object.
50
+ # NOTE: Due to how Rails injects association methods, this cannot be put in ContentBase
51
+ # TODO: Allowing assignment of a string here is not very clean.
52
+ def text_filter=(filter)
53
+ filter_object = case filter
54
+ when TextFilter
55
+ filter
56
+ else
57
+ TextFilter.find_or_default(filter)
58
+ end
59
+ self.text_filter_id = if filter_object
60
+ filter_object.id
61
+ else
62
+ filter.to_i
63
+ end
64
+ end
65
+
66
+ def shorten_url
67
+ return unless published
68
+
69
+ if redirect.present?
70
+ return if redirect.to_path == permalink_url
71
+ redirect.to_path = permalink_url
72
+ redirect.save
73
+ else
74
+ r = Redirect.new(blog: blog)
75
+ r.from_path = r.shorten
76
+ r.to_path = permalink_url
77
+ self.redirect = r
78
+ end
79
+ end
80
+
81
+ def self.find_already_published(_limit)
82
+ where('published_at < ?', Time.now).limit(1000).order('created_at DESC')
83
+ end
84
+
85
+ def self.search_with(params)
86
+ params ||= {}
87
+ scoped = unscoped
88
+ if params[:searchstring].present?
89
+ scoped = scoped.searchstring(params[:searchstring])
90
+ end
91
+
92
+ if params[:published_at].present? && /(\d\d\d\d)-(\d\d)/ =~ params[:published_at]
93
+ scoped = scoped.published_at_like(params[:published_at])
94
+ end
95
+
96
+ if params[:user_id].present? && params[:user_id].to_i > 0
97
+ scoped = scoped.user_id(params[:user_id])
98
+ end
99
+
100
+ if params[:published].present?
101
+ scoped = scoped.published if params[:published].to_s == '1'
102
+ scoped = scoped.not_published if params[:published].to_s == '0'
103
+ end
104
+
105
+ scoped
106
+ end
107
+
108
+ def whiteboard
109
+ self[:whiteboard] ||= {}
110
+ end
111
+
112
+ def withdraw!
113
+ withdraw
114
+ save!
115
+ end
116
+
117
+ def link_to_author?
118
+ !user.email.blank? && blog.link_to_author
119
+ end
120
+
121
+ def get_rss_description
122
+ return '' unless blog.rss_description
123
+ return '' unless respond_to?(:user) && user && user.name
124
+
125
+ rss_desc = blog.rss_description_text
126
+ rss_desc.gsub!('%author%', user.name)
127
+ rss_desc.gsub!('%blog_url%', blog.base_url)
128
+ rss_desc.gsub!('%blog_name%', blog.blog_name)
129
+ rss_desc.gsub!('%permalink_url%', permalink_url)
130
+ rss_desc
131
+ end
132
+
133
+ def short_url
134
+ # Double check because of crappy data in my own old database
135
+ return unless published && redirect.present?
136
+ redirect.from_url
137
+ end
138
+ end
@@ -0,0 +1,95 @@
1
+ module ContentBase
2
+ def self.included(base)
3
+ base.extend ClassMethods
4
+ end
5
+
6
+ attr_accessor :just_changed_published_status
7
+ alias just_changed_published_status? just_changed_published_status
8
+
9
+ def really_send_notifications
10
+ interested_users.each do |value|
11
+ send_notification_to_user(value)
12
+ end
13
+ true
14
+ end
15
+
16
+ def send_notification_to_user(user)
17
+ notify_user_via_email(user)
18
+ end
19
+
20
+ # Return HTML for some part of this object.
21
+ def html(field = :all)
22
+ if field == :all
23
+ generate_html(:all, content_fields.map { |f| self[f].to_s }.join("\n\n"))
24
+ elsif html_map(field)
25
+ generate_html(field)
26
+ else
27
+ raise "Unknown field: #{field.inspect} in content.html"
28
+ end
29
+ end
30
+
31
+ # Generate HTML for a specific field using the text_filter in use for this
32
+ # object.
33
+ def generate_html(field, text = nil)
34
+ text ||= self[field].to_s
35
+ prehtml = html_preprocess(field, text).to_s
36
+ html = (text_filter || default_text_filter).filter_text(prehtml) || prehtml
37
+ html_postprocess(field, html).to_s
38
+ end
39
+
40
+ # Post-process the HTML. This is a noop by default, but Comment overrides it
41
+ # to enforce HTML sanity.
42
+ def html_postprocess(_field, html)
43
+ html
44
+ end
45
+
46
+ def html_preprocess(_field, html)
47
+ html
48
+ end
49
+
50
+ def html_map(field)
51
+ content_fields.include? field
52
+ end
53
+
54
+ def excerpt_text(length = 160)
55
+ text = if respond_to?(:excerpt) && (excerpt || '') != ''
56
+ generate_html(:excerpt, excerpt)
57
+ else
58
+ html(:all)
59
+ end
60
+
61
+ text = text.strip_html
62
+
63
+ text.slice(0, length) +
64
+ (text.length > length ? '...' : '')
65
+ end
66
+
67
+ def invalidates_cache?(on_destruction = false)
68
+ @invalidates_cache ||= if on_destruction
69
+ just_changed_published_status? || published?
70
+ else
71
+ (changed? && published?) || just_changed_published_status?
72
+ end
73
+ end
74
+
75
+ def publish!
76
+ self.published = true
77
+ save!
78
+ end
79
+
80
+ # The default text filter. Generally, this is the filter specified by blog.text_filter,
81
+ # but comments may use a different default.
82
+ def default_text_filter
83
+ blog.text_filter_object
84
+ end
85
+
86
+ module ClassMethods
87
+ def content_fields(*attribs)
88
+ class_eval "def content_fields; #{attribs.inspect}; end"
89
+ end
90
+
91
+ def default_order
92
+ 'published_at DESC'
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,256 @@
1
+ module Feedback::States
2
+ class Base < Stateful::State
3
+ # Give the default 'model' a more meaningful name
4
+ alias content model
5
+
6
+ # Callback handlers
7
+ def before_save_handler
8
+ true
9
+ end
10
+
11
+ def after_initialize_handler
12
+ true
13
+ end
14
+
15
+ def post_trigger
16
+ true
17
+ end
18
+
19
+ def send_notifications
20
+ true
21
+ end
22
+
23
+ def report_classification
24
+ true
25
+ end
26
+
27
+ def withdraw; end
28
+
29
+ def confirm_classification; end
30
+
31
+ def mark_as_spam
32
+ content.state = :just_marked_as_spam
33
+ end
34
+
35
+ def mark_as_ham
36
+ content.state = :just_marked_as_ham
37
+ end
38
+ end
39
+
40
+ class Unclassified < Base
41
+ def after_initialize_handler
42
+ enter_hook
43
+ true
44
+ end
45
+
46
+ def enter_hook
47
+ super
48
+ content[:published] = false
49
+ content[:status_confirmed] = false
50
+ end
51
+
52
+ def published?
53
+ classify_content
54
+ content.published?
55
+ end
56
+
57
+ def just_published?
58
+ classify_content
59
+ content.just_published?
60
+ end
61
+
62
+ def spam?
63
+ classify_content
64
+ content.spam?
65
+ end
66
+
67
+ def classify_content
68
+ content.state = case content.classify
69
+ when :ham then :just_presumed_ham
70
+ when :spam then :presumed_spam
71
+ else :presumed_spam
72
+ end
73
+ end
74
+
75
+ def before_save_handler
76
+ classify_content
77
+ end
78
+
79
+ def to_s
80
+ 'unclassified'
81
+ end
82
+ end
83
+
84
+ class JustPresumedHam < Base
85
+ def enter_hook
86
+ super
87
+ content.just_changed_published_status = true
88
+ content.state = :presumed_ham unless content.user_id
89
+ content.state = :just_marked_as_ham if content.user_id
90
+ end
91
+
92
+ def to_s
93
+ 'just_presumed_ham'
94
+ end
95
+ end
96
+
97
+ class PresumedHam < Base
98
+ def enter_hook
99
+ super
100
+ content[:published] = true
101
+ content[:status_confirmed] = false
102
+ end
103
+
104
+ def published?
105
+ true
106
+ end
107
+
108
+ def just_published?
109
+ content.just_changed_published_status?
110
+ end
111
+
112
+ def withdraw
113
+ mark_as_spam
114
+ end
115
+
116
+ def confirm_classification
117
+ mark_as_ham
118
+ end
119
+
120
+ def mark_as_ham
121
+ content.state = :ham
122
+ end
123
+
124
+ def to_s
125
+ 'presumed_ham'
126
+ end
127
+
128
+ def send_notifications
129
+ content.really_send_notifications if content.just_changed_published_status
130
+ true
131
+ end
132
+ end
133
+
134
+ class JustMarkedAsHam < Base
135
+ def enter_hook
136
+ super
137
+ content.just_changed_published_status = true
138
+ content.state = :ham
139
+ end
140
+
141
+ def to_s
142
+ 'just_marked_as_ham'
143
+ end
144
+ end
145
+
146
+ class Ham < Base
147
+ def enter_hook
148
+ super
149
+ content[:published] = true
150
+ content[:status_confirmed] = true
151
+ end
152
+
153
+ def published?
154
+ true
155
+ end
156
+
157
+ def status_confirmed?
158
+ true
159
+ end
160
+
161
+ def mark_as_ham; end
162
+
163
+ def just_published?
164
+ content.just_changed_published_status?
165
+ end
166
+
167
+ def withdraw
168
+ mark_as_spam
169
+ end
170
+
171
+ def report_classification
172
+ content.report_as_ham if content.just_changed_published_status?
173
+ true
174
+ end
175
+
176
+ def send_notifications
177
+ content.really_send_notifications if content.just_changed_published_status?
178
+ true
179
+ end
180
+
181
+ def to_s
182
+ 'ham'
183
+ end
184
+ end
185
+
186
+ class PresumedSpam < Base
187
+ def enter_hook
188
+ super
189
+ content[:published] = false
190
+ content[:status_confirmed] = false
191
+ end
192
+
193
+ def spam?
194
+ true
195
+ end
196
+
197
+ def mark_as_ham
198
+ content.state = :just_marked_as_ham
199
+ end
200
+
201
+ def mark_as_spam
202
+ content.state = :spam
203
+ end
204
+
205
+ def withdraw
206
+ mark_as_spam
207
+ end
208
+
209
+ def confirm_classification
210
+ mark_as_spam
211
+ end
212
+
213
+ def to_s
214
+ 'presumed_spam'
215
+ end
216
+ end
217
+
218
+ class JustMarkedAsSpam < Base
219
+ def enter_hook
220
+ super
221
+ content.just_changed_published_status = true
222
+ content.state = :spam
223
+ end
224
+
225
+ def to_s
226
+ 'just_marked_as_spam'
227
+ end
228
+ end
229
+
230
+ class Spam < Base
231
+ def enter_hook
232
+ super
233
+ content[:published] = false
234
+ content[:status_confirmed] = true
235
+ end
236
+
237
+ def spam?
238
+ true
239
+ end
240
+
241
+ def status_confirmed?
242
+ true
243
+ end
244
+
245
+ def mark_as_spam; end
246
+
247
+ def report_classification
248
+ content.report_as_spam if content.just_changed_published_status?
249
+ true
250
+ end
251
+
252
+ def to_s
253
+ 'spam'
254
+ end
255
+ end
256
+ end