publify_core 10.0.4 → 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +243 -53
  3. data/app/assets/javascripts/publify.js +0 -1
  4. data/app/assets/javascripts/publify_admin.js +10 -9
  5. data/app/assets/stylesheets/accounts.css.scss +0 -2
  6. data/app/assets/stylesheets/administration_structure.css.scss +0 -4
  7. data/app/assets/stylesheets/publify.css.scss +0 -1
  8. data/app/assets/stylesheets/publify_admin.css.scss +0 -2
  9. data/app/assets/stylesheets/sidebar_admin.css.scss +0 -6
  10. data/app/controllers/admin/articles_controller.rb +1 -1
  11. data/app/controllers/admin/base_controller.rb +2 -2
  12. data/app/controllers/admin/redirects_controller.rb +2 -2
  13. data/app/controllers/admin/resources_controller.rb +1 -1
  14. data/app/controllers/admin/seo_controller.rb +2 -2
  15. data/app/controllers/admin/settings_controller.rb +2 -2
  16. data/app/controllers/admin/tags_controller.rb +1 -1
  17. data/app/controllers/admin/users_controller.rb +2 -2
  18. data/app/controllers/articles_controller.rb +2 -5
  19. data/app/controllers/authors_controller.rb +0 -2
  20. data/app/controllers/comments_controller.rb +5 -2
  21. data/app/controllers/content_controller.rb +0 -4
  22. data/app/controllers/notes_controller.rb +1 -6
  23. data/app/controllers/setup_controller.rb +1 -1
  24. data/app/controllers/tags_controller.rb +0 -1
  25. data/app/controllers/users/registrations_controller.rb +1 -0
  26. data/app/helpers/admin/base_helper.rb +20 -13
  27. data/app/helpers/admin/feedback_helper.rb +4 -4
  28. data/app/helpers/base_helper.rb +10 -6
  29. data/app/helpers/xml_helper.rb +4 -0
  30. data/app/models/article.rb +7 -7
  31. data/app/models/blog.rb +3 -3
  32. data/app/models/comment.rb +1 -1
  33. data/app/models/config_manager.rb +1 -3
  34. data/app/models/content.rb +4 -5
  35. data/app/models/content_base.rb +4 -3
  36. data/app/models/feedback.rb +1 -1
  37. data/app/models/note.rb +74 -8
  38. data/app/models/page.rb +1 -1
  39. data/app/models/redirect.rb +0 -4
  40. data/app/models/resource.rb +1 -0
  41. data/app/models/sidebar.rb +6 -14
  42. data/app/models/tag.rb +2 -2
  43. data/app/models/text_filter.rb +2 -2
  44. data/app/models/trigger.rb +1 -1
  45. data/app/models/user.rb +12 -21
  46. data/app/uploaders/resource_uploader.rb +1 -0
  47. data/app/views/admin/articles/_form.html.erb +99 -105
  48. data/app/views/admin/articles/index.html.erb +15 -23
  49. data/app/views/admin/dashboard/_comment.html.erb +1 -1
  50. data/app/views/admin/dashboard/_comments.html.erb +4 -6
  51. data/app/views/admin/dashboard/_drafts.html.erb +4 -6
  52. data/app/views/admin/dashboard/_overview.html.erb +2 -5
  53. data/app/views/admin/dashboard/_welcome.html.erb +19 -19
  54. data/app/views/admin/dashboard/index.html.erb +3 -3
  55. data/app/views/admin/feedback/_button.html.erb +7 -13
  56. data/app/views/admin/feedback/article.html.erb +17 -17
  57. data/app/views/admin/feedback/edit.html.erb +17 -21
  58. data/app/views/admin/feedback/index.html.erb +8 -8
  59. data/app/views/admin/notes/_form.html.erb +5 -5
  60. data/app/views/admin/notes/_list.html.erb +1 -1
  61. data/app/views/admin/notes/_note.html.erb +1 -1
  62. data/app/views/admin/notes/edit.html.erb +1 -1
  63. data/app/views/admin/notes/index.html.erb +1 -1
  64. data/app/views/admin/pages/_form.html.erb +23 -27
  65. data/app/views/admin/post_types/_index_and_form.html.erb +5 -5
  66. data/app/views/admin/redirects/_index_and_form.html.erb +2 -2
  67. data/app/views/admin/resources/index.html.erb +20 -8
  68. data/app/views/admin/seo/_general.html.erb +36 -36
  69. data/app/views/admin/seo/_permalinks.html.erb +22 -14
  70. data/app/views/admin/seo/_titles.html.erb +63 -63
  71. data/app/views/admin/settings/display.html.erb +23 -23
  72. data/app/views/admin/settings/feedback.html.erb +29 -29
  73. data/app/views/admin/settings/index.html.erb +21 -21
  74. data/app/views/admin/settings/write.html.erb +18 -18
  75. data/app/views/admin/shared/_flash.html.erb +6 -0
  76. data/app/views/admin/shared/_menu.html.erb +56 -68
  77. data/app/views/admin/sidebar/_config.html.erb +15 -19
  78. data/app/views/admin/sidebar/_target.html.erb +3 -3
  79. data/app/views/admin/sidebar/_target_sidebar.html.erb +14 -13
  80. data/app/views/admin/sidebar/index.html.erb +2 -2
  81. data/app/views/admin/tags/_index_and_form.html.erb +3 -3
  82. data/app/views/admin/themes/index.html.erb +21 -19
  83. data/app/views/admin/users/_form.html.erb +79 -79
  84. data/app/views/articles/search.html.erb +1 -1
  85. data/app/views/layouts/accounts.html.erb +7 -9
  86. data/app/views/layouts/administration.html.erb +2 -2
  87. data/app/views/notes/show.html.erb +15 -0
  88. data/app/views/setup/index.html.erb +1 -1
  89. data/app/views/shared/_atom_item_article.atom.builder +1 -3
  90. data/app/views/shared/_rss_item_article.rss.builder +1 -2
  91. data/config/i18n-tasks.yml +0 -1
  92. data/config/locales/ar.yml +3 -2
  93. data/config/locales/da.yml +3 -2
  94. data/config/locales/de.yml +3 -2
  95. data/config/locales/en.yml +3 -2
  96. data/config/locales/es-MX.yml +3 -2
  97. data/config/locales/fr.yml +3 -2
  98. data/config/locales/he.yml +3 -2
  99. data/config/locales/it.yml +3 -2
  100. data/config/locales/ja.yml +3 -2
  101. data/config/locales/lt.yml +3 -2
  102. data/config/locales/nb.yml +3 -2
  103. data/config/locales/nl.yml +3 -2
  104. data/config/locales/pl.yml +3 -2
  105. data/config/locales/pt-BR.yml +3 -2
  106. data/config/locales/ro.yml +3 -2
  107. data/config/locales/ru.yml +3 -2
  108. data/config/locales/zh-CN.yml +3 -2
  109. data/config/locales/zh-TW.yml +3 -2
  110. data/config/routes.rb +0 -11
  111. data/db/migrate/114_fixes_buggy_articles_and_notes.rb +2 -2
  112. data/db/migrate/20150807134129_simplify_redirect_relations.rb +1 -1
  113. data/db/migrate/20160605154632_remove_profiles.rb +1 -1
  114. data/db/migrate/20190208152646_move_text_filter_to_name.rb +2 -2
  115. data/lib/format.rb +2 -2
  116. data/lib/publify_core/engine.rb +10 -0
  117. data/lib/publify_core/string_ext.rb +1 -13
  118. data/lib/publify_core/text_filter/markdown.rb +5 -1
  119. data/lib/publify_core/text_filter/markdown_smartquotes.rb +6 -1
  120. data/lib/publify_core/version.rb +1 -1
  121. data/lib/publify_core.rb +2 -2
  122. data/lib/text_filter_plugin.rb +20 -22
  123. data/lib/theme.rb +56 -37
  124. metadata +100 -397
  125. data/app/assets/javascripts/bootstrap/affix.js +0 -164
  126. data/app/assets/javascripts/bootstrap/alert.js +0 -95
  127. data/app/assets/javascripts/bootstrap/button.js +0 -125
  128. data/app/assets/javascripts/bootstrap/collapse.js +0 -212
  129. data/app/assets/javascripts/bootstrap/dropdown.js +0 -165
  130. data/app/assets/javascripts/bootstrap/modal.js +0 -358
  131. data/app/assets/javascripts/bootstrap/tab.js +0 -155
  132. data/app/assets/javascripts/bootstrap/transition.js +0 -59
  133. data/app/assets/javascripts/bootstrap-sprockets.js +0 -8
  134. data/app/assets/javascripts/lightbox.js +0 -350
  135. data/app/assets/stylesheets/_bootstrap-compass.scss +0 -9
  136. data/app/assets/stylesheets/_bootstrap-mincer.scss +0 -19
  137. data/app/assets/stylesheets/_bootstrap-sprockets.scss +0 -9
  138. data/app/assets/stylesheets/_bootstrap.scss +0 -42
  139. data/app/assets/stylesheets/bootstrap/_alerts.scss +0 -73
  140. data/app/assets/stylesheets/bootstrap/_button-groups.scss +0 -242
  141. data/app/assets/stylesheets/bootstrap/_buttons.scss +0 -168
  142. data/app/assets/stylesheets/bootstrap/_close.scss +0 -37
  143. data/app/assets/stylesheets/bootstrap/_code.scss +0 -69
  144. data/app/assets/stylesheets/bootstrap/_component-animations.scss +0 -38
  145. data/app/assets/stylesheets/bootstrap/_dropdowns.scss +0 -213
  146. data/app/assets/stylesheets/bootstrap/_forms.scss +0 -586
  147. data/app/assets/stylesheets/bootstrap/_grid.scss +0 -94
  148. data/app/assets/stylesheets/bootstrap/_labels.scss +0 -66
  149. data/app/assets/stylesheets/bootstrap/_media.scss +0 -66
  150. data/app/assets/stylesheets/bootstrap/_mixins.scss +0 -37
  151. data/app/assets/stylesheets/bootstrap/_modals.scss +0 -150
  152. data/app/assets/stylesheets/bootstrap/_navbar.scss +0 -479
  153. data/app/assets/stylesheets/bootstrap/_navs.scss +0 -242
  154. data/app/assets/stylesheets/bootstrap/_normalize.scss +0 -427
  155. data/app/assets/stylesheets/bootstrap/_pagination.scss +0 -86
  156. data/app/assets/stylesheets/bootstrap/_panels.scss +0 -222
  157. data/app/assets/stylesheets/bootstrap/_print.scss +0 -99
  158. data/app/assets/stylesheets/bootstrap/_scaffolding.scss +0 -160
  159. data/app/assets/stylesheets/bootstrap/_tables.scss +0 -234
  160. data/app/assets/stylesheets/bootstrap/_theme.scss +0 -224
  161. data/app/assets/stylesheets/bootstrap/_type.scss +0 -296
  162. data/app/assets/stylesheets/bootstrap/_utilities.scss +0 -55
  163. data/app/assets/stylesheets/bootstrap/_variables.scss +0 -638
  164. data/app/assets/stylesheets/bootstrap/_wells.scss +0 -29
  165. data/app/assets/stylesheets/bootstrap/mixins/_alerts.scss +0 -15
  166. data/app/assets/stylesheets/bootstrap/mixins/_background-variant.scss +0 -12
  167. data/app/assets/stylesheets/bootstrap/mixins/_border-radius.scss +0 -18
  168. data/app/assets/stylesheets/bootstrap/mixins/_buttons.scss +0 -56
  169. data/app/assets/stylesheets/bootstrap/mixins/_center-block.scss +0 -7
  170. data/app/assets/stylesheets/bootstrap/mixins/_clearfix.scss +0 -22
  171. data/app/assets/stylesheets/bootstrap/mixins/_forms.scss +0 -82
  172. data/app/assets/stylesheets/bootstrap/mixins/_gradients.scss +0 -56
  173. data/app/assets/stylesheets/bootstrap/mixins/_grid-framework.scss +0 -81
  174. data/app/assets/stylesheets/bootstrap/mixins/_grid.scss +0 -122
  175. data/app/assets/stylesheets/bootstrap/mixins/_hide-text.scss +0 -21
  176. data/app/assets/stylesheets/bootstrap/mixins/_image.scss +0 -28
  177. data/app/assets/stylesheets/bootstrap/mixins/_labels.scss +0 -12
  178. data/app/assets/stylesheets/bootstrap/mixins/_nav-divider.scss +0 -10
  179. data/app/assets/stylesheets/bootstrap/mixins/_nav-vertical-align.scss +0 -9
  180. data/app/assets/stylesheets/bootstrap/mixins/_opacity.scss +0 -7
  181. data/app/assets/stylesheets/bootstrap/mixins/_pagination.scss +0 -24
  182. data/app/assets/stylesheets/bootstrap/mixins/_panels.scss +0 -20
  183. data/app/assets/stylesheets/bootstrap/mixins/_reset-filter.scss +0 -8
  184. data/app/assets/stylesheets/bootstrap/mixins/_resize.scss +0 -6
  185. data/app/assets/stylesheets/bootstrap/mixins/_responsive-visibility.scss +0 -17
  186. data/app/assets/stylesheets/bootstrap/mixins/_size.scss +0 -10
  187. data/app/assets/stylesheets/bootstrap/mixins/_tab-focus.scss +0 -9
  188. data/app/assets/stylesheets/bootstrap/mixins/_table-row.scss +0 -28
  189. data/app/assets/stylesheets/bootstrap/mixins/_text-emphasis.scss +0 -12
  190. data/app/assets/stylesheets/bootstrap/mixins/_text-overflow.scss +0 -8
  191. data/app/assets/stylesheets/bootstrap/mixins/_vendor-prefixes.scss +0 -222
  192. data/app/assets/stylesheets/lightbox.css +0 -63
  193. data/app/controllers/theme_controller.rb +0 -63
  194. data/app/views/notes/show_in_reply.html.erb +0 -18
  195. data/app/views/theme/static_view_test.html.erb +0 -1
  196. data/lib/publify_core/text_filter/twitterfilter.rb +0 -55
  197. data/lib/tasks/manifest.rake +0 -30
  198. /data/app/views/layouts/{default.html.erb → application.html.erb} +0 -0
  199. /data/themes/plain/javascripts/{theme.js → plain/theme.js} +0 -0
  200. /data/themes/plain/stylesheets/{theme.css → plain/theme.css} +0 -0
