publify_core 10.0.3 → 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 (201) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +249 -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 +11 -4
  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/testing_support/fixtures/like_plain.html +2 -0
  119. data/lib/publify_core/text_filter/markdown.rb +5 -1
  120. data/lib/publify_core/text_filter/markdown_smartquotes.rb +6 -1
  121. data/lib/publify_core/version.rb +1 -1
  122. data/lib/publify_core.rb +2 -2
  123. data/lib/text_filter_plugin.rb +20 -22
  124. data/lib/theme.rb +56 -37
  125. metadata +101 -397
  126. data/app/assets/javascripts/bootstrap/affix.js +0 -164
  127. data/app/assets/javascripts/bootstrap/alert.js +0 -95
  128. data/app/assets/javascripts/bootstrap/button.js +0 -125
  129. data/app/assets/javascripts/bootstrap/collapse.js +0 -212
  130. data/app/assets/javascripts/bootstrap/dropdown.js +0 -165
  131. data/app/assets/javascripts/bootstrap/modal.js +0 -358
  132. data/app/assets/javascripts/bootstrap/tab.js +0 -155
  133. data/app/assets/javascripts/bootstrap/transition.js +0 -59
  134. data/app/assets/javascripts/bootstrap-sprockets.js +0 -8
  135. data/app/assets/javascripts/lightbox.js +0 -350
  136. data/app/assets/stylesheets/_bootstrap-compass.scss +0 -9
  137. data/app/assets/stylesheets/_bootstrap-mincer.scss +0 -19
  138. data/app/assets/stylesheets/_bootstrap-sprockets.scss +0 -9
  139. data/app/assets/stylesheets/_bootstrap.scss +0 -42
  140. data/app/assets/stylesheets/bootstrap/_alerts.scss +0 -73
  141. data/app/assets/stylesheets/bootstrap/_button-groups.scss +0 -242
  142. data/app/assets/stylesheets/bootstrap/_buttons.scss +0 -168
  143. data/app/assets/stylesheets/bootstrap/_close.scss +0 -37
  144. data/app/assets/stylesheets/bootstrap/_code.scss +0 -69
  145. data/app/assets/stylesheets/bootstrap/_component-animations.scss +0 -38
  146. data/app/assets/stylesheets/bootstrap/_dropdowns.scss +0 -213
  147. data/app/assets/stylesheets/bootstrap/_forms.scss +0 -586
  148. data/app/assets/stylesheets/bootstrap/_grid.scss +0 -94
  149. data/app/assets/stylesheets/bootstrap/_labels.scss +0 -66
  150. data/app/assets/stylesheets/bootstrap/_media.scss +0 -66
  151. data/app/assets/stylesheets/bootstrap/_mixins.scss +0 -37
  152. data/app/assets/stylesheets/bootstrap/_modals.scss +0 -150
  153. data/app/assets/stylesheets/bootstrap/_navbar.scss +0 -479
  154. data/app/assets/stylesheets/bootstrap/_navs.scss +0 -242
  155. data/app/assets/stylesheets/bootstrap/_normalize.scss +0 -427
  156. data/app/assets/stylesheets/bootstrap/_pagination.scss +0 -86
  157. data/app/assets/stylesheets/bootstrap/_panels.scss +0 -222
  158. data/app/assets/stylesheets/bootstrap/_print.scss +0 -99
  159. data/app/assets/stylesheets/bootstrap/_scaffolding.scss +0 -160
  160. data/app/assets/stylesheets/bootstrap/_tables.scss +0 -234
  161. data/app/assets/stylesheets/bootstrap/_theme.scss +0 -224
  162. data/app/assets/stylesheets/bootstrap/_type.scss +0 -296
  163. data/app/assets/stylesheets/bootstrap/_utilities.scss +0 -55
  164. data/app/assets/stylesheets/bootstrap/_variables.scss +0 -638
  165. data/app/assets/stylesheets/bootstrap/_wells.scss +0 -29
  166. data/app/assets/stylesheets/bootstrap/mixins/_alerts.scss +0 -15
  167. data/app/assets/stylesheets/bootstrap/mixins/_background-variant.scss +0 -12
  168. data/app/assets/stylesheets/bootstrap/mixins/_border-radius.scss +0 -18
  169. data/app/assets/stylesheets/bootstrap/mixins/_buttons.scss +0 -56
  170. data/app/assets/stylesheets/bootstrap/mixins/_center-block.scss +0 -7
  171. data/app/assets/stylesheets/bootstrap/mixins/_clearfix.scss +0 -22
  172. data/app/assets/stylesheets/bootstrap/mixins/_forms.scss +0 -82
  173. data/app/assets/stylesheets/bootstrap/mixins/_gradients.scss +0 -56
  174. data/app/assets/stylesheets/bootstrap/mixins/_grid-framework.scss +0 -81
  175. data/app/assets/stylesheets/bootstrap/mixins/_grid.scss +0 -122
  176. data/app/assets/stylesheets/bootstrap/mixins/_hide-text.scss +0 -21
  177. data/app/assets/stylesheets/bootstrap/mixins/_image.scss +0 -28
  178. data/app/assets/stylesheets/bootstrap/mixins/_labels.scss +0 -12
  179. data/app/assets/stylesheets/bootstrap/mixins/_nav-divider.scss +0 -10
  180. data/app/assets/stylesheets/bootstrap/mixins/_nav-vertical-align.scss +0 -9
  181. data/app/assets/stylesheets/bootstrap/mixins/_opacity.scss +0 -7
  182. data/app/assets/stylesheets/bootstrap/mixins/_pagination.scss +0 -24
  183. data/app/assets/stylesheets/bootstrap/mixins/_panels.scss +0 -20
  184. data/app/assets/stylesheets/bootstrap/mixins/_reset-filter.scss +0 -8
  185. data/app/assets/stylesheets/bootstrap/mixins/_resize.scss +0 -6
  186. data/app/assets/stylesheets/bootstrap/mixins/_responsive-visibility.scss +0 -17
  187. data/app/assets/stylesheets/bootstrap/mixins/_size.scss +0 -10
  188. data/app/assets/stylesheets/bootstrap/mixins/_tab-focus.scss +0 -9
  189. data/app/assets/stylesheets/bootstrap/mixins/_table-row.scss +0 -28
  190. data/app/assets/stylesheets/bootstrap/mixins/_text-emphasis.scss +0 -12
  191. data/app/assets/stylesheets/bootstrap/mixins/_text-overflow.scss +0 -8
  192. data/app/assets/stylesheets/bootstrap/mixins/_vendor-prefixes.scss +0 -222
  193. data/app/assets/stylesheets/lightbox.css +0 -63
  194. data/app/controllers/theme_controller.rb +0 -63
  195. data/app/views/notes/show_in_reply.html.erb +0 -18
  196. data/app/views/theme/static_view_test.html.erb +0 -1
  197. data/lib/publify_core/text_filter/twitterfilter.rb +0 -55
  198. data/lib/tasks/manifest.rake +0 -30
  199. /data/app/views/layouts/{default.html.erb → application.html.erb} +0 -0
  200. /data/themes/plain/javascripts/{theme.js → plain/theme.js} +0 -0
  201. /data/themes/plain/stylesheets/{theme.css → plain/theme.css} +0 -0
