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,225 @@
1
+ require_dependency 'spam_protection'
2
+ require 'akismet'
3
+
4
+ class Feedback < ActiveRecord::Base
5
+ self.table_name = 'feedback'
6
+
7
+ belongs_to :text_filter
8
+ belongs_to :article
9
+
10
+ include PublifyGuid
11
+ include Stateful
12
+ include ContentBase
13
+ include States
14
+
15
+ class ContentTextHelpers
16
+ include ActionView::Helpers::UrlHelper
17
+ include ActionView::Helpers::TextHelper
18
+ include ActionView::Helpers::SanitizeHelper
19
+ end
20
+
21
+ validate :feedback_not_closed, on: :create
22
+ validates :article, presence: true
23
+
24
+ before_create :create_guid, :article_allows_this_feedback
25
+ before_save :correct_url, :before_save_handler
26
+ after_save :post_trigger, :report_classification, :invalidates_cache?
27
+ after_initialize :after_initialize_handler
28
+ after_destroy ->(c) { c.invalidates_cache?(true) }
29
+
30
+ scope :ham, -> { where(state: %w(presumed_ham ham)) }
31
+ scope :spam, -> { where(state: 'spam') }
32
+ scope :published_since, ->(time) { ham.where('published_at > ?', time) }
33
+ scope :presumed_ham, -> { where(state: 'presumed_ham') }
34
+ scope :presumed_spam, -> { where(state: 'presumed_spam') }
35
+ scope :unapproved, -> { where(status_confirmed: false) }
36
+
37
+ scope :published, -> { where(published: true) }
38
+ scope :oldest_first, -> { order(:created_at) }
39
+
40
+ has_state(:state,
41
+ valid_states: [:unclassified, :presumed_spam, :just_marked_as_spam, :spam, :just_presumed_ham, :presumed_ham, :just_marked_as_ham, :ham],
42
+ handles: [:published?, :status_confirmed?, :just_published?,
43
+ :mark_as_ham, :mark_as_spam, :confirm_classification,
44
+ :withdraw,
45
+ :before_save_handler, :after_initialize_handler,
46
+ :send_notifications, :post_trigger, :report_classification])
47
+
48
+ def self.paginated(page, per_page)
49
+ page(page).per(per_page)
50
+ end
51
+
52
+ def self.comments
53
+ Comment.where(published: true).order('created_at DESC')
54
+ end
55
+
56
+ def self.trackbacks
57
+ Trackback.where(published: true).order('created_at DESC')
58
+ end
59
+
60
+ def self.from(type, article_id = nil)
61
+ if article_id.present?
62
+ Article.find(article_id).send("published_#{type}")
63
+ else
64
+ send(type)
65
+ end
66
+ end
67
+
68
+ def parent
69
+ article
70
+ end
71
+
72
+ def permalink_url(_anchor = :ignored, only_path = false)
73
+ article.permalink_url("#{self.class.to_s.downcase}-#{id}", only_path)
74
+ end
75
+
76
+ def html_postprocess(_field, html)
77
+ helper = ContentTextHelpers.new
78
+ helper.sanitize(helper.auto_link(html))
79
+ end
80
+
81
+ def correct_url
82
+ return if url.blank?
83
+ self.url = 'http://' + url.to_s unless url =~ %r{^https?://}
84
+ end
85
+
86
+ def article_allows_this_feedback
87
+ article && blog_allows_feedback? && article_allows_feedback?
88
+ end
89
+
90
+ def blog_allows_feedback?
91
+ true
92
+ end
93
+
94
+ def akismet_options
95
+ { type: self.class.to_s.downcase,
96
+ author: originator,
97
+ author_email: email,
98
+ author_url: url,
99
+ text: body }
100
+ end
101
+
102
+ def spam_fields
103
+ [:title, :body, :ip, :url]
104
+ end
105
+
106
+ def classify
107
+ return :ham if user_id
108
+ return :spam if blog.default_moderate_comments
109
+ return :ham unless blog.sp_global
110
+
111
+ # Yeah, three state logic is evil...
112
+ case sp_is_spam? || akismet_is_spam?
113
+ when nil then :spam
114
+ when true then :spam
115
+ when false then :ham
116
+ end
117
+ end
118
+
119
+ def sp_is_spam?(_options = {})
120
+ sp = SpamProtection.new(blog)
121
+ Timeout.timeout(30) do
122
+ spam_fields.any? do |field|
123
+ sp.is_spam?(send(field))
124
+ end
125
+ end
126
+ rescue Timeout::Error
127
+ nil
128
+ end
129
+
130
+ def akismet_is_spam?(_options = {})
131
+ return false if akismet.nil?
132
+
133
+ begin
134
+ Timeout.timeout(60) do
135
+ akismet.comment_check(ip, user_agent, akismet_options)
136
+ end
137
+ rescue Timeout::Error
138
+ nil
139
+ end
140
+ end
141
+
142
+ def change_state!
143
+ result = ''
144
+ if state.spam? || state.presumed_spam?
145
+ mark_as_ham
146
+ result = 'ham'
147
+ else
148
+ mark_as_spam
149
+ result = 'spam'
150
+ end
151
+ save!
152
+ result
153
+ end
154
+
155
+ def mark_as_ham!
156
+ mark_as_ham
157
+ save!
158
+ end
159
+
160
+ def mark_as_spam!
161
+ mark_as_spam
162
+ save!
163
+ end
164
+
165
+ def report_as_spam
166
+ return if akismet.nil?
167
+ begin
168
+ Timeout.timeout(5) do
169
+ akismet.submit_spam(ip, user_agent, akismet_options)
170
+ end
171
+ rescue Timeout::Error
172
+ nil
173
+ end
174
+ end
175
+
176
+ def report_as_ham
177
+ return if akismet.nil?
178
+ begin
179
+ Timeout.timeout(5) do
180
+ akismet.ham(ip, user_agent, akismet_options)
181
+ end
182
+ rescue Timeout::Error
183
+ nil
184
+ end
185
+ end
186
+
187
+ def withdraw!
188
+ withdraw
189
+ save!
190
+ end
191
+
192
+ def confirm_classification!
193
+ confirm_classification
194
+ save
195
+ end
196
+
197
+ def feedback_not_closed
198
+ errors.add(:article_id, 'Comment are closed') if article.comments_closed?
199
+ end
200
+
201
+ delegate :blog, to: :article
202
+
203
+ private
204
+
205
+ @@akismet = nil
206
+
207
+ def akismet
208
+ @@akismet = akismet_client if @@akismet.nil?
209
+ @@akismet == false ? nil : @@akismet
210
+ end
211
+
212
+ def akismet_client
213
+ return false if blog.sp_akismet_key.blank?
214
+ client = Akismet::Client.new(blog.sp_akismet_key, blog.base_url)
215
+ begin
216
+ return client.verify_key ? client : false
217
+ rescue SocketError
218
+ nil
219
+ end
220
+ end
221
+
222
+ def blog_id
223
+ article.blog_id if article.present?
224
+ end
225
+ end
@@ -0,0 +1,8 @@
1
+ # coding: utf-8
2
+ class MetaSidebar < Sidebar
3
+ description "This widget just displays links to Publify main site, this blog's admin and RSS."
4
+
5
+ setting :title, 'Meta'
6
+ end
7
+
8
+ SidebarRegistry.register_sidebar MetaSidebar
@@ -0,0 +1,144 @@
1
+ class Note < Content
2
+ require 'twitter'
3
+ require 'json'
4
+ require 'uri'
5
+ include PublifyGuid
6
+ include ConfigManager
7
+
8
+ serialize :settings, Hash
9
+
10
+ setting :twitter_id, :string, ''
11
+ setting :in_reply_to_status_id, :string, ''
12
+ setting :in_reply_to_protected, :boolean, false
13
+ setting :in_reply_to_message, :string, ''
14
+
15
+ validates :body, presence: true
16
+ validates :permalink, :guid, uniqueness: true
17
+
18
+ after_create :set_permalink, :shorten_url
19
+ before_create :create_guid
20
+
21
+ default_scope { order('published_at DESC') }
22
+
23
+ TWITTER_FTP_URL_LENGTH = 19
24
+ TWITTER_HTTP_URL_LENGTH = 20
25
+ TWITTER_HTTPS_URL_LENGTH = 21
26
+ TWITTER_LINK_LENGTH = 22
27
+
28
+ def set_permalink
29
+ self.permalink = "#{id}-#{body.to_permalink[0..79]}" if permalink.blank?
30
+ save
31
+ end
32
+
33
+ def categories
34
+ []
35
+ end
36
+
37
+ def tags
38
+ []
39
+ end
40
+
41
+ def html_preprocess(_field, html)
42
+ PublifyApp::Textfilter::Twitterfilter.filtertext(html)
43
+ end
44
+
45
+ def truncate(message, length)
46
+ if message[length + 1] == ' '
47
+ message[0..length]
48
+ else
49
+ message[0..(message[0..length].rindex(' ') - 1)]
50
+ end
51
+ end
52
+
53
+ def twitter_message
54
+ base_message = body.strip_html
55
+ if too_long?("#{base_message} (#{short_link})")
56
+ max_length = 140 - "... (#{redirect.from_url})".length - 1
57
+ "#{truncate(base_message, max_length)}... (#{redirect.from_url})"
58
+ else
59
+ "#{base_message} (#{short_link})"
60
+ end
61
+ end
62
+
63
+ # FIXME: This breaks if the user changes or deletes their handle.
64
+ def twitter_url
65
+ File.join('https://twitter.com', user.twitter, 'status', twitter_id)
66
+ end
67
+
68
+ def send_to_twitter
69
+ return false unless blog.has_twitter_configured?
70
+ return false unless user.has_twitter_configured?
71
+
72
+ twitter = Twitter::REST::Client.new do |config|
73
+ config.consumer_key = blog.twitter_consumer_key
74
+ config.consumer_secret = blog.twitter_consumer_secret
75
+ config.access_token = user.twitter_oauth_token
76
+ config.access_token_secret = user.twitter_oauth_token_secret
77
+ end
78
+
79
+ begin
80
+ options = {}
81
+ if in_reply_to_status_id && in_reply_to_status_id != ''
82
+ options = { in_reply_to_status_id: in_reply_to_status_id }
83
+ self.in_reply_to_message = twitter.status(in_reply_to_status_id).to_json
84
+ end
85
+ tweet = twitter.update(twitter_message, options)
86
+ self.twitter_id = tweet.attrs[:id_str]
87
+ save
88
+ user.update_twitter_profile_image(tweet.attrs[:user][:profile_image_url])
89
+ true
90
+ rescue StandardError => e
91
+ Rails.logger.error("Error while sending to twitter: #{e}")
92
+ errors.add(:message, e)
93
+ false
94
+ end
95
+ end
96
+
97
+ content_fields :body
98
+
99
+ def password_protected?
100
+ false
101
+ end
102
+
103
+ def access_by?(user)
104
+ user.admin? || user_id == user.id
105
+ end
106
+
107
+ def permalink_url(anchor = nil, only_path = false)
108
+ blog.url_for(
109
+ controller: '/notes',
110
+ action: 'show',
111
+ permalink: permalink,
112
+ anchor: anchor,
113
+ only_path: only_path
114
+ )
115
+ end
116
+
117
+ def short_link
118
+ path = redirect.from_path
119
+ "#{prefix} #{path}"
120
+ end
121
+
122
+ def prefix
123
+ blog.shortener_url.sub(/^https?\:\/\//, '')
124
+ end
125
+
126
+ private
127
+
128
+ def too_long?(message)
129
+ uris = URI.extract(message, %w(http https ftp))
130
+ uris << prefix
131
+ uris.each do |uri|
132
+ payload = case uri.split(':')[0]
133
+ when 'https'
134
+ '-' * TWITTER_HTTPS_URL_LENGTH
135
+ when 'ftp'
136
+ '-' * TWITTER_FTP_URL_LENGTH
137
+ else
138
+ '-' * TWITTER_HTTP_URL_LENGTH
139
+ end
140
+ message = message.gsub(uri, payload)
141
+ end
142
+ message.length > 140
143
+ end
144
+ end
@@ -0,0 +1,36 @@
1
+ class Page < Content
2
+ validates :title, :body, presence: true
3
+ validates :name, uniqueness: true
4
+
5
+ include ConfigManager
6
+
7
+ serialize :settings, Hash
8
+ setting :password, :string, ''
9
+
10
+ before_save :set_permalink
11
+ after_save :shorten_url
12
+
13
+ def set_permalink
14
+ self.name = title.to_permalink if name.blank?
15
+ end
16
+
17
+ content_fields :body
18
+
19
+ def self.default_order
20
+ 'name ASC'
21
+ end
22
+
23
+ def self.search_with(search_hash)
24
+ super(search_hash).order('title ASC')
25
+ end
26
+
27
+ def permalink_url(anchor = nil, only_path = false)
28
+ blog.url_for(
29
+ controller: '/articles',
30
+ action: 'view_page',
31
+ name: name,
32
+ anchor: anchor,
33
+ only_path: only_path
34
+ )
35
+ end
36
+ end
@@ -0,0 +1,33 @@
1
+ # FIXME: This class is not a model anymore. Move elsewhere?
2
+ class PageCache
3
+ def self.logger
4
+ ::Rails.logger
5
+ end
6
+
7
+ def logger
8
+ ::Rails.logger
9
+ end
10
+
11
+ def self.public_path
12
+ ActionController::Base.page_cache_directory
13
+ end
14
+
15
+ # Delete all file save in path_cache by page_cache system
16
+ def self.sweep_all
17
+ zap_pages(%w(*))
18
+ end
19
+
20
+ def self.sweep_theme_cache
21
+ zap_pages(%w(images/theme/* stylesheets/theme/* javascripts/theme/*))
22
+ end
23
+
24
+ def self.zap_pages(paths)
25
+ # Ensure no one is going to wipe his own blog public directory
26
+ # It happened once on a release and was no fun at all
27
+ return if public_path == "#{::Rails.root}/public"
28
+ paths.each do |v|
29
+ FileUtils.rm_rf(Dir.glob(public_path + "/#{v}"))
30
+ end
31
+ true
32
+ end
33
+ end
@@ -0,0 +1,12 @@
1
+ class PageSidebar < Sidebar
2
+ display_name 'Page'
3
+ description 'Show pages for this blog'
4
+
5
+ setting :maximum_pages, 10
6
+
7
+ def pages
8
+ @pages ||= Page.published.order(:title)
9
+ end
10
+ end
11
+
12
+ SidebarRegistry.register_sidebar PageSidebar
@@ -0,0 +1,116 @@
1
+ require 'rexml/document'
2
+ require 'xmlrpc/client'
3
+
4
+ class Ping < ActiveRecord::Base
5
+ belongs_to :article
6
+
7
+ class Pinger
8
+ attr_accessor :article
9
+ attr_accessor :blog
10
+
11
+ def send_pingback_or_trackback
12
+ @response = Net::HTTP.get_response(URI.parse(ping.url))
13
+ send_pingback or send_trackback
14
+ rescue Timeout::Error
15
+ Rails.logger.info 'Sending pingback or trackback timed out'
16
+ return
17
+ rescue => err
18
+ Rails.logger.info "Sending pingback or trackback failed with error: #{err}"
19
+ end
20
+
21
+ def pingback_url
22
+ if response['X-Pingback']
23
+ response['X-Pingback']
24
+ elsif response.body =~ /<link rel="pingback" href="([^"]+)" ?\/?>/
25
+ Regexp.last_match[1]
26
+ end
27
+ end
28
+
29
+ attr_reader :origin_url
30
+
31
+ attr_reader :response
32
+
33
+ attr_reader :ping
34
+
35
+ def send_xml_rpc(*args)
36
+ ping.send(:send_xml_rpc, *args)
37
+ end
38
+
39
+ def trackback_url
40
+ rdfs = response.body.scan(/<rdf:RDF.*?<\/rdf:RDF>/m)
41
+ rdfs.each do |rdf|
42
+ xml = REXML::Document.new(rdf)
43
+ xml.elements.each('//rdf:Description') do |desc|
44
+ if rdfs.size == 1 || desc.attributes['dc:identifier'] == ping.url
45
+ return desc.attributes['trackback:ping']
46
+ end
47
+ end
48
+ end
49
+ # Didn't find a trackback url, so fall back to the url itself.
50
+ @ping.url
51
+ end
52
+
53
+ def send_pingback
54
+ if pingback_url
55
+ send_xml_rpc(pingback_url, 'pingback.ping', origin_url, ping.url)
56
+ true
57
+ else
58
+ false
59
+ end
60
+ end
61
+
62
+ def send_trackback
63
+ do_send_trackback(trackback_url, origin_url)
64
+ end
65
+
66
+ def do_send_trackback(trackback_url, origin_url)
67
+ trackback_uri = URI.parse(trackback_url)
68
+
69
+ post = "title=#{CGI.escape(article.title)}"
70
+ post << "&excerpt=#{CGI.escape(article.html(:body).strip_html[0..254])}"
71
+ post << "&url=#{origin_url}"
72
+ post << "&blog_name=#{CGI.escape(blog.blog_name)}"
73
+
74
+ path = trackback_uri.path
75
+ path += "?#{trackback_uri.query}" if trackback_uri.query
76
+
77
+ Net::HTTP.start(trackback_uri.host, trackback_uri.port) do |http|
78
+ http.post(path, post, 'Content-type' => 'application/x-www-form-urlencoded; charset=utf-8')
79
+ end
80
+ end
81
+
82
+ private
83
+
84
+ def initialize(origin_url, ping)
85
+ @origin_url = origin_url
86
+ @ping = ping
87
+ # Add this call to text filter cause of a strange thing around text_filter. Need to clean text_filter usage !
88
+ ping.article.default_text_filter
89
+ ping.article.text_filter
90
+ # Make sure these are fetched now for thread safety purposes.
91
+ self.article = ping.article
92
+ self.blog = article.blog
93
+ end
94
+ end
95
+
96
+ def send_pingback_or_trackback(origin_url)
97
+ t = Thread.start(Pinger.new(origin_url, self), &:send_pingback_or_trackback)
98
+ t
99
+ end
100
+
101
+ def send_weblogupdatesping(server_url, origin_url)
102
+ t = Thread.start(article.blog.blog_name) do |blog_name|
103
+ send_xml_rpc(url, 'weblogUpdates.ping', blog_name, server_url, origin_url)
104
+ end
105
+ t
106
+ end
107
+
108
+ protected
109
+
110
+ def send_xml_rpc(xml_rpc_url, name, *args)
111
+ server = XMLRPC::Client.new2(URI.parse(xml_rpc_url).to_s)
112
+ server.call(name, *args)
113
+ rescue => e
114
+ logger.error(e)
115
+ end
116
+ end
@@ -0,0 +1,15 @@
1
+ # coding: utf-8
2
+ class PostType < ActiveRecord::Base
3
+ validates :name, uniqueness: true
4
+ validates :name, presence: true
5
+ validate :name_is_not_read
6
+ before_save :sanitize_title
7
+
8
+ def name_is_not_read
9
+ errors.add(:name, I18n.t('errors.article_type_already_exist')) if name == 'read'
10
+ end
11
+
12
+ def sanitize_title
13
+ self.permalink = name.to_permalink
14
+ end
15
+ end
@@ -0,0 +1,45 @@
1
+ class Redirect < ActiveRecord::Base
2
+ belongs_to :contents
3
+ belongs_to :blog
4
+
5
+ validates :from_path, uniqueness: true
6
+ validates :to_path, presence: true
7
+ validates :blog, presence: true
8
+
9
+ def full_to_path
10
+ path = to_path
11
+ return path if path =~ /^(https?):\/\/([^\/]*)(.*)/
12
+ url_root = blog.root_path
13
+ path = File.join(url_root, path) unless url_root.nil? || path[0, url_root.length] == url_root
14
+ path
15
+ end
16
+
17
+ def shorten
18
+ if (temp_token = random_token) && self.class.find_by(from_path: temp_token).nil?
19
+ temp_token
20
+ else
21
+ shorten
22
+ end
23
+ end
24
+
25
+ def to_url
26
+ raise 'Use #from_url'
27
+ end
28
+
29
+ def from_url
30
+ File.join(blog.shortener_url, from_path)
31
+ end
32
+
33
+ private
34
+
35
+ def random_token
36
+ characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890'
37
+ temp_token = ''
38
+ srand
39
+ 6.times do
40
+ pos = rand(characters.length)
41
+ temp_token += characters[pos..pos]
42
+ end
43
+ temp_token
44
+ end
45
+ end
@@ -0,0 +1,4 @@
1
+ class Redirection < ActiveRecord::Base
2
+ belongs_to :content
3
+ belongs_to :redirect
4
+ end
@@ -0,0 +1,28 @@
1
+ require 'carrierwave'
2
+ require 'carrierwave/orm/activerecord'
3
+
4
+ class Resource < ActiveRecord::Base
5
+ belongs_to :blog
6
+ belongs_to :article
7
+
8
+ mount_uploader :upload, ResourceUploader
9
+ validate :image_mime_type_consistent
10
+ validates :upload, presence: true
11
+
12
+ scope :without_images, -> { where("mime NOT LIKE '%image%'") }
13
+ scope :images, -> { where("mime LIKE '%image%'") }
14
+ scope :by_filename, -> { order('upload') }
15
+ scope :by_created_at, -> { order('created_at DESC') }
16
+
17
+ scope :without_images_by_filename, -> { without_images.by_filename }
18
+ scope :images_by_created_at, -> { images.by_created_at }
19
+
20
+ private
21
+
22
+ def image_mime_type_consistent
23
+ if upload.content_type =~ %r{^image/}
24
+ expected_type = upload.file.send :mime_magic_content_type
25
+ errors.add(:upload, 'Has MIME type mismatch') unless upload.content_type == expected_type
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,7 @@
1
+ class SearchSidebar < Sidebar
2
+ description 'Adds basic search sidebar in your Publify blog'
3
+
4
+ setting :title, 'Search'
5
+ end
6
+
7
+ SidebarRegistry.register_sidebar SearchSidebar