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
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