data/app/models/user.rb CHANGED
@@ -8,11 +8,12 @@ class User < ApplicationRecord
8
8
  ADMIN = "admin"
9
9
  PUBLISHER = "publisher"
10
10
  CONTRIBUTOR = "contributor"
11
+ MIN_PASSWORD_SCORE = 4
11
12
 
12
13
  # Include default devise modules. Others available are:
13
14
  # :confirmable, :lockable, :timeoutable and :omniauthable
14
15
  devise :database_authenticatable, :registerable,
15
- :recoverable, :rememberable, :trackable, :validatable, :zxcvbnable
16
+ :recoverable, :rememberable, :trackable, :validatable
16
17
  include ConfigManager
17
18
  include StringLengthLimit
18
19
 
@@ -23,6 +24,7 @@ class User < ApplicationRecord
23
24
  validates :login, length: { in: 3..40 }
24
25
  validates_default_string_length :email, :text_filter_name
25
26
  validates :name, length: { maximum: 2048 }
27
+ validate :strong_password, if: :password_required?
26
28
 
27
29
  belongs_to :resource, optional: true
28
30
  has_many :notifications, foreign_key: "notify_user_id"
@@ -31,7 +33,7 @@ class User < ApplicationRecord
31
33
 
32
34
  has_many :articles
33
35
 
34
- serialize :settings, Hash
36
+ serialize :settings, type: Hash, coder: YAML
35
37
 
36
38
  STATUS = %w(active inactive).freeze
37
39
 
@@ -73,24 +75,6 @@ class User < ApplicationRecord
73
75
  .map { |f| send(f) }.delete_if(&:empty?)
74
76
  end
75
77
 
76
- # Authenticate users with old password hashes
77
- alias devise_valid_password? valid_password?
78
-
79
- def valid_password?(password)
80
- devise_valid_password?(password)
81
- rescue BCrypt::Errors::InvalidHash
82
- digest = Digest::SHA1.hexdigest("#{self.class.salt}--#{password}--")
83
- if digest == encrypted_password
84
- # Update old SHA1 password with new Devise ByCrypt password
85
- self.encrypted_password = password_digest(password)
86
- save
87
- true
88
- else
89
- # If not BCrypt password and not old SHA1 password deny access
90
- false
91
- end
92
- end
93
-
94
78
  def active_for_authentication?