@@ -3,8 +3,6 @@
3
3
  class NotesController < ContentController
4
4
  require "json"
5
5
 
6
- layout :theme_layout
7
-
8
6
  after_action :set_blog_infos
9
7
 
10
8
  def index
@@ -19,10 +17,7 @@ class NotesController < ContentController
19
17
  def show
20
18
  @note = Note.published.find_by! permalink: CGI.escape(params[:permalink])
21
19
 
22
- if @note.in_reply_to_message.present?
23
- @reply = JSON.parse(@note.in_reply_to_message)
24
- render :show_in_reply
25
- end
20
+ @reply = JSON.parse(@note.in_reply_to_message) if @note.in_reply_to_message.present?
26
21
  end
27
22
 
28
23
  private
@@ -25,7 +25,7 @@ class SetupController < BaseController
25
25
 
26
26
  sign_in @user
27
27
 
28
- if User.count == 1
28
+ if User.one?
29
29
  create_first_post @user
30
30
  create_first_page @user
31
31
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  class TagsController < ContentController
4
4
  before_action :auto_discovery_feed, only: [:show, :index]
5
- layout :theme_layout
6
5
 
7
6
  def index
8
7
  @tags = Tag.page(params[:page]).per(100)
@@ -2,6 +2,7 @@
2
2
 
