HornsAndHooves-publify_core 10.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (452) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +102 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.md +9 -0
  5. data/app/assets/fonts/bootstrap/glyphicons-halflings-regular.eot +0 -0
  6. data/app/assets/fonts/bootstrap/glyphicons-halflings-regular.svg +288 -0
  7. data/app/assets/fonts/bootstrap/glyphicons-halflings-regular.ttf +0 -0
  8. data/app/assets/fonts/bootstrap/glyphicons-halflings-regular.woff +0 -0
  9. data/app/assets/fonts/bootstrap/glyphicons-halflings-regular.woff2 +0 -0
  10. data/app/assets/fonts/open-sans-bold.woff +0 -0
  11. data/app/assets/fonts/open-sans.woff +0 -0
  12. data/app/assets/images/admin/loading.gif +0 -0
  13. data/app/assets/images/admin/typologo.gif +0 -0
  14. data/app/assets/images/calendar_date_select/calendar.gif +0 -0
  15. data/app/assets/images/close.gif +0 -0
  16. data/app/assets/images/closelabel.gif +0 -0
  17. data/app/assets/images/go.png +0 -0
  18. data/app/assets/images/loading.gif +0 -0
  19. data/app/assets/images/nextlabel.gif +0 -0
  20. data/app/assets/images/overlay.png +0 -0
  21. data/app/assets/images/powered.gif +0 -0
  22. data/app/assets/images/prevlabel.gif +0 -0
  23. data/app/assets/images/spinner-blue.gif +0 -0
  24. data/app/assets/images/spinner.gif +0 -0
  25. data/app/assets/images/thumb_blank.jpg +0 -0
  26. data/app/assets/javascripts/application.js +2 -0
  27. data/app/assets/javascripts/bootstrap/affix.js +164 -0
  28. data/app/assets/javascripts/bootstrap/alert.js +95 -0
  29. data/app/assets/javascripts/bootstrap/button.js +125 -0
  30. data/app/assets/javascripts/bootstrap/collapse.js +212 -0
  31. data/app/assets/javascripts/bootstrap/dropdown.js +165 -0
  32. data/app/assets/javascripts/bootstrap/modal.js +358 -0
  33. data/app/assets/javascripts/bootstrap/tab.js +155 -0
  34. data/app/assets/javascripts/bootstrap/transition.js +59 -0
  35. data/app/assets/javascripts/bootstrap-sprockets.js +8 -0
  36. data/app/assets/javascripts/cookies.js +60 -0
  37. data/app/assets/javascripts/datetimepicker.js +1470 -0
  38. data/app/assets/javascripts/lang/da_DK.js +17 -0
  39. data/app/assets/javascripts/lang/default.js +21 -0
  40. data/app/assets/javascripts/lang/en_US.js +21 -0
  41. data/app/assets/javascripts/lang/fr_FR.js +21 -0
  42. data/app/assets/javascripts/lang/nl_NL.js +21 -0
  43. data/app/assets/javascripts/lang/zh_TW.js +17 -0
  44. data/app/assets/javascripts/lightbox.js +350 -0
  45. data/app/assets/javascripts/observe.js +28 -0
  46. data/app/assets/javascripts/publify.js +11 -0
  47. data/app/assets/javascripts/publify_admin.js +104 -0
  48. data/app/assets/javascripts/quicktags.js +448 -0
  49. data/app/assets/javascripts/set-timeago-lang.js +3 -0
  50. data/app/assets/javascripts/sidebar.js +28 -0
  51. data/app/assets/javascripts/spinnable.js +5 -0
  52. data/app/assets/javascripts/tagmanager.js +481 -0
  53. data/app/assets/javascripts/typeahead.js +1139 -0
  54. data/app/assets/stylesheets/_bootstrap-compass.scss +9 -0
  55. data/app/assets/stylesheets/_bootstrap-mincer.scss +19 -0
  56. data/app/assets/stylesheets/_bootstrap-sprockets.scss +9 -0
  57. data/app/assets/stylesheets/_bootstrap.scss +43 -0
  58. data/app/assets/stylesheets/accounts.css.scss +7 -0
  59. data/app/assets/stylesheets/administration_structure.css.scss +255 -0
  60. data/app/assets/stylesheets/bootstrap/_alerts.scss +73 -0
  61. data/app/assets/stylesheets/bootstrap/_button-groups.scss +242 -0
  62. data/app/assets/stylesheets/bootstrap/_buttons.scss +168 -0
  63. data/app/assets/stylesheets/bootstrap/_close.scss +37 -0
  64. data/app/assets/stylesheets/bootstrap/_code.scss +69 -0
  65. data/app/assets/stylesheets/bootstrap/_component-animations.scss +38 -0
  66. data/app/assets/stylesheets/bootstrap/_dropdowns.scss +213 -0
  67. data/app/assets/stylesheets/bootstrap/_forms.scss +586 -0
  68. data/app/assets/stylesheets/bootstrap/_glyphicons.scss +307 -0
  69. data/app/assets/stylesheets/bootstrap/_grid.scss +94 -0
  70. data/app/assets/stylesheets/bootstrap/_labels.scss +66 -0
  71. data/app/assets/stylesheets/bootstrap/_media.scss +66 -0
  72. data/app/assets/stylesheets/bootstrap/_mixins.scss +37 -0
  73. data/app/assets/stylesheets/bootstrap/_modals.scss +150 -0
  74. data/app/assets/stylesheets/bootstrap/_navbar.scss +479 -0
  75. data/app/assets/stylesheets/bootstrap/_navs.scss +242 -0
  76. data/app/assets/stylesheets/bootstrap/_normalize.scss +427 -0
  77. data/app/assets/stylesheets/bootstrap/_pagination.scss +86 -0
  78. data/app/assets/stylesheets/bootstrap/_panels.scss +222 -0
  79. data/app/assets/stylesheets/bootstrap/_print.scss +99 -0
  80. data/app/assets/stylesheets/bootstrap/_scaffolding.scss +160 -0
  81. data/app/assets/stylesheets/bootstrap/_tables.scss +234 -0
  82. data/app/assets/stylesheets/bootstrap/_theme.scss +224 -0
  83. data/app/assets/stylesheets/bootstrap/_type.scss +296 -0
  84. data/app/assets/stylesheets/bootstrap/_utilities.scss +55 -0
  85. data/app/assets/stylesheets/bootstrap/_variables.scss +654 -0
  86. data/app/assets/stylesheets/bootstrap/_wells.scss +29 -0
  87. data/app/assets/stylesheets/bootstrap/mixins/_alerts.scss +15 -0
  88. data/app/assets/stylesheets/bootstrap/mixins/_background-variant.scss +12 -0
  89. data/app/assets/stylesheets/bootstrap/mixins/_border-radius.scss +18 -0
  90. data/app/assets/stylesheets/bootstrap/mixins/_buttons.scss +56 -0
  91. data/app/assets/stylesheets/bootstrap/mixins/_center-block.scss +7 -0
  92. data/app/assets/stylesheets/bootstrap/mixins/_clearfix.scss +22 -0
  93. data/app/assets/stylesheets/bootstrap/mixins/_forms.scss +82 -0
  94. data/app/assets/stylesheets/bootstrap/mixins/_gradients.scss +56 -0
  95. data/app/assets/stylesheets/bootstrap/mixins/_grid-framework.scss +81 -0
  96. data/app/assets/stylesheets/bootstrap/mixins/_grid.scss +122 -0
  97. data/app/assets/stylesheets/bootstrap/mixins/_hide-text.scss +21 -0
  98. data/app/assets/stylesheets/bootstrap/mixins/_image.scss +28 -0
  99. data/app/assets/stylesheets/bootstrap/mixins/_labels.scss +12 -0
  100. data/app/assets/stylesheets/bootstrap/mixins/_nav-divider.scss +10 -0
  101. data/app/assets/stylesheets/bootstrap/mixins/_nav-vertical-align.scss +9 -0
  102. data/app/assets/stylesheets/bootstrap/mixins/_opacity.scss +7 -0
  103. data/app/assets/stylesheets/bootstrap/mixins/_pagination.scss +24 -0
  104. data/app/assets/stylesheets/bootstrap/mixins/_panels.scss +20 -0
  105. data/app/assets/stylesheets/bootstrap/mixins/_reset-filter.scss +8 -0
  106. data/app/assets/stylesheets/bootstrap/mixins/_resize.scss +6 -0
  107. data/app/assets/stylesheets/bootstrap/mixins/_responsive-visibility.scss +17 -0
  108. data/app/assets/stylesheets/bootstrap/mixins/_size.scss +10 -0
  109. data/app/assets/stylesheets/bootstrap/mixins/_tab-focus.scss +9 -0
  110. data/app/assets/stylesheets/bootstrap/mixins/_table-row.scss +28 -0
  111. data/app/assets/stylesheets/bootstrap/mixins/_text-emphasis.scss +12 -0
  112. data/app/assets/stylesheets/bootstrap/mixins/_text-overflow.scss +8 -0
  113. data/app/assets/stylesheets/bootstrap/mixins/_vendor-prefixes.scss +222 -0
  114. data/app/assets/stylesheets/coderay.css +135 -0
  115. data/app/assets/stylesheets/datetimepicker.css +306 -0
  116. data/app/assets/stylesheets/lightbox.css +63 -0
  117. data/app/assets/stylesheets/publify.css.scss +9 -0
  118. data/app/assets/stylesheets/publify_admin.css.scss +10 -0
  119. data/app/assets/stylesheets/rss.css +53 -0
  120. data/app/assets/stylesheets/sidebar_admin.css.scss +36 -0
  121. data/app/assets/stylesheets/tagmanager.css +102 -0
  122. data/app/assets/stylesheets/user-styles.css +29 -0
  123. data/app/controllers/accounts_controller.rb +4 -0
  124. data/app/controllers/admin/base_controller.rb +27 -0
  125. data/app/controllers/admin/content_controller.rb +194 -0
  126. data/app/controllers/admin/dashboard_controller.rb +33 -0
  127. data/app/controllers/admin/feedback_controller.rb +160 -0
  128. data/app/controllers/admin/notes_controller.rb +73 -0
  129. data/app/controllers/admin/pages_controller.rb +73 -0
  130. data/app/controllers/admin/post_types_controller.rb +57 -0
  131. data/app/controllers/admin/profiles_controller.rb +57 -0
  132. data/app/controllers/admin/redirects_controller.rb +51 -0
  133. data/app/controllers/admin/resources_controller.rb +36 -0
  134. data/app/controllers/admin/seo_controller.rb +46 -0
  135. data/app/controllers/admin/settings_controller.rb +53 -0
  136. data/app/controllers/admin/sidebar_controller.rb +67 -0
  137. data/app/controllers/admin/tags_controller.rb +55 -0
  138. data/app/controllers/admin/themes_controller.rb +31 -0
  139. data/app/controllers/admin/users_controller.rb +67 -0
  140. data/app/controllers/articles_controller.rb +215 -0
  141. data/app/controllers/authors_controller.rb +30 -0
  142. data/app/controllers/base_controller.rb +43 -0
  143. data/app/controllers/comments_controller.rb +67 -0
  144. data/app/controllers/content_controller.rb +24 -0
  145. data/app/controllers/feedback_controller.rb +13 -0
  146. data/app/controllers/notes_controller.rb +35 -0
  147. data/app/controllers/setup_controller.rb +61 -0
  148. data/app/controllers/tags_controller.rb +50 -0
  149. data/app/controllers/text_controller.rb +7 -0
  150. data/app/controllers/textfilter_controller.rb +5 -0
  151. data/app/controllers/theme_controller.rb +63 -0
  152. data/app/controllers/xml_controller.rb +20 -0
  153. data/app/helpers/admin/base_helper.rb +87 -0
  154. data/app/helpers/admin/feedback_helper.rb +53 -0
  155. data/app/helpers/articles_helper.rb +10 -0
  156. data/app/helpers/authors_helper.rb +30 -0
  157. data/app/helpers/base_helper.rb +257 -0
  158. data/app/helpers/blog_helper.rb +14 -0
  159. data/app/helpers/xml_helper.rb +18 -0
  160. data/app/jobs/application_job.rb +5 -0
  161. data/app/mailers/notification_mailer.rb +40 -0
  162. data/app/models/ability.rb +53 -0
  163. data/app/models/archives_sidebar.rb +45 -0
  164. data/app/models/article/factory.rb +64 -0
  165. data/app/models/article.rb +308 -0
  166. data/app/models/blog.rb +280 -0
  167. data/app/models/comment.rb +57 -0
  168. data/app/models/config_manager.rb +83 -0
  169. data/app/models/content.rb +118 -0
  170. data/app/models/content_base.rb +88 -0
  171. data/app/models/feedback.rb +250 -0
  172. data/app/models/meta_sidebar.rb +10 -0
  173. data/app/models/note.rb +152 -0
  174. data/app/models/page.rb +42 -0
  175. data/app/models/page_sidebar.rb +14 -0
  176. data/app/models/ping.rb +5 -0
  177. data/app/models/post_type.rb +16 -0
  178. data/app/models/redirect.rb +51 -0
  179. data/app/models/redirection.rb +6 -0
  180. data/app/models/resource.rb +12 -0
  181. data/app/models/search_sidebar.rb +9 -0
  182. data/app/models/sidebar.rb +142 -0
  183. data/app/models/static_sidebar.rb +22 -0
  184. data/app/models/tag.rb +71 -0
  185. data/app/models/tag_sidebar.rb +30 -0
  186. data/app/models/text_filter.rb +141 -0
  187. data/app/models/trackback.rb +46 -0
  188. data/app/models/trigger.rb +34 -0
  189. data/app/models/user.rb +142 -0
  190. data/app/services/title_builder.rb +88 -0
  191. data/app/uploaders/resource_uploader.rb +63 -0
  192. data/app/views/accounts/confirm.html.erb +7 -0
  193. data/app/views/admin/content/_article_list.html.erb +26 -0
  194. data/app/views/admin/content/_form.html.erb +145 -0
  195. data/app/views/admin/content/autosave.js.erb +5 -0
  196. data/app/views/admin/content/edit.html.erb +3 -0
  197. data/app/views/admin/content/index.html.erb +48 -0
  198. data/app/views/admin/content/index.js.erb +1 -0
  199. data/app/views/admin/content/new.html.erb +3 -0
  200. data/app/views/admin/dashboard/_comment.html.erb +18 -0
  201. data/app/views/admin/dashboard/_comments.html.erb +10 -0
  202. data/app/views/admin/dashboard/_drafts.html.erb +25 -0
  203. data/app/views/admin/dashboard/_overview.html.erb +21 -0
  204. data/app/views/admin/dashboard/_welcome.html.erb +28 -0
  205. data/app/views/admin/dashboard/index.html.erb +16 -0
  206. data/app/views/admin/feedback/_button.html.erb +19 -0
  207. data/app/views/admin/feedback/_feedback.html.erb +7 -0
  208. data/app/views/admin/feedback/_ham.html.erb +17 -0
  209. data/app/views/admin/feedback/_spam.html.erb +23 -0
  210. data/app/views/admin/feedback/article.html.erb +69 -0
  211. data/app/views/admin/feedback/edit.html.erb +48 -0
  212. data/app/views/admin/feedback/ham.js +1 -0
  213. data/app/views/admin/feedback/index.html.erb +53 -0
  214. data/app/views/admin/feedback/spam.js +1 -0
  215. data/app/views/admin/migrations/show.html.erb +39 -0
  216. data/app/views/admin/notes/_form.html.erb +37 -0
  217. data/app/views/admin/notes/_header.html.erb +6 -0
  218. data/app/views/admin/notes/_list.html.erb +13 -0
  219. data/app/views/admin/notes/_note.html.erb +14 -0
  220. data/app/views/admin/notes/edit.html.erb +11 -0
  221. data/app/views/admin/notes/index.html.erb +11 -0
  222. data/app/views/admin/notes/show.html.erb +14 -0
  223. data/app/views/admin/pages/_form.html.erb +101 -0
  224. data/app/views/admin/pages/_pages.html.erb +21 -0
  225. data/app/views/admin/pages/edit.html.erb +1 -0
  226. data/app/views/admin/pages/index.html.erb +17 -0
  227. data/app/views/admin/pages/new.html.erb +1 -0
  228. data/app/views/admin/post_types/_index_and_form.html.erb +65 -0
  229. data/app/views/admin/post_types/edit.html.erb +1 -0
  230. data/app/views/admin/post_types/index.html.erb +1 -0
  231. data/app/views/admin/profiles/index.html.erb +10 -0
  232. data/app/views/admin/redirects/_index_and_form.html.erb +68 -0
  233. data/app/views/admin/redirects/edit.html.erb +1 -0
  234. data/app/views/admin/redirects/index.html.erb +1 -0
  235. data/app/views/admin/resources/index.html.erb +72 -0
  236. data/app/views/admin/seo/_general.html.erb +104 -0
  237. data/app/views/admin/seo/_permalinks.html.erb +53 -0
  238. data/app/views/admin/seo/_titles.html.erb +209 -0
  239. data/app/views/admin/seo/show.html.erb +28 -0
  240. data/app/views/admin/settings/display.html.erb +101 -0
  241. data/app/views/admin/settings/feedback.html.erb +118 -0
  242. data/app/views/admin/settings/index.html.erb +73 -0
  243. data/app/views/admin/settings/write.html.erb +69 -0
  244. data/app/views/admin/shared/_edit.html.erb +4 -0
  245. data/app/views/admin/shared/_menu.html.erb +121 -0
  246. data/app/views/admin/shared/_twitter_alert.html.erb +3 -0
  247. data/app/views/admin/sidebar/_available.html.erb +6 -0
  248. data/app/views/admin/sidebar/_available.json.erb +6 -0
  249. data/app/views/admin/sidebar/_config.html.erb +27 -0
  250. data/app/views/admin/sidebar/_target.html.erb +9 -0
  251. data/app/views/admin/sidebar/_target_sidebar.html.erb +20 -0
  252. data/app/views/admin/sidebar/destroy.js.erb +1 -0
  253. data/app/views/admin/sidebar/index.html.erb +31 -0
  254. data/app/views/admin/sidebar/sortable.js.erb +3 -0
  255. data/app/views/admin/sidebar/update.js.erb +2 -0
  256. data/app/views/admin/tags/_index_and_form.html.erb +59 -0
  257. data/app/views/admin/tags/edit.html.erb +1 -0
  258. data/app/views/admin/tags/index.html.erb +1 -0
  259. data/app/views/admin/themes/index.html.erb +27 -0
  260. data/app/views/admin/users/_form.html.erb +215 -0
  261. data/app/views/admin/users/edit.html.erb +8 -0
  262. data/app/views/admin/users/index.html.erb +39 -0
  263. data/app/views/admin/users/new.html.erb +8 -0
  264. data/app/views/archives_sidebar/_content.html.erb +13 -0
  265. data/app/views/articles/_archives_article.html.erb +9 -0
  266. data/app/views/articles/_article.html.erb +9 -0
  267. data/app/views/articles/_article_author.html.erb +4 -0
  268. data/app/views/articles/_article_collection.html.erb +8 -0
  269. data/app/views/articles/_article_content.html.erb +5 -0
  270. data/app/views/articles/_article_excerpt.html.erb +15 -0
  271. data/app/views/articles/_article_links.html.erb +12 -0
  272. data/app/views/articles/_comment.html.erb +1 -0
  273. data/app/views/articles/_comment_errors.html.erb +2 -0
  274. data/app/views/articles/_comment_form.html.erb +48 -0
  275. data/app/views/articles/_comment_list.html.erb +5 -0
  276. data/app/views/articles/_comment_preview.html.erb +4 -0
  277. data/app/views/articles/_full_article_content.html.erb +4 -0
  278. data/app/views/articles/_password_form.html.erb +10 -0
  279. data/app/views/articles/_trackback.html.erb +6 -0
  280. data/app/views/articles/archives.html.erb +15 -0
  281. data/app/views/articles/comment.js.erb +5 -0
  282. data/app/views/articles/comment_failed.js.erb +3 -0
  283. data/app/views/articles/error.html.erb +3 -0
  284. data/app/views/articles/feedback_atom_feed.atom.builder +10 -0
  285. data/app/views/articles/feedback_rss_feed.rss.builder +21 -0
  286. data/app/views/articles/index.html.erb +1 -0
  287. data/app/views/articles/index_atom_feed.atom.builder +9 -0
  288. data/app/views/articles/index_rss_feed.rss.builder +20 -0
  289. data/app/views/articles/live_search.html.erb +10 -0
  290. data/app/views/articles/read.html.erb +43 -0
  291. data/app/views/articles/search.html.erb +8 -0
  292. data/app/views/articles/trackback.xml.builder +7 -0
  293. data/app/views/articles/view_page.html.erb +3 -0
  294. data/app/views/authors/show.html.erb +30 -0
  295. data/app/views/authors/show_atom_feed.atom.builder +9 -0
  296. data/app/views/authors/show_rss_feed.rss.builder +20 -0
  297. data/app/views/comments/_comment.html.erb +17 -0
  298. data/app/views/comments/preview.html.erb +1 -0
  299. data/app/views/comments/preview.js.erb +3 -0
  300. data/app/views/devise/mailer/reset_password_instructions.html.erb +11 -0
  301. data/app/views/devise/passwords/edit.html.erb +28 -0
  302. data/app/views/devise/passwords/new.html.erb +20 -0
  303. data/app/views/devise/registrations/new.html.erb +36 -0
  304. data/app/views/devise/sessions/new.html.erb +32 -0
  305. data/app/views/devise/shared/_links.html.erb +11 -0
  306. data/app/views/errors/404.html.erb +2 -0
  307. data/app/views/feedback/index.atom.builder +9 -0
  308. data/app/views/feedback/index.rss.builder +20 -0
  309. data/app/views/layouts/accounts.html.erb +33 -0
  310. data/app/views/layouts/administration.html.erb +37 -0
  311. data/app/views/layouts/default.html.erb +33 -0
  312. data/app/views/layouts/editor.html.erb +17 -0
  313. data/app/views/meta_sidebar/_content.html.erb +8 -0
  314. data/app/views/notes/_note.html.erb +16 -0
  315. data/app/views/notes/error.html.erb +3 -0
  316. data/app/views/notes/index.html.erb +15 -0
  317. data/app/views/notes/show.html.erb +5 -0
  318. data/app/views/notes/show_in_reply.html.erb +18 -0
  319. data/app/views/notification_mailer/_mail_footer.html.erb +5 -0
  320. data/app/views/notification_mailer/_mail_header.html.erb +3 -0
  321. data/app/views/notification_mailer/article.html.erb +7 -0
  322. data/app/views/notification_mailer/comment.html.erb +12 -0
  323. data/app/views/notification_mailer/notif_user.html.erb +9 -0
  324. data/app/views/page_sidebar/_content.html.erb +12 -0
  325. data/app/views/search_sidebar/_content.html.erb +10 -0
  326. data/app/views/settings/done.html.erb +2 -0
  327. data/app/views/settings/install.html.erb +12 -0
  328. data/app/views/setup/index.html.erb +17 -0
  329. data/app/views/shared/_atom_header.atom.builder +6 -0
  330. data/app/views/shared/_atom_item_article.atom.builder +36 -0
  331. data/app/views/shared/_atom_item_comment.atom.builder +10 -0
  332. data/app/views/shared/_atom_item_trackback.atom.builder +10 -0
  333. data/app/views/shared/_flash.erb +10 -0
  334. data/app/views/shared/_google_analytics.html.erb +8 -0
  335. data/app/views/shared/_page_header.html.erb +23 -0
  336. data/app/views/shared/_rss_item_article.rss.builder +32 -0
  337. data/app/views/shared/_rss_item_comment.rss.builder +9 -0
  338. data/app/views/shared/_rss_item_trackback.rss.builder +9 -0
  339. data/app/views/sidebar/_row.html.erb +1 -0
  340. data/app/views/sidebar/_sidebar.html.erb +5 -0
  341. data/app/views/sidebar/display_plugins.html.erb +5 -0
  342. data/app/views/sidebar/show.html.erb +1 -0
  343. data/app/views/static_sidebar/_content.html.erb +2 -0
  344. data/app/views/tag_sidebar/_content.html.erb +10 -0
  345. data/app/views/tags/index.html.erb +15 -0
  346. data/app/views/tags/show.html.erb +1 -0
  347. data/app/views/theme/static_view_test.html.erb +1 -0
  348. data/app/views/xml/_googlesitemap_item_article.googlesitemap.builder +7 -0
  349. data/app/views/xml/_googlesitemap_item_category.googlesitemap.builder +6 -0
  350. data/app/views/xml/_googlesitemap_item_page.googlesitemap.builder +6 -0
  351. data/app/views/xml/_googlesitemap_item_tag.googlesitemap.builder +6 -0
  352. data/app/views/xml/sitemap.googlesitemap.builder +9 -0
  353. data/config/i18n-tasks.yml +49 -0
  354. data/config/initializers/devise.rb +271 -0
  355. data/config/initializers/mime_types.rb +7 -0
  356. data/config/locales/da.yml +769 -0
  357. data/config/locales/de.yml +769 -0
  358. data/config/locales/en.yml +770 -0
  359. data/config/locales/es-MX.yml +769 -0
  360. data/config/locales/fr.yml +769 -0
  361. data/config/locales/he.yml +769 -0
  362. data/config/locales/it.yml +769 -0
  363. data/config/locales/ja.yml +769 -0
  364. data/config/locales/lt.yml +783 -0
  365. data/config/locales/nb-NO.yml +769 -0
  366. data/config/locales/nl.yml +769 -0
  367. data/config/locales/pl.yml +797 -0
  368. data/config/locales/pt-BR.yml +769 -0
  369. data/config/locales/ro.yml +782 -0
  370. data/config/locales/ru.yml +797 -0
  371. data/config/locales/sidebars.da.yml +20 -0
  372. data/config/locales/sidebars.de.yml +20 -0
  373. data/config/locales/sidebars.en.yml +20 -0
  374. data/config/locales/sidebars.es-MX.yml +20 -0
  375. data/config/locales/sidebars.fr.yml +20 -0
  376. data/config/locales/sidebars.he.yml +20 -0
  377. data/config/locales/sidebars.it.yml +20 -0
  378. data/config/locales/sidebars.ja.yml +20 -0
  379. data/config/locales/sidebars.lt.yml +20 -0
  380. data/config/locales/sidebars.nb-NO.yml +20 -0
  381. data/config/locales/sidebars.nl.yml +20 -0
  382. data/config/locales/sidebars.pl.yml +20 -0
  383. data/config/locales/sidebars.pt-BR.yml +20 -0
  384. data/config/locales/sidebars.ro.yml +20 -0
  385. data/config/locales/sidebars.ru.yml +20 -0
  386. data/config/locales/sidebars.zh-CN.yml +20 -0
  387. data/config/locales/sidebars.zh-TW.yml +20 -0
  388. data/config/locales/zh-CN.yml +768 -0
  389. data/config/locales/zh-TW.yml +769 -0
  390. data/config/routes.rb +174 -0
  391. data/db/migrate/113_initial_schema.rb +210 -0
  392. data/db/migrate/114_fixes_buggy_articles_and_notes.rb +56 -0
  393. data/db/migrate/115_drops_categories_for_tags.rb +37 -0
  394. data/db/migrate/20150207131657_add_missing_indexes.rb +23 -0
  395. data/db/migrate/20150807134129_simplify_redirect_relations.rb +45 -0
  396. data/db/migrate/20150808052637_add_blog_ids.rb +39 -0
  397. data/db/migrate/20150808191127_add_blog_id_to_redirects.rb +19 -0
  398. data/db/migrate/20150810094754_add_blog_id_to_tags.rb +19 -0
  399. data/db/migrate/20160108111120_add_devise_to_users.rb +57 -0
  400. data/db/migrate/20160108184201_move_last_connection_to_last_sign_in_at.rb +20 -0
  401. data/db/migrate/20160110094906_remove_profiles_rights.rb +18 -0
  402. data/db/migrate/20160605103918_replace_profile_id_with_string.rb +32 -0
  403. data/db/migrate/20160605154632_remove_profiles.rb +31 -0
  404. data/db/migrate/20160701061851_demand_blog_id_on_contents.rb +13 -0
  405. data/db/migrate/20160701062604_add_blog_id_to_resources.rb +32 -0
  406. data/db/migrate/20170528093024_move_resources_to_content.rb +9 -0
  407. data/db/migrate/20170528094923_move_tags_to_content.rb +10 -0
  408. data/db/migrate/20170528201606_remove_separate_published_flag.rb +9 -0
  409. data/db/migrate/20170605071626_remove_extra_state_columns_from_feedback.rb +10 -0
  410. data/db/migrate/20170702105201_remove_published_at_from_feedback.rb +9 -0
  411. data/db/migrate/20190208151235_add_text_filter_name_fields.rb +11 -0
  412. data/db/migrate/20190208152646_move_text_filter_to_name.rb +99 -0
  413. data/db/migrate/20190209155717_remove_text_filter_ids.rb +21 -0
  414. data/db/migrate/20190209160610_remove_text_filters.rb +19 -0
  415. data/db/seeds.rb +25 -0
  416. data/lib/email_notify.rb +28 -0
  417. data/lib/format.rb +9 -0
  418. data/lib/publify_core/engine.rb +31 -0
  419. data/lib/publify_core/lang.rb +9 -0
  420. data/lib/publify_core/testing_support/dns_mock.rb +15 -0
  421. data/lib/publify_core/testing_support/factories.rb +242 -0
  422. data/lib/publify_core/testing_support/feed_assertions.rb +48 -0
  423. data/lib/publify_core/testing_support/fixtures/exploit.svg +4 -0
  424. data/lib/publify_core/testing_support/fixtures/fakepng.png +1 -0
  425. data/lib/publify_core/testing_support/fixtures/otherfile.txt +1 -0
  426. data/lib/publify_core/testing_support/fixtures/testfile.png +0 -0
  427. data/lib/publify_core/testing_support/fixtures/testfile.txt +1 -0
  428. data/lib/publify_core/testing_support/upload_fixtures.rb +15 -0
  429. data/lib/publify_core/version.rb +5 -0
  430. data/lib/publify_core.rb +46 -0
  431. data/lib/publify_guid.rb +11 -0
  432. data/lib/publify_plugins.rb +77 -0
  433. data/lib/publify_textfilter_markdown.rb +57 -0
  434. data/lib/publify_textfilter_none.rb +16 -0
  435. data/lib/publify_textfilter_smartypants.rb +16 -0
  436. data/lib/publify_textfilter_textile.rb +23 -0
  437. data/lib/publify_textfilter_twitterfilter.rb +39 -0
  438. data/lib/publify_time.rb +34 -0
  439. data/lib/sidebar_field.rb +127 -0
  440. data/lib/sidebar_registry.rb +35 -0
  441. data/lib/spam_protection.rb +103 -0
  442. data/lib/tasks/i18n.rake +9 -0
  443. data/lib/tasks/manifest.rake +30 -0
  444. data/lib/tasks/publify_core_tasks.rake +6 -0
  445. data/lib/text_filter_plugin.rb +183 -0
  446. data/lib/theme.rb +72 -0
  447. data/lib/transforms.rb +47 -0
  448. data/themes/plain/about.markdown +4 -0
  449. data/themes/plain/javascripts/theme.js +0 -0
  450. data/themes/plain/preview.png +0 -0
  451. data/themes/plain/stylesheets/theme.css +57 -0
  452. metadata +1062 -0