95
79
  super && state == "active"
96
80
  end
@@ -138,7 +122,14 @@ class User < ApplicationRecord
138
122
 
139
123
  private
140
124
 
125
+ def strong_password
126
+ return if password.blank?
127
+
128
+ test = Zxcvbn.test(password)
129
+ errors.add :password, :weak_password if test.score < MIN_PASSWORD_SCORE
130
+ end
131
+
141
132
  def set_default_profile
142
- self.profile ||= User.count.zero? ? "admin" : "contributor"
133
+ self.profile ||= User.none? ? "admin" : "contributor"
143
134
  end
144
135
  end
@@ -4,6 +4,7 @@ require "marcel"
4
4
 
5
5
  class ResourceUploader < CarrierWave::Uploader::Base
6
6
  include CarrierWave::MiniMagick
7
+
7
8
  before :process, :check_content_type!
8
9
 
9
10
  process :fix_exif_rotation, if: :image?
@@ -57,9 +58,11 @@ class ResourceUploader < CarrierWave::Uploader::Base
57
58
  end
58
59
 
59
60
  def check_content_type!(new_file)
60
- return unless image? new_file
61
-
62
- detected_type = file_content_content_type(new_file)
61
+ detected_type = if image? new_file
62
+ type_from_content(new_file)
63
+ else
64
+ type_from_name(new_file)
65
+ end
63
66
  if detected_type != new_file.content_type
64
67
  raise CarrierWave::IntegrityError, "has MIME type mismatch"
65
68
  end
@@ -67,7 +70,11 @@ class ResourceUploader < CarrierWave::Uploader::Base
67
70
 
68
71
  private
69
72
 
70
- def file_content_content_type(new_file)
73
+ def type_from_content(new_file)
71
74
  Marcel::MimeType.for Pathname.new(new_file.path)
72
75
  end
76
+
77
+ def type_from_name(new_file)
78
+ Marcel::MimeType.for Pathname.new(new_file.path), name: new_file.filename
79
+ end
73
80
  end
@@ -3,15 +3,15 @@
3
3
  <%= hidden_field_tag('article[id]', @article.id) if @article.present? %>
4
4
  </div>
5
5
  <div class="row save-bar">
6
- <div class="col-md-8 col-md-offset-2" id="save-bar">
7
- <div class="pull-right">
6
+ <div class="col-lg-8 offset-lg-2 d-flex justify-content-end" id="save-bar">
7
+ <div>
8
8
  <%= link_to(t('.cancel'), { action: 'index' }, { class: 'btn btn-default' }) %>
9
9
  <span id="preview_link">
10
10
  <%= link_to(t('.preview'), { controller: '/articles', action: 'preview', id: @article.id }, { target: 'new', class: 'btn btn-default' }) if @article.id %>
11
11
  </span>
12
- <input id="save_draft" type="submit" value="<%= t('.save_as_draft') %>" name="article[draft]" class="btn btn-default">
12
+ <input id="save_draft" type="submit" value="<%= t('.save_as_draft') %>" name="article[draft]" class="btn btn-secondary">
13
13
  <!-- Button trigger modal -->
14
- <button class="btn btn-success" data-toggle="modal" type="button" data-target="#publishOptions">
14
+ <button class="btn btn-success" data-bs-toggle="modal" type="button" data-bs-target="#publishOptions">
15
15
  <%= controller.action_name == 'new' ? t('.publish') : t('.save') %>
16
16
  </button>
17
17
  </div>
@@ -19,7 +19,7 @@
19
19
  </div>
20
20
 
21
21
  <div class="row">
22
- <div class="col-md-8 col-md-offset-2" id="error-message-article">
22
+ <div class="col-lg-8 offset-lg-2" id="error-message-article">
23
23
  <%= render 'shared/flash', flash: flash %>
24
24
  <% if @article.errors.any? %>
25
25
  <div id="error_explanation">
@@ -36,12 +36,12 @@
36
36
  </div>
37
37
 
38
38
  <div class="row">
39
- <div class="col-md-2" id="quicktags-block">
40
- <div id="quicktags" class="pull-right">
39
+ <div class="col-lg-2" id="quicktags-block">
40
+ <div id="quicktags" class="float-right">
41
41
  <script type="text/javascript">edToolbar('article_body_and_extended', '<%= @article.text_filter_name %>');</script>
42
42
  </div>
43
43
  </div>
44
- <div class="col-md-8">
44
+ <div class="col-lg-8">
45
45
  <%= text_field 'article', 'title', class: 'form-control', placeholder: t('.title') %>
46
46
  <div id="editor">
