spina-admin-journal 0.1.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.
Files changed (95) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +99 -0
  4. data/Rakefile +36 -0
  5. data/app/assets/config/spina_admin_journal_manifest.js +3 -0
  6. data/app/assets/javascripts/spina/admin/journal/application.es6 +72 -0
  7. data/app/assets/stylesheets/spina/admin/journal/application.css +27 -0
  8. data/app/controllers/spina/admin/journal/application_controller.rb +26 -0
  9. data/app/controllers/spina/admin/journal/articles_controller.rb +125 -0
  10. data/app/controllers/spina/admin/journal/authors_controller.rb +83 -0
  11. data/app/controllers/spina/admin/journal/institutions_controller.rb +71 -0
  12. data/app/controllers/spina/admin/journal/issues_controller.rb +129 -0
  13. data/app/controllers/spina/admin/journal/journals_controller.rb +73 -0
  14. data/app/controllers/spina/admin/journal/volumes_controller.rb +79 -0
  15. data/app/models/spina/admin/journal.rb +11 -0
  16. data/app/models/spina/admin/journal/affiliation.rb +62 -0
  17. data/app/models/spina/admin/journal/article.rb +55 -0
  18. data/app/models/spina/admin/journal/author.rb +47 -0
  19. data/app/models/spina/admin/journal/authorship.rb +19 -0
  20. data/app/models/spina/admin/journal/institution.rb +32 -0
  21. data/app/models/spina/admin/journal/issue.rb +49 -0
  22. data/app/models/spina/admin/journal/journal.rb +50 -0
  23. data/app/models/spina/admin/journal/volume.rb +37 -0
  24. data/app/validators/spina/admin/journal/uri_validator.rb +24 -0
  25. data/app/views/layouts/spina/admin/journal/articles.html.haml +10 -0
  26. data/app/views/layouts/spina/admin/journal/authors.html.haml +10 -0
  27. data/app/views/layouts/spina/admin/journal/institutions.html.haml +10 -0
  28. data/app/views/layouts/spina/admin/journal/issues.html.haml +10 -0
  29. data/app/views/layouts/spina/admin/journal/journals.html.haml +10 -0
  30. data/app/views/layouts/spina/admin/journal/volumes.html.haml +10 -0
  31. data/app/views/spina/admin/hooks/journal/_head.html.haml +2 -0
  32. data/app/views/spina/admin/hooks/journal/_primary_navigation.html.haml +27 -0
  33. data/app/views/spina/admin/hooks/journal/_website_secondary_navigation.html.haml +0 -0
  34. data/app/views/spina/admin/journal/affiliations/_affiliation.html.haml +8 -0
  35. data/app/views/spina/admin/journal/application/_empty_list.html.haml +3 -0
  36. data/app/views/spina/admin/journal/articles/_article.html.haml +10 -0
  37. data/app/views/spina/admin/journal/articles/_form.html.haml +29 -0
  38. data/app/views/spina/admin/journal/articles/_form_authors.html.haml +10 -0
  39. data/app/views/spina/admin/journal/articles/_form_details.html.haml +52 -0
  40. data/app/views/spina/admin/journal/articles/edit.html.haml +1 -0
  41. data/app/views/spina/admin/journal/articles/index.html.haml +19 -0
  42. data/app/views/spina/admin/journal/articles/new.html.haml +1 -0
  43. data/app/views/spina/admin/journal/authors/_author.html.haml +8 -0
  44. data/app/views/spina/admin/journal/authors/_form.html.haml +29 -0
  45. data/app/views/spina/admin/journal/authors/_form_affiliation.html.haml +20 -0
  46. data/app/views/spina/admin/journal/authors/_form_articles.html.haml +18 -0
  47. data/app/views/spina/admin/journal/authors/_form_details.html.haml +6 -0
  48. data/app/views/spina/admin/journal/authors/edit.html.haml +1 -0
  49. data/app/views/spina/admin/journal/authors/index.html.haml +17 -0
  50. data/app/views/spina/admin/journal/authors/new.html.haml +1 -0
  51. data/app/views/spina/admin/journal/institutions/_form.html.haml +29 -0
  52. data/app/views/spina/admin/journal/institutions/_form_details.html.haml +9 -0
  53. data/app/views/spina/admin/journal/institutions/_form_view_affiliations.html.haml +10 -0
  54. data/app/views/spina/admin/journal/institutions/_institution.html.haml +9 -0
  55. data/app/views/spina/admin/journal/institutions/edit.html.haml +1 -0
  56. data/app/views/spina/admin/journal/institutions/index.html.haml +16 -0
  57. data/app/views/spina/admin/journal/institutions/new.html.haml +1 -0
  58. data/app/views/spina/admin/journal/issues/_form.html.haml +29 -0
  59. data/app/views/spina/admin/journal/issues/_form_articles.html.haml +14 -0
  60. data/app/views/spina/admin/journal/issues/_form_details.html.haml +32 -0
  61. data/app/views/spina/admin/journal/issues/_issue.html.haml +9 -0
  62. data/app/views/spina/admin/journal/issues/edit.html.haml +1 -0
  63. data/app/views/spina/admin/journal/issues/index.html.haml +18 -0
  64. data/app/views/spina/admin/journal/issues/new.html.haml +1 -0
  65. data/app/views/spina/admin/journal/journals/_form.html.haml +35 -0
  66. data/app/views/spina/admin/journal/journals/edit.html.haml +1 -0
  67. data/app/views/spina/admin/journal/journals/index.html.haml +27 -0
  68. data/app/views/spina/admin/journal/journals/new.html.haml +1 -0
  69. data/app/views/spina/admin/journal/volumes/_form.html.haml +15 -0
  70. data/app/views/spina/admin/journal/volumes/_form_details.html.haml +10 -0
  71. data/app/views/spina/admin/journal/volumes/_form_issues.html.haml +13 -0
  72. data/app/views/spina/admin/journal/volumes/_volume.html.haml +7 -0
  73. data/app/views/spina/admin/journal/volumes/edit.html.haml +1 -0
  74. data/app/views/spina/admin/journal/volumes/index.html.haml +17 -0
  75. data/app/views/spina/admin/journal/volumes/new.html.haml +1 -0
  76. data/config/locales/en.yml +184 -0
  77. data/config/routes.rb +21 -0
  78. data/db/migrate/20201216152147_create_spina_admin_journal_journals.rb +13 -0
  79. data/db/migrate/20201216153822_create_spina_admin_journal_volumes.rb +13 -0
  80. data/db/migrate/20201216155113_create_spina_admin_journal_issues.rb +15 -0
  81. data/db/migrate/20201216161122_create_spina_admin_journal_articles.rb +16 -0
  82. data/db/migrate/20201216205633_create_spina_admin_journal_institutions.rb +11 -0
  83. data/db/migrate/20201216211224_create_spina_admin_journal_authors.rb +7 -0
  84. data/db/migrate/20201216221146_create_spina_admin_journal_affiliations.rb +15 -0
  85. data/db/migrate/20201216230816_create_spina_admin_journal_authorships.rb +14 -0
  86. data/db/migrate/20201231165231_create_spina_admin_journal_parts.rb +12 -0
  87. data/db/migrate/20210424123450_add_json_attributes_to_spina_admin_journal_journals.rb +7 -0
  88. data/db/migrate/20210424123521_add_json_attributes_to_spina_admin_journal_issues.rb +7 -0
  89. data/db/migrate/20210424123555_add_json_attributes_to_spina_admin_journal_articles.rb +7 -0
  90. data/lib/spina/admin/journal.rb +18 -0
  91. data/lib/spina/admin/journal/engine.rb +17 -0
  92. data/lib/spina/admin/journal/version.rb +9 -0
  93. data/lib/tasks/spina/admin/journal_tasks.rake +5 -0
  94. data/vendor/assets/javascripts/spina/admin/journal/html5sortable.js +1295 -0
  95. metadata +388 -0
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Admin
5
+ module Journal
6
+ # Authors are collections of {Affiliation}s.
7
+ #
8
+ # Since people can have multiple names, titles, institutions, etc., these details are all
9
+ # handled by the {Affiliation} record. This record groups said records together, in order
10
+ # that a coherent oeuvre of a single author be identified, which is more user-friendly.
11
+ #
12
+ # @note An author must have at least one Affiliation with +status: primary+, else validation
13
+ # will fail.
14
+ #
15
+ # @see Affiliation
16
+ class Author < ApplicationRecord
17
+ # @!attribute [rw] affiliations
18
+ # @return [ActiveRecord::Relation] Directly associated {Affiliation}s.
19
+ has_many :affiliations, inverse_of: :author, dependent: :destroy
20
+ accepts_nested_attributes_for :affiliations
21
+ # @!attribute [rw] institutions
22
+ # @return [ActiveRecord::Relation] The {Institution}s corresponding to this Author's affiliations.
23
+ has_many :institutions, through: :affiliations
24
+ # @!attribute [rw] articles
25
+ # @return [ActiveRecord::Relation] {Article}s associated through authorships.
26
+ has_many :articles, through: :affiliations
27
+
28
+ validate :must_have_one_primary_affiliation
29
+
30
+ # @!attribute [r] primary_affiliation
31
+ # @return [ActiveRecord::Relation] The author's primary affiliation.
32
+ def primary_affiliation
33
+ affiliations.primary.first || Affiliation.new(status: :primary)
34
+ end
35
+
36
+ private
37
+
38
+ def must_have_one_primary_affiliation
39
+ return if !affiliations.nil? && affiliations.any? \
40
+ && (affiliations.count { |affiliation| affiliation.status == 'primary' } == 1)
41
+
42
+ errors.add :affiliations, 'must have at least one primary affiliation'
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Admin
5
+ module Journal
6
+ # Joins Articles and Affiliations
7
+ # @see Article
8
+ # @see Affiliation
9
+ class Authorship < ApplicationRecord
10
+ # @!attribute [rw] article
11
+ # @return [ActiveRecord::Relation] the associated article
12
+ belongs_to :article
13
+ # @!attribute [rw] author_name
14
+ # @return [ActiveRecord::Relation] the associated affiliation of an author of the article
15
+ belongs_to :affiliation
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Admin
5
+ module Journal
6
+ # Record for an institution. Associated with {Author}s via {Affiliation}s.
7
+ #
8
+ # === Validators
9
+ # Presence:: {#name}
10
+ # Uniqueness:: {#name}
11
+ #
12
+ # === Scopes
13
+ # sorted:: sort institutions in alphabetical order
14
+ #
15
+ # @see Affiliation
16
+ class Institution < ApplicationRecord
17
+ # @!attribute [rw] name
18
+ # @return [String]
19
+ # @!attribute [rw] affiliations
20
+ # @return [ActiveRecord::Relation] the affiliations held at the institution
21
+ has_many :affiliations, dependent: :destroy
22
+ # @!attribute [rw] authors
23
+ # @return [ActiveRecord::Relation] associated authors
24
+ has_many :authors, through: :affiliations
25
+
26
+ validates :name, presence: true, uniqueness: { case_sensitive: false }
27
+
28
+ scope :sorted, -> { order(name: :asc) }
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Admin
5
+ module Journal
6
+ # Record for an issue of a journal.
7
+ # Belongs to a particular volume of a journal. Has many articles.
8
+ #
9
+ # === Validators
10
+ # Presence:: {#number}, {#date}
11
+ # Uniqueness:: {#number} (scope: volume)
12
+ #
13
+ # === Scopes
14
+ # sorted_asc:: sorted in order of increasing number
15
+ # sorted_desc:: sorted highest number first
16
+ #
17
+ # @see Article
18
+ # @see Volume
19
+ class Issue < ApplicationRecord
20
+ include AttrJson::Record
21
+ include AttrJson::NestedAttributes
22
+ include Spina::Partable
23
+ include Spina::TranslatedContent
24
+ # @!attribute [rw] number
25
+ # @return [Integer] The number
26
+ # @!attribute [rw] title
27
+ # @return [String] The title of the issue (optional)
28
+ # @!attribute [rw] date
29
+ # @return [Date] The (planned) publication date of the issue.
30
+ # @!attribute [rw] volume
31
+ # @return [ActiveRecord::Relation] the volume that contains this issue
32
+ belongs_to :volume
33
+ # @!attribute [rw] cover_img
34
+ # @return [Spina::Image, nil] the issue's cover image
35
+ belongs_to :cover_img, class_name: 'Spina::Image', optional: true
36
+
37
+ # @!attribute [rw] issue
38
+ # @return [ActiveRecord::Relation] the articles within this issue
39
+ has_many :articles, dependent: :destroy
40
+
41
+ validates :number, presence: true, uniqueness: { scope: :volume_id }
42
+ validates :date, presence: true
43
+
44
+ scope :sorted_asc, -> { includes(:volume).order('spina_admin_journal_volumes.number ASC', number: :asc) }
45
+ scope :sorted_desc, -> { includes(:volume).order('spina_admin_journal_volumes.number DESC', number: :desc) }
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Admin
5
+ module Journal
6
+ # Journal records. The top level of the database hierarchy.
7
+ #
8
+ # There should only ever be a single Journal record, which must always be accessed
9
+ # using {Journal.instance}
10
+ #
11
+ # === Validatorss
12
+ # Presence:: {#name}, {#singleton_guard}
13
+ # Uniqueness:: {#name}, {#singleton_guard}
14
+ class Journal < ApplicationRecord
15
+ include AttrJson::Record
16
+ include AttrJson::NestedAttributes
17
+ include Spina::Partable
18
+ include Spina::TranslatedContent
19
+ # @!attribute [rw] name
20
+ # @return [String] The name of the journal.
21
+ # @!attribute [rw] singleton_guard
22
+ # @return [Integer] Used to guarantee that a record is unique.
23
+ # @!attribute [rw] logo
24
+ # @return [Spina::Image, nil] The Spina::Image representing the logo of the journal.
25
+ belongs_to :logo, class_name: 'Spina::Image', optional: true
26
+
27
+ # @!attribute [rw] volumes
28
+ # @return [ActiveRecord::Relation] directly associated volumes
29
+ has_many :volumes, dependent: :destroy
30
+
31
+ validates :name, presence: true, uniqueness: true
32
+ validates :singleton_guard, presence: true, uniqueness: true
33
+
34
+ # Access the journal record.
35
+ #
36
+ # It is possible that Journal.instance gets called from two threads simultaneously.
37
+ # If this occurs, +Journal.create!+ will throw a validation error, since both records will
38
+ # be assigned the same value for +singleton_guard+. This is handled automatically, guaranteeing
39
+ # that there only ever be a single record.
40
+ def self.instance
41
+ Journal.first || Journal.create!(name: I18n.t('spina.admin.journal.unnamed_journal'), singleton_guard: 0)
42
+ rescue ActiveRecord::RecordNotUnique
43
+ # prevent race conditions leading to multiple records being created
44
+ logger.error 'Error when retrieving Journal instance. Retrying...'
45
+ retry
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Admin
5
+ module Journal
6
+ # Volumes of a journal.
7
+ # A journal has multiple volumes, a volume can have multiple issues.
8
+ #
9
+ # === Validators
10
+ # Presence:: {#number}
11
+ # Uniqueness:: {#number} (scope: journal)
12
+ #
13
+ # === Scopes
14
+ # sorted_asc:: sorted in order of increasing number
15
+ # sorted_desc:: sorted highest number first
16
+ #
17
+ # @see Issue
18
+ # @see Journal
19
+ class Volume < ApplicationRecord
20
+ # @!attribute [rw] number
21
+ # @return [Integer]
22
+ # @!attribute [rw] journal
23
+ # @return [ActiveRecord::Relation] the journal that contains this volume
24
+ belongs_to :journal
25
+
26
+ # @!attribute [rw] journal
27
+ # @return [ActiveRecord::Relation] the issues that comprise this volume
28
+ has_many :issues, dependent: :destroy
29
+
30
+ validates :number, presence: true, uniqueness: { scope: :journal_id }
31
+
32
+ scope :sorted_asc, -> { order(number: :asc) }
33
+ scope :sorted_desc, -> { order(number: :desc) }
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+
5
+ module Spina
6
+ module Admin
7
+ module Journal
8
+ # A simple validator for URIs.
9
+ class UriValidator < ActiveModel::EachValidator
10
+ def validate_each(record, attribute, value)
11
+ return if value == '' || value.nil?
12
+
13
+ uri = URI.parse(value)
14
+
15
+ return if uri.is_a?(URI::HTTP) && !uri.host.nil?
16
+
17
+ record.errors.add attribute, :invalid_uri
18
+ rescue URI::InvalidURIError
19
+ record.errors.add attribute, :invalid_uri
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,10 @@
1
+ - content_for :application do
2
+ %header#header
3
+ #header_actions
4
+ = yield :header_actions
5
+
6
+ = render partial: 'spina/admin/shared/breadcrumbs'
7
+
8
+ = yield
9
+
10
+ = render template: 'layouts/spina/admin/admin'
@@ -0,0 +1,10 @@
1
+ - content_for :application do
2
+ %header#header
3
+ #header_actions
4
+ = yield :header_actions
5
+
6
+ = render partial: 'spina/admin/shared/breadcrumbs'
7
+
8
+ = yield
9
+
10
+ = render template: 'layouts/spina/admin/admin'
@@ -0,0 +1,10 @@
1
+ - content_for :application do
2
+ %header#header
3
+ #header_actions
4
+ = yield :header_actions
5
+
6
+ = render partial: 'spina/admin/shared/breadcrumbs'
7
+
8
+ = yield
9
+
10
+ = render template: 'layouts/spina/admin/admin'
@@ -0,0 +1,10 @@
1
+ - content_for :application do
2
+ %header#header
3
+ #header_actions
4
+ = yield :header_actions
5
+
6
+ = render partial: 'spina/admin/shared/breadcrumbs'
7
+
8
+ = yield
9
+
10
+ = render template: 'layouts/spina/admin/admin'
@@ -0,0 +1,10 @@
1
+ - content_for :application do
2
+ %header#header
3
+ #header_actions
4
+ = yield :header_actions
5
+
6
+ = render partial: 'spina/admin/shared/breadcrumbs'
7
+
8
+ = yield
9
+
10
+ = render template: 'layouts/spina/admin/admin'
@@ -0,0 +1,10 @@
1
+ - content_for :application do
2
+ %header#header
3
+ #header_actions
4
+ = yield :header_actions
5
+
6
+ = render partial: 'spina/admin/shared/breadcrumbs'
7
+
8
+ = yield
9
+
10
+ = render template: 'layouts/spina/admin/admin'
@@ -0,0 +1,2 @@
1
+ = stylesheet_link_tag 'spina/admin/journal/application', media: 'all', data: { turbolinks_track: true }
2
+ = javascript_include_tag 'spina/admin/journal/application'
@@ -0,0 +1,27 @@
1
+ %li{ class: ('active' if %w[articles issues volumes].include? controller_name) }
2
+ = link_to admin_journal_volumes_path do
3
+ = icon 'pages'
4
+ = ::Spina::Admin::Journal::Journal.instance.name
5
+ = icon 'caret-right'
6
+
7
+ %ul
8
+ %li{ class: ('active' if %w[volumes].include? controller_name) }
9
+ = link_to ::Spina::Admin::Journal::Volume.model_name.human(count: :many), admin_journal_volumes_path
10
+ %li{ class: ('active' if %w[issues].include? controller_name) }
11
+ = link_to ::Spina::Admin::Journal::Issue.model_name.human(count: :many), admin_journal_issues_path
12
+ %li{ class: ('active' if %w[articles].include? controller_name) }
13
+ = link_to ::Spina::Admin::Journal::Article.model_name.human(count: :many), admin_journal_articles_path
14
+
15
+ %li{ class: ('active' if %w[journals authors institutions].include? controller_name) }
16
+ = link_to edit_admin_journal_journal_path(::Spina::Admin::Journal::Journal.instance.id) do
17
+ = icon 'dots'
18
+ = t 'spina.admin.journal.navigation_title'
19
+ = icon 'caret-right'
20
+
21
+ %ul
22
+ %li{ class: ('active' if %w[journals].include? controller_name) }
23
+ = link_to ::Spina::Admin::Journal::Journal.model_name.human(count: :one), edit_admin_journal_journal_path(::Spina::Admin::Journal::Journal.instance.id)
24
+ %li{ class: ('active' if %w[authors].include? controller_name) }
25
+ = link_to ::Spina::Admin::Journal::Author.model_name.human(count: :many), admin_journal_authors_path
26
+ %li{ class: ('active' if %w[institutions].include? controller_name) }
27
+ = link_to ::Spina::Admin::Journal::Institution.model_name.human(count: :many), admin_journal_institutions_path
@@ -0,0 +1,8 @@
1
+ %tr
2
+ %td= affiliation.name
3
+ %td= affiliation.institution.name
4
+ %td= affiliation.articles.count
5
+ %td.nowrap.align-right
6
+ = link_to edit_admin_journal_author_path(affiliation.author.id), class: 'button button-link' do
7
+ = icon 'pencil-outline'
8
+ = t '.view'
@@ -0,0 +1,3 @@
1
+ %tr
2
+ %td.align-center{ colspan: 1000 }
3
+ %em= message
@@ -0,0 +1,10 @@
1
+ %tr{ data: { id: article.id } }
2
+ %td= article.issue.volume.number
3
+ %td= article.issue.number
4
+ %td.position-display= article.number
5
+ %td= time_tag article.issue.date, format: :iso8601
6
+ %td= article.title
7
+ %td.nowrap.align-right
8
+ = link_to edit_admin_journal_article_path(article.id), class: 'button button-link' do
9
+ = icon 'pencil-outline'
10
+ = t '.view'
@@ -0,0 +1,29 @@
1
+ - if @article.errors.any?
2
+ - content_for :notifications do
3
+ .notification.notification-danger.animated.fadeInRight
4
+ = icon 'exclamation'
5
+ .notification-message
6
+ = t 'spina.notifications.alert'
7
+ %small= @article.errors.full_messages.join('<br />').html_safe
8
+ = link_to '#', data: { close_notification: true } do
9
+ = icon 'cross'
10
+
11
+ = form_for @article, html: { autocomplete: "off" } do |f|
12
+ %header#header
13
+ = render partial: 'spina/admin/shared/breadcrumbs'
14
+
15
+ #header_actions
16
+ %button.button.button-primary{ type: 'submit', style: 'margin-right: 0', data: { disable_with: t('spina.pages.saving') } }
17
+ = icon 'check'
18
+ = t '.save'
19
+
20
+ = link_to t('spina.cancel'), admin_journal_articles_path, class: 'button', style: 'margin-right: 0;'
21
+
22
+ %nav#secondary.tabs
23
+ %ul
24
+ - @tabs.each_with_index do |tab, i|
25
+ %li{ class: ('active' if i == 0) }
26
+ = link_to t(".#{tab}"), "##{tab}"
27
+
28
+ #details.tab-content.active= render 'form_details', f: f
29
+ #authors.tab-content= render 'form_authors', f: f
@@ -0,0 +1,10 @@
1
+ .table-container
2
+ %table.table
3
+ %thead
4
+ %tr
5
+ %th= ::Spina::Admin::Journal::Affiliation.human_attribute_name :name
6
+ %th= ::Spina::Admin::Journal::Affiliation.human_attribute_name :institution
7
+ %th= t '.number_of_articles'
8
+ %th
9
+ %tbody
10
+ = render @article.affiliations.any? ? @article.affiliations : 'empty_list', message: t('.no_authors')