dokno 1.1.1 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +6 -6
- data/app/assets/javascripts/dokno.js +48 -37
- data/app/assets/stylesheets/dokno/application.css +1 -1
- data/app/controllers/dokno/application_controller.rb +1 -1
- data/app/controllers/dokno/articles_controller.rb +19 -11
- data/app/controllers/dokno/categories_controller.rb +10 -7
- data/app/controllers/dokno/pagination_concern.rb +5 -5
- data/app/controllers/dokno/user_concern.rb +6 -4
- data/app/helpers/dokno/application_helper.rb +1 -1
- data/app/models/dokno/application_record.rb +3 -0
- data/app/models/dokno/article.rb +91 -42
- data/app/models/dokno/category.rb +20 -46
- data/app/views/dokno/_article_formatting.html.erb +12 -12
- data/app/views/dokno/articles/_article_form.html.erb +49 -10
- data/app/views/dokno/articles/show.html.erb +44 -41
- data/app/views/dokno/categories/_category_form.html.erb +17 -6
- data/app/views/dokno/categories/index.html.erb +54 -41
- data/app/views/layouts/dokno/application.html.erb +36 -33
- data/app/views/partials/_category_header.html.erb +30 -0
- data/app/views/partials/_form_errors.html.erb +0 -1
- data/app/views/partials/_logs.html.erb +7 -5
- data/app/views/partials/_pagination.html.erb +20 -17
- data/config/routes.rb +1 -1
- data/db/migrate/20201203190330_baseline.rb +4 -4
- data/db/migrate/20201211192306_add_review_due_at_to_articles.rb +6 -0
- data/db/migrate/20201213165700_add_starred_to_article.rb +5 -0
- data/lib/dokno/config/config.rb +53 -40
- data/lib/dokno/engine.rb +5 -5
- data/lib/dokno/version.rb +1 -1
- data/lib/generators/dokno/templates/config/initializers/dokno.rb +18 -5
- metadata +82 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 558cf95e53481112844c415ae83b6c7ed5f21bf2c269f7646868c6bca3958b71
|
|
4
|
+
data.tar.gz: 56485eb91c404fbd4f1b1b242c90f7635bf86a236cbb31767e94dcc55341a2b0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 03d3bb86f5ee4ce3b3c7acdb4569b17486634a8feec4b79bb41f12968d46e9055b487634b48fe4047e0724146f2d5c021cb02487a940e4419fb1ffd5ced200a8
|
|
7
|
+
data.tar.gz: 788fed474d13b418312e404dd229bd683d685ffb85c0cbc18a086177f981061506d8edffbd37c19ce37f5db1a0324c8d657f5505fc3fda9e0f3d59fb4109ed49
|
data/README.md
CHANGED
|
@@ -36,11 +36,11 @@ To enable [in-context articles](#in-context-article-links) in your app, add the
|
|
|
36
36
|
<%= render 'dokno/article_panel' %>
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
### Configuration
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
#### Dokno Settings
|
|
42
|
+
|
|
43
|
+
Running `rails g dokno:install` creates `/config/initializers/dokno.rb` within your app, containing the available Dokno configuration options. Remember to restart your app whenever you make configuration changes.
|
|
44
44
|
|
|
45
45
|
### Articles
|
|
46
46
|
|
|
@@ -72,8 +72,6 @@ Clicking a link fetches the article asynchronously and reveals it to the user vi
|
|
|
72
72
|
|
|
73
73
|
<%= dokno_article_link({link-text}, slug: {unique-article-slug}) %>
|
|
74
74
|
|
|
75
|
-
<img src="./README/host_app_flyout.png" width="50%">
|
|
76
|
-
|
|
77
75
|
### Dokno Data Querying
|
|
78
76
|
You typically won't need to interact with Dokno data directly, but it is stored within your database and is accessible via ActiveRecord as is any other model.
|
|
79
77
|
|
|
@@ -119,6 +117,8 @@ $ bundle exec rspec
|
|
|
119
117
|
```
|
|
120
118
|
|
|
121
119
|
## Hat Tips
|
|
120
|
+
- [tailwindcss](https://tailwindcss.com/)
|
|
121
|
+
- CSS framework
|
|
122
122
|
- [diffy](https://github.com/samg/diffy)
|
|
123
123
|
- Text diffing Ruby gem
|
|
124
124
|
- [Feather Icons](https://github.com/feathericons/feather)
|
|
@@ -1,5 +1,23 @@
|
|
|
1
|
+
const dokno__search_hotkey_listener = function(e) {
|
|
2
|
+
if (e.key === '/') { handleSearchHotKey(); }
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function handleSearchHotKey() {
|
|
6
|
+
const search_input = elem('input#search_term');
|
|
7
|
+
search_input.focus();
|
|
8
|
+
search_input.select();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function enableSearchHotkey() {
|
|
12
|
+
document.addEventListener('keyup', dokno__search_hotkey_listener, false);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function disableSearchHotkey() {
|
|
16
|
+
document.removeEventListener('keyup', dokno__search_hotkey_listener, false);
|
|
17
|
+
}
|
|
18
|
+
|
|
1
19
|
function copyToClipboard(text) {
|
|
2
|
-
window.prompt('Copy to clipboard: CTRL+C, Enter', text);
|
|
20
|
+
window.prompt('Copy to clipboard: CTRL + C, Enter', text);
|
|
3
21
|
}
|
|
4
22
|
|
|
5
23
|
function elem(selector) {
|
|
@@ -22,17 +40,18 @@ function selectOption(id, value) {
|
|
|
22
40
|
}
|
|
23
41
|
}
|
|
24
42
|
|
|
25
|
-
function
|
|
43
|
+
function applyCategoryCriteria(category_code, term, order) {
|
|
26
44
|
goToPage(dokno__base_path + category_code + '?search_term=' + term + '&order=' + order);
|
|
27
45
|
}
|
|
28
46
|
|
|
29
|
-
function search(term, order) {
|
|
30
|
-
goToPage('?search_term=' + term + '&order=' + order);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
47
|
function goToPage(url) {
|
|
34
48
|
var param_join = url.indexOf('?') >= 0 ? '&' : '?';
|
|
35
|
-
location.href=url + param_join + '_=' + Math.round(new Date().getTime());
|
|
49
|
+
location.href = url + param_join + '_=' + Math.round(new Date().getTime());
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function reloadPage() {
|
|
53
|
+
window.onbeforeunload = function () { window.scrollTo(0, 0); }
|
|
54
|
+
location.reload();
|
|
36
55
|
}
|
|
37
56
|
|
|
38
57
|
function sendRequest(url, data, callback, method) {
|
|
@@ -55,24 +74,9 @@ function sendRequest(url, data, callback, method) {
|
|
|
55
74
|
request.send(JSON.stringify(data));
|
|
56
75
|
}
|
|
57
76
|
|
|
58
|
-
function
|
|
59
|
-
const callback = function(_data) {
|
|
60
|
-
|
|
61
|
-
elem('div#article-deprecated-alert').classList.remove('hidden');
|
|
62
|
-
elem('button#article-activate-button').classList.remove('hidden');
|
|
63
|
-
reloadLogs();
|
|
64
|
-
}
|
|
65
|
-
sendRequest(dokno__base_path + 'article_status', { slug: slug, active: false }, callback, 'POST');
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function activeArticle(slug) {
|
|
69
|
-
const callback = function(_data) {
|
|
70
|
-
elem('button#article-activate-button').classList.add('hidden');
|
|
71
|
-
elem('div#article-deprecated-alert').classList.add('hidden');
|
|
72
|
-
elem('button#article-deactivate-button').classList.remove('hidden');
|
|
73
|
-
reloadLogs();
|
|
74
|
-
}
|
|
75
|
-
sendRequest(dokno__base_path + 'article_status', { slug: slug, active: true }, callback, 'POST');
|
|
77
|
+
function setArticleStatus(slug, active) {
|
|
78
|
+
const callback = function(_data) { reloadPage(); }
|
|
79
|
+
sendRequest(dokno__base_path + 'article_status', { slug: slug, active: active }, callback, 'POST');
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
function deleteArticle(id) {
|
|
@@ -148,18 +152,6 @@ function toggleVisibility(selector_id) {
|
|
|
148
152
|
initIcons();
|
|
149
153
|
}
|
|
150
154
|
|
|
151
|
-
function reloadLogs() {
|
|
152
|
-
var $log_container = elem('div#dokno-article-log-container');
|
|
153
|
-
var category_id = $log_container.getAttribute('data-category-id');
|
|
154
|
-
var article_id = $log_container.getAttribute('data-article-id');
|
|
155
|
-
|
|
156
|
-
const callback = function(markup) {
|
|
157
|
-
elem('div#dokno-article-log-container').innerHTML = markup;
|
|
158
|
-
initIcons();
|
|
159
|
-
}
|
|
160
|
-
sendRequest(dokno__base_path + 'article_log', { category_id: category_id, article_id: article_id }, callback, 'POST');
|
|
161
|
-
}
|
|
162
|
-
|
|
163
155
|
// Pass containers_selector as class name (no prefix)
|
|
164
156
|
function highlightTerm(terms, containers_selector) {
|
|
165
157
|
var containers = elems(containers_selector);
|
|
@@ -178,3 +170,22 @@ function highlightTerm(terms, containers_selector) {
|
|
|
178
170
|
function wrapTermWithHTML(term) {
|
|
179
171
|
return `<span title="Matching search term" class="dokno-search-term bg-yellow-300 text-gray-900 p-2 rounded mx-1">${term}</span>`
|
|
180
172
|
}
|
|
173
|
+
|
|
174
|
+
function setReviewForm() {
|
|
175
|
+
const reset_review_date_checkbox = elem('input#reset_review_date');
|
|
176
|
+
const review_notes_textarea = elem('textarea#review_notes');
|
|
177
|
+
|
|
178
|
+
if (!reset_review_date_checkbox) {
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (reset_review_date_checkbox.checked) {
|
|
183
|
+
review_notes_textarea.removeAttribute('disabled');
|
|
184
|
+
review_notes_textarea.classList.remove('cursor-not-allowed');
|
|
185
|
+
review_notes_textarea.focus();
|
|
186
|
+
} else {
|
|
187
|
+
review_notes_textarea.setAttribute('disabled', 'disabled');
|
|
188
|
+
review_notes_textarea.classList.add('cursor-not-allowed');
|
|
189
|
+
reset_review_date_checkbox.focus();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -8,7 +8,19 @@ module Dokno
|
|
|
8
8
|
|
|
9
9
|
def show
|
|
10
10
|
redirect_to root_path if @article.blank?
|
|
11
|
+
|
|
11
12
|
@search_term = params[:search_term]
|
|
13
|
+
@order = params[:order]
|
|
14
|
+
@category = Category.find_by(code: params[:cat_code].to_s.strip) if params[:cat_code].present?
|
|
15
|
+
@category = @article.categories.first if @category.blank?
|
|
16
|
+
|
|
17
|
+
if !@article.active
|
|
18
|
+
flash.now[:yellow] = 'This article is no longer active'
|
|
19
|
+
elsif @article.up_for_review?
|
|
20
|
+
flash_msg = @article.review_due_days_string
|
|
21
|
+
flash_msg += " - <a href='#{edit_article_path(@article.slug)}' class='font-bold'>review it now</a>" if can_edit?
|
|
22
|
+
flash.now[:gray] = flash_msg
|
|
23
|
+
end
|
|
12
24
|
end
|
|
13
25
|
|
|
14
26
|
def new
|
|
@@ -27,7 +39,7 @@ module Dokno
|
|
|
27
39
|
set_editor_username
|
|
28
40
|
|
|
29
41
|
if @article.save
|
|
30
|
-
flash[:green]
|
|
42
|
+
flash[:green] = 'Article was created'
|
|
31
43
|
@article.categories = Category.where(code: params[:category_code]) if params[:category_code].present?
|
|
32
44
|
redirect_to article_path @article.slug
|
|
33
45
|
else
|
|
@@ -44,12 +56,14 @@ module Dokno
|
|
|
44
56
|
set_editor_username
|
|
45
57
|
|
|
46
58
|
if @article.update(article_params)
|
|
47
|
-
flash[:green]
|
|
59
|
+
flash[:green] = 'Article was updated'
|
|
48
60
|
@article.categories = Category.where(code: params[:category_code])
|
|
49
61
|
redirect_to article_path @article.slug
|
|
50
62
|
else
|
|
51
|
-
flash.now[:red]
|
|
52
|
-
@category_codes
|
|
63
|
+
flash.now[:red] = 'Article could not be updated'
|
|
64
|
+
@category_codes = params[:category_code]
|
|
65
|
+
@reset_review_date = params[:reset_review_date]
|
|
66
|
+
@review_notes = params[:review_notes]
|
|
53
67
|
render :edit
|
|
54
68
|
end
|
|
55
69
|
end
|
|
@@ -79,16 +93,10 @@ module Dokno
|
|
|
79
93
|
render json: {}, layout: false
|
|
80
94
|
end
|
|
81
95
|
|
|
82
|
-
# Ajax-fetched article change log
|
|
83
|
-
def article_log
|
|
84
|
-
render partial: '/partials/logs',
|
|
85
|
-
locals: { article: Article.find_by(id: params[:article_id].to_i) }, layout: false
|
|
86
|
-
end
|
|
87
|
-
|
|
88
96
|
private
|
|
89
97
|
|
|
90
98
|
def article_params
|
|
91
|
-
params.permit(:slug, :title, :summary, :markdown)
|
|
99
|
+
params.permit(:slug, :title, :summary, :markdown, :reset_review_date, :review_notes, :starred)
|
|
92
100
|
end
|
|
93
101
|
|
|
94
102
|
def fetch_article
|
|
@@ -6,11 +6,14 @@ module Dokno
|
|
|
6
6
|
before_action :fetch_category, only: [:index, :edit, :update]
|
|
7
7
|
|
|
8
8
|
def index
|
|
9
|
-
@search_term
|
|
10
|
-
@order
|
|
11
|
-
@order
|
|
9
|
+
@search_term = params[:search_term]
|
|
10
|
+
@order = params[:order]&.strip
|
|
11
|
+
@order = 'updated' unless %w(updated newest views alpha).include?(@order)
|
|
12
|
+
@show_up_for_review = can_edit? && !request.path.include?(up_for_review_path)
|
|
12
13
|
|
|
13
|
-
articles = if
|
|
14
|
+
articles = if request.path.include? up_for_review_path
|
|
15
|
+
Article.up_for_review(order: @order&.to_sym)
|
|
16
|
+
elsif @search_term.present?
|
|
14
17
|
Article.search(term: @search_term, category_id: @category&.id, order: @order&.to_sym)
|
|
15
18
|
elsif @category.present?
|
|
16
19
|
@category.articles_in_branch(order: @order&.to_sym)
|
|
@@ -22,7 +25,7 @@ module Dokno
|
|
|
22
25
|
end
|
|
23
26
|
|
|
24
27
|
def new
|
|
25
|
-
@category
|
|
28
|
+
@category = Category.new
|
|
26
29
|
@parent_category_code = params[:parent_category_code]
|
|
27
30
|
end
|
|
28
31
|
|
|
@@ -38,7 +41,7 @@ module Dokno
|
|
|
38
41
|
flash[:green] = 'Category was created'
|
|
39
42
|
redirect_to article_index_path(@category.code)
|
|
40
43
|
else
|
|
41
|
-
flash.now[:red]
|
|
44
|
+
flash.now[:red] = 'Category could not be created'
|
|
42
45
|
@parent_category_code = params[:parent_category_code]
|
|
43
46
|
render :new
|
|
44
47
|
end
|
|
@@ -51,7 +54,7 @@ module Dokno
|
|
|
51
54
|
flash[:green] = 'Category was updated'
|
|
52
55
|
redirect_to article_index_path(@category.code)
|
|
53
56
|
else
|
|
54
|
-
flash.now[:red]
|
|
57
|
+
flash.now[:red] = 'Category could not be updated'
|
|
55
58
|
@parent_category_code = params[:parent_category_code]
|
|
56
59
|
render :edit
|
|
57
60
|
end
|
|
@@ -3,11 +3,11 @@ module Dokno
|
|
|
3
3
|
extend ActiveSupport::Concern
|
|
4
4
|
|
|
5
5
|
def paginate(records, max_per_page: 10)
|
|
6
|
-
@page
|
|
7
|
-
@total_records
|
|
8
|
-
@total_pages
|
|
9
|
-
@total_pages
|
|
10
|
-
@page
|
|
6
|
+
@page = params[:page].to_i
|
|
7
|
+
@total_records = records.size
|
|
8
|
+
@total_pages = (@total_records.to_f / max_per_page).ceil
|
|
9
|
+
@total_pages = 1 unless @total_pages.positive?
|
|
10
|
+
@page = 1 unless @page.positive? && @page <= @total_pages
|
|
11
11
|
|
|
12
12
|
records.offset((@page - 1) * max_per_page).limit(max_per_page)
|
|
13
13
|
end
|
|
@@ -8,8 +8,6 @@ module Dokno
|
|
|
8
8
|
|
|
9
9
|
def user
|
|
10
10
|
# Attempt to eval the currently signed in 'user' object from the host app
|
|
11
|
-
sanitized_user_obj_string = Dokno.config.app_user_object.to_s.split(/\b/).first
|
|
12
|
-
|
|
13
11
|
proc {
|
|
14
12
|
$safe = 1
|
|
15
13
|
eval sanitized_user_obj_string
|
|
@@ -19,14 +17,18 @@ module Dokno
|
|
|
19
17
|
nil
|
|
20
18
|
end
|
|
21
19
|
|
|
20
|
+
def sanitized_user_obj_string
|
|
21
|
+
Dokno.config.app_user_object.to_s.split(/\b/).first
|
|
22
|
+
end
|
|
23
|
+
|
|
22
24
|
def username
|
|
23
25
|
user&.send(Dokno.config.app_user_name_method.to_sym).to_s
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
def can_edit?
|
|
27
29
|
# Allow editing by default if host app user object is not configured
|
|
28
|
-
return true unless
|
|
29
|
-
return false unless user
|
|
30
|
+
return true unless sanitized_user_obj_string.present?
|
|
31
|
+
return false unless user&.respond_to? Dokno.config.app_user_auth_method.to_sym
|
|
30
32
|
|
|
31
33
|
user.send(Dokno.config.app_user_auth_method.to_sym)
|
|
32
34
|
end
|
|
@@ -9,7 +9,7 @@ module Dokno
|
|
|
9
9
|
|
|
10
10
|
return "Dokno article slug '#{slug}' not found" if article.blank?
|
|
11
11
|
|
|
12
|
-
%Q(<a href="javascript:;" onclick="doknoOpenPanel('#{j article.slug}');">#{link_text.presence || article.title}</a>).html_safe
|
|
12
|
+
%Q(<a class="dokno-link" href="javascript:;" onclick="doknoOpenPanel('#{j article.slug}');">#{link_text.presence || article.title}</a>).html_safe
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
15
|
end
|
data/app/models/dokno/article.rb
CHANGED
|
@@ -3,9 +3,6 @@ require 'redcarpet'
|
|
|
3
3
|
|
|
4
4
|
module Dokno
|
|
5
5
|
class Article < ApplicationRecord
|
|
6
|
-
include Engine.routes.url_helpers
|
|
7
|
-
include ActionView::Helpers::DateHelper
|
|
8
|
-
|
|
9
6
|
has_and_belongs_to_many :categories
|
|
10
7
|
has_many :logs, dependent: :destroy
|
|
11
8
|
has_many :article_slugs, dependent: :destroy
|
|
@@ -15,18 +12,28 @@ module Dokno
|
|
|
15
12
|
validates :title, length: { in: 5..255 }
|
|
16
13
|
validate :unique_slug_check
|
|
17
14
|
|
|
15
|
+
attr_accessor :editor_username, :reset_review_date, :review_notes
|
|
16
|
+
|
|
17
|
+
before_save :set_review_date, if: :should_set_review_date?
|
|
18
18
|
before_save :log_changes
|
|
19
19
|
before_save :track_slug
|
|
20
20
|
|
|
21
|
-
scope :
|
|
22
|
-
scope :
|
|
23
|
-
scope :
|
|
24
|
-
scope :
|
|
25
|
-
|
|
26
|
-
attr_accessor :editor_username
|
|
21
|
+
scope :active, -> { where(active: true) }
|
|
22
|
+
scope :alpha_order, -> { order(active: :desc, starred: :desc, title: :asc) }
|
|
23
|
+
scope :views_order, -> { order(active: :desc, starred: :desc, views: :desc, title: :asc) }
|
|
24
|
+
scope :newest_order, -> { order(active: :desc, starred: :desc, created_at: :desc, title: :asc) }
|
|
25
|
+
scope :updated_order, -> { order(active: :desc, starred: :desc, updated_at: :desc, title: :asc) }
|
|
27
26
|
|
|
28
27
|
MARKDOWN_PARSER = Redcarpet::Markdown.new(Redcarpet::Render::HTML, autolink: true, tables: true)
|
|
29
28
|
|
|
29
|
+
def review_due_at
|
|
30
|
+
super || (Date.today + 30.years)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def markdown
|
|
34
|
+
super || ''
|
|
35
|
+
end
|
|
36
|
+
|
|
30
37
|
def reading_time
|
|
31
38
|
minutes_decimal = (("#{summary} #{markdown}".squish.scan(/[\w-]+/).size) / 200.0)
|
|
32
39
|
approx_minutes = minutes_decimal.ceil
|
|
@@ -35,8 +42,22 @@ module Dokno
|
|
|
35
42
|
"~ #{approx_minutes} minutes"
|
|
36
43
|
end
|
|
37
44
|
|
|
38
|
-
def
|
|
39
|
-
|
|
45
|
+
def review_due_days
|
|
46
|
+
(review_due_at.to_date - Date.today).to_i
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def up_for_review?
|
|
50
|
+
active && review_due_days <= Dokno.config.article_review_prompt_days
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def review_due_days_string
|
|
54
|
+
if review_due_days.positive?
|
|
55
|
+
"This article is up for an accuracy / relevance review in #{review_due_days} #{'day'.pluralize(review_due_days)}"
|
|
56
|
+
elsif review_due_days.negative?
|
|
57
|
+
"This article was up for an accuracy / relevance review #{review_due_days.abs} #{'day'.pluralize(review_due_days)} ago"
|
|
58
|
+
else
|
|
59
|
+
"This article is up for an accuracy / relevance review today"
|
|
60
|
+
end
|
|
40
61
|
end
|
|
41
62
|
|
|
42
63
|
def markdown_parsed
|
|
@@ -52,7 +73,10 @@ module Dokno
|
|
|
52
73
|
.where(dokno_articles_categories: { article_id: id })
|
|
53
74
|
.all
|
|
54
75
|
.map do |category|
|
|
55
|
-
|
|
76
|
+
next if context_category_id == category.id
|
|
77
|
+
|
|
78
|
+
"<a class='underline' href='#{article_index_path(category.code)}?search_term="\
|
|
79
|
+
"#{CGI.escape(search_term.to_s)}&order=#{CGI.escape(order.to_s)}'>#{category.name}</a>"
|
|
56
80
|
end.compact
|
|
57
81
|
|
|
58
82
|
return '' if names.blank?
|
|
@@ -111,34 +135,24 @@ module Dokno
|
|
|
111
135
|
.to_sentence
|
|
112
136
|
end
|
|
113
137
|
|
|
114
|
-
# All
|
|
115
|
-
def self.
|
|
138
|
+
# All articles up for review
|
|
139
|
+
def self.up_for_review(order: :updated)
|
|
116
140
|
records = Article
|
|
117
141
|
.includes(:categories_dokno_articles, :categories)
|
|
118
|
-
.
|
|
119
|
-
.where(
|
|
120
|
-
|
|
121
|
-
records = records.updated_order if order == :updated
|
|
122
|
-
records = records.newest_order if order == :newest
|
|
123
|
-
records = records.view_order if order == :views
|
|
124
|
-
records = records.alpha_order if order == :alpha
|
|
125
|
-
|
|
126
|
-
records
|
|
127
|
-
end
|
|
142
|
+
.where(active: true)
|
|
143
|
+
.where('review_due_at <= ?', Date.today + Dokno.config.article_review_prompt_days)
|
|
128
144
|
|
|
129
|
-
|
|
130
|
-
ActionController::Base.helpers.sanitize(
|
|
131
|
-
MARKDOWN_PARSER.render(content),
|
|
132
|
-
tags: Dokno.config.tag_whitelist,
|
|
133
|
-
attributes: Dokno.config.attr_whitelist
|
|
134
|
-
)
|
|
145
|
+
apply_sort(records, order: order)
|
|
135
146
|
end
|
|
136
147
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
148
|
+
# All uncategorized Articles
|
|
149
|
+
def self.uncategorized(order: :updated)
|
|
150
|
+
records = Article
|
|
151
|
+
.includes(:categories_dokno_articles, :categories)
|
|
152
|
+
.left_joins(:categories)
|
|
153
|
+
.where(dokno_categories: { id: nil })
|
|
140
154
|
|
|
141
|
-
|
|
155
|
+
apply_sort(records, order: order)
|
|
142
156
|
end
|
|
143
157
|
|
|
144
158
|
def self.search(term:, category_id: nil, order: :updated)
|
|
@@ -152,10 +166,7 @@ module Dokno
|
|
|
152
166
|
.includes(:categories_dokno_articles)
|
|
153
167
|
.includes(:categories)
|
|
154
168
|
|
|
155
|
-
records = records
|
|
156
|
-
records = records.newest_order if order == :newest
|
|
157
|
-
records = records.view_order if order == :views
|
|
158
|
-
records = records.alpha_order if order == :alpha
|
|
169
|
+
records = apply_sort(records, order: order)
|
|
159
170
|
|
|
160
171
|
return records unless category_id.present?
|
|
161
172
|
|
|
@@ -169,6 +180,28 @@ module Dokno
|
|
|
169
180
|
)
|
|
170
181
|
end
|
|
171
182
|
|
|
183
|
+
def self.parse_markdown(content)
|
|
184
|
+
ActionController::Base.helpers.sanitize(
|
|
185
|
+
MARKDOWN_PARSER.render(content),
|
|
186
|
+
tags: Dokno.config.tag_whitelist,
|
|
187
|
+
attributes: Dokno.config.attr_whitelist
|
|
188
|
+
)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def self.template
|
|
192
|
+
template_file = File.join(Rails.root, 'config', 'dokno_template.md')
|
|
193
|
+
return unless File.exist?(template_file)
|
|
194
|
+
|
|
195
|
+
File.read(template_file).to_s
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def self.apply_sort(records, order: :updated)
|
|
199
|
+
order_scope = "#{order}_order"
|
|
200
|
+
return records unless records.respond_to? order_scope
|
|
201
|
+
|
|
202
|
+
records.send(order_scope.to_sym)
|
|
203
|
+
end
|
|
204
|
+
|
|
172
205
|
private
|
|
173
206
|
|
|
174
207
|
# Ensure there isn't another Article with the same slug
|
|
@@ -188,7 +221,7 @@ module Dokno
|
|
|
188
221
|
end
|
|
189
222
|
|
|
190
223
|
def log_changes
|
|
191
|
-
return if changes.blank?
|
|
224
|
+
return if changes.blank? && !reset_review_date
|
|
192
225
|
|
|
193
226
|
meta_changes = changes.with_indifferent_access.slice(:slug, :title, :active)
|
|
194
227
|
content_changes = changes.with_indifferent_access.slice(:summary, :markdown)
|
|
@@ -206,10 +239,26 @@ module Dokno
|
|
|
206
239
|
content[:after] += values.last.to_s + ' '
|
|
207
240
|
end
|
|
208
241
|
|
|
209
|
-
|
|
242
|
+
if meta.present?
|
|
243
|
+
diff = Diffy::SplitDiff.new(content[:before].squish, content[:after].squish, format: :html)
|
|
244
|
+
logs << Log.new(username: editor_username, meta: meta.to_sentence, diff_left: diff.left, diff_right: diff.right)
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# Reviewed for accuracy / relevance?
|
|
248
|
+
return unless reset_review_date
|
|
249
|
+
|
|
250
|
+
review_log = "Reviewed for accuracy / relevance. Next review date reset to #{review_due_at.to_date}."
|
|
251
|
+
review_log += " Review notes: #{review_notes.squish}" if review_notes.present?
|
|
252
|
+
logs << Log.new(username: editor_username, meta: review_log)
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def should_set_review_date?
|
|
256
|
+
# User requested a reset or it's a new article w/out a review due date
|
|
257
|
+
reset_review_date || (!persisted? && review_due_at.blank?)
|
|
258
|
+
end
|
|
210
259
|
|
|
211
|
-
|
|
212
|
-
|
|
260
|
+
def set_review_date
|
|
261
|
+
self.review_due_at = Date.today + Dokno.config.article_review_period
|
|
213
262
|
end
|
|
214
263
|
end
|
|
215
264
|
end
|