47
47
  <%= text_area('article', 'body_and_extended', class: 'form-control ', style: 'height: 360px', placeholder: t('.type_your_post')) %>
@@ -60,134 +60,128 @@
60
60
  </h2>
61
61
  </div>
62
62
  <div class="modal-body">
63
- <div class="well">
64
- <h4><%= t('.tags') %></h4>
65
- <div class="form-group">
66
- <%= text_field 'article', 'keywords', autocomplete: 'off', class: 'form-control tm-input' %>
63
+ <div class="card mb-3">
64
+ <div class="card-body">
65
+ <h4 class="card-title"><%= t('.tags') %></h4>
66
+ <div class="form-group">
67
+ <%= text_field 'article', 'keywords', autocomplete: 'off', class: 'form-control tm-input' %>
68
+ </div>
69
+ <p class="alert alert-warning"><%= t('.tags_explanation_html') %></p>
67
70
  </div>
68
- <p class="alert alert-warning"><%= t('.tags_explanation_html') %></p>
69
71
  </div>
70
72
 
71
73
  <% post_types = @post_types || [] %>
72
74
  <% if post_types.size.zero? %>
73
75
  <%= hidden_field_tag 'article[post_type]', 'read' %>
74
76
  <% else %>
75
- <div class="well">
76
- <h4><%= t('.article_type') %></h4>
77
- <%= select :article, :post_type, post_types.map { |pt| [pt.name, pt.permalink] }, include_blank: t('.default') %>
77
+ <div class="card mb-3">
78
+ <div class="card-body">
79
+ <h4 class="card-title"><%= t('.article_type') %></h4>
80
+ <%= select :article, :post_type, post_types.map { |pt| [pt.name, pt.permalink] }, include_blank: t('.default') %>
81
+ </div>
78
82
  </div>
79
83
  <% end %>
80
84
 
81
- <div class="well">
82
- <fieldset>
83
- <legend><%= t('.publish_settings') %></legend>
84
- <div class='control-group'>
85
- <p>
86
- <%= t('.permalink') %>:
87
- <%= toggle_element('permalink') %>
88
- </p>
89
- <div id="permalink" class="collapse">
90
- <div class="form-group">
91
- <%= text_field 'article', 'permalink', autocomplete: 'off', class: 'form-control' %>
92
- </div>
85
+ <div class="card">
86
+ <div class="card-body">
87
+ <fieldset>
88
+ <legend><%= t('.publish_settings') %></legend>
89
+ <div class='control-group'>
93
90
  <p>
94
- <span class="btn btn-mini btn-default">
95
- <%= toggle_element('permalink', t('.ok')) %>
96
- </span>
91
+ <%= t('.permalink') %>:
92
+ <%= toggle_element('permalink') %>
97
93
  </p>
94
+ <div id="permalink" class="mb-3 collapse">
95
+ <div class="mb-3">
96
+ <%= text_field 'article', 'permalink', autocomplete: 'off', class: 'form-control' %>
97
+ </div>
98
+ <%= toggle_element('permalink', t('.ok'), class: "btn btn-mini btn-secondary") %>
99
+ </div>
98
100
  </div>
99
- </div>
100
- <div class="control-group">
101
- <p>
102
- <%= t('.status') %>: <strong><%= @article.state.to_s.downcase %></strong>
103
- <%= toggle_element('status') %>
104
- </p>
105
- <div id="status" class="collapse">
106
- <label for="article_published" class="checkbox">
107
- <%= check_box 'article', 'published?' %>
108
- <%= t('.published') %>
109
- </label>
101
+ <div class="control-group">
110
102
  <p>
111
- <span class="btn btn-mini btn-default">
112
- <%= toggle_element 'status', t('.ok') %>
113
- </span>
103
+ <%= t('.status') %>: <strong><%= @article.state.to_s.downcase %></strong>
104
+ <%= toggle_element('status') %>
114
105
  </p>
106
+ <div id="status" class="mb-3 collapse">
107
+ <div class="mb-3">
108
+ <label for="article_published" class="checkbox">
109
+ <%= check_box 'article', 'published?' %>
110
+ <%= t('.published') %>
111
+ </label>
112
+ </div>
113
+ <%= toggle_element('status', t('.ok'), class: "btn btn-mini btn-secondary") %>
114
+ </div>
115
115
  </div>
116
- </div>
117
- <div class="control-group">
118
- <p>
119
- <%= t('.allowed_comments_html', allow_comment: content_tag(:strong, @article.allow_comments? ? t('.allow_comments_status.enabled') : t('.allow_comments_status.disabled'))) %>
120
- <%= toggle_element('conversation') %>
121
- </p>
122
- <div id="conversation" class="collapse">
123
- <label for="article_allow_comments" class="checkbox">
124
- <%= check_box 'article', 'allow_comments' %>
125
- <%= t('.allow_comments') %>
126
- </label>
116
+ <div class="control-group">
127
117
  <p>
