dokno 1.1.1 → 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -6
  3. data/app/assets/javascripts/dokno.js +48 -37
  4. data/app/assets/stylesheets/dokno/application.css +1 -1
  5. data/app/controllers/dokno/application_controller.rb +1 -1
  6. data/app/controllers/dokno/articles_controller.rb +19 -11
  7. data/app/controllers/dokno/categories_controller.rb +10 -7
  8. data/app/controllers/dokno/pagination_concern.rb +5 -5
  9. data/app/controllers/dokno/user_concern.rb +6 -4
  10. data/app/helpers/dokno/application_helper.rb +1 -1
  11. data/app/models/dokno/application_record.rb +3 -0
  12. data/app/models/dokno/article.rb +91 -42
  13. data/app/models/dokno/category.rb +20 -46
  14. data/app/views/dokno/_article_formatting.html.erb +12 -12
  15. data/app/views/dokno/articles/_article_form.html.erb +49 -10
  16. data/app/views/dokno/articles/show.html.erb +44 -41
  17. data/app/views/dokno/categories/_category_form.html.erb +17 -6
  18. data/app/views/dokno/categories/index.html.erb +54 -41
  19. data/app/views/layouts/dokno/application.html.erb +36 -33
  20. data/app/views/partials/_category_header.html.erb +30 -0
  21. data/app/views/partials/_form_errors.html.erb +0 -1
  22. data/app/views/partials/_logs.html.erb +7 -5
  23. data/app/views/partials/_pagination.html.erb +20 -17
  24. data/config/routes.rb +1 -1
  25. data/db/migrate/20201203190330_baseline.rb +4 -4
  26. data/db/migrate/20201211192306_add_review_due_at_to_articles.rb +6 -0
  27. data/db/migrate/20201213165700_add_starred_to_article.rb +5 -0
  28. data/lib/dokno/config/config.rb +53 -40
  29. data/lib/dokno/engine.rb +5 -5
  30. data/lib/dokno/version.rb +1 -1
  31. data/lib/generators/dokno/templates/config/initializers/dokno.rb +18 -5
  32. metadata +82 -11
@@ -1,41 +1,33 @@
1
1
  module Dokno
2
2
  class Category < ApplicationRecord
3
- belongs_to :parent,
4
- class_name: 'Dokno::Category',
5
- primary_key: 'id',
6
- foreign_key: 'category_id',
7
- optional: true,
8
- inverse_of: :children
9
- has_many :children,
10
- class_name: 'Dokno::Category',
11
- primary_key: 'id',
12
- foreign_key: 'category_id',
13
- dependent: :nullify,
14
- inverse_of: :parent
3
+ belongs_to :parent, class_name: 'Dokno::Category', primary_key: 'id', foreign_key: 'category_id', inverse_of: :children, optional: true
4
+ has_many :children, class_name: 'Dokno::Category', primary_key: 'id', foreign_key: 'category_id', inverse_of: :parent, dependent: :nullify
15
5
 
16
6
  has_and_belongs_to_many :articles
17
7
 
8
+ before_validation :set_code
18
9
  validates :name, :code, presence: true, uniqueness: true
19
10
  validate :circular_parent_check
20
11
 
21
- before_validation :set_code
22
-
23
12
  scope :alpha_order, -> { order(:name) }
24
13
 
25
- # The display breadcrumb for the Category
26
- def breadcrumb
27
- crumbs = [name]
14
+ def breadcrumb(**args)
15
+ crumbs = [(category_link(self, args) unless args[:hide_self])]
28
16
  parent_category_id = category_id
29
17
 
30
18
  loop do
31
19
  break if parent_category_id.blank?
32
20
 
33
- parent_category = self.class.find(parent_category_id)
34
- crumbs.prepend parent_category.name
21
+ parent_category = Category.find(parent_category_id)
22
+ crumbs.prepend category_link(parent_category, args)
35
23
  parent_category_id = parent_category.category_id
36
24
  end
37
25
 
