spina-admin-journal 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 75a11c42a861742be9bee3d10d3a84f745f2d489b8a1ad61bf8e699f4f7fdc04
4
+ data.tar.gz: 343abc141c3af6b35492ec12a4c99325b116a3f63c5058fe7c61359cdfcd0da2
5
+ SHA512:
6
+ metadata.gz: 8209f366bfdd31dee8b559a9ca0c3966f3383687560b87a20cda19fbb198eb8faa5929d07e1b97b773464ab950fb5d2c298b4e168a22afacec726c7bb7fee2a4
7
+ data.tar.gz: 7182be875c118c6c59322b6e5a3fd51d81d5d51700be8db549ae48438df05bbb2fd9c290219cdbcae3d28b5530e172e5891ceb1636954c7aa36410406f96573b
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2021 Louis Van Steene
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,99 @@
1
+ # Spina::Admin::Journal
2
+
3
+ ![Rails tests](https://github.com/louis-vs/spina-admin-journal/workflows/Verify/badge.svg?branch=master&event=push)
4
+ [![codecov](https://codecov.io/gh/louis-vs/spina-admin-journal/branch/master/graph/badge.svg?token=9TZ9QGGLAH)](https://codecov.io/gh/louis-vs/spina-admin-journal)
5
+ [![CodeFactor](https://www.codefactor.io/repository/github/louis-vs/spina-admin-journal/badge)](https://www.codefactor.io/repository/github/louis-vs/spina-admin-journal)
6
+ [![LGTM alerts](https://img.shields.io/lgtm/alerts/g/louis-vs/spina-admin-journal.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/louis-vs/spina-admin-journal/alerts/)
7
+ [![Code quality: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/louis-vs/spina-admin-journal.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/louis-vs/spina-admin-journal/context:javascript)
8
+ [![Inline docs](http://inch-ci.org/github/louis-vs/spina-admin-journal.svg?branch=master)](http://inch-ci.org/github/louis-vs/spina-admin-journal)
9
+
10
+ *Journal* is a plugin for [Spina](https://www.spinacms.com/), a content management system built in [Ruby on Rails](http://rubyonrails.org/). *Journal* augments Spina by providing an admin interface for managing an academic journal.
11
+
12
+ ## Usage
13
+
14
+ The plugin adds two menus to Spina's primary navigation:
15
+
16
+ * **Journal settings** provides access to global properties of the journal. It is divided into 3 submenus:
17
+ * **Journal** allows you to change the name of the journal.
18
+ * **Institutions** allows you to add institutions, to which authors can be affiliated.
19
+ * **Authors** allows you to add authors, who can then be added to articles.
20
+ * The **journal content** menu will be named after the journal as specified in Journal Settings. It provides a means of editing the content of the journal and is divided into three submenus:
21
+ * **Volumes** allows you to add and remove volumes of the journal, and edit their respective issues.
22
+ * **Issues** allows you to add, edit and remove issues of particular volumes of the journal, and edit their respective articles.
23
+ * **Articles** allows you to add, edit and remove articles belonging to particular issues.
24
+
25
+ The plugin does not provide any public-facing frontend. However, an example implementation can be found at **[TBC]**.
26
+
27
+ **NB this plugin is currently a work in progress**: please wait until an official release to use this plugin in production.
28
+
29
+ ## Installation
30
+
31
+ ### From scratch
32
+
33
+ Make sure you have a working installation of Ruby on Rails 6.1. You can find a setup guide [here](https://guides.rubyonrails.org/getting_started.html).
34
+
35
+ Then run:
36
+
37
+ ```bash
38
+ $ rails new your-app --database=postgresql
39
+ $ cd your-app
40
+ $ bin/rails db:create
41
+ $ bin/rails active_storage:install
42
+ ```
43
+
44
+ Add this line to your new application's Gemfile:
45
+
46
+ ```ruby
47
+ gem 'spina', '~> 2.0'
48
+ ```
49
+
50
+ And then execute:
51
+
52
+ ```bash
53
+ $ bundle install
54
+ ```
55
+
56
+ Run the Spina install generator:
57
+
58
+ ```bash
59
+ $ bin/rails g spina:install
60
+ ```
61
+
62
+ And follow the prompts. Once this is complete, follow the instructions below.
63
+
64
+ ### For existing Spina installations
65
+
66
+ Add this line to your application's Gemfile:
67
+
68
+ ```ruby
69
+ gem 'spina-admin-journal', '~> 0.1'
70
+ ```
71
+
72
+ And then execute:
73
+
74
+ ```bash
75
+ $ bundle install
76
+ ```
77
+
78
+ You'll then need to install and run the migrations for the journal:
79
+
80
+ ```bash
81
+ $ bin/rails spina_admin_journal_engine:install:migrations
82
+ $ bin/rails db:migrate
83
+ ```
84
+
85
+ You can then start a local server to test that everything's working.
86
+
87
+ ```bash
88
+ $ bin/rails s
89
+ ```
90
+
91
+ You can manually populate the database from within the app, or alternatively you can use seed data for testing. A sample `seeds.rb` file can be found [here](../master/test/dummy/db/seeds.rb).
92
+
93
+ ## Contributing
94
+
95
+ Bug reports and feature requests are welcome in the [Issues](https://github.com/louis-vs/spina-admin-journal/issues) section. Translations are also very welcome!
96
+
97
+ ## License
98
+
99
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'bundler/setup'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+
9
+ require 'yard'
10
+
11
+ YARD::Rake::YardocTask.new(:yardoc) do |t|
12
+ t.files = ['lib/**/*.rb', 'app/**/*.rb']
13
+ end
14
+
15
+ APP_RAKEFILE = File.expand_path('test/dummy/Rakefile', __dir__)
16
+ load 'rails/tasks/engine.rake'
17
+
18
+ load 'rails/tasks/statistics.rake'
19
+
20
+ require 'bundler/gem_tasks'
21
+
22
+ require 'rake/testtask'
23
+
24
+ Rake::TestTask.new(:test) do |t|
25
+ t.libs << 'test'
26
+ t.pattern = 'test/**/*_test.rb'
27
+ t.verbose = false
28
+ end
29
+
30
+ require 'rubocop/rake_task'
31
+
32
+ RuboCop::RakeTask.new do |t|
33
+ t.options << '--parallel' if ENV['CI']
34
+ end
35
+
36
+ task default: :test
@@ -0,0 +1,3 @@
1
+ //= link spina/admin/journal/application.js
2
+ //= link spina/admin/journal/application.css
3
+ //= link spina_manifest.js
@@ -0,0 +1,72 @@
1
+ //= require spina/admin/journal/html5sortable
2
+ /* global sortable */
3
+
4
+ $(document).on('turbolinks:load', () => {
5
+ // html5sortable configuration
6
+ sortable('.html5sortable', {
7
+ items: 'tr',
8
+ itemSerializer: (serializedItem) => {
9
+ return {
10
+ position: serializedItem.index + 1,
11
+ id: serializedItem.node.getAttribute('data-id')
12
+ };
13
+ },
14
+ containerSerializer: (serializedContainer) => {
15
+ return {
16
+ name: serializedContainer.node.getAttribute('data-sorted-collection')
17
+ };
18
+ }
19
+ });
20
+
21
+ // add sorting event listener if there is a sortable container on the page
22
+ if(typeof sortable('.html5sortable')[0] !== 'undefined') {
23
+ sortable('.html5sortable')[0].addEventListener('sortupdate', e => {
24
+ let container = e.detail.destination.container;
25
+ let serialized = sortable('.html5sortable', 'serialize')[0];
26
+ let data = serialized.items.reduce((map, obj) => {
27
+ map[`${serialized.container.name}[list[${obj.id}]]`] = obj.position;
28
+ return map;
29
+ }, {});
30
+
31
+ let displayError = (...args) => {
32
+ let message = 'An unknown error occured.'; // not translateable (but should never happen)
33
+ if(typeof args[0] === 'string') {
34
+ message = args[0];
35
+ }
36
+ // display error message
37
+ $('.sort-message').html('');
38
+ $('.sort-message').removeClass('animate-fadein success');
39
+ $('.sort-message').width(); // trigger reflow
40
+ $('.sort-message').html(message).addClass('error animate-fadein');
41
+ // TODO: return element to its origin
42
+ // this needs to be done manually, but may not really be necessary since the displayed
43
+ // numbers won't change
44
+ };
45
+
46
+ // send sort request
47
+ $.ajax({
48
+ url: $('.html5sortable').eq(0).data('sort-url'),
49
+ type: 'PATCH',
50
+ dataType: 'json',
51
+ data: data,
52
+ success: (data) => {
53
+ if(data.success) {
54
+ // display success message
55
+ $('.sort-message').html(''); // clear message
56
+ $('.sort-message').removeClass('animate-fadein error');
57
+ $('.sort-message').width(); // trigger reflow (for animation to work)
58
+ $('.sort-message').html(data.message).addClass('success animate-fadein');
59
+ // update displayed position number
60
+ // NB this is done entirely clientside, so there is the potential for desync
61
+ for(let i = 0; i < container.children.length; i++) {
62
+ container.children[i].getElementsByClassName('position-display')[0].innerHTML = (i+1).toString();
63
+ }
64
+ } else {
65
+ displayError(data.message);
66
+ }
67
+ },
68
+ error: displayError
69
+ });
70
+ });
71
+ }
72
+ });
@@ -0,0 +1,27 @@
1
+ /* Completely overkill animated message for successful sorting */
2
+ .sort-message {
3
+ padding-top: 10px;
4
+ }
5
+
6
+ .sort-message.success {
7
+ color: green;
8
+ }
9
+
10
+ .sort-message.error {
11
+ color: red;
12
+ }
13
+
14
+ .animate-fadein {
15
+ animation-name: fadein;
16
+ animation-duration: 500ms;
17
+ animation-timing-function: ease-out;
18
+ }
19
+
20
+ @keyframes fadein {
21
+ from {
22
+ opacity: 0.1;
23
+ }
24
+ to {
25
+ opacity: 1;
26
+ }
27
+ }
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Admin
5
+ module Journal
6
+ # Custom controller for journal plugin. Sets the layout and adds a flash type.
7
+ class ApplicationController < AdminController
8
+ add_flash_types :success
9
+
10
+ layout :admin_layout, only: %i[new edit]
11
+
12
+ before_action :set_locale
13
+
14
+ private
15
+
16
+ def admin_layout
17
+ 'spina/admin/admin'
18
+ end
19
+
20
+ def set_locale
21
+ @locale = params[:locale] || I18n.default_locale
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Admin
5
+ module Journal
6
+ # Controller for {Article} records.
7
+ class ArticlesController < 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 = [:issue_id, :title, :url, :doi, { affiliation_ids: [], **CONTENT_PARAMS }].freeze
23
+ PARTS = %w[abstract attachment].freeze
24
+
25
+ before_action :set_breadcrumb
26
+ before_action :set_tabs, except: %i[index destroy sort]
27
+ before_action :set_article, 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
+ @articles = Article.sorted_desc
33
+ end
34
+
35
+ def new
36
+ @article = Article.new
37
+ build_parts
38
+ add_breadcrumb t('.new')
39
+ end
40
+
41
+ def edit; end
42
+
43
+ def create
44
+ @article = Article.new(article_params)
45
+ sister_articles = Article.where(issue: @article.issue_id)
46
+ @article.number = sister_articles.any? ? sister_articles.sorted_desc.first.number + 1 : 1
47
+
48
+ if @article.save
49
+ redirect_to admin_journal_articles_path, success: t('.saved')
50
+ else
51
+ render :new
52
+ end
53
+ end
54
+
55
+ def update
56
+ if @article.update(article_params)
57
+ redirect_to admin_journal_articles_path, success: t('.saved')
58
+ else
59
+ render :edit
60
+ end
61
+ end
62
+
63
+ def destroy
64
+ @article.destroy
65
+ respond_to do |format|
66
+ format.html do
67
+ redirect_to admin_journal_articles_path, success: t('.deleted')
68
+ end
69
+ end
70
+ end
71
+
72
+ def sort
73
+ ActiveRecord::Base.transaction do
74
+ sort_params.each do |id, new_pos|
75
+ Article.find(id.to_i).update_attribute(:number, new_pos.to_i) # rubocop:disable Rails/SkipsModelValidations
76
+ end
77
+ validate_sort_order
78
+ end
79
+ render json: { success: true, message: t('.sort_success') }
80
+ rescue ActiveRecord::RecordInvalid
81
+ render json: { success: false, message: t('.sort_error') }
82
+ end
83
+
84
+ private
85
+
86
+ def article_params
87
+ params.require(:admin_journal_article).permit(PARAMS)
88
+ end
89
+
90
+ def sort_params
91
+ params.require(:admin_journal_articles).require(:list).permit!
92
+ end
93
+
94
+ def validate_sort_order
95
+ Article.where(issue_id: params[:issue_id]).each do |article|
96
+ raise ActiveRecord::RecordInvalid if article.invalid?
97
+ end
98
+ end
99
+
100
+ def set_breadcrumb
101
+ add_breadcrumb Article.model_name.human(count: :many), admin_journal_articles_path
102
+ end
103
+
104
+ def set_tabs
105
+ @tabs = %w[details authors]
106
+ end
107
+
108
+ def set_article
109
+ @article = Article.find(params[:id])
110
+ add_breadcrumb @article.title
111
+ end
112
+
113
+ def set_parts_attributes
114
+ @parts_attributes = current_theme.parts.select { |part| PARTS.include? part[:name] }
115
+ end
116
+
117
+ def build_parts
118
+ return unless @parts_attributes.is_a? Array
119
+
120
+ @parts = @parts_attributes.collect { |part_attributes| @article.part(part_attributes) }
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Admin
5
+ module Journal
6
+ # Controller for {Author} records and their corresponding {Affiliation}s.
7
+ class AuthorsController < ApplicationController
8
+ before_action :set_breadcrumb
9
+ before_action :set_tabs, except: %i[index destroy]
10
+ before_action :set_author, only: %i[edit update destroy]
11
+
12
+ def index
13
+ @authors = Author.all
14
+ end
15
+
16
+ def new
17
+ @author = Author.new
18
+ @author.affiliations << Affiliation.new(status: :primary)
19
+ end
20
+
21
+ def edit; end
22
+
23
+ def create
24
+ @author = Author.new(modified_params)
25
+ if @author.save
26
+ redirect_to admin_journal_authors_path, success: t('.saved')
27
+ else
28
+ render :new
29
+ end
30
+ end
31
+
32
+ def update
33
+ if @author.update(modified_params)
34
+ redirect_to admin_journal_authors_path, success: t('.saved')
35
+ else
36
+ render :edit
37
+ end
38
+ end
39
+
40
+ def destroy
41
+ @author.destroy
42
+ respond_to do |format|
43
+ format.html do
44
+ redirect_to admin_journal_authors_path, success: t('.deleted')
45
+ end
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def author_params
52
+ params.require(:admin_journal_author).permit(:primary_affiliation_index,
53
+ affiliations_attributes: %i[id institution_id first_name
54
+ surname])
55
+ end
56
+
57
+ def modified_params
58
+ primary_affiliation_index = params[:admin_journal_author][:primary_affiliation_index]
59
+ new_params = author_params.except :primary_affiliation_index
60
+ unless new_params[:affiliations_attributes].nil? || primary_affiliation_index.nil?
61
+ new_params[:affiliations_attributes].each_key do |index|
62
+ new_params[:affiliations_attributes][index][:status] = index == primary_affiliation_index ? 'primary' : 'other' # rubocop:disable Layout/LineLength
63
+ end
64
+ end
65
+ new_params
66
+ end
67
+
68
+ def set_breadcrumb
69
+ add_breadcrumb Author.model_name.human(count: :many), admin_journal_authors_path
70
+ end
71
+
72
+ def set_tabs
73
+ @tabs = %w[details articles]
74
+ end
75
+
76
+ def set_author
77
+ @author = Author.find(params[:id])
78
+ add_breadcrumb @author.primary_affiliation.name
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end