128
- <span class="btn btn-mini btn-default">
129
- <%= toggle_element('conversation', t('.ok')) %>
130
- </span>
118
+ <%= t('.allowed_comments_html', allow_comment: content_tag(:strong, @article.allow_comments? ? t('.allow_comments_status.enabled') : t('.allow_comments_status.disabled'))) %>
119
+ <%= toggle_element('conversation') %>
131
120
  </p>
121
+ <div id="conversation" class="mb-3 collapse">
122
+ <div class="mb-3">
123
+ <label for="article_allow_comments" class="checkbox">
124
+ <%= check_box 'article', 'allow_comments' %>
125
+ <%= t('.allow_comments') %>
126
+ </label>
127
+ </div>
128
+ <div>
129
+ <%= toggle_element('conversation', t('.ok'), class: "btn btn-mini btn-secondary") %>
130
+ </div>
131
+ </div>
132
132
  </div>
133
- </div>
134
- <div class="control-group">
135
- <p>
136
- <%= t('.published') %>
137
- <strong>
138
- <% if @article.published_at.present? %>
139
- <%= display_date_and_time(@article.published_at) %>
140
- <% else %>
141
- <%= t('.now') %>
142
- <% end %>
143
- </strong>
144
- <%= toggle_element('publish') %>
145
- </p>
146
- <div id="publish" class="collapse">
147
- <%= datetime_field 'article', 'published_at' %>
133
+ <div class="control-group">
148
134
  <p>
149
- <span class="btn btn-mini btn-default">
150
- <%= toggle_element('publish', t('.ok')) %>
151
- </span>
135
+ <%= t('.published') %>
136
+ <strong>
137
+ <% if @article.published_at.present? %>
138
+ <%= display_date_and_time(@article.published_at) %>
139
+ <% else %>
140
+ <%= t('.now') %>
141
+ <% end %>
142
+ </strong>
143
+ <%= toggle_element('publish') %>
152
144
  </p>
145
+ <div id="publish" class="mb-3 collapse">
146
+ <div class="mb-3">
147
+ <%= datetime_field 'article', 'published_at' %>
148
+ </div>
149
+ <%= toggle_element('publish', t('.ok'), class: "btn btn-mini btn-secondary") %>
150
+ </div>
153
151
  </div>
154
- </div>
155
- <div class="control-group">
156
- <p>
157
- <%= t('.visibility') %>: <strong><%= @article.password.blank? ? t('.public') : t('.protected') %></strong>
158
- <%= toggle_element('visibility') %>
159
- </p>
160
- <div id="visibility" class="collapse">
161
- <label for="article_password"><%= t('.password') %></label>
162
- <%= text_field 'article', 'password', class: 'form-control' %>
152
+ <div class="control-group">
163
153
  <p>
164
- <span class="btn btn-mini btn-default">
165
- <%= toggle_element('visibility', t('.ok')) %>
166
- </span>
154
+ <%= t('.visibility') %>: <strong><%= @article.password.blank? ? t('.public') : t('.protected') %></strong>
155
+ <%= toggle_element('visibility') %>
167
156
  </p>
157
+ <div id="visibility" class="mb-3 collapse">
158
+ <div class="mb-3">
159
+ <label for="article_password"><%= t('.password') %></label>
160
+ <%= text_field 'article', 'password', class: 'form-control' %>
161
+ </div>
162
+ <%= toggle_element('visibility', t('.ok'), class: "btn btn-mini btn-secondary") %>
163
+ </div>
168
164
  </div>
169
- </div>
170
- <div class="control-group">
171
- <p>
172
- <%= t('.article_filter') %>: <strong><%= @article.text_filter.description %></strong>
173
- <%= toggle_element 'text_filter' %>
174
- </p>
175
- <div id="text_filter" class="collapse">
176
- <select name="article[text_filter_name]" id="text_filter_name" class='form-control'>
177
- <%= options_for_select text_filter_options, @article.text_filter_name %>
178
- </select>
165
+ <div class="control-group">
179
166
  <p>
180
- <span class="btn btn-mini btn-default">
181
- <%= toggle_element 'text_filter', 'OK' %>
182
- </span>
167
+ <%= t('.article_filter') %>: <strong><%= @article.text_filter.description %></strong>
168
+ <%= toggle_element 'text_filter' %>
183
169
  </p>
170
+ <div id="text_filter" class="collapse">
171
+ <div class="mb-3">
172
+ <select name="article[text_filter_name]" id="text_filter_name" class='form-select'>
173
+ <%= options_for_select text_filter_options, @article.text_filter_name %>
174
+ </select>
175
+ </div>
176
+ <%= toggle_element('text_filter', t('.ok'), class: "btn btn-mini btn-secondary") %>
177
+ </div>
184
178
  </div>