3
3
  class Users::RegistrationsController < Devise::RegistrationsController
4
4
  include BlogHelper
5
+
5
6
  before_action :require_signup_allowed
6
7
 
7
8
  private
@@ -3,8 +3,8 @@
3
3
  module Admin::BaseHelper
4
4
  include ActionView::Helpers::DateHelper
5
5
 
6
- def toggle_element(element, label = t("generic.change"))
7
- link_to label, "##{element}", data: { toggle: :collapse }
6
+ def toggle_element(element, label = t("generic.change"), **)
7
+ link_to(label, "##{element}", data: { "bs-toggle": :collapse }, **)
8
8
  end
9
9
 
10
10
  def class_for_admin_state(sidebar, this_position)
@@ -35,15 +35,13 @@ module Admin::BaseHelper
35
35
  end
36
36
 
37
37
  def show_actions(item)
38
- tag.div(class: "action", style: "") do
39
- safe_join [button_to_edit(item),
40
- button_to_delete(item),
41
- button_to_short_url(item)], " "
42
- end
38
+ safe_join [button_to_edit(item),
39
+ button_to_delete(item),
40
+ button_to_short_url(item)], " "
43
41
  end
44
42
 
45
43
  def display_pagination(collection, cols, _first = "", _last = "")
46
- return if collection.count == 0
44
+ return if collection.none?
47
45
 
