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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +3 -0
- data/Rakefile +35 -0
- data/app/assets/fonts/open-sans-bold.woff +0 -0
- data/app/assets/fonts/open-sans.woff +0 -0
- data/app/assets/images/admin/loading.gif +0 -0
- data/app/assets/images/admin/typologo.gif +0 -0
- data/app/assets/images/calendar_date_select/calendar.gif +0 -0
- data/app/assets/images/close.gif +0 -0
- data/app/assets/images/closelabel.gif +0 -0
- data/app/assets/images/go.png +0 -0
- data/app/assets/images/loading.gif +0 -0
- data/app/assets/images/nextlabel.gif +0 -0
- data/app/assets/images/overlay.png +0 -0
- data/app/assets/images/powered.gif +0 -0
- data/app/assets/images/prevlabel.gif +0 -0
- data/app/assets/images/spinner-blue.gif +0 -0
- data/app/assets/images/spinner.gif +0 -0
- data/app/assets/images/thumb_blank.jpg +0 -0
- data/app/assets/javascripts/application.js +2 -0
- data/app/assets/javascripts/cookies.js +60 -0
- data/app/assets/javascripts/datetimepicker.js +1470 -0
- data/app/assets/javascripts/lang/da_DK.js +17 -0
- data/app/assets/javascripts/lang/default.js +21 -0
- data/app/assets/javascripts/lang/en_US.js +21 -0
- data/app/assets/javascripts/lang/fr_FR.js +21 -0
- data/app/assets/javascripts/lang/nl_NL.js +21 -0
- data/app/assets/javascripts/lang/zh_TW.js +17 -0
- data/app/assets/javascripts/lightbox.js +350 -0
- data/app/assets/javascripts/observe.js +28 -0
- data/app/assets/javascripts/publify.js +11 -0
- data/app/assets/javascripts/publify_admin.js +138 -0
- data/app/assets/javascripts/quicktags.js +440 -0
- data/app/assets/javascripts/set-timeago-lang.js +3 -0
- data/app/assets/javascripts/sidebar.js +28 -0
- data/app/assets/javascripts/spinnable.js +5 -0
- data/app/assets/javascripts/tagmanager.js +481 -0
- data/app/assets/javascripts/typeahead.js +1139 -0
- data/app/assets/javascripts/widearea.js +486 -0
- data/app/assets/stylesheets/accounts.css.scss +7 -0
- data/app/assets/stylesheets/administration_structure.css.scss +239 -0
- data/app/assets/stylesheets/coderay.css +135 -0
- data/app/assets/stylesheets/datetimepicker.css +306 -0
- data/app/assets/stylesheets/lightbox.css +63 -0
- data/app/assets/stylesheets/publify.css.scss +9 -0
- data/app/assets/stylesheets/publify_admin.css.scss +11 -0
- data/app/assets/stylesheets/rss.css +53 -0
- data/app/assets/stylesheets/sidebar_admin.css.scss +36 -0
- data/app/assets/stylesheets/tagmanager.css +102 -0
- data/app/assets/stylesheets/user-styles.css +29 -0
- data/app/assets/stylesheets/widearea.css +133 -0
- data/app/controllers/accounts_controller.rb +2 -0
- data/app/controllers/admin/base_controller.rb +41 -0
- data/app/controllers/admin/cache_controller.rb +33 -0
- data/app/controllers/admin/content_controller.rb +174 -0
- data/app/controllers/admin/dashboard_controller.rb +87 -0
- data/app/controllers/admin/feedback_controller.rb +159 -0
- data/app/controllers/admin/migrations_controller.rb +20 -0
- data/app/controllers/admin/notes_controller.rb +69 -0
- data/app/controllers/admin/pages_controller.rb +88 -0
- data/app/controllers/admin/post_types_controller.rb +56 -0
- data/app/controllers/admin/profiles_controller.rb +48 -0
- data/app/controllers/admin/redirects_controller.rb +47 -0
- data/app/controllers/admin/resources_controller.rb +30 -0
- data/app/controllers/admin/seo_controller.rb +45 -0
- data/app/controllers/admin/settings_controller.rb +53 -0
- data/app/controllers/admin/sidebar_controller.rb +66 -0
- data/app/controllers/admin/tags_controller.rb +53 -0
- data/app/controllers/admin/textfilters_controller.rb +6 -0
- data/app/controllers/admin/themes_controller.rb +37 -0
- data/app/controllers/admin/users_controller.rb +65 -0
- data/app/controllers/articles_controller.rb +205 -0
- data/app/controllers/authors_controller.rb +27 -0
- data/app/controllers/base_controller.rb +45 -0
- data/app/controllers/comments_controller.rb +69 -0
- data/app/controllers/content_controller.rb +31 -0
- data/app/controllers/feedback_controller.rb +47 -0
- data/app/controllers/notes_controller.rb +37 -0
- data/app/controllers/setup_controller.rb +62 -0
- data/app/controllers/tags_controller.rb +55 -0
- data/app/controllers/text_controller.rb +9 -0
- data/app/controllers/textfilter_controller.rb +3 -0
- data/app/controllers/theme_controller.rb +59 -0
- data/app/controllers/trackbacks_controller.rb +36 -0
- data/app/controllers/xml_controller.rb +70 -0
- data/app/helpers/admin/base_helper.rb +87 -0
- data/app/helpers/admin/feedback_helper.rb +42 -0
- data/app/helpers/articles_helper.rb +8 -0
- data/app/helpers/authors_helper.rb +39 -0
- data/app/helpers/base_helper.rb +246 -0
- data/app/helpers/blog_helper.rb +12 -0
- data/app/helpers/xml_helper.rb +16 -0
- data/app/mailers/notification_mailer.rb +38 -0
- data/app/models/ability.rb +52 -0
- data/app/models/archives_sidebar.rb +45 -0
- data/app/models/article/factory.rb +56 -0
- data/app/models/article/states.rb +178 -0
- data/app/models/article.rb +321 -0
- data/app/models/blog.rb +290 -0
- data/app/models/blog_sweeper.rb +86 -0
- data/app/models/comment.rb +53 -0
- data/app/models/config_manager.rb +81 -0
- data/app/models/content.rb +138 -0
- data/app/models/content_base.rb +95 -0
- data/app/models/feedback/states.rb +256 -0
- data/app/models/feedback.rb +225 -0
- data/app/models/meta_sidebar.rb +8 -0
- data/app/models/note.rb +144 -0
- data/app/models/page.rb +36 -0
- data/app/models/page_cache.rb +33 -0
- data/app/models/page_sidebar.rb +12 -0
- data/app/models/ping.rb +116 -0
- data/app/models/post_type.rb +15 -0
- data/app/models/redirect.rb +45 -0
- data/app/models/redirection.rb +4 -0
- data/app/models/resource.rb +28 -0
- data/app/models/search_sidebar.rb +7 -0
- data/app/models/sidebar.rb +138 -0
- data/app/models/static_sidebar.rb +20 -0
- data/app/models/tag.rb +63 -0
- data/app/models/tag_sidebar.rb +27 -0
- data/app/models/text_filter.rb +61 -0
- data/app/models/trackback.rb +58 -0
- data/app/models/trigger.rb +45 -0
- data/app/models/user.rb +148 -0
- data/app/services/migrator.rb +25 -0
- data/app/services/title_builder.rb +80 -0
- data/app/uploaders/resource_uploader.rb +30 -0
- data/app/views/accounts/confirm.html.erb +8 -0
- data/app/views/admin/cache/show.html.erb +18 -0
- data/app/views/admin/content/_article_list.html.erb +26 -0
- data/app/views/admin/content/_form.html.erb +165 -0
- data/app/views/admin/content/autosave.js.erb +5 -0
- data/app/views/admin/content/edit.html.erb +3 -0
- data/app/views/admin/content/index.html.erb +48 -0
- data/app/views/admin/content/index.js.erb +1 -0
- data/app/views/admin/content/new.html.erb +3 -0
- data/app/views/admin/dashboard/_comment.html.erb +18 -0
- data/app/views/admin/dashboard/_comments.html.erb +10 -0
- data/app/views/admin/dashboard/_drafts.html.erb +25 -0
- data/app/views/admin/dashboard/_inbound.html.erb +31 -0
- data/app/views/admin/dashboard/_overview.html.erb +23 -0
- data/app/views/admin/dashboard/_welcome.html.erb +28 -0
- data/app/views/admin/dashboard/index.html.erb +17 -0
- data/app/views/admin/feedback/_button.html.erb +19 -0
- data/app/views/admin/feedback/_feedback.html.erb +7 -0
- data/app/views/admin/feedback/_ham.html.erb +17 -0
- data/app/views/admin/feedback/_spam.html.erb +23 -0
- data/app/views/admin/feedback/article.html.erb +69 -0
- data/app/views/admin/feedback/edit.html.erb +48 -0
- data/app/views/admin/feedback/ham.js +1 -0
- data/app/views/admin/feedback/index.html.erb +53 -0
- data/app/views/admin/feedback/spam.js +1 -0
- data/app/views/admin/migrations/show.html.erb +39 -0
- data/app/views/admin/notes/_form.html.erb +37 -0
- data/app/views/admin/notes/_header.html.erb +6 -0
- data/app/views/admin/notes/_list.html.erb +13 -0
- data/app/views/admin/notes/_note.html.erb +14 -0
- data/app/views/admin/notes/edit.html.erb +11 -0
- data/app/views/admin/notes/index.html.erb +11 -0
- data/app/views/admin/notes/show.html.erb +14 -0
- data/app/views/admin/pages/_form.html.erb +101 -0
- data/app/views/admin/pages/_pages.html.erb +21 -0
- data/app/views/admin/pages/edit.html.erb +1 -0
- data/app/views/admin/pages/index.html.erb +17 -0
- data/app/views/admin/pages/new.html.erb +1 -0
- data/app/views/admin/post_types/_index_and_form.html.erb +65 -0
- data/app/views/admin/post_types/edit.html.erb +1 -0
- data/app/views/admin/post_types/index.html.erb +1 -0
- data/app/views/admin/profiles/index.html.erb +10 -0
- data/app/views/admin/redirects/_index_and_form.html.erb +68 -0
- data/app/views/admin/redirects/edit.html.erb +1 -0
- data/app/views/admin/redirects/index.html.erb +1 -0
- data/app/views/admin/resources/index.html.erb +68 -0
- data/app/views/admin/seo/_general.html.erb +123 -0
- data/app/views/admin/seo/_permalinks.html.erb +53 -0
- data/app/views/admin/seo/_titles.html.erb +210 -0
- data/app/views/admin/seo/show.html.erb +32 -0
- data/app/views/admin/settings/display.html.erb +110 -0
- data/app/views/admin/settings/feedback.html.erb +125 -0
- data/app/views/admin/settings/index.html.erb +73 -0
- data/app/views/admin/settings/write.html.erb +87 -0
- data/app/views/admin/shared/_edit.html.erb +4 -0
- data/app/views/admin/shared/_menu.html.erb +122 -0
- data/app/views/admin/shared/_twitter_alert.html.erb +3 -0
- data/app/views/admin/sidebar/_available.html.erb +6 -0
- data/app/views/admin/sidebar/_available.json.erb +6 -0
- data/app/views/admin/sidebar/_config.html.erb +27 -0
- data/app/views/admin/sidebar/_target.html.erb +9 -0
- data/app/views/admin/sidebar/_target_sidebar.html.erb +20 -0
- data/app/views/admin/sidebar/destroy.js.erb +1 -0
- data/app/views/admin/sidebar/index.html.erb +31 -0
- data/app/views/admin/sidebar/sortable.js.erb +3 -0
- data/app/views/admin/sidebar/update.js.erb +2 -0
- data/app/views/admin/tags/_index_and_form.html.erb +59 -0
- data/app/views/admin/tags/edit.html.erb +1 -0
- data/app/views/admin/tags/index.html.erb +1 -0
- data/app/views/admin/themes/index.html.erb +27 -0
- data/app/views/admin/users/_form.html.erb +215 -0
- data/app/views/admin/users/edit.html.erb +8 -0
- data/app/views/admin/users/index.html.erb +39 -0
- data/app/views/admin/users/new.html.erb +8 -0
- data/app/views/archives_sidebar/_content.html.erb +13 -0
- data/app/views/articles/_article.html.erb +9 -0
- data/app/views/articles/_article_collection.html.erb +8 -0
- data/app/views/articles/_article_content.html.erb +5 -0
- data/app/views/articles/_article_excerpt.html.erb +13 -0
- data/app/views/articles/_article_links.html.erb +10 -0
- data/app/views/articles/_comment.html.erb +1 -0
- data/app/views/articles/_comment_errors.html.erb +2 -0
- data/app/views/articles/_comment_form.html.erb +48 -0
- data/app/views/articles/_comment_list.html.erb +5 -0
- data/app/views/articles/_comment_preview.html.erb +4 -0
- data/app/views/articles/_full_article_content.html.erb +2 -0
- data/app/views/articles/_password_form.html.erb +10 -0
- data/app/views/articles/_protected_article_content.html.erb +6 -0
- data/app/views/articles/_trackback.html.erb +6 -0
- data/app/views/articles/archives.html.erb +25 -0
- data/app/views/articles/comment.js.erb +5 -0
- data/app/views/articles/comment_failed.js.erb +3 -0
- data/app/views/articles/error.html.erb +3 -0
- data/app/views/articles/feedback_atom_feed.atom.builder +8 -0
- data/app/views/articles/feedback_rss_feed.rss.builder +21 -0
- data/app/views/articles/index.html.erb +1 -0
- data/app/views/articles/index_atom_feed.atom.builder +8 -0
- data/app/views/articles/index_rss_feed.rss.builder +20 -0
- data/app/views/articles/live_search.html.erb +10 -0
- data/app/views/articles/read.html.erb +61 -0
- data/app/views/articles/search.html.erb +8 -0
- data/app/views/articles/trackback.xml.builder +5 -0
- data/app/views/articles/view_page.html.erb +3 -0
- data/app/views/authors/show.html.erb +40 -0
- data/app/views/authors/show_atom_feed.atom.builder +8 -0
- data/app/views/authors/show_rss_feed.rss.builder +20 -0
- data/app/views/comments/_comment.html.erb +16 -0
- data/app/views/comments/index.html.erb +1 -0
- data/app/views/comments/index_atom_feed.atom.builder +8 -0
- data/app/views/comments/index_rss_feed.rss.builder +20 -0
- data/app/views/comments/preview.html.erb +1 -0
- data/app/views/comments/preview.js.erb +3 -0
- data/app/views/devise/mailer/reset_password_instructions.html.erb +13 -0
- data/app/views/devise/passwords/edit.html.erb +28 -0
- data/app/views/devise/passwords/new.html.erb +20 -0
- data/app/views/devise/registrations/new.html.erb +36 -0
- data/app/views/devise/sessions/new.html.erb +32 -0
- data/app/views/devise/shared/_links.html.erb +15 -0
- data/app/views/errors/404.html.erb +2 -0
- data/app/views/layouts/accounts.html.erb +33 -0
- data/app/views/layouts/administration.html.erb +37 -0
- data/app/views/layouts/default.html.erb +32 -0
- data/app/views/layouts/editor.html.erb +31 -0
- data/app/views/meta_sidebar/_content.html.erb +8 -0
- data/app/views/notes/_note.html.erb +15 -0
- data/app/views/notes/error.html.erb +3 -0
- data/app/views/notes/index.html.erb +15 -0
- data/app/views/notes/show.html.erb +5 -0
- data/app/views/notes/show_in_reply.html.erb +16 -0
- data/app/views/notification_mailer/_mail_footer.html.erb +7 -0
- data/app/views/notification_mailer/_mail_header.html.erb +1 -0
- data/app/views/notification_mailer/article.html.erb +6 -0
- data/app/views/notification_mailer/comment.html.erb +11 -0
- data/app/views/notification_mailer/notif_user.html.erb +14 -0
- data/app/views/page_sidebar/_content.html.erb +12 -0
- data/app/views/search_sidebar/_content.html.erb +10 -0
- data/app/views/settings/done.html.erb +2 -0
- data/app/views/settings/install.html.erb +12 -0
- data/app/views/setup/index.html.erb +13 -0
- data/app/views/shared/_atom_header.atom.builder +6 -0
- data/app/views/shared/_atom_item_article.atom.builder +39 -0
- data/app/views/shared/_atom_item_comment.atom.builder +10 -0
- data/app/views/shared/_atom_item_trackback.atom.builder +9 -0
- data/app/views/shared/_flash.erb +10 -0
- data/app/views/shared/_page_header.html.erb +26 -0
- data/app/views/shared/_rss_item_article.rss.builder +35 -0
- data/app/views/shared/_rss_item_comment.rss.builder +8 -0
- data/app/views/shared/_rss_item_trackback.rss.builder +7 -0
- data/app/views/sidebar/_row.html.erb +1 -0
- data/app/views/sidebar/_sidebar.html.erb +5 -0
- data/app/views/sidebar/display_plugins.html.erb +5 -0
- data/app/views/sidebar/show.html.erb +1 -0
- data/app/views/static_sidebar/_content.html.erb +2 -0
- data/app/views/tag_sidebar/_content.html.erb +10 -0
- data/app/views/tags/index.html.erb +15 -0
- data/app/views/tags/show.html.erb +1 -0
- data/app/views/theme/static_view_test.html.erb +1 -0
- data/app/views/trackbacks/index_atom_feed.atom.builder +7 -0
- data/app/views/trackbacks/index_rss_feed.rss.builder +20 -0
- data/app/views/trackbacks/trackback.xml.builder +4 -0
- data/app/views/xml/_googlesitemap_item_article.googlesitemap.builder +5 -0
- data/app/views/xml/_googlesitemap_item_category.googlesitemap.builder +4 -0
- data/app/views/xml/_googlesitemap_item_page.googlesitemap.builder +4 -0
- data/app/views/xml/_googlesitemap_item_tag.googlesitemap.builder +4 -0
- data/app/views/xml/feed.googlesitemap.builder +7 -0
- data/app/views/xml/rsd.rsd.builder +8 -0
- data/config/i18n-tasks.yml +49 -0
- data/config/initializers/devise.rb +265 -0
- data/config/initializers/mime_types.rb +6 -0
- data/config/locales/da.yml +827 -0
- data/config/locales/de.yml +827 -0
- data/config/locales/en.yml +827 -0
- data/config/locales/es-MX.yml +827 -0
- data/config/locales/fr.yml +827 -0
- data/config/locales/he.yml +827 -0
- data/config/locales/it.yml +827 -0
- data/config/locales/ja.yml +827 -0
- data/config/locales/lt.yml +827 -0
- data/config/locales/nb-NO.yml +827 -0
- data/config/locales/nl.yml +827 -0
- data/config/locales/pl.yml +827 -0
- data/config/locales/pt-BR.yml +827 -0
- data/config/locales/ro.yml +827 -0
- data/config/locales/ru.yml +827 -0
- data/config/locales/sidebars.da.yml +20 -0
- data/config/locales/sidebars.de.yml +20 -0
- data/config/locales/sidebars.en.yml +20 -0
- data/config/locales/sidebars.es-MX.yml +20 -0
- data/config/locales/sidebars.fr.yml +20 -0
- data/config/locales/sidebars.he.yml +20 -0
- data/config/locales/sidebars.it.yml +20 -0
- data/config/locales/sidebars.ja.yml +20 -0
- data/config/locales/sidebars.lt.yml +20 -0
- data/config/locales/sidebars.nb-NO.yml +20 -0
- data/config/locales/sidebars.nl.yml +20 -0
- data/config/locales/sidebars.pl.yml +20 -0
- data/config/locales/sidebars.pt-BR.yml +20 -0
- data/config/locales/sidebars.ro.yml +20 -0
- data/config/locales/sidebars.ru.yml +20 -0
- data/config/locales/sidebars.zh-CN.yml +20 -0
- data/config/locales/sidebars.zh-TW.yml +20 -0
- data/config/locales/zh-CN.yml +827 -0
- data/config/locales/zh-TW.yml +827 -0
- data/config/routes.rb +177 -0
- data/db/migrate/113_initial_schema.rb +205 -0
- data/db/migrate/114_fixes_buggy_articles_and_notes.rb +52 -0
- data/db/migrate/115_drops_categories_for_tags.rb +34 -0
- data/db/migrate/20150207131657_add_missing_indexes.rb +19 -0
- data/db/migrate/20150807134129_simplify_redirect_relations.rb +38 -0
- data/db/migrate/20150808052637_add_blog_ids.rb +33 -0
- data/db/migrate/20150808191127_add_blog_id_to_redirects.rb +15 -0
- data/db/migrate/20150810094754_add_blog_id_to_tags.rb +15 -0
- data/db/migrate/20160108111120_add_devise_to_users.rb +53 -0
- data/db/migrate/20160108184201_move_last_connection_to_last_sign_in_at.rb +16 -0
- data/db/migrate/20160110094906_remove_profiles_rights.rb +14 -0
- data/db/migrate/20160605103918_replace_profile_id_with_string.rb +30 -0
- data/db/migrate/20160605154632_remove_profiles.rb +24 -0
- data/db/migrate/20160701061851_demand_blog_id_on_contents.rb +9 -0
- data/db/migrate/20160701062604_add_blog_id_to_resources.rb +28 -0
- data/db/seeds.rb +37 -0
- data/lib/email_notify.rb +26 -0
- data/lib/format.rb +17 -0
- data/lib/publify_core/engine.rb +23 -0
- data/lib/publify_core/lang.rb +5 -0
- data/lib/publify_core/version.rb +3 -0
- data/lib/publify_core.rb +56 -0
- data/lib/publify_guid.rb +9 -0
- data/lib/publify_plugins.rb +72 -0
- data/lib/publify_textfilter_markdown.rb +44 -0
- data/lib/publify_textfilter_none.rb +14 -0
- data/lib/publify_textfilter_smartypants.rb +14 -0
- data/lib/publify_textfilter_textile.rb +21 -0
- data/lib/publify_textfilter_twitterfilter.rb +33 -0
- data/lib/publify_time.rb +30 -0
- data/lib/sidebar_field.rb +115 -0
- data/lib/sidebar_registry.rb +33 -0
- data/lib/spam_protection.rb +101 -0
- data/lib/stateful.rb +106 -0
- data/lib/tasks/publify_core_tasks.rake +4 -0
- data/lib/text_filter_plugin.rb +182 -0
- data/lib/theme.rb +72 -0
- data/lib/transforms.rb +45 -0
- metadata +865 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
require 'uri'
|
|
3
|
+
require 'net/http'
|
|
4
|
+
|
|
5
|
+
class Article < Content
|
|
6
|
+
include PublifyGuid
|
|
7
|
+
include ConfigManager
|
|
8
|
+
|
|
9
|
+
serialize :settings, Hash
|
|
10
|
+
|
|
11
|
+
content_fields :body, :extended
|
|
12
|
+
|
|
13
|
+
validates :guid, uniqueness: true
|
|
14
|
+
validates :title, presence: true
|
|
15
|
+
|
|
16
|
+
has_many :pings, dependent: :destroy
|
|
17
|
+
has_many :trackbacks, dependent: :destroy
|
|
18
|
+
has_many :feedback
|
|
19
|
+
has_many :resources, dependent: :nullify
|
|
20
|
+
has_many :triggers, as: :pending_item
|
|
21
|
+
has_many :comments, dependent: :destroy
|
|
22
|
+
|
|
23
|
+
has_and_belongs_to_many :tags, join_table: 'articles_tags'
|
|
24
|
+
|
|
25
|
+
before_create :create_guid
|
|
26
|
+
before_save :set_published_at, :set_permalink
|
|
27
|
+
after_save :post_trigger, :keywords_to_tags, :shorten_url
|
|
28
|
+
after_save :send_pings
|
|
29
|
+
after_save :send_notifications
|
|
30
|
+
|
|
31
|
+
scope :drafts, -> { where(state: 'draft').order('created_at DESC') }
|
|
32
|
+
scope :child_of, ->(article_id) { where(parent_id: article_id) }
|
|
33
|
+
scope :published_at, ->(time_params) { published.where(published_at: PublifyTime.delta(*time_params)).order('published_at DESC') }
|
|
34
|
+
scope :published_since, ->(time) { published.where('published_at > ?', time).order('published_at DESC') }
|
|
35
|
+
scope :withdrawn, -> { where(state: 'withdrawn').order('published_at DESC') }
|
|
36
|
+
scope :pending, -> { where('state = ? and published_at > ?', 'publication_pending', Time.now).order('published_at DESC') }
|
|
37
|
+
|
|
38
|
+
scope :bestof, lambda {
|
|
39
|
+
joins(:feedback).
|
|
40
|
+
where('feedback.published' => true, 'feedback.type' => 'Comment',
|
|
41
|
+
'contents.published' => true).
|
|
42
|
+
group('contents.id').
|
|
43
|
+
order('count(feedback.id) DESC').
|
|
44
|
+
select('contents.*, count(feedback.id) as comment_count').
|
|
45
|
+
limit(5)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
setting :password, :string, ''
|
|
49
|
+
|
|
50
|
+
attr_accessor :draft, :keywords
|
|
51
|
+
|
|
52
|
+
include Article::States
|
|
53
|
+
|
|
54
|
+
has_state(:state, valid_states: [:new, :draft,
|
|
55
|
+
:publication_pending, :just_published, :published,
|
|
56
|
+
:just_withdrawn, :withdrawn],
|
|
57
|
+
initial_state: :new,
|
|
58
|
+
handles: [:withdraw,
|
|
59
|
+
:post_trigger,
|
|
60
|
+
:send_pings, :send_notifications,
|
|
61
|
+
:published_at=, :published=, :just_published?])
|
|
62
|
+
|
|
63
|
+
def set_permalink
|
|
64
|
+
return if state == 'draft' || permalink.present?
|
|
65
|
+
self.permalink = title.to_permalink
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def has_child?
|
|
69
|
+
Article.exists?(parent_id: id)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def post_type
|
|
73
|
+
post_type = self[:post_type]
|
|
74
|
+
post_type = 'read' if post_type.blank?
|
|
75
|
+
post_type
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def self.last_draft(article_id)
|
|
79
|
+
article = Article.find(article_id)
|
|
80
|
+
article = Article.child_of(article.id).first while article.has_child?
|
|
81
|
+
article
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def self.search_with(params)
|
|
85
|
+
params ||= {}
|
|
86
|
+
scoped = super(params)
|
|
87
|
+
if %w(no_draft drafts published withdrawn pending).include?(params[:state])
|
|
88
|
+
scoped = scoped.send(params[:state])
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
scoped.order('created_at DESC')
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# FIXME: Use keyword params to clean up call sites.
|
|
95
|
+
def permalink_url(anchor = nil, only_path = false)
|
|
96
|
+
@cached_permalink_url ||= {}
|
|
97
|
+
@cached_permalink_url["#{anchor}#{only_path}"] ||= blog.url_for(permalink_url_options, anchor: anchor, only_path: only_path)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def save_attachments!(files)
|
|
101
|
+
files ||= {}
|
|
102
|
+
files.values.each { |f| save_attachment!(f) }
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def save_attachment!(file)
|
|
106
|
+
resources.create!(upload: file, blog: blog)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def trackback_url
|
|
110
|
+
blog.url_for("trackbacks?article_id=#{id}", only_path: false)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def comment_url
|
|
114
|
+
blog.url_for("comments?article_id=#{id}", only_path: true)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def preview_comment_url
|
|
118
|
+
blog.url_for("comments/preview?article_id=#{id}", only_path: true)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def feed_url(format)
|
|
122
|
+
"#{permalink_url}.#{format.gsub(/\d/, '')}"
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def really_send_pings
|
|
126
|
+
return unless blog.send_outbound_pings
|
|
127
|
+
|
|
128
|
+
blog.urls_to_ping_for(self).each do |url_to_ping|
|
|
129
|
+
begin
|
|
130
|
+
url_to_ping.send_weblogupdatesping(blog.base_url, permalink_url)
|
|
131
|
+
rescue => e
|
|
132
|
+
logger.error(e)
|
|
133
|
+
# in case the remote server doesn't respond or gives an error,
|
|
134
|
+
# we should throw an xmlrpc error here.
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
html_urls_to_ping.each do |url_to_ping|
|
|
139
|
+
begin
|
|
140
|
+
url_to_ping.send_pingback_or_trackback(permalink_url)
|
|
141
|
+
rescue => e
|
|
142
|
+
logger.error(e)
|
|
143
|
+
# in case the remote server doesn't respond or gives an error,
|
|
144
|
+
# we should throw an xmlrpc error here.
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def next
|
|
150
|
+
Article.where('published_at > ?', published_at).order('published_at asc').limit(1).first
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def previous
|
|
154
|
+
Article.where('published_at < ?', published_at).order('published_at desc').limit(1).first
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def self.publication_months
|
|
158
|
+
result = select('published_at').where('published_at is not NULL').where(type: 'Article')
|
|
159
|
+
result.map { |d| [d.published_at.strftime('%Y-%m')] }.uniq
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Finds one article which was posted on a certain date and matches the supplied dashed-title
|
|
163
|
+
# params is a Hash
|
|
164
|
+
def self.requested_article(params)
|
|
165
|
+
date_range = PublifyTime.delta(params[:year], params[:month], params[:day])
|
|
166
|
+
|
|
167
|
+
req_params = {}
|
|
168
|
+
req_params[:permalink] = params[:title] if params[:title]
|
|
169
|
+
req_params[:published_at] = date_range if date_range
|
|
170
|
+
|
|
171
|
+
return if req_params.empty? # no search if no params send
|
|
172
|
+
article = published.find_by(req_params)
|
|
173
|
+
return article if article
|
|
174
|
+
|
|
175
|
+
if params[:title]
|
|
176
|
+
req_params[:permalink] = CGI.escape(params[:title])
|
|
177
|
+
article = published.find_by(req_params)
|
|
178
|
+
return article if article
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Fulltext searches the body of published articles
|
|
183
|
+
def self.search(query, args = {})
|
|
184
|
+
query_s = query.to_s.strip
|
|
185
|
+
if !query_s.empty? && args.empty?
|
|
186
|
+
Article.searchstring(query)
|
|
187
|
+
elsif !query_s.empty? && !args.empty?
|
|
188
|
+
Article.searchstring(query).page(args[:page]).per(args[:per])
|
|
189
|
+
else
|
|
190
|
+
[]
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def keywords_to_tags
|
|
195
|
+
Tag.create_from_article!(self)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def interested_users
|
|
199
|
+
User.where(notify_on_new_articles: true)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def notify_user_via_email(user)
|
|
203
|
+
EmailNotify.send_article(self, user) if user.notify_via_email?
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def comments_closed?
|
|
207
|
+
!(allow_comments? && in_feedback_window?)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def html_urls
|
|
211
|
+
urls = []
|
|
212
|
+
html.gsub(/<a\s+[^>]*>/) do |tag|
|
|
213
|
+
if tag =~ /\bhref=(["']?)([^ >"]+)\1/
|
|
214
|
+
urls.push(Regexp.last_match[2].strip)
|
|
215
|
+
end
|
|
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.blank?
|
|
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 already_ping?(url)
|
|
266
|
+
pings.map(&:url).include?(url)
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def allow_comments?
|
|
270
|
+
return allow_comments unless allow_comments.nil?
|
|
271
|
+
blog.default_allow_comments
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def allow_pings?
|
|
275
|
+
return allow_pings unless allow_pings.nil?
|
|
276
|
+
blog.default_allow_pings
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def published_comments
|
|
280
|
+
comments.published.oldest_first
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def published_trackbacks
|
|
284
|
+
trackbacks.published.oldest_first
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def published_feedback
|
|
288
|
+
feedback.published.oldest_first
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
protected
|
|
292
|
+
|
|
293
|
+
def set_published_at
|
|
294
|
+
if published && self[:published_at].nil?
|
|
295
|
+
self[:published_at] = created_at || Time.now
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
private
|
|
300
|
+
|
|
301
|
+
def permalink_url_options
|
|
302
|
+
format_url = blog.permalink_format.dup
|
|
303
|
+
format_url.gsub!('%year%', published_at.year.to_s)
|
|
304
|
+
format_url.gsub!('%month%', sprintf('%.2d', published_at.month))
|
|
305
|
+
format_url.gsub!('%day%', sprintf('%.2d', published_at.day))
|
|
306
|
+
format_url.gsub!('%title%', URI.encode(permalink.to_s))
|
|
307
|
+
if format_url[0, 1] == '/'
|
|
308
|
+
format_url[1..-1]
|
|
309
|
+
else
|
|
310
|
+
format_url
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def html_urls_to_ping
|
|
315
|
+
urls_to_ping = []
|
|
316
|
+
html_urls.delete_if { |url| already_ping?(url) }.uniq.each do |url_to_ping|
|
|
317
|
+
urls_to_ping << pings.build('url' => url_to_ping)
|
|
318
|
+
end
|
|
319
|
+
urls_to_ping
|
|
320
|
+
end
|
|
321
|
+
end
|
data/app/models/blog.rb
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# The Blog class represents the one and only blog. It stores most
|
|
2
|
+
# configuration settings and is linked to most of the assorted content
|
|
3
|
+
# classes via has_many.
|
|
4
|
+
#
|
|
5
|
+
# Once upon a time, there were plans to make publify handle multiple blogs,
|
|
6
|
+
# but it never happened and publify is now firmly single-blog.
|
|
7
|
+
#
|
|
8
|
+
class Blog < ActiveRecord::Base
|
|
9
|
+
include ConfigManager
|
|
10
|
+
include Rails.application.routes.url_helpers
|
|
11
|
+
|
|
12
|
+
has_many :contents
|
|
13
|
+
has_many :articles
|
|
14
|
+
has_many :feedback, through: :articles
|
|
15
|
+
|
|
16
|
+
has_many(:published_articles,
|
|
17
|
+
->() { includes(:tags).where(published: true).order('contents.published_at DESC') },
|
|
18
|
+
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
|
+
setting :title_prefix, :integer, 0 # Deprecated but needed for a migration
|
|
42
|
+
|
|
43
|
+
# Spam
|
|
44
|
+
setting :sp_global, :boolean, false
|
|
45
|
+
setting :sp_article_auto_close, :integer, 0
|
|
46
|
+
setting :sp_url_limit, :integer, 0
|
|
47
|
+
setting :sp_akismet_key, :string, ''
|
|
48
|
+
setting :use_recaptcha, :boolean, false
|
|
49
|
+
|
|
50
|
+
# Mostly Behaviour
|
|
51
|
+
setting :text_filter, :string, 'markdown smartypants'
|
|
52
|
+
setting :comment_text_filter, :string, 'markdown smartypants'
|
|
53
|
+
setting :limit_article_display, :integer, 10
|
|
54
|
+
setting :limit_archives_display, :integer, 20
|
|
55
|
+
setting :limit_rss_display, :integer, 10
|
|
56
|
+
setting :default_allow_pings, :boolean, false
|
|
57
|
+
setting :default_allow_comments, :boolean, true
|
|
58
|
+
setting :default_moderate_comments, :boolean, false
|
|
59
|
+
setting :link_to_author, :boolean, false
|
|
60
|
+
setting :show_extended_on_rss, :boolean, true # deprecated but still needed for backward compatibility
|
|
61
|
+
setting :hide_extended_on_rss, :boolean, false
|
|
62
|
+
setting :theme, :string, 'plain'
|
|
63
|
+
setting :plugin_avatar, :string, ''
|
|
64
|
+
setting :global_pings_disable, :boolean, false
|
|
65
|
+
setting :ping_urls, :string, "http://blogsearch.google.com/ping/RPC2\nhttp://rpc.technorati.com/rpc/ping\nhttp://ping.blo.gs/\nhttp://rpc.weblogs.com/RPC2"
|
|
66
|
+
setting :send_outbound_pings, :boolean, true
|
|
67
|
+
setting :email_from, :string, 'publify@example.com'
|
|
68
|
+
setting :allow_signup, :integer, 0
|
|
69
|
+
setting :date_format, :string, '%d/%m/%Y'
|
|
70
|
+
setting :time_format, :string, '%Hh%M'
|
|
71
|
+
setting :image_avatar_size, :integer, 48
|
|
72
|
+
setting :image_thumb_size, :integer, 125
|
|
73
|
+
setting :image_medium_size, :integer, 600
|
|
74
|
+
|
|
75
|
+
# SEO
|
|
76
|
+
setting :meta_description, :string, ''
|
|
77
|
+
setting :meta_keywords, :string, ''
|
|
78
|
+
setting :google_analytics, :string, ''
|
|
79
|
+
setting :feedburner_url, :string, ''
|
|
80
|
+
setting :rss_description, :boolean, false
|
|
81
|
+
setting :rss_description_text, :string, <<EOS
|
|
82
|
+
<hr />
|
|
83
|
+
<p><small>Original article written by %author% and published on <a href='%blog_url%'>%blog_name%</a>
|
|
84
|
+
| <a href='%permalink_url%'>direct link to this article</a>
|
|
85
|
+
| If you are reading this article anywhere other than on <a href='%blog_url%'>%blog_name%</a>,
|
|
86
|
+
it has been illegally reproduced and without proper authorization.</small></p>
|
|
87
|
+
EOS
|
|
88
|
+
setting :permalink_format, :string, '/%year%/%month%/%day%/%title%'
|
|
89
|
+
setting :robots, :string, 'User-agent: *\nAllow: /\nDisallow: /admin\n'
|
|
90
|
+
setting :humans, :string, <<EOS
|
|
91
|
+
/* TEAM */
|
|
92
|
+
Your title: Your name.
|
|
93
|
+
Site: email, link to a contact form, etc.
|
|
94
|
+
Twitter: your Twitter username.
|
|
95
|
+
|
|
96
|
+
/* SITE */
|
|
97
|
+
Software: Publify [http://publify.co] #{PublifyCore::VERSION}
|
|
98
|
+
EOS
|
|
99
|
+
setting :index_categories, :boolean, true # deprecated but still needed for backward compatibility
|
|
100
|
+
setting :unindex_categories, :boolean, false
|
|
101
|
+
setting :index_tags, :boolean, true # deprecated but still needed for backward compatibility
|
|
102
|
+
setting :unindex_tags, :boolean, false
|
|
103
|
+
setting :admin_display_elements, :integer, 10
|
|
104
|
+
setting :google_verification, :string, ''
|
|
105
|
+
setting :nofollowify, :boolean, true # deprecated but still needed for backward compatibility
|
|
106
|
+
setting :dofollowify, :boolean, false
|
|
107
|
+
setting :use_canonical_url, :boolean, false
|
|
108
|
+
setting :use_meta_keyword, :boolean, true
|
|
109
|
+
setting :home_title_template, :string, '%blog_name% | %blog_subtitle%' # spec OK
|
|
110
|
+
setting :home_desc_template, :string, '%blog_name% | %blog_subtitle% | %meta_keywords%' # OK
|
|
111
|
+
setting :article_title_template, :string, '%title% | %blog_name%'
|
|
112
|
+
setting :article_desc_template, :string, '%excerpt%'
|
|
113
|
+
setting :page_title_template, :string, '%title% | %blog_name%'
|
|
114
|
+
setting :page_desc_template, :string, '%excerpt%'
|
|
115
|
+
setting :paginated_title_template, :string, '%blog_name% | %blog_subtitle% %page%'
|
|
116
|
+
setting :paginated_desc_template, :string, '%blog_name% | %blog_subtitle% | %meta_keywords% %page%'
|
|
117
|
+
setting :tag_title_template, :string, 'Tag: %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, 'Archives for %blog_name% %date% %page% %blog_subtitle%'
|
|
123
|
+
setting :search_title_template, :string, 'Results for %search% | %blog_name% %page%'
|
|
124
|
+
setting :search_desc_template, :string, 'Results for %search% | %blog_name% | %blog_subtitle% %page%'
|
|
125
|
+
setting :statuses_title_template, :string, 'Notes | %blog_name% %page%'
|
|
126
|
+
setting :statuses_desc_template, :string, 'Notes | %blog_name% | %blog_subtitle% %page%'
|
|
127
|
+
setting :status_title_template, :string, '%body% | %blog_name%'
|
|
128
|
+
setting :status_desc_template, :string, '%excerpt%'
|
|
129
|
+
|
|
130
|
+
setting :custom_tracking_field, :string, ''
|
|
131
|
+
# setting :meta_author_template, :string, "%blog_name% | %nickname%"
|
|
132
|
+
|
|
133
|
+
setting :twitter_consumer_key, :string, ''
|
|
134
|
+
setting :twitter_consumer_secret, :string, ''
|
|
135
|
+
setting :custom_url_shortener, :string, ''
|
|
136
|
+
setting :statuses_in_timeline, :boolean, true
|
|
137
|
+
|
|
138
|
+
validate :permalink_has_identifier
|
|
139
|
+
# validates :base_url, presence: true
|
|
140
|
+
|
|
141
|
+
# Find the Blog that matches a specific base URL. If no Blog object is found
|
|
142
|
+
# that matches, then grab the first blog. If *that* fails, then create a new
|
|
143
|
+
# Blog. The last case should only be used when Publify is first installed.
|
|
144
|
+
def self.find_blog(base_url)
|
|
145
|
+
Blog.find_by(base_url: base_url) || Blog.first || Blog.new
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# In settings with :article_id
|
|
149
|
+
def ping_article!(settings)
|
|
150
|
+
unless global_pings_enabled? && settings.key?(:url) && settings.key?(:article_id)
|
|
151
|
+
throw :error, 'Invalid trackback or trackbacks not enabled'
|
|
152
|
+
end
|
|
153
|
+
article = Article.find(settings[:article_id])
|
|
154
|
+
throw :error, 'Trackback not saved' unless article.allow_pings?
|
|
155
|
+
article.trackbacks.create!(settings)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def global_pings_enabled?
|
|
159
|
+
!global_pings_disable?
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Check that all required blog settings have a value.
|
|
163
|
+
def configured?
|
|
164
|
+
settings.key?('blog_name')
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# The +Theme+ object for the current theme.
|
|
168
|
+
def current_theme(reload = nil)
|
|
169
|
+
@current_theme = nil if reload
|
|
170
|
+
@current_theme ||= Theme.find(theme) || Theme.new('', '')
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Generate a URL based on the +base_url+. This allows us to generate URLs
|
|
174
|
+
# without needing a controller handy, so we can produce URLs from within models
|
|
175
|
+
# where appropriate.
|
|
176
|
+
#
|
|
177
|
+
# It also caches the result in the Rails cache, so repeated URL generation
|
|
178
|
+
# requests should be fast, as they bypass all of Rails' route logic.
|
|
179
|
+
def url_for_with_base_url(options = {}, extra_params = {})
|
|
180
|
+
case options
|
|
181
|
+
when String
|
|
182
|
+
url_generated = if extra_params[:only_path]
|
|
183
|
+
root_path
|
|
184
|
+
else
|
|
185
|
+
base_url
|
|
186
|
+
end
|
|
187
|
+
url_generated += "/#{options}" # They asked for 'url_for "/some/path"', so return it unedited.
|
|
188
|
+
url_generated += "##{extra_params[:anchor]}" if extra_params[:anchor]
|
|
189
|
+
url_generated
|
|
190
|
+
when Hash
|
|
191
|
+
merged_opts = options.reverse_merge!(only_path: false, controller: '',
|
|
192
|
+
action: 'permalink',
|
|
193
|
+
host: host_with_port,
|
|
194
|
+
script_name: root_path)
|
|
195
|
+
cache_key = merged_opts.values.prepend('blog-urlfor-withbaseurl').join('-')
|
|
196
|
+
unless Rails.cache.exist?(cache_key)
|
|
197
|
+
Rails.cache.write(cache_key, url_for_without_base_url(merged_opts))
|
|
198
|
+
end
|
|
199
|
+
Rails.cache.read(cache_key)
|
|
200
|
+
else
|
|
201
|
+
raise "Invalid URL in url_for: #{options.inspect}"
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
alias_method_chain :url_for, :base_url
|
|
206
|
+
|
|
207
|
+
# The URL for a static file.
|
|
208
|
+
# FIXME: Let carrierwave handle this by itself
|
|
209
|
+
def file_url(filename)
|
|
210
|
+
if CarrierWave.configure { |config| config.storage == CarrierWave::Storage::Fog }
|
|
211
|
+
filename
|
|
212
|
+
else
|
|
213
|
+
url_for "files/#{filename}", only_path: false
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def articles_matching(query, args = {})
|
|
218
|
+
Article.search(query, args)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def per_page(format)
|
|
222
|
+
return limit_article_display if format.nil? || format == 'html'
|
|
223
|
+
limit_rss_display
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def rss_limit_params
|
|
227
|
+
limit = limit_rss_display.to_i
|
|
228
|
+
if limit.zero?
|
|
229
|
+
{}
|
|
230
|
+
else
|
|
231
|
+
{ limit: limit }
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def permalink_has_identifier
|
|
236
|
+
unless permalink_format =~ /(%title%)/
|
|
237
|
+
errors.add(:base, I18n.t('errors.permalink_need_a_title'))
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
if permalink_format =~ /\.(atom|rss)$/
|
|
241
|
+
errors.add(:permalink_format, I18n.t('errors.cant_end_with_rss_or_atom'))
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def root_path
|
|
246
|
+
split_base_url[:root_path]
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def text_filter_object
|
|
250
|
+
TextFilter.find_or_default(text_filter)
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def urls_to_ping_for(article)
|
|
254
|
+
urls_to_ping = []
|
|
255
|
+
ping_urls.gsub(/ +/, '').split(/[\n\r]+/).map(&:strip).delete_if { |u| article.already_ping?(u) }.uniq.each do |url|
|
|
256
|
+
urls_to_ping << article.pings.build('url' => url)
|
|
257
|
+
end
|
|
258
|
+
urls_to_ping
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def has_twitter_configured?
|
|
262
|
+
return false if twitter_consumer_key.nil? || twitter_consumer_secret.nil?
|
|
263
|
+
return false if twitter_consumer_key.empty? || twitter_consumer_secret.empty?
|
|
264
|
+
true
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def allow_signup?
|
|
268
|
+
allow_signup == 1
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def shortener_url
|
|
272
|
+
custom_url_shortener.present? ? custom_url_shortener : base_url
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
private
|
|
276
|
+
|
|
277
|
+
def host_with_port
|
|
278
|
+
split_base_url[:host_with_port]
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def split_base_url
|
|
282
|
+
unless @split_base_url
|
|
283
|
+
unless base_url =~ /(https?):\/\/([^\/]*)(.*)/
|
|
284
|
+
raise "Invalid base_url: #{base_url}"
|
|
285
|
+
end
|
|
286
|
+
@split_base_url = { protocol: Regexp.last_match[1], host_with_port: Regexp.last_match[2], root_path: Regexp.last_match[3].gsub(%r{/$}, '') }
|
|
287
|
+
end
|
|
288
|
+
@split_base_url
|
|
289
|
+
end
|
|
290
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
class BlogSweeper < ActionController::Caching::Sweeper
|
|
2
|
+
observe Blog, User, Article, Page, Comment, Trackback, Note, Tag
|
|
3
|
+
|
|
4
|
+
def pending_sweeps
|
|
5
|
+
@pending_sweeps ||= Set.new
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def run_pending_page_sweeps
|
|
9
|
+
pending_sweeps.each do |each|
|
|
10
|
+
send(each)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def after_comments_create
|
|
15
|
+
expire_for(controller.send(:instance_variable_get, :@comment))
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
alias after_comments_update after_comments_create
|
|
19
|
+
alias after_articles_comment after_comments_create
|
|
20
|
+
|
|
21
|
+
def after_comments_destroy
|
|
22
|
+
expire_for(controller.send(:instance_variable_get, :@comment), true)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
alias after_articles_nuke_comment after_comments_destroy
|
|
26
|
+
|
|
27
|
+
def after_articles_trackback
|
|
28
|
+
expire_for(controller.send(:instance_variable_get, :@trackback))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def after_articles_nuke_trackback
|
|
32
|
+
expire_for(controller.send(:instance_variable_get, :@trackback), true)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def after_save(record)
|
|
36
|
+
expire_for(record) unless record.is_a?(Article) && record.state == :draft
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def after_destroy(record)
|
|
40
|
+
expire_for(record, true)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# TODO: Simplify this. Almost every sweep amounts to a sweep_all.
|
|
44
|
+
def expire_for(record, destroying = false)
|
|
45
|
+
case record
|
|
46
|
+
when Page
|
|
47
|
+
pending_sweeps << :sweep_pages
|
|
48
|
+
when Content
|
|
49
|
+
if record.invalidates_cache?(destroying)
|
|
50
|
+
pending_sweeps << :sweep_articles << :sweep_pages
|
|
51
|
+
end
|
|
52
|
+
when Tag
|
|
53
|
+
pending_sweeps << :sweep_articles << :sweep_pages
|
|
54
|
+
when Blog, User, Comment, Trackback
|
|
55
|
+
pending_sweeps << :sweep_all << :sweep_theme
|
|
56
|
+
end
|
|
57
|
+
run_pending_page_sweeps unless controller
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def sweep_all
|
|
61
|
+
PageCache.sweep_all
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def sweep_theme
|
|
65
|
+
PageCache.sweep_theme_cache
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def sweep_articles
|
|
69
|
+
PageCache.sweep_all
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def sweep_pages
|
|
73
|
+
PageCache.zap_pages(%w(pages))
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def logger
|
|
77
|
+
@logger ||= ::Rails.logger || Logger.new(STDERR)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def callback(timing)
|
|
83
|
+
super
|
|
84
|
+
run_pending_page_sweeps if timing == :after
|
|
85
|
+
end
|
|
86
|
+
end
|