38
- crumbs.join(' > ')
26
+ crumbs.compact.join("&nbsp;&nbsp;>&nbsp;&nbsp;").html_safe
27
+ end
28
+
29
+ def category_link(category, args={})
30
+ %(<a href="#{article_index_path(category.code)}?search_term=#{CGI.escape(args[:search_term].to_s)}&order=#{CGI.escape(args[:order].to_s)}">#{category.name}</a>)
39
31
  end
40
32
 
41
33
  # All Articles in the Category, including all child Categories
@@ -45,12 +37,7 @@ module Dokno
45
37
  .joins(:categories)
46
38
  .where(dokno_categories: { id: self.class.branch(parent_category_id: id).pluck(:id) })
47
39
 
48
- records = records.updated_order if order == :updated
49
- records = records.newest_order if order == :newest
50
- records = records.view_order if order == :views
51
- records = records.alpha_order if order == :alpha
52
-
53
- records
40
+ Article.apply_sort(records, order: order)
54
41
  end
55
42
 
56
43
  def branch
@@ -59,7 +46,7 @@ module Dokno
59
46
 
60
47
  # Used to invalidate the fragment cache of the hierarchical category select options
61
48
  def self.cache_key
62
- maximum :updated_at
49
+ [maximum(:updated_at), Article.maximum(:updated_at)].compact.max
63
50
  end
64
51
 
65
52
  # The given Category and all child Categories. Useful for filtering associated articles.
@@ -78,26 +65,14 @@ module Dokno
78
65
  categories.flatten
79
66
  end
80
67
 
81
- def self.select_option_markup(selected_category_codes: nil, exclude_category_id: nil, context_category: nil, level: 0)
68
+ def self.select_option_markup(selected_category_codes: nil, context_category: nil, level: 0)
82
69
  return '' if level.positive? && context_category.blank?
83
70
 
84
71
  options = []
85
72
  level_categories = where(category_id: context_category&.id).alpha_order
86
-
87
73
  level_categories.each do |category|
88
- options << option_markup(
89
- category: category,
90
- selected_category_codes: selected_category_codes,
91
- exclude_category_id: exclude_category_id,
92
- level: level
93
- )
94
-
95
- options << select_option_markup(
96
- selected_category_codes: selected_category_codes,
97
- exclude_category_id: exclude_category_id,
98
- context_category: category,
99
- level: (level + 1)
100
- )
74
+ options << option_markup(category: category, selected_category_codes: selected_category_codes, level: level)
75
+ options << select_option_markup(selected_category_codes: selected_category_codes, context_category: category, level: (level + 1))
101
76
  end
102
77
 
103
78
  options.join
@@ -105,11 +80,10 @@ module Dokno
105
80
 
106
81
  private
107
82
 
108
- def self.option_markup(category:, selected_category_codes:, exclude_category_id:, level: 0)
109
- return '' if category.id == exclude_category_id
110
-
83
+ def self.option_markup(category:, selected_category_codes:, level: 0)
111
84
  selected = selected_category_codes&.include?(category.code)
112
- %(<option value="#{category.code}" #{'selected="selected"' if selected}>#{('&nbsp;&nbsp;' * level)}#{category.name}</option>)
85
+ article_count = category.articles_in_branch.size
86
+ %(<option value="#{category.code}" #{'selected="selected"' if selected}>#{('&nbsp;&nbsp;' * level)}#{category.name}#{' (' + article_count.to_s + ')' if article_count.positive?}</option>)
113
87
  end
114
88
 
115
89
  # Never allow setting of parent to self
@@ -2,8 +2,8 @@
2
2
  /* Article formatting */
3
3
  .dokno-article-content-markup {
4
4
  font-weight: 400;
5
- line-height: 1.75rem;
6
- font-size: 1rem;
5
+ line-height: 1.75em;
6
+ font-size: 1em;
7
7
  }
8
8
 
9
9
  .dokno-article-content-markup code {
@@ -23,7 +23,7 @@
23
23
  .dokno-article-content-markup ol,
24
24
  .dokno-article-content-markup hr,
25
25
  .dokno-article-content-markup table {
26
- margin-bottom: 1.25rem;
26
+ margin-bottom: 1.25em;
27
27
  }
28
28
 
29
29
  .dokno-article-content-markup h1,
@@ -32,28 +32,28 @@
32
32
  .dokno-article-content-markup h4,
33
33
  .dokno-article-content-markup h5,
34
34
  .dokno-article-content-markup h6 {
35
- margin-bottom: 1.25rem;
35
+ margin-bottom: 1.25em;
36
36
  font-weight: 600;
37
37
  }
38
38
 
39
39
  .dokno-article-content-markup h1 {
40
- font-size: 1.625rem;
40
+ font-size: 1.625em;
41
41
  }
42
42
 
43
43
  .dokno-article-content-markup h2 {
44
- font-size: 1.25rem;
44
+ font-size: 1.25em;
45
45
  }
46
46
 
47
47
  .dokno-article-content-markup h3,
48
48
  .dokno-article-content-markup h4,
49
49
  .dokno-article-content-markup h5,
50
50
  .dokno-article-content-markup h6 {
51
- font-size: 1rem;
51
+ font-size: 1em;
52
52
  }
53
53
 
54
54
  .dokno-article-content-markup blockquote {
55
55
  border-left: 4px solid #edf2f7;
56
- padding-left: 1.25rem;
56
+ padding-left: 1.25em;
57
57
  }
58
58
 
59
59
  .dokno-article-content-markup ul,
@@ -75,10 +75,10 @@
75
75
 
76
76
  .dokno-article-content-markup table th,
77
77
  .dokno-article-content-markup table td {
78
- padding-left: 1rem;
79
- padding-right: 1rem;
80
- padding-top: 0.5rem;
81
- padding-bottom: 0.5rem;
78
+ padding-left: 1em;
79
+ padding-right: 1em;
80
+ padding-top: 0.5em;
81
+ padding-bottom: 0.5em;
82
82
  font-weight: normal;
83
83
  }
84
84
 
@@ -3,8 +3,31 @@
3
3
  <section class="mb-10">
4
4
 
5
5
  <div class="text-2xl mb-5 text-gray-500"><%= article.persisted? ? 'Edit' : 'New' %> Article</div>
6
- <h1 class="text-4xl mb-5 leading-tight font-light"><%= article.title %></h1>
7
- <hr class="mb-5" />
6
+ <h1 class="text-4xl leading-tight font-light"><%= article.title %></h1>
7
+ <hr class="mt-8 mb-10" />
8
+
9
+ <% if article.up_for_review? %>
10
+ <div class="bg-gray-800 text-gray-100 rounded mb-10">
11
+ <h2 class="bg-gray-900 p-10 rounded-t text-2xl leading-tight font-light">Review for Accuracy / Relevance</h2>
12
+
13
+ <div class="flex p-10">
14
+ <div class="w-1/2 pr-5">
15
+ <div>
16
+ <span class="text-lg font-semibold"><label for="reset_review_date">Reset the next review date?</label></span>
17
+ <input onchange="setReviewForm();" type="checkbox" name="reset_review_date" id="reset_review_date" value="true" class="ml-2 text-lg" <%= "checked='checked'" if @reset_review_date %> />
18
+ </div>
19
+ <div class="text-gray-400">
20
+ <%= article.review_due_days_string %>.
21
+ Check the box above to mark this article as reviewed and reset the next review date to <span class="px-2 py-1 bg-gray-900 rounded text-white font-semibold"><%= "#{Date.today + Dokno.config.article_review_period}" %></span>
22
+ </div>
23
+ </div>
24
+ <div class="w-1/2">
25
+ <div class="text-lg font-semibold"><label for="review_notes">Reviewer Notes</label></div>
26
+ <textarea placeholder="Any review notes here will be added to the change history for this article" name="review_notes" id="review_notes" class="rounded text-xl shadow-inner bg-gray-100 p-3 mt-2 w-full text-gray-900" rows="2"><%= @review_notes %></textarea>
27
+ </div>
28
+ </div>
29
+ </div>
30
+ <% end %>
8
31
 
9
32
  <div class="mb-5">
10
33
  <div class="text-lg font-semibold"><label for="slug">Slug</label></div>
@@ -13,9 +36,9 @@
13
36
  </div>
14
37
 
15
38
  <% if Dokno::Category.exists? %>
16
- <div class="mb-5">
17
- <div class="text-lg font-semibold"><label for="category_id"><%= 'Category'.pluralize article.categories.count %></label></div>
18
- <select name="category_code[]" id="category_code" multiple="multiple" size="<%= (cat_count = Dokno::Category.count) <= 4 ? (cat_count + 1).to_s : '5' %>" class="rounded text-xl shadow-inner bg-gray-100 p-2 w-full mt-2 w-full max-w-full">
39
+ <div class="mb-5 w-2/3">
40
+ <div class="text-lg font-semibold"><label for="category_code"><%= 'Category'.pluralize article.categories.count %></label></div>
41
+ <select name="category_code[]" id="category_code" multiple="multiple" size="<%= (cat_count = Dokno::Category.count) <= 9 ? (cat_count + 1).to_s : '10' %>" class="rounded text-xl shadow-inner bg-gray-100 p-2 w-full mt-2 w-full max-w-full">
19
42
  <option></option>
20
43
  <%= Dokno::Category.select_option_markup(selected_category_codes: @category_codes).html_safe %>
21
44
  </select>
@@ -27,7 +50,7 @@
27
50
  </div>
28
51
  <% end %>
29
52
 
30
- <hr class="mb-5" />
53
+ <hr class="my-10" />
31
54
 
32
55
  <div class="mb-5">
33
56
  <div class="text-lg font-semibold"><label for="title">Title</label></div>
@@ -47,16 +70,32 @@
47
70
  <a id="markdown_write_link" href="javascript:;" onclick="writeArticleToggle();" class="float-right hidden"><i data-feather="pen-tool" class="inline"></i> Write</a>
48
71
  <a id="markdown_preview_link" href="javascript:;" onclick="previewArticleToggle();" class="float-right"><i data-feather="eye" class="inline"></i> Preview</a>
49
72
  </div>
50
- <textarea placeholder="Full article content" name="markdown" id="markdown" class="rounded text-xl shadow-inner bg-gray-100 p-3 mt-2 w-full" rows="10"><%= article.persisted? ? article.markdown : (article.markdown.presence || @template) %></textarea>
73
+ <textarea placeholder="Full article content" name="markdown" id="markdown" class="rounded text-xl shadow-inner bg-gray-100 p-3 mt-2 w-full" rows="20"><%= article.persisted? ? article.markdown : (article.markdown.presence || @template) %></textarea>
51
74
  <div id="markdown_preview" class="dokno-article-content-markup hidden text-lg overflow-hidden overflow-y-auto rounded p-10 mt-2 bg-gray-100 shadow-inner"></div>
52
75
  <div class="text-gray-500 mt-2">Detailed documentation of the described topic. Basic HTML &amp; <a target="_blank" href="https://commonmark.org/help/" title="Markdown formatting examples">markdown</a> OK.</div>
53
76
  </div>
54
77
 
55
- <div class="mt-10">
78
+ <div class="mb-5">
79
+ <div>
80
+ <span class="text-lg font-semibold"><label for="starred">Starred article?</label></span>
81
+ <input type="hidden" name="starred" value="false" />
82
+ <input type="checkbox" name="starred" id="starred" value="true" class="ml-2 text-lg" <%= "checked='checked'" if article.starred %> />
83
+ </div>
84
+ <div class="text-gray-400">
85
+ Starred articles are always listed first when browsing.
86
+ </div>
87
+ </div>
88
+
89
+ <hr class="my-10" />
90
+
91
+ <div class="text-right">
92
+ <span class="text-lg mr-5"><a class="no-underline" href="<%= article.persisted? ? article_path(article.slug) : root_path %>">Cancel</a></span>
56
93
  <button type="submit" class="bg-gray-700 text-gray-300 hover:bg-gray-900 hover:text-white rounded py-2 px-3 font-bold"><i data-feather="check" class="inline h-5"></i> SAVE ARTICLE</button>
57
- <span class="text-lg ml-5"><a class="no-underline" href="<%= article.persisted? ? article_path(article.slug) : root_path %>">Cancel</a></span>
58
94
  </div>
59
95
 
60
96
  </section>
61
97
 
62
- <script> elem('input#slug').focus(); </script>
98
+ <script>
99
+ elem('input#slug').focus();
100
+ setReviewForm();
101
+ </script>
@@ -1,34 +1,37 @@
1
1
  <%= render 'dokno/article_formatting' %>
2
2
 
3
- <section>
4
- <% if @article.categories.present? %>
5
- <div class="text-gray-500 mb-5">
6
- <% @article.categories.each do |category| %>
7
- <div><a href="<%= article_index_path(category.code) %>"><%= category.breadcrumb %></a></div>
3
+ <% if @category.present? %>
4
+ <div class="no-print text-gray-500 mb-5">
5
+ <div>
6
+ <% if request.referrer&.include? article_index_path %>
7
+ <a class="fixed left-0 ml-6" title="Back" href="javascript:history.back();"><i data-feather="chevrons-left" class="text-gray-300 h-5 align-text-bottom inline"></i></a>
8
8
  <% end %>
9
+ <span class="text-gray-500 mr-1">Under</span> <%= @category.breadcrumb(search_term: @search_term, order: @order) %>
9
10
  </div>
10
- <% end %>
11
+ </div>
12
+ <% end %>
13
+
14
+ <%= render 'partials/category_header' %>
15
+
16
+ <div class="my-10 no-print"><hr /></div>
11
17
 
18
+ <section>
12
19
  <div class="flex">
13
20
  <div id="dokno-article-sidebar" class="w-2/5 pr-10">
14
- <div id="article-deprecated-alert" class="<%= 'hidden' if @article.active %> bg-yellow-700 p-4 mb-5 rounded text-lg border-t-4 border-yellow-900 text-white font-base">
15
- <i data-feather="alert-circle" class="inline-block mr-2"></i> This article is no longer active
16
- </div>
17
-
18
- <h1 class="dokno-article-content-highlight text-4xl mb-10 leading-tight font-light"><%= @article.title %></h1>
21
+ <h1 class="dokno-article-content-highlight text-4xl mb-8 leading-tight font-light">
22
+ <%= @article.title %>
23
+ <% if @article.starred %><i title="Starred article" data-feather="star" class="inline align-middle"></i><% end %>
24
+ </h1>
19
25
 
20
26
  <div>
21
- <div class="mb-10 no-print">
22
- <button title="Email article" class="bg-gray-600 text-gray-300 hover:text-white rounded mr-3 py-2 px-3 font-bold text-xs" onclick="window.open('mailto:?subject=<%= CGI.escape(@article.title) %>&body=<%= CGI.escape(article_url(@article.slug)) %>');"><i data-feather="send" class="inline h-4"></i> EMAIL THIS</button>
23
- <button title="Print article" class="bg-gray-600 text-gray-300 hover:text-white rounded mr-3 py-2 px-3 font-bold text-xs" onclick="window.print();"><i data-feather="printer" class="inline h-4"></i> PRINT THIS</button>
24
-
25
- <% if can_edit? %>
26
- <button title="Edit article" class="bg-blue-900 text-gray-300 hover:text-white rounded py-2 px-3 font-bold text-xs" onclick="location.href='<%= edit_article_path(@article.slug) %>';"><i data-feather="edit" class="inline h-4"></i> EDIT THIS</button>
27
- <% end %>
27
+ <div class="mb-8 no-print">
28
+ <button title="Copy link" class="bg-gray-600 text-gray-300 hover:text-white rounded my-2 mr-2 py-2 px-3 font-bold text-sm" onclick="copyToClipboard('<%= @article.permalink(request.base_url) %>');"><i data-feather="clipboard" class="inline h-5"></i> COPY</button>
29
+ <button title="Email article" class="bg-gray-600 text-gray-300 hover:text-white rounded my-2 mr-2 py-2 px-3 font-bold text-sm" onclick="window.open('mailto:?subject=<%= CGI.escape(@article.title.to_s) %>&body=<%= CGI.escape(article_url(@article.slug)) %>');"><i data-feather="send" class="inline h-5"></i> EMAIL</button>
30
+ <button title="Print article" class="bg-gray-600 text-gray-300 hover:text-white rounded my-2 py-2 px-3 font-bold text-sm" onclick="window.print();"><i data-feather="printer" class="inline h-5"></i> PRINT</button>
28
31
  </div>
29
32
 
30
33
  <div class="flex">
31
- <div class="no-print w-8"><i data-feather="clock" class="inline-block h-5"></i></div>
34
+ <div class="no-print w-8"><i data-feather="clock" class="inline-block h-5 mr-2"></i></div>
32
35
  <div class="w-full">
33
36
  Last updated:<br />
34
37
  <%= time_ago_in_words @article.updated_at %> ago
@@ -39,7 +42,7 @@
39
42
  </div>
40
43
 
41
44
  <div class="mt-5 flex no-print">
42
- <div class="no-print w-8"><i data-feather="eye" class="inline-block h-5"></i></div>
45
+ <div class="no-print w-8"><i data-feather="eye" class="inline-block h-5 mr-2"></i></div>
43
46
  <div class="w-full">
44
47
  Views:<br />
45
48
  <%= number_with_delimiter(@article.views, delimiter: ',') %>
@@ -48,7 +51,7 @@
48
51
 
49
52
  <% if @article.contributors.present? %>
50
53
  <div class="mt-5 flex">
51
- <div class="no-print w-8"><i data-feather="user-check" class="inline-block h-5"></i></div>
54
+ <div class="no-print w-8"><i data-feather="user-check" class="inline-block h-5 mr-2"></i></div>
52
55
  <div class="w-full">
53
56
  Contributors:<br />
54
57
  <%= @article.contributors %>
@@ -58,7 +61,7 @@
58
61
 
59
62
  <% if @article.reading_time.present? %>
60
63
  <div class="mt-5 flex no-print">
61
- <div class="no-print w-8"><i data-feather="watch" class="inline-block h-5"></i></div>
64
+ <div class="no-print w-8"><i data-feather="watch" class="inline-block h-5 mr-2"></i></div>
62
65
  <div class="w-full">
63
66
  Reading time:<br />
64
67
  <%= @article.reading_time %>
@@ -67,7 +70,7 @@
67
70
  <% end %>
68
71
 
69
72
  <div class="mt-5 flex">
70
- <div class="no-print w-8"><i data-feather="link" class="inline-block h-5"></i></div>
73
+ <div class="no-print w-8"><i data-feather="link" class="inline-block h-5 mr-2"></i></div>
71
74
  <div class="w-full">
72
75
  Permalink:<br />
73
76
  <a class="inline-block mt-1 -ml-2 px-2 py-1 bg-blue-100 rounded" title="Copy to clipboard" href="javascript:;" onclick="copyToClipboard('<%= @article.permalink(request.base_url) %>');"><%= @article.permalink(request.base_url) %></a>
@@ -76,7 +79,7 @@
76
79
 
77
80
  <% if can_edit? %>
78
81
  <div class="mt-5 flex no-print">
79
- <div class="no-print w-8"><i data-feather="crosshair" class="inline-block h-5"></i></div>
82
+ <div class="no-print w-8"><i data-feather="crosshair" class="inline-block h-5 mr-2"></i></div>
80
83
  <div class="w-full">
81
84
  Unique slug:<br />
82
85
  <a class="inline-block mt-1 -ml-2 px-2 py-1 bg-blue-100 rounded" title="Copy to clipboard" href="javascript:;" onclick="copyToClipboard('<%= j @article.slug %>');"><%= @article.slug %></a>
@@ -93,12 +96,11 @@
93
96
  </div>
94
97
  <% end %>
95
98
 
96
- <% if @article.categories.exists? %>
99
+ <% if (category_name_list = @article.category_name_list(context_category_id: @category&.id)).present? %>
97
100
  <div class="mt-5 flex no-print">
98
- <div class="no-print w-8"><i data-feather="folder" class="inline-block h-5"></i></div>
101
+ <div class="no-print w-8"><i data-feather="folder" class="inline-block h-5 mr-2"></i></div>
99
102
  <div class="w-full">
100
- <%= 'Category'.pluralize @article.categories.count %>:<br />
101
- <%= @article.category_name_list.split(':').last.html_safe %>
103
+ <%= category_name_list.sub(':', ':<br />').html_safe %>
102
104
  </div>
103
105
  </div>
104
106
  <% end %>
@@ -118,21 +120,22 @@
118
120
  <% if @article.summary.blank? && @article.markdown.blank? %>
119
121
  <div class="mb-10">No content</div>
120
122
  <% end %>
121
-
122
- <% if can_edit? %>
123
- <div class="no-print">
124
- <hr class="mt-5" />
125
- <div class="mt-10 text-right">
126
- <button title="Deactivate article" id="article-deactivate-button" class="bg-yellow-700 text-gray-300 hover:text-white rounded mr-3 py-2 px-3 font-bold text-xs <% if !@article.active %>hidden<% end %>" onclick="deactiveArticle('<%= @article.slug %>');"><i data-feather="sunset" class="inline h-4"></i> DEACTIVATE THIS</button>
127
- <button title="Re-activate article" id="article-activate-button" class="bg-green-700 text-gray-300 hover:text-white rounded mr-3 py-2 px-3 font-bold text-xs <% if @article.active %>hidden<% end %>" onclick="activeArticle('<%= @article.slug %>');"><i data-feather="sunrise" class="inline h-4"></i> RE-ACTIVATE THIS</button>
128
- <button title="Permanently delete article" class="bg-red-700 text-gray-300 hover:text-white rounded py-2 px-3 font-bold text-xs" onclick="deleteArticle('<%= @article.id %>');"><i data-feather="trash" class="inline h-4"></i> DELETE THIS</button>
129
- </div>
130
- </div>
131
- <% end %>
132
123
  </div>
133
124
  </div>
134
125
  </section>
135
126
 
136
- <% if @search_term.present? %>
137
- <script>highlightTerm(['<%= j @search_term.strip %>'], 'dokno-article-content-highlight');</script>
127
+ <% if can_edit? %>
128
+ <div class="no-print">
129
+ <hr class="mt-5 mb-10" />
130
+ <div class="text-right">
131
+ <button title="Edit article" class="bg-blue-900 text-gray-300 hover:text-white rounded mr-5 py-2 px-3 font-bold text-sm" onclick="location.href='<%= edit_article_path(@article.slug) %>';"><i data-feather="edit-2" class="inline h-5"></i> EDIT</button>
132
+
133
+ <% if @article.active %>
134
+ <button title="Deactivate article" id="article-deactivate-button" class="bg-yellow-700 text-gray-300 hover:text-white rounded mr-5 py-2 px-3 font-bold text-base" onclick="setArticleStatus('<%= @article.slug %>', false);"><i data-feather="file-minus" class="inline h-5"></i> DEACTIVATE</button>
135
+ <% else %>
136
+ <button title="Re-activate article" id="article-activate-button" class="bg-green-700 text-gray-300 hover:text-white rounded mr-5 py-2 px-3 font-bold text-base" onclick="setArticleStatus('<%= @article.slug %>', true);"><i data-feather="file-plus" class="inline h-5"></i> RE-ACTIVATE</button>
137
+ <% end %>
138
+ <button title="Permanently delete article" class="bg-red-700 text-gray-300 hover:text-white rounded py-2 px-3 font-bold text-base" onclick="deleteArticle('<%= @article.id %>');"><i data-feather="slash" class="inline h-5"></i> DELETE</button>
139
+ </div>
140
+ </div>
138
141
  <% end %>
@@ -10,13 +10,20 @@
10
10
  </div>
11
11
 
12
12
  <% if Dokno::Category.exists? %>
13
- <div class="mb-5">
13
+ <div class="mb-5 w-1/2">
14
14
  <div class="text-lg font-semibold"><label for="parent_category_code">Parent Category</label></div>
15
- <select name="parent_category_code" id="parent_category_code" class="rounded text-xl shadow-inner bg-gray-100 p-2 mt-2 w-1/2 max-w-full">
16
- <option></option>
17
- <%= Dokno::Category.select_option_markup(selected_category_codes: [@parent_category_code], exclude_category_id: category.id).html_safe %>
15
+
16
+ <select name="parent_category_code" id="parent_category_code" size="1" class="rounded text-xl shadow-inner bg-gray-100 p-2 w-full max-w-full">
17
+ <option value="">No Parent Category</option>
18
+
19
+ <optgroup label="Categories">
20
+ <% cache Dokno::Category do %>
21
+ <%= Dokno::Category.select_option_markup.html_safe %>
22
+ <% end %>
23
+ </optgroup>
18
24
  </select>
19
- <div class="text-gray-500 mt-2 w-1/2">
25
+
26
+ <div class="text-gray-500 mt-2">
20
27
  The existing category to which this category belongs.
21
28
  <% if (article_count = category.articles.count).positive? %>
22
29
  There <%= article_count > 1 ? "are #{article_count} articles" : 'is an article' %> currently in this category.
@@ -35,4 +42,8 @@
35
42
  </div>
36
43
  </section>
37
44
 
38
- <script> elem('input#name').focus(); </script>
45
+ <script>
46
+ // Client-side select of cached select list
47
+ selectOption('parent_category_code', '<%= j @parent_category_code %>');
48
+ elem('input#name').focus();
49
+ </script>