48
46
  tag.tr do
49
47
  tag.td(paginate(collection), class: "paginate", colspan: cols)
@@ -53,7 +51,7 @@ module Admin::BaseHelper
53
51
  def button_to_edit(item)
54
52
  link_to(t("generic.edit"),
55
53
  { action: "edit", id: item.id },
56
- { class: "btn btn-primary btn-xs btn-action" })
54
+ { class: "btn btn-primary btn-sm" })
57
55
  end
58
56
 
59
57
  def button_to_delete(item)
@@ -61,7 +59,7 @@ module Admin::BaseHelper
61
59
  element: item.class.model_name.human.downcase)
62
60
  link_to(t("generic.delete"),
63
61
  { action: "destroy", id: item.id },
64
- { class: "btn btn-danger btn-xs btn-action", method: :delete,
62
+ { class: "btn btn-danger btn-sm", method: :delete,
65
63
  data: { confirm: confirm_text } })
66
64
  end
67
65
 
@@ -69,18 +67,27 @@ module Admin::BaseHelper
69
67
  return "" if item.short_url.nil?
70
68
 
71
69
  link_to(t("generic.short_url"), item.short_url,
72
- class: "btn btn-success btn-xs btn-action")
70
+ class: "btn btn-success btn-sm")
73
71
  end
74
72
 
75
73
  def twitter_available?(blog, user)
76
74
  blog.has_twitter_configured? && user.has_twitter_configured?
77
75
  end
78
76
 
77
+ def top_menu_item(name, url)
78
+ if current_page? url
79
+ tag.li(link_to(name, "#", class: "active nav-link", "aria-current": "page"),
80
+ class: "nav-item")
81
+ else
82
+ tag.li(link_to(name, url, class: "nav-link"), class: "nav-item")
83
+ end
84
+ end
85
+
79
86
  def menu_item(name, url)
80
87
  if current_page? url
81
- tag.li(link_to(name, "#"), class: "active")
88
+ tag.li(link_to(name, "#", class: "active dropdown-item", "aria-current": "page"))
82
89
  else