@@ -0,0 +1,308 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "aasm"
4
+ require "uri"
5
+ require "net/http"
6
+
7
+ class Article < Content
8
+ include PublifyGuid
9
+ include ConfigManager
10
+
11
+ serialize :settings, Hash
12
+
13
+ content_fields :body, :extended
14
+
15
+ validates :guid, uniqueness: true
16
+ validates :title, presence: true
17
+
18
+ has_many :pings, dependent: :destroy
19
+ has_many :trackbacks, dependent: :destroy
20
+ has_many :feedback
21
+ has_many :triggers, as: :pending_item
22
+ has_many :comments, dependent: :destroy
23
+
24
+ belongs_to :resource
25
+
26
+ before_create :create_guid
27
+ before_save :set_permalink
28
+ after_save :keywords_to_tags, :shorten_url
29
+
30
+ scope :child_of, ->(article_id) { where(parent_id: article_id) }
31
+ scope :published_since, ->(time) { published.where("published_at > ?", time) }
32
+ scope :withdrawn, -> { where(state: "withdrawn").order(default_order) }
33
+ scope :pending, -> { where(state: "publication_pending"). order(default_order) }
34
+
35
+ scope :bestof, lambda {
36
+ joins(:feedback).
37
+ where("feedback.type" => "Comment",
38
+ "contents.state" => "published").
39
+ group("contents.id").
40
+ select("contents.*, count(feedback.id) as comment_count").
41
+ order("comment_count DESC").
42
+ limit(5)
43
+ }
44
+
45
+ setting :password, :string, ""
46
+
47
+ attr_accessor :draft, :keywords
48
+
49
+ include AASM
50
+
51
+ aasm column: :state do
52
+ state :draft, initial: true
53
+ # TODO: Disallow if published_at in past
54
+ state :publication_pending, after_enter: :trigger_publication
55
+ state :published, after_enter: :really_send_notifications
56
+ state :withdrawn
57
+
58
+ event :withdraw do
59
+ transitions from: :published, to: :withdrawn
60
+ transitions from: :publication_pending, to: :draft
61
+ end
62
+
63
+ event :publish do
64
+ before do
65
+ self.published_at ||= Time.zone.now
66
+ end
67
+
68
+ transitions from: [:new, :draft], to: :publication_pending do
69
+ guard do
70
+ published_at > Time.zone.now
71
+ end
72
+ end
73
+
74
+ transitions from: [:new, :draft, :publication_pending], to: :published do
75
+ guard do
76
+ published_at <= Time.zone.now
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ def set_permalink
83
+ return if draft? || permalink.present?
84
+
85
+ self.permalink = title.to_permalink
86
+ end
87
+
88
+ def has_child?
89
+ Article.exists?(parent_id: id)
90
+ end
91
+
92
+ def post_type
93
+ post_type = self[:post_type]
94
+ post_type = "read" if post_type.blank?
95
+ post_type
96
+ end
97
+
98
+ def self.last_draft(article_id)
99
+ article = Article.find(article_id)
100
+ article = Article.child_of(article.id).first while article.has_child?
101
+ article
102
+ end
103
+
104
+ def self.search_with(params)
105
+ params ||= {}
106
+ scoped = super(params)
107
+ if %w(no_draft drafts published withdrawn pending).include?(params[:state])
108
+ scoped = scoped.send(params[:state])
109
+ end
110
+
111
+ scoped.order("created_at DESC")
112
+ end
113
+
114
+ # FIXME: Use keyword params to clean up call sites.
115
+ def permalink_url(anchor = nil, only_path = false)
116
+ return unless published?
117
+
118
+ @cached_permalink_url ||= {}
119
+ @cached_permalink_url["#{anchor}#{only_path}"] ||=
120
+ blog.url_for(permalink_url_options, anchor: anchor, only_path: only_path)
121
+ end
122
+
123
+ def save_attachments!(files)
124
+ files ||= {}
125
+ files.each_value { |f| save_attachment!(f) }
126
+ end
127
+
128
+ def save_attachment!(file)
129
+ resources.create!(upload: file, blog: blog)
130
+ end
131
+
132
+ def comment_url
133
+ blog.url_for("comments?article_id=#{id}", only_path: true)
134
+ end
135
+
136
+ def preview_comment_url
137
+ blog.url_for("comments/preview?article_id=#{id}", only_path: true)
138
+ end
139
+
140
+ def feed_url(format)
141
+ "#{permalink_url}.#{format.gsub(/\d/, "")}"
142
+ end
143
+
144
+ def next
145
+ Article.where("published_at > ?", published_at).order("published_at asc").
146
+ limit(1).first
147
+ end
148
+
149
+ def previous
150
+ Article.where("published_at < ?", published_at).order("published_at desc").
151
+ limit(1).first
152
+ end
153
+
154
+ def publication_month
155
+ published_at.strftime("%Y-%m")
156
+ end
157
+
158
+ def self.publication_months
159
+ result = select("published_at").where("published_at is not NULL").where(type: "Article")
160
+ result.map { |it| [it.publication_month] }.uniq
161
+ end
162
+
163
+ # Finds one article which was posted on a certain date and matches the
164
+ # supplied dashed-title params is a Hash
165
+ def self.requested_article(params)
166
+ date_range = PublifyTime.delta(params[:year], params[:month], params[:day])
167
+
168
+ req_params = {}
169
+ req_params[:permalink] = params[:title] if params[:title]
170
+ req_params[:published_at] = date_range if date_range
171
+
172
+ return if req_params.empty? # no search if no params send
173
+
174
+ article = published.find_by(req_params)
175
+ return article if article
176
+
177
+ if params[:title]
178
+ req_params[:permalink] = CGI.escape(params[:title])
179
+ article = published.find_by(req_params)
180
+ return article if article
181
+ end
182
+ end
183
+
184
+ # Fulltext searches the body of published articles
185
+ def self.search(query, args = {})
186
+ query_s = query.to_s.strip
187
+ if !query_s.empty? && args.empty?
188
+ Article.searchstring(query)
189
+ elsif !query_s.empty? && !args.empty?
190
+ Article.searchstring(query).page(args[:page]).per(args[:per])
191
+ else
192
+ []
193
+ end
194
+ end
195
+
196
+ def keywords_to_tags
197
+ Tag.create_from_article!(self)
198
+ end
199
+
200
+ def interested_users
201
+ User.where(notify_on_new_articles: true)
202
+ end
203
+
204
+ def notify_user_via_email(user)
205
+ EmailNotify.send_article(self, user) if user.notify_via_email?
206
+ end
207
+
208
+ def comments_closed?
209
+ !(allow_comments? && in_feedback_window?)
210
+ end
211
+
212
+ def html_urls
213
+ urls = []
214
+ html.gsub(/<a\s+[^>]*>/) do |tag|
215
+ urls.push(Regexp.last_match[2].strip) if tag =~ /\bhref=(["']?)([^ >"]+)\1/
216
+ end
217
+ urls.uniq
218
+ end
219
+
220
+ def pings_closed?
221
+ !(allow_pings? && in_feedback_window?)
222
+ end
223
+
224
+ # check if time to comment is open or not
225
+ def in_feedback_window?
226
+ blog.sp_article_auto_close.zero? ||
227
+ published_at.to_i > blog.sp_article_auto_close.days.ago.to_i
228
+ end
229
+
230
+ def content_fields
231
+ [:body, :extended]
232
+ end
233
+
234
+ # The web interface no longer distinguishes between separate "body" and
235
+ # "extended" fields, and instead edits everything in a single edit field,
236
+ # separating the extended content using "\<!--more-->".
237
+ def body_and_extended
238
+ if extended.blank?
239
+ body
240
+ else
241
+ body + "\n<!--more-->\n" + extended
242
+ end
243
+ end
244
+
245
+ # Split apart value around a "\<!--more-->" comment and assign it to our
246
+ # #body and #extended fields.
247
+ def body_and_extended=(value)
248
+ parts = value.split(/\n?<!--more-->\n?/, 2)
249
+ self.body = parts[0]
250
+ self.extended = parts[1] || ""
251
+ end
252
+
253
+ def password_protected?
254
+ password.present?
255
+ end
256
+
257
+ def add_comment(params)
258
+ comments.build(params)
259
+ end
260
+
261
+ def access_by?(user)
262
+ user.admin? || user_id == user.id
263
+ end
264
+
265
+ def allow_comments?
266
+ return allow_comments unless allow_comments.nil?
267
+
268
+ blog.default_allow_comments
269
+ end
270
+
271
+ def allow_pings?
272
+ return allow_pings unless allow_pings.nil?
273
+
274
+ blog.default_allow_pings
275
+ end
276
+
277
+ def published_comments
278
+ comments.published.oldest_first
279
+ end
280
+
281
+ def published_trackbacks
282
+ trackbacks.published.oldest_first
283
+ end
284
+
285
+ def published_feedback
286
+ feedback.published.oldest_first
287
+ end
288
+
289
+ private
290
+
291
+ def permalink_url_options
292
+ format_url = blog.permalink_format.dup
293
+ format_url.gsub!("%year%", published_at.year.to_s)
294
+ format_url.gsub!("%month%", sprintf("%.2d", published_at.month))
295
+ format_url.gsub!("%day%", sprintf("%.2d", published_at.day))
296
+ format_url.gsub!("%title%", URI::DEFAULT_PARSER.escape(permalink.to_s))
297
+ if format_url[0, 1] == "/"
298
+ format_url[1..-1]
299
+ else
300
+ format_url
301
+ end
302
+ end
303
+
304
+ def trigger_publication
305
+ # TODO: Skip if already published, update when published_at changes
306
+ Trigger.post_action(published_at, self, "publish!")
307
+ end
308
+ end
@@ -0,0 +1,280 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The Blog class represents the one and only blog. It stores most
4
+ # configuration settings and is linked to most of the assorted content
5
+ # classes via has_many.
6
+ #
7
+ # Once upon a time, there were plans to make publify handle multiple blogs,
8
+ # but it never happened and publify is now firmly single-blog.
9
+ #
10
+ class Blog < ApplicationRecord
11
+ include ConfigManager
12
+ include Rails.application.routes.url_helpers
13
+
14
+ has_many :contents
15
+ has_many :articles
16
+ has_many :feedback, through: :articles
17
+
18
+ has_many :published_articles, ->() { published }, class_name: "Article"
19
+
20
+ has_many :pages
21
+ has_many :tags
22
+ has_many :notes
23
+
24
+ has_many :redirects
25
+ has_many :sidebars, ->() { order("active_position ASC") }
26
+
27
+ attr_accessor :custom_permalink
28
+
29
+ default_scope -> { order("id") }
30
+
31
+ validates :blog_name, presence: true
32
+
33
+ serialize :settings, Hash
34
+
35
+ # Description
36
+ setting :blog_name, :string, "My Shiny Weblog!"
37
+ setting :blog_subtitle, :string, ""
38
+ setting :geourl_location, :string, ""
39
+ setting :canonical_server_url, :string, "" # Deprecated
40
+ setting :lang, :string, "en_US"
41
+
42
+ # Spam
43
+ setting :sp_global, :boolean, false
44
+ setting :sp_article_auto_close, :integer, 0
45
+ setting :sp_url_limit, :integer, 0
46
+ setting :sp_akismet_key, :string, ""
47
+ setting :use_recaptcha, :boolean, false
48
+
49
+ # Mostly Behaviour
50
+ setting :text_filter, :string, "markdown smartypants"
51
+ setting :comment_text_filter, :string, "markdown smartypants"
52
+ setting :limit_article_display, :integer, 9
53
+ setting :limit_archives_display, :integer, 9
54
+ setting :limit_rss_display, :integer, 10
55
+ setting :default_allow_pings, :boolean, false
56
+ setting :default_allow_comments, :boolean, false
57
+ setting :default_moderate_comments, :boolean, false
58
+ # deprecated but still needed for backward compatibility
59
+ setting :show_extended_on_rss, :boolean, false
60
+ setting :hide_extended_on_rss, :boolean, false
61
+ setting :theme, :string, "plain"
62
+ setting :plugin_avatar, :string, ""
63
+ setting :global_pings_disable, :boolean, false
64
+ setting :send_outbound_pings, :boolean, true
65
+ setting :email_from, :string, "publify@example.com"
66
+ setting :allow_signup, :integer, 0
67
+ setting :date_format, :string, "%d/%m/%Y"
68
+ setting :time_format, :string, "%Hh%M"
69
+ setting :image_avatar_size, :integer, 48
70
+ setting :image_thumb_size, :integer, 360
71
+ setting :image_medium_size, :integer, 805
72
+
73
+ # SEO
74
+ setting :meta_description, :string, ""
75
+ setting :meta_keywords, :string, ""
76
+ setting :google_analytics, :string, ""
77
+ setting :rss_description, :boolean, false
78
+ setting :rss_description_text, :string, <<-HTML.strip_heredoc
79
+ <hr />
80
+ <p><small>Original article written by %author% and published on <a href='%blog_url%'>%blog_name%</a>
81
+ | <a href='%permalink_url%'>direct link to this article</a>
82
+ | If you are reading this article anywhere other than on <a href='%blog_url%'>%blog_name%</a>,
83
+ it has been illegally reproduced and without proper authorization.</small></p>
84
+ HTML
85
+ setting :permalink_format, :string, "/%year%/%month%/%day%/%title%"
86
+ setting :robots, :string, 'User-agent: *\nAllow: /\nDisallow: /admin\n'
87
+ setting :humans, :string, <<-TEXT.strip_heredoc
88
+ /* TEAM */
89
+ Your title: Your name.
90
+ Site: email, link to a contact form, etc.
91
+ Twitter: your Twitter username.
92
+
93
+ /* SITE */
94
+ Software: Publify [https://publify.github.io/] #{PublifyCore::VERSION}
95
+ TEXT
96
+ # deprecated but still needed for backward compatibility
97
+ setting :index_categories, :boolean, true
98
+ setting :unindex_categories, :boolean, false
99
+ # deprecated but still needed for backward compatibility
100
+ setting :index_tags, :boolean, true
101
+ setting :unindex_tags, :boolean, false
102
+ setting :admin_display_elements, :integer, 10
103
+ setting :google_verification, :string, ""
104
+ # deprecated but still needed for backward compatibility
105
+ setting :nofollowify, :boolean, true
106
+ setting :dofollowify, :boolean, false
107
+ setting :use_meta_keyword, :boolean, true
108
+ setting :home_title_template, :string, "%blog_name% | %blog_subtitle%"
109
+ setting :home_desc_template, :string, "%blog_name% | %blog_subtitle% | %meta_keywords%"
110
+ setting :article_title_template, :string, "%title% | %blog_name%"
111
+ setting :article_desc_template, :string, "%excerpt%"
112
+ setting :page_title_template, :string, "%title% | %blog_name%"
113
+ setting :page_desc_template, :string, "%excerpt%"
114
+ setting :paginated_title_template, :string, "%blog_name% | %blog_subtitle% %page%"
115
+ setting :paginated_desc_template, :string,
116
+ "%blog_name% | %blog_subtitle% | %meta_keywords% %page%"
117
+ setting :tag_title_template, :string, "Category: %name% | %blog_name% %page%"
118
+ setting :tag_desc_template, :string, "%name% | %blog_name% | %blog_subtitle% %page%"
119
+ setting :author_title_template, :string, "%author% | %blog_name%"
120
+ setting :author_desc_template, :string, "%author% | %blog_name% | %blog_subtitle%"
121
+ setting :archives_title_template, :string, "Archives for %blog_name% %date% %page%"
122
+ setting :archives_desc_template, :string,
123
+ "Archives for %blog_name% %date% %page% %blog_subtitle%"
124
+ setting :search_title_template, :string, "Results for %search% | %blog_name% %page%"
125
+ setting :search_desc_template, :string,
126
+ "Results for %search% | %blog_name% | %blog_subtitle% %page%"
127
+ setting :statuses_title_template, :string, "Notes | %blog_name% %page%"
128
+ setting :statuses_desc_template, :string, "Notes | %blog_name% | %blog_subtitle% %page%"
129
+ setting :status_title_template, :string, "%body% | %blog_name%"
130
+ setting :status_desc_template, :string, "%excerpt%"
131
+
132
+ setting :custom_tracking_field, :string, ""
133
+ # setting :meta_author_template, :string, "%blog_name% | %nickname%"
134
+
135
+ setting :twitter_consumer_key, :string, ""
136
+ setting :twitter_consumer_secret, :string, ""
137
+ setting :custom_url_shortener, :string, ""
138
+ setting :statuses_in_timeline, :boolean, true
139
+
140
+ validate :permalink_has_identifier
141
+ # validates :base_url, presence: true
142
+
143
+ # Find the Blog that matches a specific base URL. If no Blog object is found
144
+ # that matches, then grab the first blog. If *that* fails, then create a new
145
+ # Blog. The last case should only be used when Publify is first installed.
146
+ def self.find_blog(base_url)
147
+ Blog.find_by(base_url: base_url) || Blog.first || Blog.new
148
+ end
149
+
150
+ def global_pings_enabled?
151
+ !global_pings_disable?
152
+ end
153
+
154
+ # Check that all required blog settings have a value.
155
+ def configured?
156
+ settings.key?("blog_name")
157
+ end
158
+
159
+ # The +Theme+ object for the current theme.
160
+ def current_theme(reload = nil)
161
+ @current_theme = nil if reload
162
+ @current_theme ||= Theme.find(theme) || Theme.new("", "")
163
+ end
164
+
165
+ module BasedUrlFor
166
+ # Generate a URL based on the +base_url+. This allows us to generate URLs
167
+ # without needing a controller handy, so we can produce URLs from within models
168
+ # where appropriate.
169
+ #
170
+ # It also caches the result in the Rails cache, so repeated URL generation
171
+ # requests should be fast, as they bypass all of Rails' route logic.
172
+ def url_for(options = {}, extra_params = {})
173
+ case options
174
+ when String
175
+ options = options.sub(%r{^/}, "")
176
+ url_generated = if extra_params[:only_path]
177
+ root_path
178
+ else
179
+ base_url
180
+ end
181
+ # They asked for 'url_for "/some/path"', so return it unedited.
182
+ url_generated += "/#{options}"
183
+ url_generated += "##{extra_params[:anchor]}" if extra_params[:anchor]
184
+ url_generated
185
+ when Hash
186
+ merged_opts = options.reverse_merge!(only_path: false, controller: "",
187
+ action: "permalink",
188
+ host: host_with_port,
189
+ script_name: root_path)
190
+ cache_key = merged_opts.values.prepend("blog-urlfor-withbaseurl").join("-")
191
+ unless Rails.cache.exist?(cache_key)
192
+ Rails.cache.write(cache_key, super(merged_opts))
193
+ end
194
+ Rails.cache.read(cache_key)
195
+ else
196
+ raise "Invalid URL in url_for: #{options.inspect}"
197
+ end
198
+ end
199
+ end
200
+
201
+ prepend BasedUrlFor
202
+
203
+ # The URL for a static file.
204
+ def file_url(filename)
205
+ if CarrierWave.configure { |config| config.storage.name == "CarrierWave::Storage::Fog" }
206
+ filename
207
+ else
208
+ url_for filename, only_path: false
209
+ end
210
+ end
211
+
212
+ def articles_matching(query, args = {})
213
+ Article.search(query, args)
214
+ end
215
+
216
+ def per_page(format)
217
+ return limit_article_display if format.nil? || format == "html"
218
+
219
+ limit_rss_display
220
+ end
221
+
222
+ def rss_limit_params
223
+ limit = limit_rss_display.to_i
224
+ if limit.zero?
225
+ {}
226
+ else
227
+ { limit: limit }
228
+ end
229
+ end
230
+
231
+ def permalink_has_identifier
232
+ unless /(%title%)/.match?(permalink_format)
233
+ errors.add(:base, I18n.t("errors.permalink_need_a_title"))
234
+ end
235
+
236
+ if /\.(atom|rss)$/.match?(permalink_format)
237
+ errors.add(:permalink_format, I18n.t("errors.cant_end_with_rss_or_atom"))
238
+ end
239
+ end
240
+
241
+ def root_path
242
+ split_base_url[:root_path]
243
+ end
244
+
245
+ def text_filter_object
246
+ TextFilter.make_filter(text_filter)
247
+ end
248
+
249
+ def has_twitter_configured?
250
+ return false if twitter_consumer_key.nil? || twitter_consumer_secret.nil?
251
+ return false if twitter_consumer_key.empty? || twitter_consumer_secret.empty?
252
+
253
+ true
254
+ end
255
+
256
+ def allow_signup?
257
+ allow_signup == 1
258
+ end
259
+
260
+ def shortener_url
261
+ custom_url_shortener.present? ? custom_url_shortener : base_url
262
+ end
263
+
264
+ private
265
+
266
+ def host_with_port
267
+ split_base_url[:host_with_port]
268
+ end
269
+
270
+ def split_base_url
271
+ unless @split_base_url
272
+ raise "Invalid base_url: #{base_url}" unless base_url =~ %r{(https?)://([^/]*)(.*)}
273
+
274
+ @split_base_url = { protocol: Regexp.last_match[1],
275
+ host_with_port: Regexp.last_match[2],
276
+ root_path: Regexp.last_match[3].gsub(%r{/$}, "") }
277
+ end
278
+ @split_base_url
279
+ end
280
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "timeout"
4
+
5
+ class Comment < Feedback
6
+ belongs_to :user, optional: true
7
+ content_fields :body
8
+ validates :author, :body, presence: true
9
+
10
+ attr_accessor :referrer, :permalink
11
+
12
+ scope :spam, -> { where(state: "spam") }
13
+ scope :not_spam, -> { where("state != 'spam'") }
14
+ scope :presumed_spam, -> { where(state: "presumed_spam") }
15
+ scope :presumed_ham, -> { where(state: "presumed_ham") }
16
+ scope :ham, -> { where(state: "ham") }
17
+ scope :unconfirmed, -> { where(state: %w(presumed_spam presumed_ham)) }
18
+
19
+ scope :last_published, -> { published.limit(5).order("created_at DESC") }
20
+
21
+ def notify_user_via_email(user)
22
+ # EmailNotify.send_comment(self, user) if user.notify_via_email?
23
+ end
24
+
25
+ def interested_users
26
+ User.where(notify_on_comments: true)
27
+ end
28
+
29
+ def default_text_filter
30
+ TextFilter.find_or_default(blog.comment_text_filter)
31
+ end
32
+
33
+ def feed_title
34
+ "Comment on #{article.title} by #{author}"
35
+ end
36
+
37
+ def send_notifications
38
+ really_send_notifications
39
+ end
40
+
41
+ protected
42
+
43
+ def article_allows_feedback?
44
+ return true if article.allow_comments?
45
+
46
+ errors.add(:article, "Article is not open to comments")
47
+ false
48
+ end
49
+
50
+ def originator
51
+ author
52
+ end
53
+
54
+ def content_fields
55
+ [:body]
56
+ end
57
+ end