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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +99 -0
- data/Rakefile +36 -0
- data/app/assets/config/spina_admin_journal_manifest.js +3 -0
- data/app/assets/javascripts/spina/admin/journal/application.es6 +72 -0
- data/app/assets/stylesheets/spina/admin/journal/application.css +27 -0
- data/app/controllers/spina/admin/journal/application_controller.rb +26 -0
- data/app/controllers/spina/admin/journal/articles_controller.rb +125 -0
- data/app/controllers/spina/admin/journal/authors_controller.rb +83 -0
- data/app/controllers/spina/admin/journal/institutions_controller.rb +71 -0
- data/app/controllers/spina/admin/journal/issues_controller.rb +129 -0
- data/app/controllers/spina/admin/journal/journals_controller.rb +73 -0
- data/app/controllers/spina/admin/journal/volumes_controller.rb +79 -0
- data/app/models/spina/admin/journal.rb +11 -0
- data/app/models/spina/admin/journal/affiliation.rb +62 -0
- data/app/models/spina/admin/journal/article.rb +55 -0
- data/app/models/spina/admin/journal/author.rb +47 -0
- data/app/models/spina/admin/journal/authorship.rb +19 -0
- data/app/models/spina/admin/journal/institution.rb +32 -0
- data/app/models/spina/admin/journal/issue.rb +49 -0
- data/app/models/spina/admin/journal/journal.rb +50 -0
- data/app/models/spina/admin/journal/volume.rb +37 -0
- data/app/validators/spina/admin/journal/uri_validator.rb +24 -0
- data/app/views/layouts/spina/admin/journal/articles.html.haml +10 -0
- data/app/views/layouts/spina/admin/journal/authors.html.haml +10 -0
- data/app/views/layouts/spina/admin/journal/institutions.html.haml +10 -0
- data/app/views/layouts/spina/admin/journal/issues.html.haml +10 -0
- data/app/views/layouts/spina/admin/journal/journals.html.haml +10 -0
- data/app/views/layouts/spina/admin/journal/volumes.html.haml +10 -0
- data/app/views/spina/admin/hooks/journal/_head.html.haml +2 -0
- data/app/views/spina/admin/hooks/journal/_primary_navigation.html.haml +27 -0
- data/app/views/spina/admin/hooks/journal/_website_secondary_navigation.html.haml +0 -0
- data/app/views/spina/admin/journal/affiliations/_affiliation.html.haml +8 -0
- data/app/views/spina/admin/journal/application/_empty_list.html.haml +3 -0
- data/app/views/spina/admin/journal/articles/_article.html.haml +10 -0
- data/app/views/spina/admin/journal/articles/_form.html.haml +29 -0
- data/app/views/spina/admin/journal/articles/_form_authors.html.haml +10 -0
- data/app/views/spina/admin/journal/articles/_form_details.html.haml +52 -0
- data/app/views/spina/admin/journal/articles/edit.html.haml +1 -0
- data/app/views/spina/admin/journal/articles/index.html.haml +19 -0
- data/app/views/spina/admin/journal/articles/new.html.haml +1 -0
- data/app/views/spina/admin/journal/authors/_author.html.haml +8 -0
- data/app/views/spina/admin/journal/authors/_form.html.haml +29 -0
- data/app/views/spina/admin/journal/authors/_form_affiliation.html.haml +20 -0
- data/app/views/spina/admin/journal/authors/_form_articles.html.haml +18 -0
- data/app/views/spina/admin/journal/authors/_form_details.html.haml +6 -0
- data/app/views/spina/admin/journal/authors/edit.html.haml +1 -0
- data/app/views/spina/admin/journal/authors/index.html.haml +17 -0
- data/app/views/spina/admin/journal/authors/new.html.haml +1 -0
- data/app/views/spina/admin/journal/institutions/_form.html.haml +29 -0
- data/app/views/spina/admin/journal/institutions/_form_details.html.haml +9 -0
- data/app/views/spina/admin/journal/institutions/_form_view_affiliations.html.haml +10 -0
- data/app/views/spina/admin/journal/institutions/_institution.html.haml +9 -0
- data/app/views/spina/admin/journal/institutions/edit.html.haml +1 -0
- data/app/views/spina/admin/journal/institutions/index.html.haml +16 -0
- data/app/views/spina/admin/journal/institutions/new.html.haml +1 -0
- data/app/views/spina/admin/journal/issues/_form.html.haml +29 -0
- data/app/views/spina/admin/journal/issues/_form_articles.html.haml +14 -0
- data/app/views/spina/admin/journal/issues/_form_details.html.haml +32 -0
- data/app/views/spina/admin/journal/issues/_issue.html.haml +9 -0
- data/app/views/spina/admin/journal/issues/edit.html.haml +1 -0
- data/app/views/spina/admin/journal/issues/index.html.haml +18 -0
- data/app/views/spina/admin/journal/issues/new.html.haml +1 -0
- data/app/views/spina/admin/journal/journals/_form.html.haml +35 -0
- data/app/views/spina/admin/journal/journals/edit.html.haml +1 -0
- data/app/views/spina/admin/journal/journals/index.html.haml +27 -0
- data/app/views/spina/admin/journal/journals/new.html.haml +1 -0
- data/app/views/spina/admin/journal/volumes/_form.html.haml +15 -0
- data/app/views/spina/admin/journal/volumes/_form_details.html.haml +10 -0
- data/app/views/spina/admin/journal/volumes/_form_issues.html.haml +13 -0
- data/app/views/spina/admin/journal/volumes/_volume.html.haml +7 -0
- data/app/views/spina/admin/journal/volumes/edit.html.haml +1 -0
- data/app/views/spina/admin/journal/volumes/index.html.haml +17 -0
- data/app/views/spina/admin/journal/volumes/new.html.haml +1 -0
- data/config/locales/en.yml +184 -0
- data/config/routes.rb +21 -0
- data/db/migrate/20201216152147_create_spina_admin_journal_journals.rb +13 -0
- data/db/migrate/20201216153822_create_spina_admin_journal_volumes.rb +13 -0
- data/db/migrate/20201216155113_create_spina_admin_journal_issues.rb +15 -0
- data/db/migrate/20201216161122_create_spina_admin_journal_articles.rb +16 -0
- data/db/migrate/20201216205633_create_spina_admin_journal_institutions.rb +11 -0
- data/db/migrate/20201216211224_create_spina_admin_journal_authors.rb +7 -0
- data/db/migrate/20201216221146_create_spina_admin_journal_affiliations.rb +15 -0
- data/db/migrate/20201216230816_create_spina_admin_journal_authorships.rb +14 -0
- data/db/migrate/20201231165231_create_spina_admin_journal_parts.rb +12 -0
- data/db/migrate/20210424123450_add_json_attributes_to_spina_admin_journal_journals.rb +7 -0
- data/db/migrate/20210424123521_add_json_attributes_to_spina_admin_journal_issues.rb +7 -0
- data/db/migrate/20210424123555_add_json_attributes_to_spina_admin_journal_articles.rb +7 -0
- data/lib/spina/admin/journal.rb +18 -0
- data/lib/spina/admin/journal/engine.rb +17 -0
- data/lib/spina/admin/journal/version.rb +9 -0
- data/lib/tasks/spina/admin/journal_tasks.rake +5 -0
- data/vendor/assets/javascripts/spina/admin/journal/html5sortable.js +1295 -0
- metadata +388 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spina
|
|
4
|
+
module Admin
|
|
5
|
+
module Journal
|
|
6
|
+
# Controller for {Institution} records
|
|
7
|
+
class InstitutionsController < ApplicationController
|
|
8
|
+
before_action :set_breadcrumb
|
|
9
|
+
before_action :set_tabs, except: %i[index destroy]
|
|
10
|
+
before_action :set_institution, only: %i[edit update destroy]
|
|
11
|
+
|
|
12
|
+
def index
|
|
13
|
+
@institutions = Institution.all
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def new
|
|
17
|
+
@institution = Institution.new
|
|
18
|
+
add_breadcrumb t('.new')
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def edit; end
|
|
22
|
+
|
|
23
|
+
def create
|
|
24
|
+
@institution = Institution.new(institution_params)
|
|
25
|
+
|
|
26
|
+
if @institution.save
|
|
27
|
+
redirect_to admin_journal_institutions_path, success: t('.saved')
|
|
28
|
+
else
|
|
29
|
+
render :new
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def update
|
|
34
|
+
if @institution.update(institution_params)
|
|
35
|
+
redirect_to admin_journal_institutions_path, success: t('.saved')
|
|
36
|
+
else
|
|
37
|
+
render :edit
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def destroy
|
|
42
|
+
@institution.destroy
|
|
43
|
+
respond_to do |format|
|
|
44
|
+
format.html do
|
|
45
|
+
redirect_to admin_journal_institutions_path, success: t('.deleted')
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def institution_params
|
|
53
|
+
params.require(:admin_journal_institution).permit(:name)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def set_breadcrumb
|
|
57
|
+
add_breadcrumb Institution.model_name.human(count: :many), admin_journal_institutions_path
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def set_tabs
|
|
61
|
+
@tabs = %w[details view_affiliations]
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def set_institution
|
|
65
|
+
@institution = Institution.find(params[:id])
|
|
66
|
+
add_breadcrumb @institution.name
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spina
|
|
4
|
+
module Admin
|
|
5
|
+
module Journal
|
|
6
|
+
# Controller for {Issue} records.
|
|
7
|
+
class IssuesController < ApplicationController
|
|
8
|
+
PARTS_PARAMS = [
|
|
9
|
+
:name, :title, :type, :content, :filename, :signed_blob_id, :alt, :attachment_id, :image_id,
|
|
10
|
+
{ images_attributes: %i[filename signed_blob_id image_id alt],
|
|
11
|
+
content_attributes: [
|
|
12
|
+
:name, :title,
|
|
13
|
+
{ parts_attributes: [
|
|
14
|
+
:name, :title, :type, :content, :filename, :signed_blob_id, :alt, :attachment_id, :image_id,
|
|
15
|
+
{ images_attributes: %i[filename signed_blob_id image_id alt] }
|
|
16
|
+
] }
|
|
17
|
+
] }
|
|
18
|
+
].freeze
|
|
19
|
+
CONTENT_PARAMS = Spina.config.locales.inject({}) do |params, locale|
|
|
20
|
+
params.merge("#{locale}_content_attributes": [*PARTS_PARAMS])
|
|
21
|
+
end
|
|
22
|
+
PARAMS = [:volume_id, :title, :date, { **CONTENT_PARAMS }].freeze
|
|
23
|
+
PARTS = %w[cover_img description].freeze
|
|
24
|
+
|
|
25
|
+
before_action :set_breadcrumb
|
|
26
|
+
before_action :set_tabs, except: %i[index destroy sort]
|
|
27
|
+
before_action :set_issue, only: %i[edit update destroy]
|
|
28
|
+
before_action :set_parts_attributes, only: %i[new edit]
|
|
29
|
+
before_action :build_parts, only: %i[edit]
|
|
30
|
+
|
|
31
|
+
def index
|
|
32
|
+
@issues = Issue.sorted_desc
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def new
|
|
36
|
+
@issue = Issue.new
|
|
37
|
+
build_parts
|
|
38
|
+
add_breadcrumb t('.new')
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def edit
|
|
42
|
+
add_breadcrumb t('spina.admin.journal.volumes.volume_number', number: @issue.volume.number),
|
|
43
|
+
edit_admin_journal_volume_path(@issue.volume)
|
|
44
|
+
add_breadcrumb t('spina.admin.journal.issues.issue_number', number: @issue.number)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def create
|
|
48
|
+
@issue = Issue.new(issue_params)
|
|
49
|
+
sister_issues = Issue.where(volume: @issue.volume_id)
|
|
50
|
+
@issue.number = sister_issues.any? ? sister_issues.sorted_desc.first.number + 1 : 1
|
|
51
|
+
|
|
52
|
+
if @issue.save
|
|
53
|
+
redirect_to admin_journal_issues_path, success: t('.saved')
|
|
54
|
+
else
|
|
55
|
+
render :new
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def update
|
|
60
|
+
if @issue.update(issue_params)
|
|
61
|
+
redirect_to admin_journal_issues_path, success: t('.saved')
|
|
62
|
+
else
|
|
63
|
+
render :edit
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def destroy
|
|
68
|
+
@issue.destroy
|
|
69
|
+
respond_to do |format|
|
|
70
|
+
format.html do
|
|
71
|
+
redirect_to admin_journal_issues_path, success: t('.deleted')
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def sort
|
|
77
|
+
ActiveRecord::Base.transaction do
|
|
78
|
+
sort_params.each do |id, new_pos|
|
|
79
|
+
# ignore uniqueness validation for now
|
|
80
|
+
Issue.find(id.to_i).update_attribute(:number, new_pos.to_i) # rubocop:disable Rails/SkipsModelValidations
|
|
81
|
+
end
|
|
82
|
+
validate_sort_order
|
|
83
|
+
end
|
|
84
|
+
render json: { success: true, message: t('.sort_success') }
|
|
85
|
+
rescue ActiveRecord::RecordInvalid
|
|
86
|
+
render json: { success: false, message: t('.sort_error') }
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
def set_issue
|
|
92
|
+
@issue = Issue.find(params[:id])
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def issue_params
|
|
96
|
+
params.require(:admin_journal_issue).permit(PARAMS)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def sort_params
|
|
100
|
+
params.require(:admin_journal_issues).require(:list).permit!
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def validate_sort_order
|
|
104
|
+
Issue.where(volume_id: params[:volume_id]).each do |issue|
|
|
105
|
+
raise ActiveRecord::RecordInvalid if issue.invalid?
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def set_breadcrumb
|
|
110
|
+
add_breadcrumb Issue.model_name.human(count: :many), admin_journal_issues_path
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def set_tabs
|
|
114
|
+
@tabs = %w[details articles]
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def set_parts_attributes
|
|
118
|
+
@parts_attributes = current_theme.parts.select { |part| PARTS.include? part[:name] }
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def build_parts
|
|
122
|
+
return unless @parts_attributes.is_a? Array
|
|
123
|
+
|
|
124
|
+
@parts = @parts_attributes.collect { |part_attributes| @issue.part(part_attributes) }
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spina
|
|
4
|
+
module Admin
|
|
5
|
+
module Journal
|
|
6
|
+
# Controller for {Journal} records.
|
|
7
|
+
# A site only ever has a single journal, so the index action is not needed.
|
|
8
|
+
class JournalsController < ApplicationController
|
|
9
|
+
PARTS_PARAMS = [
|
|
10
|
+
:name, :title, :type, :content, :filename, :signed_blob_id, :alt, :attachment_id, :image_id,
|
|
11
|
+
{ images_attributes: %i[filename signed_blob_id image_id alt],
|
|
12
|
+
content_attributes: [
|
|
13
|
+
:name, :title,
|
|
14
|
+
{ parts_attributes: [
|
|
15
|
+
:name, :title, :type, :content, :filename, :signed_blob_id, :alt, :attachment_id, :image_id,
|
|
16
|
+
{ images_attributes: %i[filename signed_blob_id image_id alt] }
|
|
17
|
+
] }
|
|
18
|
+
] }
|
|
19
|
+
].freeze
|
|
20
|
+
CONTENT_PARAMS = Spina.config.locales.inject({}) do |params, locale|
|
|
21
|
+
params.merge("#{locale}_content_attributes": [*PARTS_PARAMS])
|
|
22
|
+
end
|
|
23
|
+
PARAMS = [:name, { **CONTENT_PARAMS }].freeze
|
|
24
|
+
PARTS = %w[logo description].freeze
|
|
25
|
+
|
|
26
|
+
before_action :set_journal
|
|
27
|
+
before_action :set_parts_attributes
|
|
28
|
+
before_action :build_parts, only: %i[edit]
|
|
29
|
+
|
|
30
|
+
def edit
|
|
31
|
+
add_breadcrumb @journal.name
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def update
|
|
35
|
+
if @journal.update(journal_params)
|
|
36
|
+
redirect_to edit_admin_journal_journal_path(@journal), success: t('.saved')
|
|
37
|
+
else
|
|
38
|
+
render :edit
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def destroy
|
|
43
|
+
@journal.destroy
|
|
44
|
+
respond_to do |format|
|
|
45
|
+
format.html do
|
|
46
|
+
redirect_to edit_admin_journal_journal_path(Journal.instance), success: t('.deleted')
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def set_journal
|
|
54
|
+
@journal = Journal.instance
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def set_parts_attributes
|
|
58
|
+
@parts_attributes = current_theme.parts.select { |part| PARTS.include? part[:name] }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def build_parts
|
|
62
|
+
return unless @parts_attributes.is_a? Array
|
|
63
|
+
|
|
64
|
+
@parts = @parts_attributes.collect { |part_attributes| @journal.part(part_attributes) }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def journal_params
|
|
68
|
+
params.require(:admin_journal_journal).permit(PARAMS)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spina
|
|
4
|
+
module Admin
|
|
5
|
+
module Journal
|
|
6
|
+
# Controller for {Volume} records.
|
|
7
|
+
class VolumesController < ApplicationController
|
|
8
|
+
before_action :set_breadcrumb
|
|
9
|
+
before_action :set_tabs, except: %i[index destroy]
|
|
10
|
+
before_action :set_volume, only: %i[edit destroy]
|
|
11
|
+
|
|
12
|
+
def index
|
|
13
|
+
@volumes = Volume.sorted_asc
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def edit; end
|
|
17
|
+
|
|
18
|
+
def create # rubocop:disable Metrics/AbcSize
|
|
19
|
+
@volume = Volume.new
|
|
20
|
+
@volume.journal_id = Journal.instance.id
|
|
21
|
+
@volume.number = Volume.any? ? Volume.sorted_desc.first.number + 1 : 1
|
|
22
|
+
@volume.save!
|
|
23
|
+
redirect_to admin_journal_volumes_path, success: t('.created', number: @volume.number)
|
|
24
|
+
rescue ActiveRecord::RecordNotUnique
|
|
25
|
+
# can only happen because of some race condition where two Volumes are created at the same time
|
|
26
|
+
logger.error 'Error when creating new volume. Retrying...'
|
|
27
|
+
retry
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def destroy
|
|
31
|
+
@volume.destroy
|
|
32
|
+
respond_to do |format|
|
|
33
|
+
format.html do
|
|
34
|
+
redirect_to admin_journal_volumes_path, success: 'Volume deleted.'
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def sort
|
|
40
|
+
ActiveRecord::Base.transaction do
|
|
41
|
+
sort_params.each do |id, new_pos|
|
|
42
|
+
# ignore uniqueness validation for now
|
|
43
|
+
Volume.find(id.to_i).update_attribute(:number, new_pos.to_i) # rubocop:disable Rails/SkipsModelValidations
|
|
44
|
+
end
|
|
45
|
+
validate_sort_order
|
|
46
|
+
end
|
|
47
|
+
render json: { success: true, message: t('.sort_success') }
|
|
48
|
+
rescue ActiveRecord::RecordInvalid
|
|
49
|
+
render json: { success: false, message: t('.sort_error') }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def sort_params
|
|
55
|
+
params.require(:admin_journal_volumes).require(:list).permit!
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def validate_sort_order
|
|
59
|
+
Volume.where(journal_id: params[:journal_id]).each do |volume|
|
|
60
|
+
raise ActiveRecord::RecordInvalid if volume.invalid?
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def set_breadcrumb
|
|
65
|
+
add_breadcrumb Volume.model_name.human(count: :many), admin_journal_volumes_path
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def set_volume
|
|
69
|
+
@volume = Volume.find(params[:id])
|
|
70
|
+
add_breadcrumb t('spina.admin.journal.volumes.volume_number', number: @volume.number)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def set_tabs
|
|
74
|
+
@tabs = %w[details issues]
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spina
|
|
4
|
+
module Admin
|
|
5
|
+
module Journal
|
|
6
|
+
# Rich join between an Institution and an Author. Associated with Articles via Authorships.
|
|
7
|
+
#
|
|
8
|
+
# Since authors can in principle use different names in different articles, names are associated
|
|
9
|
+
# with affiilations, rather than with authors directly.
|
|
10
|
+
#
|
|
11
|
+
# === Validators
|
|
12
|
+
# Presence:: {#first_name}, {#surname}
|
|
13
|
+
#
|
|
14
|
+
# === Scopes
|
|
15
|
+
# sorted:: sorted by surname in alphabetical order
|
|
16
|
+
#
|
|
17
|
+
# @see Institution
|
|
18
|
+
# @see Author
|
|
19
|
+
# @see Article
|
|
20
|
+
# @see Authorship
|
|
21
|
+
class Affiliation < ApplicationRecord
|
|
22
|
+
# @!attribute [rw] first_name
|
|
23
|
+
# @return [String] the first name(s) of the author
|
|
24
|
+
# @!attribute [rw] surname
|
|
25
|
+
# @return [String] the last name(s) of the author
|
|
26
|
+
# @!attribute [rw] status
|
|
27
|
+
# @return [Integer] the status of the author, which can be either primary (1) or other (0)
|
|
28
|
+
# @!attribute [rw] author
|
|
29
|
+
# @return [ActiveRecord::Relation] the {Author} whose affiliation this record holds
|
|
30
|
+
belongs_to :author
|
|
31
|
+
# @!attribute [rw] institution
|
|
32
|
+
# @return [ActiveRecord::Relation] the {Institution} to which the author is affiliated
|
|
33
|
+
belongs_to :institution
|
|
34
|
+
# @!attribute [rw] authorships
|
|
35
|
+
# @return [ActiveRecord::Relation] directly associated {Authorship}s
|
|
36
|
+
has_many :authorships, dependent: :destroy
|
|
37
|
+
# @!attribute [rw] articles
|
|
38
|
+
# @return [ActiveRecord::Relation] {Article}s associated through {Authorship}s
|
|
39
|
+
has_many :articles, through: :authorships
|
|
40
|
+
|
|
41
|
+
validates :first_name, presence: true
|
|
42
|
+
validates :surname, presence: true
|
|
43
|
+
|
|
44
|
+
enum status: { primary: 1, other: 0 }
|
|
45
|
+
|
|
46
|
+
scope :sorted, -> { order(surname: :asc) }
|
|
47
|
+
|
|
48
|
+
# @!attribute [r] name
|
|
49
|
+
# @return [String] The full name of the author.
|
|
50
|
+
def name
|
|
51
|
+
"#{first_name} #{surname}"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @!attribute [r] reversed_name
|
|
55
|
+
# @return [String] The full name of the author, surname first.
|
|
56
|
+
def reversed_name
|
|
57
|
+
"#{surname}, #{first_name}"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spina
|
|
4
|
+
module Admin
|
|
5
|
+
module Journal
|
|
6
|
+
# Record for an individual article.
|
|
7
|
+
#
|
|
8
|
+
# === Validators
|
|
9
|
+
# Presence:: {#number}, {#title}
|
|
10
|
+
# Uniqueness:: {#number} (scope: issue)
|
|
11
|
+
# URI:: {#url}
|
|
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 Author
|
|
19
|
+
# @see Authorship
|
|
20
|
+
class Article < ApplicationRecord
|
|
21
|
+
include AttrJson::Record
|
|
22
|
+
include AttrJson::NestedAttributes
|
|
23
|
+
include Spina::Partable
|
|
24
|
+
include Spina::TranslatedContent
|
|
25
|
+
# @!attribute [rw] number
|
|
26
|
+
# @return [Integer] The position of the article within its issue.
|
|
27
|
+
# @!attribute [rw] title
|
|
28
|
+
# @return [String] The article's title.
|
|
29
|
+
# @!attribute [rw] url
|
|
30
|
+
# @return [String] An external link to the article.
|
|
31
|
+
# @!attribute [rw] doi
|
|
32
|
+
# @return [String] A digital object intentifier for the article.
|
|
33
|
+
# @!attribute [rw] issue
|
|
34
|
+
# @return [Issue] The issue that contains this article.
|
|
35
|
+
belongs_to :issue
|
|
36
|
+
# @!attribute [rw] file
|
|
37
|
+
# @return [Spina::Attachment] The attached file
|
|
38
|
+
belongs_to :file, class_name: 'Spina::Attachment', optional: true
|
|
39
|
+
|
|
40
|
+
# @!attribute [rw] authorships
|
|
41
|
+
has_many :authorships, dependent: :destroy
|
|
42
|
+
# @!attribute [rw] affiliations
|
|
43
|
+
# @return [ActiveRecord::Relation] The authors of the article.
|
|
44
|
+
has_many :affiliations, through: :authorships
|
|
45
|
+
|
|
46
|
+
validates :number, presence: true, uniqueness: { scope: :issue_id }
|
|
47
|
+
validates :title, presence: true
|
|
48
|
+
validates :url, 'spina/admin/journal/uri': true
|
|
49
|
+
|
|
50
|
+
scope :sorted_asc, -> { includes(:issue).order('spina_admin_journal_issues.number ASC', number: :asc) }
|
|
51
|
+
scope :sorted_desc, -> { includes(:issue).order('spina_admin_journal_issues.number DESC', number: :desc) }
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|