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,138 @@
1
+ require 'sidebar_field'
2
+
3
+ # This class cannot be autoloaded since other sidebar classes depend on it.
4
+ class Sidebar < ActiveRecord::Base
5
+ serialize :config, Hash
6
+
7
+ belongs_to :blog
8
+
9
+ scope :valid, ->() { where(type: SidebarRegistry.available_sidebar_types) }
10
+
11
+ def self.ordered_sidebars
12
+ os = []
13
+ Sidebar.valid.each do |s|
14
+ if s.staged_position
15
+ os[s.staged_position] = ((os[s.staged_position] || []) << s).uniq
16
+ elsif s.active_position
17
+ os[s.active_position] = ((os[s.active_position] || []) << s).uniq
18
+ end
19
+ if s.active_position.nil? && s.staged_position.nil?
20
+ s.destroy # neither staged nor active: destroy. Full stop.
21
+ end
22
+ end
23
+ os.flatten.compact
24
+ end
25
+
26
+ def self.purge
27
+ delete_all('active_position is null and staged_position is null')
28
+ end
29
+
30
+ def self.setting(key, default = nil, options = {})
31
+ key = key.to_s
32
+
33
+ return if instance_methods.include?(key)
34
+
35
+ fields << SidebarField.build(key, default, options)
36
+
37
+ send(:define_method, key) do
38
+ if config.key? key
39
+ config[key]
40
+ else
41
+ default
42
+ end
43
+ end
44
+
45
+ send(:define_method, "#{key}=") do |newval|
46
+ config[key] = newval
47
+ end
48
+ end
49
+
50
+ def self.fields
51
+ @fields ||= []
52
+ end
53
+
54
+ def self.description(desc = nil)
55
+ if desc
56
+ @description = desc
57
+ else
58
+ @description || ''
59
+ end
60
+ end
61
+
62
+ def self.short_name
63
+ to_s.underscore.split(/_/).first
64
+ end
65
+
66
+ def self.path_name
67
+ to_s.underscore
68
+ end
69
+
70
+ def self.display_name(new_dn = nil)
71
+ @display_name = new_dn if new_dn
72
+ @display_name || short_name.humanize
73
+ end
74
+
75
+ class << self
76
+ attr_writer :fields
77
+ end
78
+
79
+ def self.apply_staging_on_active!
80
+ Sidebar.transaction do
81
+ Sidebar.find_each do |s|
82
+ s.active_position = s.staged_position
83
+ s.save!
84
+ end
85
+ end
86
+ end
87
+
88
+ def publish
89
+ self.active_position = staged_position
90
+ end
91
+
92
+ def html_id
93
+ short_name + '-' + id.to_s
94
+ end
95
+
96
+ def parse_request(_contents, _params)
97
+ end
98
+
99
+ def fields
100
+ self.class.fields
101
+ end
102
+
103
+ def fieldmap(field = nil)
104
+ if field
105
+ self.class.fieldmap[field.to_s]
106
+ else
107
+ self.class.fieldmap
108
+ end
109
+ end
110
+
111
+ def description
112
+ self.class.description
113
+ end
114
+
115
+ def short_name
116
+ self.class.short_name
117
+ end
118
+
119
+ def display_name
120
+ self.class.display_name
121
+ end
122
+
123
+ def content_partial
124
+ "/#{self.class.path_name}/content"
125
+ end
126
+
127
+ def to_locals_hash
128
+ fields.reduce(sidebar: self) do |hash, field|
129
+ hash.merge(field.key => config[field.key])
130
+ end
131
+ end
132
+
133
+ def admin_state
134
+ return :active if active_position && (staged_position == active_position || staged_position.nil?)
135
+ return :will_change_position if active_position != staged_position
136
+ raise "Unknown admin_state: active: #{active_position}, staged: #{staged_position}"
137
+ end
138
+ end
@@ -0,0 +1,20 @@
1
+ # coding: utf-8
2
+ class StaticSidebar < Sidebar
3
+ DEFAULT_TEXT = '
4
+ <ul>
5
+ <li><a href="http://www.publify.co/" title="Publify">Publify</a></li>
6
+ <li><a href="http://t37.net/" title="Le Rayon UX">Frédéric</a></li>
7
+ <li><a href="http://www.matijs.net/" title="Matijs">Matijs</a></li>
8
+ <li><a href="http://elsif.fr/" title="Yannick">Yannick</a></li>
9
+ <li><a href="http://blog.ookook.fr/" title="Thomas">Thomas</a></li>
10
+ <li><a href="/admin">Admin</a></li>
11
+ </ul>
12
+
13
+ '.freeze
14
+ description 'Static content, like links to other sites, advertisements, or blog meta-information'
15
+
16
+ setting :title, 'Links'
17
+ setting :body, DEFAULT_TEXT, input_type: :text_area
18
+ end
19
+
20
+ SidebarRegistry.register_sidebar StaticSidebar
data/app/models/tag.rb ADDED
@@ -0,0 +1,63 @@
1
+ class Tag < ActiveRecord::Base
2
+ belongs_to :blog
3
+ has_and_belongs_to_many :articles, order: 'created_at DESC', join_table: 'articles_tags'
4
+
5
+ validates :name, uniqueness: { scope: :blog_id }
6
+ validates :blog, presence: true
7
+ validates :name, presence: true
8
+
9
+ before_validation :ensure_naming_conventions
10
+
11
+ attr_accessor :description, :keywords
12
+
13
+ def self.create_from_article!(article)
14
+ return if article.keywords.nil?
15
+ tags = []
16
+ Tag.transaction do
17
+ tagwords = article.keywords.to_s.scan(/((['"]).*?\2|[\.:[[:alnum:]]]+)/).map do |x|
18
+ x.first.tr("\"'", '')
19
+ end
20
+ tagwords.uniq.each do |tagword|
21
+ tagname = tagword.to_url
22
+ tags << article.blog.tags.find_or_create_by(name: tagname) do |tag|
23
+ tag.display_name = tagword
24
+ end
25
+ end
26
+ end
27
+ article.tags = tags
28
+ tags
29
+ end
30
+
31
+ def ensure_naming_conventions
32
+ self.display_name = name if display_name.blank?
33
+ self.name = display_name.to_url unless display_name.blank?
34
+ end
35
+
36
+ def self.find_all_with_article_counters
37
+ Tag.joins(:articles).
38
+ where(contents: { published: true }).
39
+ select(*Tag.column_names, 'COUNT(articles_tags.article_id) as article_counter').
40
+ group(*Tag.column_names).
41
+ order('article_counter DESC').limit(1000)
42
+ end
43
+
44
+ def self.find_with_char(char)
45
+ where('name LIKE ? ', "%#{char}%").order('name ASC')
46
+ end
47
+
48
+ def self.collection_to_string(tags)
49
+ tags.map(&:display_name).sort.map { |name| name =~ / / ? "\"#{name}\"" : name }.join ', '
50
+ end
51
+
52
+ def published_articles
53
+ articles.already_published
54
+ end
55
+
56
+ def permalink
57
+ name
58
+ end
59
+
60
+ def permalink_url(_anchor = nil, only_path = false)
61
+ blog.url_for(controller: 'tags', action: 'show', id: permalink, only_path: only_path)
62
+ end
63
+ end
@@ -0,0 +1,27 @@
1
+ class TagSidebar < Sidebar
2
+ display_name 'Tags'
3
+ description 'Show most popular tags for this blog'
4
+
5
+ setting :maximum_tags, 20
6
+
7
+ def tags
8
+ @tags ||= Tag.find_all_with_article_counters.
9
+ take(maximum_tags.to_i).sort_by(&:name)
10
+ end
11
+
12
+ def sizes
13
+ return @sizes if @sizes
14
+ total = @tags.reduce(0) { |sum, tag| sum + tag.article_counter }
15
+ average = total.to_f / @tags.size.to_f
16
+ @sizes = @tags.reduce({}) do |h, tag|
17
+ size = tag.article_counter.to_f / average
18
+ h.merge tag => [[2.0 / 3.0, size].max, 2].min * 100
19
+ end
20
+ end
21
+
22
+ def font_multiplier
23
+ 80
24
+ end
25
+ end
26
+
27
+ SidebarRegistry.register_sidebar TagSidebar
@@ -0,0 +1,61 @@
1
+ require 'net/http'
2
+
3
+ class TextFilter < ActiveRecord::Base
4
+ serialize :filters, Array
5
+ serialize :params, Hash
6
+
7
+ def sanitize(*args, &blk)
8
+ self.class.sanitize(*args, &blk)
9
+ end
10
+
11
+ def self.find_or_default(name)
12
+ find_by(name: name) || find_by(name: 'none')
13
+ end
14
+
15
+ def self.filter_text(text, filters)
16
+ map = TextFilterPlugin.filter_map
17
+
18
+ filters.each do |filter|
19
+ next if filter.nil?
20
+ filter_class = map[filter.to_s]
21
+ next unless filter_class
22
+ text = filter_class.filtertext(text)
23
+ end
24
+
25
+ text
26
+ end
27
+
28
+ def filter_text(text)
29
+ self.class.filter_text(text, [:macropre, markup, :macropost, filters].flatten)
30
+ end
31
+
32
+ def help
33
+ filter_map = TextFilterPlugin.filter_map
34
+ filter_types = TextFilterPlugin.available_filter_types
35
+
36
+ help = []
37
+ help.push(filter_map[markup])
38
+ filter_types['macropre'].sort_by(&:short_name).each { |f| help.push f }
39
+ filter_types['macropost'].sort_by(&:short_name).each { |f| help.push f }
40
+ filters.each { |f| help.push(filter_map[f.to_s]) }
41
+
42
+ help_text = help.map do |f|
43
+ f.help_text.blank? ? '' : "<h3>#{f.display_name}</h3>\n#{BlueCloth.new(f.help_text).to_html}\n"
44
+ end
45
+
46
+ help_text.join("\n")
47
+ end
48
+
49
+ def commenthelp
50
+ filter_map = TextFilterPlugin.filter_map
51
+
52
+ help = [filter_map[markup]]
53
+ filters.each { |f| help.push(filter_map[f.to_s]) }
54
+
55
+ help_text = help.map do |f|
56
+ f.help_text.blank? ? '' : "#{BlueCloth.new(f.help_text).to_html}\n"
57
+ end.join("\n")
58
+
59
+ help_text
60
+ end
61
+ end
@@ -0,0 +1,58 @@
1
+ require_dependency 'spam_protection'
2
+
3
+ class Trackback < Feedback
4
+ content_fields :excerpt
5
+ validates :title, :excerpt, :url, presence: true
6
+
7
+ # attr_accessible :url, :blog_name, :title, :excerpt, :ip, :published, :article_id
8
+
9
+ def initialize(*args, &block)
10
+ super(*args, &block)
11
+ self.title ||= url
12
+ self.blog_name ||= ''
13
+ end
14
+
15
+ before_create :process_trackback
16
+
17
+ def process_trackback
18
+ if excerpt.length >= 251
19
+ # this limits excerpt to 250 chars, including the trailing "..."
20
+ self.excerpt = excerpt[0..246] << '...'
21
+ end
22
+ end
23
+
24
+ def article_allows_feedback?
25
+ return true if article.allow_pings?
26
+ errors.add(:article, 'Article is not pingable')
27
+ false
28
+ end
29
+
30
+ def blog_allows_feedback?
31
+ return true unless blog.global_pings_disable
32
+ errors.add(:article, 'Pings are disabled')
33
+ false
34
+ end
35
+
36
+ def originator
37
+ blog_name
38
+ end
39
+
40
+ def body
41
+ excerpt
42
+ end
43
+
44
+ def body=(newval)
45
+ self.excerpt = newval
46
+ end
47
+
48
+ def rss_author(_xml)
49
+ end
50
+
51
+ def rss_title(xml)
52
+ xml.title feed_title
53
+ end
54
+
55
+ def feed_title
56
+ "Trackback from #{blog_name}: #{title} on #{article.title}"
57
+ end
58
+ end
@@ -0,0 +1,45 @@
1
+ class Trigger < ActiveRecord::Base
2
+ belongs_to :pending_item, polymorphic: true
3
+
4
+ class << self
5
+ def post_action(due_at, item, method = 'came_due')
6
+ create!(due_at: due_at, pending_item: item,
7
+ trigger_method: method)
8
+ fire
9
+ end
10
+
11
+ def fire
12
+ destroy_all ['due_at <= ?', Time.now]
13
+ true
14
+ rescue
15
+ migrator = Migrator.new
16
+
17
+ unless migrator.pending_migrations.empty?
18
+ starting_version = migrator.current_schema_version
19
+ migrator.migrate
20
+
21
+ if starting_version == 0
22
+ load "#{Rails.root}/Rakefile"
23
+ Rake::Task['db:seed'].invoke
24
+ User.reset_column_information
25
+ Article.reset_column_information
26
+ Page.reset_column_information
27
+ end
28
+ end
29
+ end
30
+
31
+ def remove(pending_item, conditions = {})
32
+ return if pending_item.new_record?
33
+ conditions = conditions.merge(pending_item_id: pending_item.id,
34
+ pending_item_type: pending_item.class.to_s)
35
+ where(conditions).delete_all
36
+ end
37
+ end
38
+
39
+ before_destroy :trigger_pending_item
40
+
41
+ def trigger_pending_item
42
+ pending_item.send(trigger_method) if pending_item
43
+ true
44
+ end
45
+ end
@@ -0,0 +1,148 @@
1
+ require 'digest/sha1'
2
+
3
+ # Publify user.
4
+ # TODO: Should belong to a blog
5
+ class User < ActiveRecord::Base
6
+ ADMIN = 'admin'.freeze
7
+ PUBLISHER = 'publisher'.freeze
8
+ CONTRIBUTOR = 'contributor'.freeze
9
+
10
+ # Include default devise modules. Others available are:
11
+ # :confirmable, :lockable, :timeoutable and :omniauthable
12
+ devise :database_authenticatable, :registerable,
13
+ :recoverable, :rememberable, :trackable, :validatable
14
+ include ConfigManager
15
+
16
+ belongs_to :text_filter
17
+ belongs_to :resource
18
+
19
+ delegate :name, to: :text_filter, prefix: true
20
+
21
+ has_many :notifications, foreign_key: 'notify_user_id'
22
+ has_many :notify_contents, -> { uniq }, through: :notifications,
23
+ source: 'notify_content'
24
+
25
+ has_many :articles
26
+
27
+ serialize :settings, Hash
28
+
29
+ STATUS = %w(active inactive).freeze
30
+
31
+ attr_accessor :filename
32
+
33
+ # Settings
34
+ setting :notify_watch_my_articles, :boolean, true
35
+ setting :firstname, :string, ''
36
+ setting :lastname, :string, ''
37
+ setting :nickname, :string, ''
38
+ setting :description, :string, ''
39
+ setting :url, :string, ''
40
+ setting :msn, :string, ''
41
+ setting :aim, :string, ''
42
+ setting :yahoo, :string, ''
43
+ setting :twitter, :string, ''
44
+ setting :jabber, :string, ''
45
+ setting :admin_theme, :string, 'blue'
46
+ setting :twitter_account, :string, ''
47
+ setting :twitter_oauth_token, :string, ''
48
+ setting :twitter_oauth_token_secret, :string, ''
49
+ setting :twitter_profile_image, :string, ''
50
+
51
+ # echo "publify" | sha1sum -
52
+ class_attribute :salt
53
+
54
+ def self.salt
55
+ '20ac4d290c2293702c64b3b287ae5ea79b26a5c1'
56
+ end
57
+
58
+ def first_and_last_name
59
+ return '' unless firstname.present? && lastname.present?
60
+ "#{firstname} #{lastname}"
61
+ end
62
+
63
+ def display_names
64
+ [:login, :nickname, :firstname, :lastname, :first_and_last_name].map { |f| send(f) }.delete_if(&:empty?)
65
+ end
66
+
67
+ # Authenticate users with old password hashes
68
+ alias devise_valid_password? valid_password?
69
+
70
+ def valid_password?(password)
71
+ devise_valid_password?(password)
72
+ rescue BCrypt::Errors::InvalidHash
73
+ digest = Digest::SHA1.hexdigest("#{self.class.salt}--#{password}--")
74
+ if digest == encrypted_password
75
+ # Update old SHA1 password with new Devise ByCrypt password
76
+ self.encrypted_password = password_digest(password)
77
+ save
78
+ return true
79
+ else
80
+ # If not BCrypt password and not old SHA1 password deny access
81
+ return false
82
+ end
83
+ end
84
+
85
+ def active_for_authentication?
86
+ super && state == 'active'
87
+ end
88
+
89
+ def default_text_filter
90
+ text_filter
91
+ end
92
+
93
+ def self.to_prefix
94
+ 'author'
95
+ end
96
+
97
+ def article_counter
98
+ articles.size
99
+ end
100
+
101
+ def display_name
102
+ if !nickname.blank?
103
+ nickname
104
+ elsif !name.blank?
105
+ name
106
+ else
107
+ login
108
+ end
109
+ end
110
+
111
+ def permalink
112
+ login
113
+ end
114
+
115
+ def admin?
116
+ profile == User::ADMIN
117
+ end
118
+
119
+ def update_twitter_profile_image(img)
120
+ return if twitter_profile_image == img
121
+ self.twitter_profile_image = img
122
+ save
123
+ end
124
+
125
+ def generate_password!
126
+ chars = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a
127
+ newpass = ''
128
+ 8.times { newpass << chars[rand(chars.size - 1)] }
129
+ self.password = newpass
130
+ end
131
+
132
+ def has_twitter_configured?
133
+ twitter_oauth_token.present? && twitter_oauth_token_secret.present?
134
+ end
135
+
136
+ protected
137
+
138
+ before_validation :set_default_profile
139
+
140
+ def set_default_profile
141
+ self.profile ||= User.count.zero? ? 'admin' : 'contributor'
142
+ end
143
+
144
+ validates :login, uniqueness: true, on: :create
145
+ validates :email, uniqueness: true, on: :create
146
+ validates :email, :login, presence: true
147
+ validates :login, length: { in: 3..40 }
148
+ end
@@ -0,0 +1,25 @@
1
+ class Migrator
2
+ def current_schema_version
3
+ ActiveRecord::Migrator.current_version
4
+ end
5
+
6
+ def migrations_pending?
7
+ pending_migrations.any?
8
+ end
9
+
10
+ def pending_migrations
11
+ all_migrations = ActiveRecord::Migrator.migrations(migrations_paths)
12
+ migrator = ActiveRecord::Migrator.new(:up, all_migrations)
13
+ migrator.pending_migrations
14
+ end
15
+
16
+ def migrate
17
+ ActiveRecord::Migrator.migrate(migrations_paths)
18
+ end
19
+
20
+ private
21
+
22
+ def migrations_paths
23
+ ActiveRecord::Migrator.migrations_paths
24
+ end
25
+ end
@@ -0,0 +1,80 @@
1
+ class TitleBuilder
2
+ def initialize(template)
3
+ @template = template
4
+ end
5
+
6
+ def build(item, settings, parameters)
7
+ s = @template
8
+
9
+ s = substitute_parameters(s, parameters)
10
+ s = substitute_settings(s, settings)
11
+ s = substitute_item(s, item)
12
+ s = substitute_time(s, settings)
13
+
14
+ s
15
+ end
16
+
17
+ private
18
+
19
+ def substitute_time(s, settings)
20
+ # Other
21
+ s = s.gsub('%currentdate%', Time.now.strftime(settings.date_format))
22
+ s = s.gsub('%currenttime%', Time.now.strftime(settings.time_format))
23
+ s = s.gsub('%currentmonth%', Time.now.strftime('%B'))
24
+ s = s.gsub('%currentyear%', Time.now.year.to_s)
25
+ s
26
+ end
27
+
28
+ def substitute_item(s, item)
29
+ # Tags for item
30
+ s = s.gsub('%title%', item.title) if s =~ /%title/ && item.respond_to?(:title)
31
+ s = s.gsub('%excerpt%', item.excerpt_text) if s =~ /%excerpt%/ && item.respond_to?(:excerpt_text)
32
+ s = s.gsub('%description%', item.description) if s =~ /%description%/ && item.respond_to?(:description)
33
+ s = s.gsub('%name%', item.name) if s =~ /%name%/ && item.respond_to?(:name)
34
+ s = s.gsub('%author%', item.name) if s =~ /%author%/ && item.respond_to?(:name)
35
+ s = s.gsub('%body%', item.body) if s =~ /%body%/ && item.respond_to?(:body)
36
+
37
+ if s =~ /%categories%/ && item.respond_to?(:categories)
38
+ s = s.gsub('%categories%', item.categories.map(&:name).join(', '))
39
+ end
40
+
41
+ if s =~ /%tags%/ && item.respond_to?(:tags)
42
+ s = s.gsub('%tags%', item.tags.map(&:display_name).join(', '))
43
+ end
44
+
45
+ s
46
+ end
47
+
48
+ def substitute_settings(s, settings)
49
+ # Tags for settings
50
+ s = s.gsub('%blog_name%', settings.blog_name)
51
+ s = s.gsub('%blog_subtitle%', settings.blog_subtitle)
52
+ s = s.gsub('%meta_keywords%', settings.meta_keywords)
53
+
54
+ s
55
+ end
56
+
57
+ def substitute_parameters(s, parameters)
58
+ s = s.gsub('%date%', parse_date(s, parameters)) if s =~ /%date%/
59
+ s = s.gsub('%search%', parameters[:q]) if parameters[:q]
60
+ s = s.gsub('%page%', parse_page(s, parameters)) if s =~ /%page%/
61
+
62
+ s
63
+ end
64
+
65
+ def parse_date(string, params)
66
+ return '' unless params[:year]
67
+
68
+ format = ''
69
+ format << '%A %d ' if params[:day]
70
+ format << '%B ' if params[:month]
71
+ format << '%Y' if params[:year]
72
+
73
+ string.gsub('%date%', Time.mktime(*params.values_at(:year, :month, :day)).strftime(format))
74
+ end
75
+
76
+ def parse_page(_string, params)
77
+ return '' unless params[:page]
78
+ "#{I18n.t('articles.index.page')} #{params[:page]}"
79
+ end
80
+ end
@@ -0,0 +1,30 @@
1
+ class ResourceUploader < CarrierWave::Uploader::Base
2
+ include CarrierWave::MiniMagick
3
+
4
+ def store_dir
5
+ "files/#{model.class.to_s.underscore}/#{model.id}"
6
+ end
7
+
8
+ version :thumb, if: :image? do
9
+ process dynamic_resize_to_fit: :thumb
10
+ end
11
+
12
+ version :medium, if: :image? do
13
+ process dynamic_resize_to_fit: :medium
14
+ end
15
+
16
+ version :avatar, if: :image? do
17
+ process dynamic_resize_to_fit: :avatar
18
+ end
19
+
20
+ def dynamic_resize_to_fit(size)
21
+ resize_setting = model.blog.send("image_#{size}_size").to_i
22
+
23
+ resize_to_fit(resize_setting, resize_setting)
24
+ end
25
+
26
+ def image?(new_file)
27
+ mime_magic_content_type = new_file.send :mime_magic_content_type
28
+ mime_magic_content_type && mime_magic_content_type.include?('image')
29
+ end
30
+ end