185
- </div>
186
- </fieldset>
179
+ </fieldset>
180
+ </div>
187
181
  </div>
188
182
  </div>
189
183
  <div class="modal-footer">
190
- <button type="button" class="btn btn-default" data-dismiss="modal"><%= t('.cancel') %></button>
184
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><%= t('.cancel') %></button>
191
185
  <%= submit_tag(t('.publish'), class: 'btn btn-success') %>
192
186
  </div>
193
187
  </div>
@@ -1,40 +1,32 @@
1
1
  <% content_for :page_heading do %>
2
2
  <h2>
3
3
  <%= t('.manage_articles') %>
4
- <%= link_to(t('.new_article'), { controller: 'articles', action: 'new' }, { id: 'dialog-link', class: 'btn btn-info pull-right' }) %>
4
+ <%= link_to(t('.new_article'), { controller: 'articles', action: 'new' }, { id: 'dialog-link', class: 'btn btn-info float-right' }) %>
5
5
  </h2>
6
6
  <% end %>
7
7
 
8
- <%= form_tag({ action: 'index' }, { method: :get, name: 'article', remote: true, class: 'form-inline spinnable', "data-update-success": 'articleList' }) do %>
8
+ <%= form_tag({ action: 'index' }, { method: :get, name: 'article', remote: true, class: 'spinnable', "data-update-success": 'articleList' }) do %>
9
9
 
10
10
  <% if params[:search] and params[:search]['state'] %>
11
11
  <input type="hidden" name="search[state]" value="<%= params[:search]['state'] %>">
12
12
  <% end %>
13
13
 
14
14
  <p>
15
- <%= link_to(t('.all_articles'), { action: 'index' }, { class: 'label label-default' }) %>
16
- <%= link_to(t('.published'), { action: 'index', search: { state: 'published' } }, { class: 'label label-success' }) %>
17
- <%= link_to(t('.withdrawn'), { action: 'index', search: { state: 'withdrawn' } }, { class: 'label label-danger' }) %>
18
- <%= link_to(t('.drafts'), { action: 'index', search: { state: 'drafts' } }, { class: 'label label-info' }) %>
19
- <%= link_to(t('.publication_pending'), { action: 'index', search: { state: 'pending' } }, { class: 'label label-warning' }) %>
15
+ <%= link_to(t('.all_articles'), { action: 'index' }, { class: 'badge text-bg-secondary' }) %>
16
+ <%= link_to(t('.published'), { action: 'index', search: { state: 'published' } }, { class: 'badge text-bg-success' }) %>
17
+ <%= link_to(t('.withdrawn'), { action: 'index', search: { state: 'withdrawn' } }, { class: 'badge text-bg-danger' }) %>
18
+ <%= link_to(t('.drafts'), { action: 'index', search: { state: 'drafts' } }, { class: 'badge text-bg-info' }) %>
19
+ <%= link_to(t('.publication_pending'), { action: 'index', search: { state: 'pending' } }, { class: 'badge text-bg-warning' }) %>
20
20
  </p>
21
21
 
22
- <div class="panel panel-default">
23
- <div class="panel-heading clearfix">
24
- <div class="pull-right">
25
- <div class="form-group">
26
- <%= select_tag('search[user_id]', options_from_collection_for_select(User.all, 'id', 'name'), prompt: t('.select_an_author'), class: 'form-control') %>
27
- </div>
28
- <div class="form-group">
29
- <%= select_tag('search[published_at]', options_for_select(Article.publication_months), prompt: t('.publication_date'), class: 'form-control') %>
30
- </div>
31
- <div class="form-group">
32
- <input id="search" type="text" name="search[searchstring]" class="form-control" autocomplete="off">
33
- </div>
34
- <div class="form-group">
35
- <%= submit_tag(t('.search'), class: 'btn btn-success') %>
36
- <span id="spinner" class="hidden"><%= image_tag('spinner.gif') %></span>
37
- </div>
22
+ <div class="card mb-4">
23
+ <div class="card-header">
24
+ <div class="d-sm-flex">
25
+ <%= select_tag('search[user_id]', options_from_collection_for_select(User.all, 'id', 'name'), prompt: t('.select_an_author'), class: 'form-select ms-sm-auto me-sm-2') %>
26
+ <%= select_tag('search[published_at]', options_for_select(Article.publication_months), prompt: t('.publication_date'), class: 'form-select me-sm-2') %>
27
+ <input id="search" type="text" name="search[searchstring]" class="form-control me-sm-2" autocomplete="off">
28
+ <%= submit_tag(t('.search'), class: 'btn btn-success') %>
29
+ <span id="spinner" class="d-none"><%= image_tag('spinner.gif') %></span>
38
30
  </div>
