dokno 1.1.1 → 1.4.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|