dokno 1.0.0 → 1.3.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.
- checksums.yaml +4 -4
- data/README.md +6 -6
- data/app/assets/javascripts/dokno.js +79 -27
- data/app/assets/stylesheets/dokno/application.css +1 -1
- data/app/controllers/dokno/application_controller.rb +3 -0
- data/app/controllers/dokno/articles_controller.rb +22 -8
- data/app/controllers/dokno/categories_controller.rb +15 -2
- data/app/controllers/dokno/user_concern.rb +5 -3
- data/app/helpers/dokno/application_helper.rb +1 -3
- data/app/models/dokno/article.rb +87 -38
- data/app/models/dokno/category.rb +39 -15
- data/app/views/dokno/_article_formatting.html.erb +17 -18
- data/app/views/dokno/_article_panel.html.erb +16 -18
- data/app/views/dokno/_panel_formatting.html.erb +47 -57
- data/app/views/dokno/articles/_article_form.html.erb +47 -6
- data/app/views/dokno/articles/show.html.erb +45 -39
- data/app/views/dokno/categories/_category_form.html.erb +6 -1
- data/app/views/dokno/categories/index.html.erb +40 -37
- data/app/views/layouts/dokno/application.html.erb +34 -9
- data/app/views/partials/_category_header.html.erb +29 -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 -18
- 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 +4 -4
- data/lib/dokno/version.rb +1 -1
- data/lib/generators/dokno/templates/config/initializers/dokno.rb +18 -5
- metadata +87 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: edd6bc7f282e312599b5d844e48382a5eff63604c13b05d41859c1beb587e878
|
4
|
+
data.tar.gz: b692a98a50654ae4d7d7a77b123289da686ab37131a0bbdf0f4ffc9151b5a1ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce6b652432db90585a4b72fe2327a5201409d9872c3408b74d7e5a52253d960b2ce1286fad4c762e09f2d4b72134d49174c4bbe3d4fc02203de1b3d95cc8077c
|
7
|
+
data.tar.gz: ae70e70feb6f78cc30daf751085ef58f50a26b45c9faa1276b5723fdb685c08ada77ff02ba5dee1a781a90c3852d0822702e7a410a2e952baef1545ded3408b2
|
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) {
|
@@ -10,6 +28,32 @@ function elems(selector) {
|
|
10
28
|
return document.getElementsByClassName(selector);
|
11
29
|
}
|
12
30
|
|
31
|
+
function selectOption(id, value) {
|
32
|
+
var sel = elem('#' + id);
|
33
|
+
var opts = sel.options;
|
34
|
+
|
35
|
+
for (var opt, j = 0; opt = opts[j]; j++) {
|
36
|
+
if (opt.value == value) {
|
37
|
+
sel.selectedIndex = j;
|
38
|
+
break;
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
function applyCategoryCriteria(category_code, term, order) {
|
44
|
+
goToPage(dokno__base_path + category_code + '?search_term=' + term + '&order=' + order);
|
45
|
+
}
|
46
|
+
|
47
|
+
function goToPage(url) {
|
48
|
+
var param_join = url.indexOf('?') >= 0 ? '&' : '?';
|
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();
|
55
|
+
}
|
56
|
+
|
13
57
|
function sendRequest(url, data, callback, method) {
|
14
58
|
const request = new XMLHttpRequest();
|
15
59
|
request.open(method, url, true);
|
@@ -30,23 +74,13 @@ function sendRequest(url, data, callback, method) {
|
|
30
74
|
request.send(JSON.stringify(data));
|
31
75
|
}
|
32
76
|
|
33
|
-
function
|
34
|
-
const callback = function(_data) {
|
35
|
-
elem('button#article-deactivate-button').classList.add('hidden');
|
36
|
-
elem('div#article-deprecated-alert').classList.remove('hidden');
|
37
|
-
elem('button#article-activate-button').classList.remove('hidden');
|
38
|
-
reloadLogs();
|
39
|
-
}
|
77
|
+
function deactivateArticle(slug) {
|
78
|
+
const callback = function(_data) { reloadPage(); }
|
40
79
|
sendRequest(dokno__base_path + 'article_status', { slug: slug, active: false }, callback, 'POST');
|
41
80
|
}
|
42
81
|
|
43
|
-
function
|
44
|
-
const callback = function(_data) {
|
45
|
-
elem('button#article-activate-button').classList.add('hidden');
|
46
|
-
elem('div#article-deprecated-alert').classList.add('hidden');
|
47
|
-
elem('button#article-deactivate-button').classList.remove('hidden');
|
48
|
-
reloadLogs();
|
49
|
-
}
|
82
|
+
function activateArticle(slug) {
|
83
|
+
const callback = function(_data) { reloadPage(); }
|
50
84
|
sendRequest(dokno__base_path + 'article_status', { slug: slug, active: true }, callback, 'POST');
|
51
85
|
}
|
52
86
|
|
@@ -61,6 +95,17 @@ function deleteArticle(id) {
|
|
61
95
|
sendRequest(dokno__base_path + 'articles/' + id, {}, callback, 'DELETE');
|
62
96
|
}
|
63
97
|
|
98
|
+
function deleteCategory(id) {
|
99
|
+
if (!confirm('Delete Category\n\nThis will remove this category. Any articles in this category will become uncategorized and appear on the home page until re-categorized.')) {
|
100
|
+
return true;
|
101
|
+
}
|
102
|
+
|
103
|
+
const callback = function(_data) {
|
104
|
+
location.href = dokno__base_path;
|
105
|
+
}
|
106
|
+
sendRequest(dokno__base_path + 'categories/' + id, {}, callback, 'DELETE');
|
107
|
+
}
|
108
|
+
|
64
109
|
function previewArticleToggle() {
|
65
110
|
const markdown = elem('div#dokno-content-container textarea#markdown').value;
|
66
111
|
const callback = function(data) {
|
@@ -112,18 +157,6 @@ function toggleVisibility(selector_id) {
|
|
112
157
|
initIcons();
|
113
158
|
}
|
114
159
|
|
115
|
-
function reloadLogs() {
|
116
|
-
var $log_container = elem('div#dokno-article-log-container');
|
117
|
-
var category_id = $log_container.getAttribute('data-category-id');
|
118
|
-
var article_id = $log_container.getAttribute('data-article-id');
|
119
|
-
|
120
|
-
const callback = function(markup) {
|
121
|
-
elem('div#dokno-article-log-container').innerHTML = markup;
|
122
|
-
initIcons();
|
123
|
-
}
|
124
|
-
sendRequest(dokno__base_path + 'article_log', { category_id: category_id, article_id: article_id }, callback, 'POST');
|
125
|
-
}
|
126
|
-
|
127
160
|
// Pass containers_selector as class name (no prefix)
|
128
161
|
function highlightTerm(terms, containers_selector) {
|
129
162
|
var containers = elems(containers_selector);
|
@@ -142,3 +175,22 @@ function highlightTerm(terms, containers_selector) {
|
|
142
175
|
function wrapTermWithHTML(term) {
|
143
176
|
return `<span title="Matching search term" class="dokno-search-term bg-yellow-300 text-gray-900 p-2 rounded mx-1">${term}</span>`
|
144
177
|
}
|
178
|
+
|
179
|
+
function setReviewForm() {
|
180
|
+
const reset_review_date_checkbox = elem('input#reset_review_date');
|
181
|
+
const review_notes_textarea = elem('textarea#review_notes');
|
182
|
+
|
183
|
+
if (!reset_review_date_checkbox) {
|
184
|
+
return true;
|
185
|
+
}
|
186
|
+
|
187
|
+
if (reset_review_date_checkbox.checked) {
|
188
|
+
review_notes_textarea.removeAttribute('disabled');
|
189
|
+
review_notes_textarea.classList.remove('cursor-not-allowed');
|
190
|
+
review_notes_textarea.focus();
|
191
|
+
} else {
|
192
|
+
review_notes_textarea.setAttribute('disabled', 'disabled');
|
193
|
+
review_notes_textarea.classList.add('cursor-not-allowed');
|
194
|
+
reset_review_date_checkbox.focus();
|
195
|
+
}
|
196
|
+
}
|
@@ -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,9 +39,11 @@ module Dokno
|
|
27
39
|
set_editor_username
|
28
40
|
|
29
41
|
if @article.save
|
42
|
+
flash[:green] = 'Article was created'
|
30
43
|
@article.categories = Category.where(code: params[:category_code]) if params[:category_code].present?
|
31
44
|
redirect_to article_path @article.slug
|
32
45
|
else
|
46
|
+
flash.now[:red] = 'Article could not be created'
|
33
47
|
@category_codes = params[:category_code]
|
34
48
|
render :new
|
35
49
|
end
|
@@ -42,16 +56,22 @@ module Dokno
|
|
42
56
|
set_editor_username
|
43
57
|
|
44
58
|
if @article.update(article_params)
|
59
|
+
flash[:green] = 'Article was updated'
|
45
60
|
@article.categories = Category.where(code: params[:category_code])
|
46
61
|
redirect_to article_path @article.slug
|
47
62
|
else
|
48
|
-
|
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]
|
49
67
|
render :edit
|
50
68
|
end
|
51
69
|
end
|
52
70
|
|
53
71
|
def destroy
|
54
72
|
Article.find(params[:id].to_i).destroy!
|
73
|
+
|
74
|
+
flash[:green] = 'Article was deleted'
|
55
75
|
render json: {}, layout: false
|
56
76
|
end
|
57
77
|
|
@@ -73,16 +93,10 @@ module Dokno
|
|
73
93
|
render json: {}, layout: false
|
74
94
|
end
|
75
95
|
|
76
|
-
# Ajax-fetched article change log
|
77
|
-
def article_log
|
78
|
-
render partial: '/partials/logs',
|
79
|
-
locals: { article: Article.find_by(id: params[:article_id].to_i) }, layout: false
|
80
|
-
end
|
81
|
-
|
82
96
|
private
|
83
97
|
|
84
98
|
def article_params
|
85
|
-
params.permit(:slug, :title, :summary, :markdown)
|
99
|
+
params.permit(:slug, :title, :summary, :markdown, :reset_review_date, :review_notes, :starred)
|
86
100
|
end
|
87
101
|
|
88
102
|
def fetch_article
|
@@ -10,7 +10,9 @@ module Dokno
|
|
10
10
|
@order = params[:order]&.strip
|
11
11
|
@order = 'updated' unless %w(updated newest views alpha).include?(@order)
|
12
12
|
|
13
|
-
articles = if
|
13
|
+
articles = if request.path.include? up_for_review_path
|
14
|
+
Article.up_for_review(order: @order&.to_sym)
|
15
|
+
elsif @search_term.present?
|
14
16
|
Article.search(term: @search_term, category_id: @category&.id, order: @order&.to_sym)
|
15
17
|
elsif @category.present?
|
16
18
|
@category.articles_in_branch(order: @order&.to_sym)
|
@@ -22,7 +24,7 @@ module Dokno
|
|
22
24
|
end
|
23
25
|
|
24
26
|
def new
|
25
|
-
@category
|
27
|
+
@category = Category.new
|
26
28
|
@parent_category_code = params[:parent_category_code]
|
27
29
|
end
|
28
30
|
|
@@ -35,8 +37,10 @@ module Dokno
|
|
35
37
|
@category = Category.new(name: params[:name], parent: Category.find_by(code: params[:parent_category_code]))
|
36
38
|
|
37
39
|
if @category.save
|
40
|
+
flash[:green] = 'Category was created'
|
38
41
|
redirect_to article_index_path(@category.code)
|
39
42
|
else
|
43
|
+
flash.now[:red] = 'Category could not be created'
|
40
44
|
@parent_category_code = params[:parent_category_code]
|
41
45
|
render :new
|
42
46
|
end
|
@@ -46,13 +50,22 @@ module Dokno
|
|
46
50
|
return redirect_to root_path if @category.blank?
|
47
51
|
|
48
52
|
if @category.update(name: params[:name], parent: Category.find_by(code: params[:parent_category_code]))
|
53
|
+
flash[:green] = 'Category was updated'
|
49
54
|
redirect_to article_index_path(@category.code)
|
50
55
|
else
|
56
|
+
flash.now[:red] = 'Category could not be updated'
|
51
57
|
@parent_category_code = params[:parent_category_code]
|
52
58
|
render :edit
|
53
59
|
end
|
54
60
|
end
|
55
61
|
|
62
|
+
def destroy
|
63
|
+
Category.find(params[:id].to_i).destroy!
|
64
|
+
|
65
|
+
flash[:green] = 'Category was deleted'
|
66
|
+
render json: {}, layout: false
|
67
|
+
end
|
68
|
+
|
56
69
|
private
|
57
70
|
|
58
71
|
def fetch_category
|
@@ -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,13 +17,17 @@ 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
|
30
|
+
return true unless sanitized_user_obj_string.present?
|
29
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)
|
@@ -9,9 +9,7 @@ module Dokno
|
|
9
9
|
|
10
10
|
return "Dokno article slug '#{slug}' not found" if article.blank?
|
11
11
|
|
12
|
-
%Q(
|
13
|
-
<a href="javascript:;" onclick="doknoOpenPanel('#{j article.slug}');">#{link_text.presence || article.title}</a>
|
14
|
-
).html_safe
|
12
|
+
%Q(<a href="javascript:;" onclick="doknoOpenPanel('#{j article.slug}');">#{link_text.presence || article.title}</a>).html_safe
|
15
13
|
end
|
16
14
|
end
|
17
15
|
end
|
data/app/models/dokno/article.rb
CHANGED
@@ -15,18 +15,28 @@ module Dokno
|
|
15
15
|
validates :title, length: { in: 5..255 }
|
16
16
|
validate :unique_slug_check
|
17
17
|
|
18
|
+
attr_accessor :editor_username, :reset_review_date, :review_notes
|
19
|
+
|
20
|
+
before_save :set_review_date, if: :should_set_review_date?
|
18
21
|
before_save :log_changes
|
19
22
|
before_save :track_slug
|
20
23
|
|
21
|
-
scope :
|
22
|
-
scope :
|
23
|
-
scope :
|
24
|
-
scope :
|
25
|
-
|
26
|
-
attr_accessor :editor_username
|
24
|
+
scope :active, -> { where(active: true) }
|
25
|
+
scope :alpha_order, -> { order(active: :desc, starred: :desc, title: :asc) }
|
26
|
+
scope :views_order, -> { order(active: :desc, starred: :desc, views: :desc, title: :asc) }
|
27
|
+
scope :newest_order, -> { order(active: :desc, starred: :desc, created_at: :desc, title: :asc) }
|
28
|
+
scope :updated_order, -> { order(active: :desc, starred: :desc, updated_at: :desc, title: :asc) }
|
27
29
|
|
28
30
|
MARKDOWN_PARSER = Redcarpet::Markdown.new(Redcarpet::Render::HTML, autolink: true, tables: true)
|
29
31
|
|
32
|
+
def review_due_at
|
33
|
+
super || (Date.today + 30.years)
|
34
|
+
end
|
35
|
+
|
36
|
+
def markdown
|
37
|
+
super || ''
|
38
|
+
end
|
39
|
+
|
30
40
|
def reading_time
|
31
41
|
minutes_decimal = (("#{summary} #{markdown}".squish.scan(/[\w-]+/).size) / 200.0)
|
32
42
|
approx_minutes = minutes_decimal.ceil
|
@@ -35,8 +45,22 @@ module Dokno
|
|
35
45
|
"~ #{approx_minutes} minutes"
|
36
46
|
end
|
37
47
|
|
38
|
-
def
|
39
|
-
|
48
|
+
def review_due_days
|
49
|
+
(review_due_at.to_date - Date.today).to_i
|
50
|
+
end
|
51
|
+
|
52
|
+
def up_for_review?
|
53
|
+
active && review_due_days <= Dokno.config.article_review_prompt_days
|
54
|
+
end
|
55
|
+
|
56
|
+
def review_due_days_string
|
57
|
+
if review_due_days.positive?
|
58
|
+
"This article is up for an accuracy / relevance review in #{review_due_days} #{'day'.pluralize(review_due_days)}"
|
59
|
+
elsif review_due_days.negative?
|
60
|
+
"This article was up for an accuracy / relevance review #{review_due_days.abs} #{'day'.pluralize(review_due_days)} ago"
|
61
|
+
else
|
62
|
+
"This article is up for an accuracy / relevance review today"
|
63
|
+
end
|
40
64
|
end
|
41
65
|
|
42
66
|
def markdown_parsed
|
@@ -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(
|
142
|
+
.where(active: true)
|
143
|
+
.where('review_due_at <= ?', Date.today + Dokno.config.article_review_prompt_days)
|
120
144
|
|
121
|
-
records
|
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
|
145
|
+
apply_sort(records, order: order)
|
127
146
|
end
|
128
147
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
end
|
136
|
-
|
137
|
-
def self.template
|
138
|
-
template_file = File.join(Rails.root, 'config', 'dokno_template.md')
|
139
|
-
return unless File.exist?(template_file)
|
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
|