39
31
  </div>
40
32
  </div>
@@ -9,7 +9,7 @@
9
9
  <%= display_date_and_time comment.created_at %>
10
10
  </h5>
11
11
  <p>
12
- <%= comment.html.strip_html.slice(0..300) %>
12
+ <%= comment.excerpt_text(300) %>
13
13
  </p>
14
14
  <%= button_to_conversation(comment) %>
15
15
  <hr>
@@ -1,9 +1,7 @@
1
- <div class="panel panel-default">
2
- <div class="panel-heading">
3
- <h3 class='panel-title'><%= t('.latest_comments') %></h3>
4
- </div>
5
- <div class="panel-body">
6
- <ul class='list-unstyled' id="commentList">
1
+ <div class="card mb-4">
2
+ <h3 class="card-header"><%= t('.latest_comments') %></h3>
3
+ <div class="card-body">
4
+ <ul class="list-unstyled" id="commentList">
7
5
  <%= render partial: 'comment' %>
8
6
  </ul>
9
7
  </div>
@@ -1,8 +1,6 @@
1
- <div class="panel panel-default">
2
- <div class="panel-heading">
3
- <h3 class='panel-title'><%= t('.your_drafts') %></h3>
4
- </div>
5
- <div class="panel-body">
1
+ <div class="card">
2
+ <h3 class="card-header"><%= t('.your_drafts') %></h3>
3
+ <div class="card-body">
6
4
  <ul class='list-unstyled'>
7
5
  <% if @drafts.empty? %>
8
6
  <li class='alert alert-info'>
@@ -15,7 +13,7 @@
15
13
  <%= link_to(post.title, controller: 'admin/articles', action: 'edit', id: post.id) %>
16
14
  <%= display_date_and_time(post.created_at) %>
17
15
  </h5>
18
- <p><%= post.body&.strip_html&.slice(0, 300) %></p>
16
+ <p><%= post.excerpt_text(300) %></p>
19
17
  <hr>
20
18
  </li>
21
19
  <% end %>
@@ -1,8 +1,5 @@
1
- <div class='alert alert-info'>
2
- <button type="button" class="close" data-dismiss="alert">
3
- <span aria-hidden="true">&times;</span>
4
- <span class="sr-only">Close</span>
5
- </button>
1
+ <div class="alert alert-info alert-dismissible fade show">
2
+ <button type="button" class="btn-close float-right" data-bs-dismiss="alert" aria-label="Close"></button>
6
3
  <% links = []
7
4
  links << link_to(t('.write_a_post'), controller: 'articles', action: 'new') if can? :new, 'admin/articles'
8
5
  links << link_to(t('.write_a_page'), controller: 'pages', action: 'new') if can? :new, 'admin/pages'
@@ -1,27 +1,27 @@
1
- <div class="panel panel-default">
2
- <div class="panel-heading">
3
- <h3 class="panel-title"><%= t('.today') %></h3>
4
- </div>
5
- <div class="panel-body">
1
+ <div class="card mb-4">
2
+ <h3 class="card-header"><%= t('.today') %></h3>
3
+ <div class="card-body">
6
4
  <p><%= t('.articles_and_comments_count_since', articles_count: @newposts_count, comments_count: @newcomments_count) %></p>
7
5
  <p><%= t('.running_publify', version: PublifyCore::VERSION) %> <%= @version_message %></p>
8
- <div class='col-md-6'>
6
+ <div class='row'>
9
7
  <% if can? :index, 'admin/articles' %>
10
- <h4 class='page-header'><%= t('.content') %></h4>
11
- <p><%= link_to(t('.articles_count', count: @statposts), controller: 'admin/articles') %></p>
12
- <p><%= link_to(t('.your_articles_count', count: @statuserposts), controller: 'admin/articles', 'search[user_id]' => current_user.id) %></p>
13
- <p><%= link_to(t('.drafts_count', count: @statsdrafts), controller: 'admin/articles', 'search[state]' => 'drafts') %></p>
14
- <p><%= link_to(t('.pages_count', count: @statspages), controller: 'admin/pages') %></p>
15
- <p><%= link_to(t('.notes_count', count: @statuses), controller: 'admin/notes') %></p>
8
+ <div class="col-lg-6">
9
+ <h4 class='mb-2 pb-2 border-bottom'><%= t('.content') %></h4>
10
+ <p><%= link_to(t('.articles_count', count: @statposts), controller: 'admin/articles') %></p>
11
+ <p><%= link_to(t('.your_articles_count', count: @statuserposts), controller: 'admin/articles', 'search[user_id]' => current_user.id) %></p>
12
+ <p><%= link_to(t('.drafts_count', count: @statsdrafts), controller: 'admin/articles', 'search[state]' => 'drafts') %></p>
13
+ <p><%= link_to(t('.pages_count', count: @statspages), controller: 'admin/pages') %></p>
14
+ <p><%= link_to(t('.notes_count', count: @statuses), controller: 'admin/notes') %></p>
15
+ </div>
16
16
  <% end %>