83
- tag.li(link_to(name, url))
90
+ tag.li(link_to(name, url, class: "dropdown-item"))
84
91
  end
85
92
  end
86
93
  end
@@ -23,19 +23,19 @@ module Admin::FeedbackHelper
23
23
  def button_to_edit_comment(item)
24
24
  link_to(t("generic.edit"),
25
25
  { controller: "admin/feedback", action: "edit", id: item.id },
26
- { class: "btn btn-primary btn-xs btn-action" })
26
+ { class: "btn btn-primary btn-sm" })
27
27
  end
28
28
 
29
29
  def button_to_delete_comment(item)
30
30
  link_to(t("generic.delete"),
31
31
  { controller: "admin/feedback", action: "destroy", id: item.id },
32
- { class: "btn btn-danger btn-xs btn-action" })
32
+ { class: "btn btn-danger btn-sm" })
33
33
  end
34
34
 
35
35
  def button_to_conversation(item)
36
36
  link_to(t("generic.conversation"),
37
37
  { controller: "admin/feedback", action: "article", id: item.article_id },
38
- { class: "btn btn-default btn-xs btn-action" })
38
+ { class: "btn btn-default btn-sm" })
39
39
  end
40
40
 
41
41
  def change_status(item, context = "listing")
@@ -46,7 +46,7 @@ module Admin::FeedbackHelper
46
46
  link_to(link_text,
47
47
  { controller: "admin/feedback", action: "change_state",
48
48
  id: item.id, context: context },
49
- { class: "btn btn-#{button_type} btn-xs btn-action",
49
+ { class: "btn btn-#{button_type} btn-sm",
50
50
  method: :post, remote: true })
51
51
  end
52
52
  end
@@ -28,17 +28,21 @@ module BaseHelper
28
28
  end
29
29
 
30
30
  def themeable_stylesheet_link_tag(name)
31
- src = this_blog.current_theme.path + "/stylesheets/#{name}.css"
32
- stylesheet_link_tag "/stylesheets/theme/#{name}.css" if File.exist? src
31
+ theme = this_blog.current_theme
32
+ src = theme.path.join("stylesheets", theme.name, "#{name}.css")
33
+
34
+ stylesheet_link_tag "#{theme.name}/#{name}.css" if File.exist? src
33
35
  end
34
36
 
35
37
  def themeable_javascript_include_tag(name)
36
- src = this_blog.current_theme.path + "/javascripts/#{name}.js"
37
- javascript_include_tag "/javascripts/theme/#{name}.js" if File.exist? src
38
+ theme = this_blog.current_theme
39
+ src = theme.path.join("javascripts", theme.name, "#{name}.js")
40
+
41
+ javascript_include_tag "#{theme.name}/#{name}.js" if File.exist? src
38
42
  end
39
43
 
40
- def render_to_string(*args, &block)
41
- controller.send(:render_to_string, *args, &block)
44
+ def render_to_string(...)
45
+ controller.send(:render_to_string, ...)
42
46
  end
43
47
 
44
48
  def link_to_permalink(item, title,
@@ -15,4 +15,8 @@ module XmlHelper
15
15
  times.max.xmlschema
16
16
  end
17
17
  end
18
+
19
+ def truncated_body(item)
20
+ truncate(strip_tags(item.html(:body)), length: 80, separator: " ", omissions: "...")
21
+ end
18
22
  end
@@ -8,7 +8,7 @@ class Article < Content
8
8
  include PublifyGuid
9
9
  include ConfigManager
10
10
 
11
- serialize :settings, Hash
11
+ serialize :settings, type: Hash, coder: YAML
12
12
 
13
13
  content_fields :body, :extended
14
14
 
@@ -36,7 +36,7 @@ class Article < Content
36
36
  "contents.state" => "published")
37
37
  .group("contents.id")
38
38
  .select("contents.*, count(feedback.id) as comment_count")
39
- .order("comment_count DESC")
39
+ .order(comment_count: :desc)
40
40
  .limit(5)
41
41
  }
42
42
 
@@ -101,12 +101,12 @@ class Article < Content
101
101
 
102
102
  def self.search_with(params)
103
103
  params ||= {}
104
- scoped = super(params)
104
+ scoped = super
105
105
  if %w(no_draft drafts published withdrawn pending).include?(params[:state])
106
106
  scoped = scoped.send(params[:state])
107
107
  end
108
108
 
109
- scoped.order("created_at DESC")
109
+ scoped.order(created_at: :desc)
110
110
  end
111
111
 
112
112
  # FIXME: Use keyword params to clean up call sites.
@@ -140,12 +140,12 @@ class Article < Content
140
140
  end
141
141
 
142
142
  def next
143
- Article.where("published_at > ?", published_at).order("published_at asc")
143
+ Article.where("published_at > ?", published_at).order(:published_at)
144
144
  .limit(1).first
