spina-pro 0.0.1 → 0.13.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/CHANGELOG.md +83 -0
- data/LICENSE +21 -0
- data/README.md +66 -0
- data/Rakefile +13 -0
- data/app/assets/config/spina/pro/manifest.js +2 -0
- data/app/assets/javascripts/spina/pro/controllers/form_autosave_controller.js +19 -0
- data/app/assets/javascripts/spina/pro/controllers/search_controller.js +64 -0
- data/app/assets/javascripts/spina/pro/controllers/seo_controller.js +66 -0
- data/app/assets/javascripts/spina/pro/libraries/debounce.js +65 -0
- data/app/components/spina/pro/messages/field_component.html.erb +1 -0
- data/app/components/spina/pro/messages/field_component.rb +36 -0
- data/app/components/spina/pro/messages/message_component.html.erb +31 -0
- data/app/components/spina/pro/messages/message_component.rb +30 -0
- data/app/components/spina/pro/pages/page_revision_component.html.erb +42 -0
- data/app/components/spina/pro/pages/page_revision_component.rb +15 -0
- data/app/components/spina/pro/user_interface/hint_component.html.erb +6 -0
- data/app/components/spina/pro/user_interface/hint_component.rb +17 -0
- data/app/controllers/spina/admin/pro/drafts_controller.rb +41 -0
- data/app/controllers/spina/admin/pro/inboxes_controller.rb +50 -0
- data/app/controllers/spina/admin/pro/message_archive_controller.rb +15 -0
- data/app/controllers/spina/admin/pro/message_spam_controller.rb +15 -0
- data/app/controllers/spina/admin/pro/messages_controller.rb +78 -0
- data/app/controllers/spina/admin/pro/not_found_errors_controller.rb +35 -0
- data/app/controllers/spina/admin/pro/rewrite_rules_controller.rb +53 -0
- data/app/controllers/spina/admin/pro/search_controller.rb +41 -0
- data/app/controllers/spina/admin/pro/version_history_controller.rb +18 -0
- data/app/mailers/spina/pro/message_mailer.rb +15 -0
- data/app/models/spina/parts/pro/date.rb +11 -0
- data/app/models/spina/parts/pro/date_time.rb +11 -0
- data/app/models/spina/pro/inbox.rb +31 -0
- data/app/models/spina/pro/message.rb +142 -0
- data/app/models/spina/pro/not_found_error.rb +15 -0
- data/app/models/spina/pro/page_revision.rb +22 -0
- data/app/models/spina/pro/search_document.rb +19 -0
- data/app/views/spina/admin/hooks/pro/_primary_navigation.html.erb +40 -0
- data/app/views/spina/admin/hooks/pro/_website_secondary_navigation.html.erb +1 -0
- data/app/views/spina/admin/pages/_button_metadata.html.erb +4 -0
- data/app/views/spina/admin/pages/_button_pro_seo.html.erb +5 -0
- data/app/views/spina/admin/pages/_form.html.erb +52 -0
- data/app/views/spina/admin/pages/_form_metadata.html.erb +10 -0
- data/app/views/spina/admin/pages/_form_page_content.html.erb +15 -0
- data/app/views/spina/admin/pages/_form_pro_seo.html.erb +64 -0
- data/app/views/spina/admin/parts/pro/date_times/_form.html.erb +6 -0
- data/app/views/spina/admin/parts/pro/dates/_form.html.erb +6 -0
- data/app/views/spina/admin/pro/drafts/index.html.erb +52 -0
- data/app/views/spina/admin/pro/inboxes/_unread_count.html.erb +5 -0
- data/app/views/spina/admin/pro/inboxes/_unread_indicator.html.erb +3 -0
- data/app/views/spina/admin/pro/inboxes/edit.html.erb +21 -0
- data/app/views/spina/admin/pro/inboxes/show.html.erb +50 -0
- data/app/views/spina/admin/pro/message_archive/show.html.erb +34 -0
- data/app/views/spina/admin/pro/message_spam/show.html.erb +34 -0
- data/app/views/spina/admin/pro/messages/_message.html.erb +3 -0
- data/app/views/spina/admin/pro/messages/_modal.html.erb +83 -0
- data/app/views/spina/admin/pro/messages/edit.html.erb +26 -0
- data/app/views/spina/admin/pro/messages/fields/_boolean.html.erb +1 -0
- data/app/views/spina/admin/pro/messages/fields/_string.html.erb +1 -0
- data/app/views/spina/admin/pro/messages/fields/_text.html.erb +1 -0
- data/app/views/spina/admin/pro/messages/show.html.erb +1 -0
- data/app/views/spina/admin/pro/messages/update.turbo_stream.erb +2 -0
- data/app/views/spina/admin/pro/not_found_errors/_not_found_error.html.erb +36 -0
- data/app/views/spina/admin/pro/not_found_errors/ignored.html.erb +10 -0
- data/app/views/spina/admin/pro/not_found_errors/index.html.erb +25 -0
- data/app/views/spina/admin/pro/rewrite_rules/_form.html.erb +31 -0
- data/app/views/spina/admin/pro/rewrite_rules/_rewrite_rule.html.erb +17 -0
- data/app/views/spina/admin/pro/rewrite_rules/index.html.erb +31 -0
- data/app/views/spina/admin/pro/rewrite_rules/new.html.erb +3 -0
- data/app/views/spina/admin/pro/search/_results.html.erb +9 -0
- data/app/views/spina/admin/pro/search/new.html.erb +40 -0
- data/app/views/spina/admin/pro/search/results/_page.html.erb +25 -0
- data/app/views/spina/admin/pro/shared/_redirects_navigation.html.erb +30 -0
- data/app/views/spina/admin/pro/version_history/show.html.erb +34 -0
- data/app/views/spina/pro/message_mailer/forward.html.erb +70 -0
- data/app/views/spina/pro/message_mailer/forward.text.erb +14 -0
- data/config/locales/en.yml +96 -0
- data/config/locales/nl.yml +96 -0
- data/config/routes.rb +44 -0
- data/db/migrate/1_create_spina_pro_not_found_errors.rb +14 -0
- data/db/migrate/2_add_created_manually_to_spina_rewrite_rules.rb +5 -0
- data/db/migrate/3_create_spina_pro_inboxes.rb +11 -0
- data/db/migrate/4_create_spina_pro_messages.rb +14 -0
- data/db/migrate/5_add_archived_at_to_spina_pro_messages.rb +5 -0
- data/db/migrate/6_create_spina_pro_search_documents.rb +9 -0
- data/db/migrate/7_create_spina_pro_page_revisions.rb +12 -0
- data/db/migrate/8_add_draft_to_spina_pro_page_revisions.rb +5 -0
- data/lib/generators/spina/pro/install_generator.rb +13 -0
- data/lib/generators/spina/pro/message_generator.rb +14 -0
- data/lib/generators/spina/pro/templates/initializer.rb.tt +6 -0
- data/lib/generators/spina/pro/templates/message.rb.tt +5 -0
- data/lib/spina/pro/engine.rb +57 -0
- data/lib/spina/pro/inboxes_helper.rb +17 -0
- data/lib/spina/pro/order_by_date.rb +16 -0
- data/lib/spina/pro/page_ordering.rb +34 -0
- data/lib/spina/pro/page_revisions/load_page_revision.rb +20 -0
- data/lib/spina/pro/page_revisions/page_decorator.rb +40 -0
- data/lib/spina/pro/page_revisions/pages_controller_decorator.rb +52 -0
- data/lib/spina/pro/page_search.rb +20 -0
- data/lib/spina/pro/recently_visited.rb +33 -0
- data/lib/spina/pro/replace_seo_tab.rb +18 -0
- data/lib/spina/pro/search.rb +45 -0
- data/lib/spina/pro/track_not_found_errors.rb +23 -0
- data/lib/spina/pro/version.rb +5 -0
- data/lib/spina/pro.rb +38 -0
- metadata +122 -23
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module Spina::Admin
|
|
2
|
+
module Pro
|
|
3
|
+
class InboxesController < AdminController
|
|
4
|
+
admin_section :inboxes
|
|
5
|
+
|
|
6
|
+
before_action :set_inbox
|
|
7
|
+
|
|
8
|
+
def show
|
|
9
|
+
@messages = scope_search(params[:search], @inbox.messages.not_archived.ham.newest.page(params[:page]).per(25))
|
|
10
|
+
add_breadcrumb @inbox.label
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def edit
|
|
14
|
+
add_breadcrumb @inbox.label, spina.admin_pro_inbox_path(@inbox), class: 'text-gray-400'
|
|
15
|
+
add_breadcrumb t("spina.pro.inboxes.forward_messages")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def update
|
|
19
|
+
if @inbox.update(inbox_params)
|
|
20
|
+
redirect_to spina.admin_pro_inbox_path(@inbox)
|
|
21
|
+
else
|
|
22
|
+
render :edit, status: :unprocessable_entity
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def mark_all_as_read
|
|
27
|
+
@inbox.messages.unread.update_all(read_at: Time.current)
|
|
28
|
+
@inbox.update_unread_count
|
|
29
|
+
flash[:info] = t('spina.pro.inboxes.marked_all_as_read')
|
|
30
|
+
redirect_to spina.admin_pro_inbox_path(@inbox)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def scope_search(search, records)
|
|
36
|
+
return records if search.blank?
|
|
37
|
+
records.where("email LIKE :search", search: "%#{search}%")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def set_inbox
|
|
41
|
+
@inbox = Spina::Pro::Inbox.find(params[:id])
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def inbox_params
|
|
45
|
+
params.require(:inbox).permit(:label, :forward_email)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Spina
|
|
2
|
+
module Admin
|
|
3
|
+
module Pro
|
|
4
|
+
class MessageArchiveController < AdminController
|
|
5
|
+
admin_section :inboxes
|
|
6
|
+
|
|
7
|
+
def show
|
|
8
|
+
@messages = Spina::Pro::Message.archived.newest.ham.page(params[:page]).per(25)
|
|
9
|
+
add_breadcrumb t("spina.pro.messages.message_archive")
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Spina
|
|
2
|
+
module Admin
|
|
3
|
+
module Pro
|
|
4
|
+
class MessageSpamController < AdminController
|
|
5
|
+
admin_section :inboxes
|
|
6
|
+
|
|
7
|
+
def show
|
|
8
|
+
@messages = Spina::Pro::Message.spam.newest.page(params[:page]).per(25)
|
|
9
|
+
add_breadcrumb t("spina.pro.messages.spam")
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
module Spina::Admin
|
|
2
|
+
module Pro
|
|
3
|
+
class MessagesController < AdminController
|
|
4
|
+
admin_section :inboxes
|
|
5
|
+
|
|
6
|
+
before_action :set_inbox
|
|
7
|
+
before_action :set_message
|
|
8
|
+
|
|
9
|
+
def show
|
|
10
|
+
@message.mark_as_read!
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def edit
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def update
|
|
17
|
+
if @message.update(message_params)
|
|
18
|
+
render :update
|
|
19
|
+
else
|
|
20
|
+
render :edit, status: :unprocessable_entity
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def destroy
|
|
25
|
+
@message.destroy
|
|
26
|
+
redirect_to spina.admin_pro_inbox_path(@inbox)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def mark_as_unread
|
|
30
|
+
@message.mark_as_unread!
|
|
31
|
+
redirect_to spina.admin_pro_inbox_path(@inbox)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def mark_as_spam
|
|
35
|
+
@message.mark_as_spam!
|
|
36
|
+
redirect_to spina.admin_pro_inbox_path(@inbox)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def mark_as_ham
|
|
40
|
+
@message.mark_as_ham!
|
|
41
|
+
redirect_to spina.admin_pro_message_spam_path
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def forward
|
|
45
|
+
Spina::Pro::MessageMailer.forward(@message).deliver_later if @inbox.forwarding?
|
|
46
|
+
flash[:info] = t("spina.pro.messages.forwarded", email: @inbox.forward_email)
|
|
47
|
+
redirect_to spina.admin_pro_inbox_path(@inbox)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def archive
|
|
51
|
+
@message.archive!
|
|
52
|
+
flash[:info] = t("spina.pro.messages.archived")
|
|
53
|
+
redirect_to spina.admin_pro_inbox_path(@inbox)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def unarchive
|
|
57
|
+
@message.unarchive!
|
|
58
|
+
flash[:info] = t("spina.pro.messages.unarchived")
|
|
59
|
+
redirect_to spina.admin_pro_message_archive_path
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def message_params
|
|
65
|
+
params.require(:message).permit!
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def set_message
|
|
69
|
+
@message = @inbox.messages.find(params[:id])
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def set_inbox
|
|
73
|
+
@inbox = Spina::Pro::Inbox.find(params[:inbox_id])
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Spina::Admin
|
|
2
|
+
module Pro
|
|
3
|
+
class NotFoundErrorsController < AdminController
|
|
4
|
+
|
|
5
|
+
def index
|
|
6
|
+
add_breadcrumb t("spina.pro.rewrite_rules.title")
|
|
7
|
+
@not_found_errors = Spina::Pro::NotFoundError.not_ignored.order(visits: :desc, last_visited: :desc).page(params[:page]).per(50)
|
|
8
|
+
scope_search(params[:search])
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def ignored
|
|
12
|
+
@not_found_errors = Spina::Pro::NotFoundError.ignored.order(visits: :desc, last_visited: :desc).page(params[:page]).per(50)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def update
|
|
16
|
+
@not_found_error = Spina::Pro::NotFoundError.find(params[:id])
|
|
17
|
+
@not_found_error.update(not_found_error_params)
|
|
18
|
+
render turbo_stream: turbo_stream.update(@not_found_error, partial: "not_found_error", object: @not_found_error)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def scope_search(search)
|
|
25
|
+
return if search.blank?
|
|
26
|
+
@not_found_errors = @not_found_errors.where("path LIKE :search", search: "%#{search}%")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def not_found_error_params
|
|
30
|
+
params.require(:not_found_error).permit(:ignored)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Spina::Admin
|
|
2
|
+
module Pro
|
|
3
|
+
class RewriteRulesController < AdminController
|
|
4
|
+
before_action :set_breadcrumbs
|
|
5
|
+
|
|
6
|
+
def new
|
|
7
|
+
@rewrite_rule = Spina::RewriteRule.new(old_path: params[:old_path])
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def create
|
|
11
|
+
@rewrite_rule = Spina::RewriteRule.new(rewrite_rule_params)
|
|
12
|
+
if @rewrite_rule.save
|
|
13
|
+
redirect_to spina.admin_pro_rewrite_rules_path
|
|
14
|
+
else
|
|
15
|
+
render turbo_stream: turbo_stream.update(view_context.dom_id(@rewrite_rule, :form), partial: "form")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def index
|
|
20
|
+
@rewrite_rules = Spina::RewriteRule.where(created_manually: true).page(params[:page]).per(50)
|
|
21
|
+
scope_search(params[:search])
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def destroy
|
|
25
|
+
@rewrite_rule = Spina::RewriteRule.find(params[:id])
|
|
26
|
+
@rewrite_rule.destroy
|
|
27
|
+
render turbo_stream: turbo_stream.remove(@rewrite_rule)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def auto_generated
|
|
31
|
+
@rewrite_rules = Spina::RewriteRule.where(created_manually: false).page(params[:page]).per(50)
|
|
32
|
+
scope_search(params[:search])
|
|
33
|
+
render :index
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def scope_search(search)
|
|
39
|
+
return if search.blank?
|
|
40
|
+
@rewrite_rules = @rewrite_rules.where("old_path LIKE :search OR new_path LIKE :search", search: "%#{search}%")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def set_breadcrumbs
|
|
44
|
+
add_breadcrumb t('spina.pro.rewrite_rules.title')
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def rewrite_rule_params
|
|
48
|
+
params.require(:rewrite_rule).permit(:old_path, :new_path).merge(created_manually: true)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Spina::Admin
|
|
2
|
+
module Pro
|
|
3
|
+
class SearchController < AdminController
|
|
4
|
+
|
|
5
|
+
def create
|
|
6
|
+
if params[:query].present?
|
|
7
|
+
@results = Spina::Pro::SearchDocument.search(params[:query]).limit(8)
|
|
8
|
+
else
|
|
9
|
+
@pages = recently_visited_pages
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
render :new
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def mac_or_ios?
|
|
16
|
+
@mac_os ||= browser.platform.mac? || browser.platform.ios?
|
|
17
|
+
end
|
|
18
|
+
helper_method :mac_or_ios?
|
|
19
|
+
|
|
20
|
+
def recently_visited_pages
|
|
21
|
+
@pages ||= GlobalID::Locator.locate_many(recently_visited_gids, ignore_missing: true).first(3)
|
|
22
|
+
end
|
|
23
|
+
helper_method :recently_visited_pages
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def recently_visited_gids
|
|
28
|
+
begin
|
|
29
|
+
JSON.parse(cookies[:recently_visited])
|
|
30
|
+
rescue
|
|
31
|
+
[]
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def browser
|
|
36
|
+
@browser ||= Browser.new(request.user_agent)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Spina::Admin
|
|
2
|
+
module Pro
|
|
3
|
+
class VersionHistoryController < AdminController
|
|
4
|
+
before_action :set_page
|
|
5
|
+
|
|
6
|
+
def show
|
|
7
|
+
@version_history = @page.version_history.order(updated_at: :desc).page(params[:page]).per(20)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def set_page
|
|
13
|
+
@page = Spina::Page.find(params[:page_id])
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Spina
|
|
2
|
+
module Pro
|
|
3
|
+
class MessageMailer < ApplicationMailer
|
|
4
|
+
|
|
5
|
+
def forward(message)
|
|
6
|
+
@message = message
|
|
7
|
+
@inbox = message.inbox
|
|
8
|
+
|
|
9
|
+
mail to: @inbox.forward_email,
|
|
10
|
+
subject: t("spina.pro.message_mailer.forward.subject", type: @message.model_name.human, name: @message.to_name)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Spina
|
|
2
|
+
module Pro
|
|
3
|
+
class Inbox < ApplicationRecord
|
|
4
|
+
has_many :messages, dependent: :restrict_with_exception
|
|
5
|
+
|
|
6
|
+
validates :name, :label, presence: true
|
|
7
|
+
validates :name, uniqueness: true
|
|
8
|
+
|
|
9
|
+
after_update_commit :broadcast_unread_count, :broadcast_unread_indicator
|
|
10
|
+
|
|
11
|
+
def update_unread_count
|
|
12
|
+
update(unread_count: messages.unread.not_archived.ham.count)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def forwarding?
|
|
16
|
+
forward_email.present?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def broadcast_unread_count
|
|
22
|
+
broadcast_update_later_to Spina::Current.account, :messages_unread, target: "messages_unread_count", partial: "spina/admin/pro/inboxes/unread_count"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def broadcast_unread_indicator
|
|
26
|
+
broadcast_update_later_to Spina::Current.account, :messages_unread, target: "unread_indicator_inbox_#{id}", partial: "spina/admin/pro/inboxes/unread_indicator"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
module Spina
|
|
2
|
+
module Pro
|
|
3
|
+
class Message < ApplicationRecord
|
|
4
|
+
include AttrJson::Record
|
|
5
|
+
include Gravatar
|
|
6
|
+
|
|
7
|
+
# Class attributes
|
|
8
|
+
class_attribute :index_attributes, default: []
|
|
9
|
+
class_attribute :inbox_name, default: "messages"
|
|
10
|
+
class_attribute :inbox_label
|
|
11
|
+
|
|
12
|
+
belongs_to :inbox
|
|
13
|
+
|
|
14
|
+
scope :read, -> { where.not(read_at: nil) }
|
|
15
|
+
scope :unread, -> { where(read_at: nil) }
|
|
16
|
+
scope :newest, -> { order(created_at: :desc) }
|
|
17
|
+
scope :not_archived, -> { where(archived_at: nil) }
|
|
18
|
+
scope :archived, -> { where.not(archived_at: nil) }
|
|
19
|
+
scope :ham, -> { where(spam: false) }
|
|
20
|
+
scope :spam, -> { where(spam: true) }
|
|
21
|
+
|
|
22
|
+
# If no inbox is specified, store message in the default "Messages" inbox
|
|
23
|
+
before_validation :store_in_default_inbox, on: :create, if: -> { inbox.nil? }
|
|
24
|
+
|
|
25
|
+
# After commit, update unread count on inbox
|
|
26
|
+
after_commit -> { inbox.update_unread_count }
|
|
27
|
+
|
|
28
|
+
after_create_commit :broadcast_prepend_to_inbox
|
|
29
|
+
after_update_commit :broadcast_replace_to_inbox
|
|
30
|
+
|
|
31
|
+
# Forward message to email if Inbox forwarding is setup
|
|
32
|
+
after_create_commit :forward_message, if: -> { ham? && inbox.forwarding? }
|
|
33
|
+
|
|
34
|
+
def to_name
|
|
35
|
+
if message_attributes.include? :name
|
|
36
|
+
name
|
|
37
|
+
elsif message_attributes.include?(:first_name) && message_attributes.include?(:last_name)
|
|
38
|
+
[first_name, last_name].join(" ")
|
|
39
|
+
else
|
|
40
|
+
email
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def name_attributes
|
|
45
|
+
[:first_name, :last_name, :name]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def ham?
|
|
49
|
+
!spam?
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def label
|
|
53
|
+
id
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def unread?
|
|
57
|
+
read_at.nil?
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def archived?
|
|
61
|
+
archived_at.present?
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def index_attributes
|
|
65
|
+
self.class.index_attributes
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def message_attributes
|
|
69
|
+
self.class.attr_json_registry.attribute_names
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def body_attributes
|
|
73
|
+
message_attributes - name_attributes
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def field_type(key)
|
|
77
|
+
self.class.attr_json_registry.type_for_attribute(key)&.type
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def archive!
|
|
81
|
+
update!(archived_at: Time.current) if archived_at.nil?
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def unarchive!
|
|
85
|
+
update!(archived_at: nil)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def mark_as_read!
|
|
89
|
+
update!(read_at: Time.current) if read_at.nil?
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def mark_as_unread!
|
|
93
|
+
update!(read_at: nil)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def mark_as_spam!
|
|
97
|
+
update!(spam: true)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def mark_as_ham!
|
|
101
|
+
update!(spam: false)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def fallback_url
|
|
105
|
+
"https://eu.ui-avatars.com/api/#{CGI.escape(json_attributes.values.first.to_s)}/128"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
class << self
|
|
109
|
+
|
|
110
|
+
def field(name, type, **options)
|
|
111
|
+
attr_json(name, type, **options.except(:index))
|
|
112
|
+
self.index_attributes += [name] if options[:index]
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def inbox(name, label = nil)
|
|
116
|
+
self.inbox_name = name
|
|
117
|
+
self.inbox_label = label
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
private
|
|
123
|
+
|
|
124
|
+
def store_in_default_inbox
|
|
125
|
+
self.inbox = Inbox.where(name: self.class.inbox_name).first_or_create(label: self.class.inbox_label || self.class.inbox_name.capitalize)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def broadcast_prepend_to_inbox
|
|
129
|
+
broadcast_prepend_later_to inbox, target: :messages, partial: "spina/admin/pro/messages/message", locals: {message: self}
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def broadcast_replace_to_inbox
|
|
133
|
+
broadcast_replace_later_to inbox, partial: "spina/admin/pro/messages/message", locals: {message: self}
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def forward_message
|
|
137
|
+
MessageMailer.forward(self).deliver_later
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Spina
|
|
2
|
+
module Pro
|
|
3
|
+
class NotFoundError < ApplicationRecord
|
|
4
|
+
scope :ignored, -> { where(ignored: true) }
|
|
5
|
+
scope :not_ignored, -> { where(ignored: false) }
|
|
6
|
+
|
|
7
|
+
validates :path, :last_visited, presence: true
|
|
8
|
+
|
|
9
|
+
def uri
|
|
10
|
+
URI.parse(request_url)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Spina
|
|
2
|
+
module Pro
|
|
3
|
+
class PageRevision < ApplicationRecord
|
|
4
|
+
include AttrJson::Record
|
|
5
|
+
include AttrJson::NestedAttributes
|
|
6
|
+
include TranslatedContent
|
|
7
|
+
|
|
8
|
+
belongs_to :page
|
|
9
|
+
|
|
10
|
+
scope :draft, -> { where(draft: true) }
|
|
11
|
+
scope :published, -> { where(draft: false) }
|
|
12
|
+
|
|
13
|
+
def load
|
|
14
|
+
content.each do |key, value|
|
|
15
|
+
page[key.to_sym] = value
|
|
16
|
+
end
|
|
17
|
+
page
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Spina
|
|
2
|
+
module Pro
|
|
3
|
+
class SearchDocument < ApplicationRecord
|
|
4
|
+
include PgSearch::Model
|
|
5
|
+
|
|
6
|
+
belongs_to :searchable, polymorphic: true
|
|
7
|
+
|
|
8
|
+
pg_search_scope :search,
|
|
9
|
+
against: :content,
|
|
10
|
+
using: {
|
|
11
|
+
tsearch: {
|
|
12
|
+
dictionary: Spina::Pro.config.postgresql_dictionary,
|
|
13
|
+
prefix: true
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<% unless Spina::Pro.config.hide_inboxes %>
|
|
2
|
+
<%= render Spina::MainNavigation::SubNavComponent.new(:inboxes) do |nav| %>
|
|
3
|
+
<% nav.with_icon do %>
|
|
4
|
+
<%= heroicon('inbox', style: :solid, class: 'w-8 h-8 text-white md:mr-3 transform scale-95') %>
|
|
5
|
+
|
|
6
|
+
<%= turbo_stream_from Spina::Current.account, :messages_unread %>
|
|
7
|
+
<div id="messages_unread_count">
|
|
8
|
+
<%= render partial: "spina/admin/pro/inboxes/unread_count" %>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<div class="text-white font-semibold hidden md:block transform -translate-x-2 ease-in-out duration-300 absolute md:relative opacity-0 transition-all" data-navigation-target="label">
|
|
12
|
+
Inbox
|
|
13
|
+
</div>
|
|
14
|
+
<% end %>
|
|
15
|
+
|
|
16
|
+
<% nav.with_links do %>
|
|
17
|
+
<% inboxes.each do |inbox| %>
|
|
18
|
+
|
|
19
|
+
<li <%= "data-navigation-active" if @inbox == inbox %>>
|
|
20
|
+
<%= link_to spina.admin_pro_inbox_path(inbox), class: "flex items-center p-3 leading-none text-white hover:opacity-100 transition-opacity font-semibold #{'opacity-50' if @inbox != inbox}" do %>
|
|
21
|
+
<%= inbox.label %>
|
|
22
|
+
|
|
23
|
+
<div id="<%= dom_id(inbox, :unread_indicator) %>">
|
|
24
|
+
<%= render "spina/admin/pro/inboxes/unread_indicator", inbox: inbox %>
|
|
25
|
+
</div>
|
|
26
|
+
<% end %>
|
|
27
|
+
</li>
|
|
28
|
+
|
|
29
|
+
<% end %>
|
|
30
|
+
|
|
31
|
+
<%= render Spina::MainNavigation::LinkComponent.new(t("spina.pro.messages.message_archive"), spina.admin_pro_message_archive_path, active: controller_name.in?(["message_archive", "message_spam"])) %>
|
|
32
|
+
<% end %>
|
|
33
|
+
<% end %>
|
|
34
|
+
<% end %>
|
|
35
|
+
|
|
36
|
+
<li class="fixed bottom-20" data-controller="hotkeys" data-hotkeys="cmd+k, ctrl+k">
|
|
37
|
+
<%= button_to spina.admin_pro_search_path, class: "flex w-full items-center p-3 relative opacity-50 hover:opacity-100 transition-opacity", data: {turbo_frame: "modal", hotkeys_target: "button"} do %>
|
|
38
|
+
<%= heroicon('magnifying-glass', style: :mini, class: 'w-8 h-8 text-white md:mr-3') %>
|
|
39
|
+
<% end %>
|
|
40
|
+
</li>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= render Spina::MainNavigation::LinkComponent.new t('spina.pro.rewrite_rules.title'), spina.admin_pro_rewrite_rules_path, active: controller_name.in?(%w(rewrite_rules not_found_errors)) %>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<%= render Spina::Pages::TabButtonComponent.new(tab_name: 'pro_seo') do %>
|
|
2
|
+
<%= heroicon('search', style: :solid, class: 'w-4 h-4 mr-1 -ml-1 opacity-75') %>
|
|
3
|
+
<span class="hidden md:inline"><%= t("spina.pages.search_engines") %></span>
|
|
4
|
+
<span class="md:hidden">SEO</span>
|
|
5
|
+
<% end %>
|