17
- </div>
18
- <div class='col-md-6'>
19
17
  <% if can? :index, 'admin/feedback' %>
20
- <h4 class='page-header'><%= t('.feedback') %></h4>
21
- <p><%= link_to(t('.comments_count', count: @statcomments), controller: 'admin/feedback') %></p>
22
- <p><%= link_to(t('.approved_count', count: @confirmed), controller: 'admin/feedback', only: 'ham') %></p>
23
- <p><%= link_to(t('.unconfirmed_count', count: @unconfirmed), controller: 'admin/feedback', only: 'unapproved') %></p>
24
- <p><%= link_to(t('.spam_count', count: @statspam), controller: 'admin/feedback', only: 'spam') %></p>
18
+ <div class="col-lg-6">
19
+ <h4 class='mb-2 pb-2 border-bottom'><%= t('.feedback') %></h4>
20
+ <p><%= link_to(t('.comments_count', count: @statcomments), controller: 'admin/feedback') %></p>
21
+ <p><%= link_to(t('.approved_count', count: @confirmed), controller: 'admin/feedback', only: 'ham') %></p>
22
+ <p><%= link_to(t('.unconfirmed_count', count: @unconfirmed), controller: 'admin/feedback', only: 'unapproved') %></p>
23
+ <p><%= link_to(t('.spam_count', count: @statspam), controller: 'admin/feedback', only: 'spam') %></p>
24
+ </div>
25
25
  <% end %>
26
26
  </div>
27
27
  </div>
@@ -1,15 +1,15 @@
1
1
  <% content_for :page_heading do %>
2
- <h2 class="page-title"><%= t('.welcome_back', user_name: current_user.name) %></h2>
2
+ <h2 class="display-4"><%= t('.welcome_back', user_name: current_user.name) %></h2>
3
3
  <% end %>
4
4
 
5
5
  <%= render 'overview' %>
6
6
 
7
7
  <div class='row'>
8
- <div class='col-md-6'>
8
+ <div class='col-lg-6'>
9
9
  <%= render 'welcome' %>
10
10
  <%= render 'comments' %>
11
11
  </div>
12
- <div class='col-md-6'>
12
+ <div class='col-lg-6'>
13
13
  <%= render 'drafts' %>
14
14
  </div>
15
15
  </div>
@@ -1,19 +1,13 @@
1
1
  <%= hidden_field_tag 'confirmed', params[:confirmed] %>
2
2
  <%= hidden_field_tag 'published', params[:published] %>
3
3
 
4
- <div class="panel panel-default">
5
- <div class="panel-heading">
6
- <div class='form-group'>
7
- <button name='<%= "bulkop_#{position}" %>' class="btn btn-success btn-sm" value='Mark Checked Items as Ham'><%= t('.mark_checked_items_as_ham') %></button>
8
- </div>
9
- <div class='form-group'>
10
- <button name='<%= "bulkop_#{position}" %>' class="btn btn-warning btn-sm" value='Mark Checked Items as Spam'><%= t('.mark_checked_items_as_spam') %></button>
11
- </div>
12
- <div class='form-group'>
13
- <button name='<%= "bulkop_#{position}" %>' class="btn btn-danger btn-sm" value='Delete Checked Items'><%= t('.delete_checked_items') %></button>
14
- </div>
15
- <div class='form-group pull-right'>
16
- <button name='<%= "bulkop_#{position}" %>' class="btn btn-danger btn-sm" value='Delete all spam'><%= t('.delete_all_spam') %></button>
4
+ <div class="card mb-4">
5
+ <div class="card-header">
6
+ <div class="d-sm-flex">
7
+ <button name='<%= "bulkop_#{position}" %>' class="btn btn-success btn-sm me-sm-2" value='Mark Checked Items as Ham'><%= t('.mark_checked_items_as_ham') %></button>
8
+ <button name='<%= "bulkop_#{position}" %>' class="btn btn-warning btn-sm me-sm-2" value='Mark Checked Items as Spam'><%= t('.mark_checked_items_as_spam') %></button>
9
+ <button name='<%= "bulkop_#{position}" %>' class="btn btn-danger btn-sm me-sm-2" value='Delete Checked Items'><%= t('.delete_checked_items') %></button>
10
+ <button name='<%= "bulkop_#{position}" %>' class="btn btn-danger btn-sm ms-sm-auto" value='Delete all spam'><%= t('.delete_all_spam') %></button>
17
11
  </div>
18
12
  </div>
19
13
  </div>