145
145
  end
146
146
 
147
147
  def previous
148
- Article.where("published_at < ?", published_at).order("published_at desc")
148
+ Article.where(published_at: ...published_at).order(published_at: :desc)
149
149
  .limit(1).first
150
150
  end
151
151
 
@@ -155,7 +155,7 @@ class Article < Content
155
155
 
156
156
  def self.publication_months
157
157
  result = select("published_at").where.not(published_at: nil).where(type: "Article")
158
- result.map { |it| [it.publication_month] }.uniq
158
+ result.map { [_1.publication_month] }.uniq
159
159
  end
160
160
 
161
161
  # Finds one article which was posted on a certain date and matches the
data/app/models/blog.rb CHANGED
@@ -24,15 +24,15 @@ class Blog < ApplicationRecord
24
24
  has_many :notes
25
25
 
26
26
  has_many :redirects
27
- has_many :sidebars, ->() { order("active_position ASC") }
27
+ has_many :sidebars, ->() { order(:active_position) }
28
28
 
29
29
  attr_accessor :custom_permalink
30
30
 
31
- default_scope -> { order("id") }
31
+ default_scope -> { order(:id) }
32
32
 
33
33
  validates :blog_name, presence: true
34
34
 
35
- serialize :settings, Hash
35
+ serialize :settings, type: Hash, coder: YAML
36
36
 
37
37
  # Description
38
38
  setting :blog_name, :string, "My Shiny Weblog!"
@@ -16,7 +16,7 @@ class Comment < Feedback
16
16
  scope :ham, -> { where(state: "ham") }
17
17
  scope :unconfirmed, -> { where(state: %w(presumed_spam presumed_ham)) }
18
18
 
19
- scope :last_published, -> { published.limit(5).order("created_at DESC") }
19
+ scope :last_published, -> { published.limit(5).order(created_at: :desc) }
20
20
 
21
21
  def notify_user_via_email(user)
22
22
  EmailNotify.send_comment(self, user) if user.notify_via_email?
@@ -69,9 +69,7 @@ module ConfigManager
69
69
  self.class.fields[key.to_s].canonicalize(value)
70
70
  end
71
71
 
72
- def settings_keys
73
- self.class.settings_keys
74
- end
72
+ delegate :settings_keys, to: :class
75
73
 
76
74
  class Item
77
75
  VALID_TYPES = [:boolean, :integer, :string, :text].freeze
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "set"
4
3
  require "uri"
5
4
 
6
5
  class Content < ApplicationRecord
@@ -22,8 +21,8 @@ class Content < ApplicationRecord
22
21
  published.where(published_at: PublifyTime.delta(*time_params))
23
22
  }
24
23
  scope :not_published, -> { where.not(state: "published") }
25
- scope :drafts, -> { where(state: "draft").order("created_at DESC") }
26
- scope :no_draft, -> { where.not(state: "draft").order("published_at DESC") }
24
+ scope :drafts, -> { where(state: "draft").order(created_at: :desc) }
25
+ scope :no_draft, -> { where.not(state: "draft").order(published_at: :desc) }
27
26
  scope :searchstring, lambda { |search_string|
28
27
  result = where(state: "published")
29
28
 
@@ -40,7 +39,7 @@ class Content < ApplicationRecord
40
39
  where(published_at: PublifyTime.delta_like(date_at))
41
40
  }
42
41
 
43
- serialize :whiteboard
42
+ serialize :whiteboard, coder: YAML
44
43
 
45
44
  validates_default_string_length :title, :author, :permalink, :name,
46
45
  :post_type, :text_filter_name
@@ -120,6 +119,6 @@ class Content < ApplicationRecord
120
119
  end
121
120
 
122
121
  def short_url
123
- redirect.from_url if redirect.present?
122
+ redirect&.from_url
124
123
  end
125
124
  end
@@ -3,6 +3,7 @@
3
3
  module ContentBase
4
4
  def self.included(base)
5
5
  base.extend ClassMethods
6
+ base.include ActionView::Helpers::SanitizeHelper
6
7
  end
7
8
 
8
9
  attr_accessor :just_changed_published_status
@@ -33,7 +34,7 @@ module ContentBase
33
34
  # Generate HTML for a specific field using the text_filter in use for this
34
35
  # object.
35
36
  def generate_html(field, text = nil)
36
- text ||= self[field].to_s
37
+ text ||= self[field] || ""
37
38
  html = (text_filter || default_text_filter).filter_text(text) || text
38
39
  html_postprocess(field, html).to_s
39
40
  end
@@ -49,13 +50,13 @@ module ContentBase
49
50
  end
50
51
 
51
52
  def excerpt_text(length = 160)
52
- text = if respond_to?(:excerpt) && (excerpt || "") != ""
53
+ text = if respond_to?(:excerpt) && excerpt.present?
53
54
  generate_html(:excerpt, excerpt)
54
55
  else
55
56
  html(:all)
56
57
  end
57
58
 
58
- text = text.strip_html
59
+ text = strip_tags(text)
59
60
 
60
61
  text.slice(0, length) +
61
62
  (text.length > length ? "..." : "")
@@ -252,7 +252,7 @@ class Feedback < ApplicationRecord
252
252
  end
253
253
 
254
254
  def blog_id
255
- article.blog_id if article.present?
255
+ article.blog_id
256
256
  end
257
257
 
258
258
  def plural_model_name
data/app/models/note.rb CHANGED
@@ -1,13 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "twitter"
4
+ require "json"
5
+ require "uri"
6
+ require "html_pipeline"
7
+ require "html_pipeline/node_filter/mention_filter"
8
+
3
9
  class Note < Content
4
- require "twitter"
5
- require "json"
6
- require "uri"
7
10
  include PublifyGuid
8
11
  include ConfigManager
9
12
 
10
- serialize :settings, Hash
13
+ serialize :settings, type: Hash, coder: YAML
11
14
 
12
15
  setting :twitter_id, :string, ""
13
16
  setting :in_reply_to_status_id, :string, ""
@@ -21,15 +24,63 @@ class Note < Content
21
24
  after_create :set_permalink, :shorten_url
22
25
 
23
26
  scope :published, lambda {
24
- where(state: "published").where("published_at <= ?", Time.zone.now).order(default_order)
27
+ where(state: "published").where(published_at: ..Time.zone.now).order(default_order)
25
28
  }
26
- default_scope { order("published_at DESC") }
29
+ default_scope { order(published_at: :desc) }
27
30
 
28
31
  TWITTER_FTP_URL_LENGTH = 19
29
32
  TWITTER_HTTP_URL_LENGTH = 20
30
33
  TWITTER_HTTPS_URL_LENGTH = 21
31
34
  TWITTER_LINK_LENGTH = 22
32
35
 
36
+ class TwitterHashtagFilter < HTMLPipeline::NodeFilter
37
+ def after_initialize
38
+ context[:tag_url] ||= "https://twitter.com/search?q=%%23%<tag>s&src=tren&mode=realtime"
39
+ end
40
+
41
+ SELECTOR = Selma::Selector.new(match_text_within: "*",
42
+ ignore_text_within: ["a"])
43
+
44
+ def selector
45
+ SELECTOR
46
+ end
47
+
48
+ HASHTAG_PATTERN = /(?<=^|\W)#([-_A-Za-z0-9]+)(?=\W|$)/
49
+
50
+ def handle_text_chunk(chunk)
51
+ text = chunk.to_s
52
+
53
+ html = text.gsub(HASHTAG_PATTERN) do |match|
54
+ tag = Regexp.last_match(1)
55
+ url = format context[:tag_url], tag: tag
56
+ "<a href=\"#{url}\">#{match}</a>"
57
+ end
58
+
59
+ return chunk if html == text
60
+
61
+ chunk.replace(html, as: :html)
62
+ end
63
+ end
64
+
65
+ class TwitterMentionFilter < HTMLPipeline::NodeFilter::MentionFilter
66
+ def after_initialize
67
+ super
68
+ context[:base_url] ||= "https://twitter.com"
69
+ context[:info_url] ||= "https://foo.com"
70
+ end
71
+
72
+ # Override base link creator, removing the class
73
+ def link_to_mentioned_user(base_url, login)
74
+ result[:mentioned_usernames] |= [login]
75
+
76
+ url = base_url.dup
77
+ excluded_prefixes = %r{[/(?:~|@]\z}
78
+ url << "/" unless excluded_prefixes.match?(url)
79
+
80
+ "<a href=\"#{url << login}\">@#{login}</a>"
81
+ end
82
+ end
83
+
33
84
  def set_permalink
34
85
  self.permalink = "#{id}-#{body.to_permalink[0..79]}" if permalink.blank?
35
86
  save
@@ -43,8 +94,23 @@ class Note < Content
43
94
  []
44
95
  end
45
96
 
97
+ def generate_html(field, text = nil)
98
+ if field == :in_reply_to
99
+ helper = PublifyCore::ContentTextHelpers.new
100
+ html = helper.simple_format(text)
101
+ html_postprocess(field, html).to_s
102
+ else
103
+ super
104
+ end
105
+ end
106
+
46
107
  def html_postprocess(field, html)
47
- super(field, PublifyCore::TextFilter::Twitterfilter.filtertext(html))
108
+ helper = PublifyCore::ContentTextHelpers.new
109
+ html = helper.auto_link(html)
110
+
111
+ html = TwitterHashtagFilter.call(html)
112
+ html = TwitterMentionFilter.call(html).to_s
113
+ super
48
114
  end
49
115
 
50
116
  def truncate(message, length)
@@ -56,7 +122,7 @@ class Note < Content
56
122
  end
57
123
 
58
124
  def twitter_message
59
- base_message = body.strip_html
125
+ base_message = strip_tags(body)
60
126
  if too_long?("#{base_message} (#{short_link})")
61
127
  max_length = 140 - "... (#{redirect.from_url})".length - 1
62
128
  "#{truncate(base_message, max_length)}... (#{redirect.from_url})"
data/app/models/page.rb CHANGED
@@ -6,7 +6,7 @@ class Page < Content
6
6
 
7
7
  include ConfigManager
8
8
 
9
- serialize :settings, Hash
9
+ serialize :settings, type: Hash, coder: YAML
10
10
  setting :password, :string, ""
11
11
 
12
12
  before_save :set_permalink
@@ -31,10 +31,6 @@ class Redirect < ApplicationRecord
31
31
  end
32
32
  end
33
33
 
34
- def to_url
35
- raise "Use #from_url"
36
- end
37
-
38
34
  def from_url
39
35
  File.join(blog.shortener_url, from_path)
40
36
  end
@@ -5,6 +5,7 @@ require "carrierwave/orm/activerecord"
5
5
 
6
6
  class Resource < ApplicationRecord
7
7
  include StringLengthLimit
8
+
8
9
  belongs_to :blog
9
10
  belongs_to :content, optional: true
10
11
 
@@ -4,7 +4,7 @@ require "sidebar_field"
4
4
 
5
5
  # This class cannot be autoloaded since other sidebar classes depend on it.
6
6
  class Sidebar < ApplicationRecord
7
- serialize :config, Hash
7
+ serialize :config, type: Hash, coder: YAML
8
8
 
9
9
  belongs_to :blog
10
10
 
@@ -32,7 +32,7 @@ class Sidebar < ApplicationRecord
32
32
  def self.setting(key, default = nil, options = {})
33
33
  key = key.to_s
34
34
 
35
- return if instance_methods.include?(key)
35
+ return if method_defined?(key)
36
36
 
37
37
  fields << SidebarField.build(key, default, options)
38
38
 
@@ -97,9 +97,7 @@ class Sidebar < ApplicationRecord
97
97
 
98
98
  def parse_request(_contents, _params); end
99
99
 
100
- def fields
101
- self.class.fields
102
- end
100
+ delegate :fields, to: :class
103
101
 
104
102
  def fieldmap(field = nil)
105
103
  if field
@@ -109,17 +107,11 @@ class Sidebar < ApplicationRecord
109
107
  end
110
108
  end
111
109
 
112
- def description
113
- self.class.description
114
- end
110
+ delegate :description, to: :class
115
111
 
116
- def short_name
117
- self.class.short_name
118
- end
112
+ delegate :short_name, to: :class
119
113
 
120
- def display_name
121
- self.class.display_name
122
- end
114
+ delegate :display_name, to: :class
123
115
 
124
116
  def content_partial
125
117
  "/#{self.class.path_name}/content"
data/app/models/tag.rb CHANGED
@@ -43,11 +43,11 @@ class Tag < ApplicationRecord
43
43
  .where(contents: { state: "published" })
44
44
  .select(*Tag.column_names, "COUNT(contents_tags.content_id) as content_counter")
45
45
  .group(*Tag.column_names)
46
- .order("content_counter DESC").limit(1000)
46
+ .order(content_counter: :desc).limit(1000)
47
47
  end
48
48
 
49
49
  def self.find_with_char(char)
50
- where("name LIKE ? ", "%#{char}%").order("name ASC")
50
+ where("name LIKE ? ", "%#{char}%").order(:name)
51
51
  end
52
52
 
53
53
  def self.collection_to_string(tags)
@@ -44,7 +44,7 @@ class TextFilter
44
44
  if f.help_text.blank?
45
45
  ""
46
46
  else
47
- "<h3>#{f.display_name}</h3>\n#{CommonMarker.render_html(f.help_text, :DEFAULT)}"
47
+ "<h3>#{f.display_name}</h3>\n#{Commonmarker.to_html(f.help_text)}"
48
48
  end
49
49
  end
50
50
 
@@ -56,7 +56,7 @@ class TextFilter
56
56
  .expand_filter_list([markup, filters].flatten)
57
57
 
58
58
  help_filters.map do |f|
59
- f.help_text.blank? ? "" : CommonMarker.render_html(f.help_text)
59
+ f.help_text.blank? ? "" : Commonmarker.to_html(f.help_text)
60
60
  end.join("\n")
61
61
  end
62
62
 
@@ -11,7 +11,7 @@ class Trigger < ApplicationRecord
11
11
  end
12
12
 
13
13
  def fire
14
- where("due_at <= ?", Time.zone.now).destroy_all
14
+ where(due_at: ..Time.zone.now).destroy_all
15
15
  true
16
16
  end
17
17