1+ # frozen_string_literal: true
2+
3+ module Nova
4+ # Controller for the Contributors page
5+ class ContributorsController < ApplicationController
6+ include OrgSelectable
7+ helper PaginableHelper
8+
9+ before_action :fetch_plan
10+ before_action :fetch_contributor , only : %i[ edit update destroy ]
11+ after_action :verify_authorized
12+
13+ # GET /plans/:plan_id/contributors
14+ def index
15+ authorize @plan , :show?
16+ @contributors = @plan . contributors
17+ end
18+
19+ # GET /plans/:plan_id/contributors/new
20+ def new
21+ authorize @plan
22+ default_org = @plan . org . present? ? @plan . org : current_user . org
23+ @contributor = Contributor . new ( plan : @plan , org : default_org )
24+ end
25+
26+ # GET /plans/:plan_id/contributors/:id/edit
27+ def edit
28+ authorize @plan
29+ end
30+
31+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
32+ # POST /plans/:plan_id/contributors
33+ def create
34+ authorize @plan , :edit?
35+
36+ args = translate_roles ( hash : contributor_params )
37+ args = process_org ( hash : args )
38+ if args . blank?
39+ @contributor = Contributor . new ( args )
40+ @contributor . errors . add ( :affiliation , 'invalid' )
41+ flash [ :alert ] = failure_message ( @contributor , _ ( 'add' ) )
42+ render :new
43+ else
44+ args = process_orcid_for_create ( hash : args )
45+
46+ # Check if ORCID exists and validate using the orcid_validator class method
47+ if args [ :identifiers_attributes ] . present?
48+ orcid_value = args [ :identifiers_attributes ] [ :'0' ] [ :value ]
49+ orcid_id = OrcidValidator . extract_orcid_id ( orcid_value )
50+
51+ unless orcid_id && OrcidValidator . orcid_id_is_valid? ( orcid_id )
52+ @contributor = Contributor . new ( args )
53+ @contributor . errors . add ( :base , 'ORCID iD is invalid' )
54+ flash [ :alert ] = failure_message ( @contributor , _ ( 'add' ) )
55+ render :new
56+ return
57+ end
58+ end
59+
60+ args [ :plan_id ] = @plan . id
61+ @contributor = Contributor . new ( args )
62+ stash_orcid
63+
64+ if @contributor . save
65+ # Now that the model has been ssaved, go ahead and save the identifiers
66+ save_orcid
67+ redirect_to plan_contributors_path ( @plan ) ,
68+ notice : success_message ( @contributor , _ ( 'added' ) )
69+ else
70+ flash [ :alert ] = failure_message ( @contributor , _ ( 'add' ) )
71+ render :new
72+ end
73+ end
74+ end
75+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
76+
77+ # PUT /plans/:plan_id/contributors/:id
78+ def update
79+ authorize @plan
80+ args = translate_roles ( hash : contributor_params )
81+ args = process_org ( hash : args )
82+ args = process_orcid_for_update ( hash : args )
83+
84+ # Check if ORCID exists and validate using the orcid_validator class method
85+ if args [ :identifiers_attributes ] . present?
86+ orcid_value = args [ :identifiers_attributes ] [ :'0' ] [ :value ]
87+ orcid_id = OrcidValidator . extract_orcid_id ( orcid_value )
88+
89+ unless orcid_id && OrcidValidator . orcid_id_is_valid? ( orcid_id )
90+ @contributor = Contributor . new ( args )
91+ @contributor . errors . add ( :base , 'ORCID iD is invalid' )
92+ flash [ :alert ] = failure_message ( @contributor , _ ( 'add' ) )
93+ render :new
94+ return
95+ end
96+ end
97+
98+ if @contributor . update ( args )
99+ redirect_to edit_plan_contributor_path ( @plan , @contributor ) ,
100+ notice : success_message ( @contributor , _ ( 'saved' ) )
101+ else
102+ flash . now [ :alert ] = failure_message ( @contributor , _ ( 'save' ) )
103+ render :edit
104+ end
105+ end
106+ # rubocop:enable
107+
108+ # DELETE /plans/:plan_id/contributors/:id
109+ def destroy
110+ authorize @plan
111+ if @contributor . destroy
112+ msg = success_message ( @contributor , _ ( 'removed' ) )
113+ redirect_to plan_contributors_path ( @plan ) , notice : msg
114+ else
115+ flash . now [ :alert ] = failure_message ( @contributor , _ ( 'remove' ) )
116+ render :edit
117+ end
118+ end
119+
120+ private
121+
122+ def contributor_params
123+ base_params = %i[ name email phone org_id org_name org_crosswalk ]
124+ role_params = Contributor . new . all_roles
125+
126+ params . require ( :contributor ) . permit (
127+ base_params ,
128+ role_params ,
129+ identifiers_attributes : %i[ id identifier_scheme_id value attrs ]
130+ )
131+ end
132+
133+ # Translate the check boxes values of "1" and "0" to true/false
134+ def translate_roles ( hash :)
135+ roles = Contributor . new . all_roles
136+ roles . each { |role | hash [ role . to_sym ] = hash [ role . to_sym ] == '1' }
137+ hash
138+ end
139+
140+ # Convert the Org Hash into an Org object (creating it if allowed)
141+ # and then remove all of the Org args
142+ def process_org ( hash :)
143+ return hash unless hash . present? && hash [ :org_id ] . present?
144+
145+ allow = !Rails . configuration . x . application . restrict_orgs
146+ org = org_from_params ( params_in : hash ,
147+ allow_create : allow )
148+
149+ hash = remove_org_selection_params ( params_in : hash )
150+
151+ return hash if org . blank? && !allow
152+ return hash unless org . present?
153+
154+ hash [ :org_id ] = org . id
155+ hash
156+ end
157+
158+ # When creating, just remove the ORCID if it was left blank
159+ def process_orcid_for_create ( hash :)
160+ return hash unless hash [ :identifiers_attributes ] . present?
161+
162+ id_hash = hash [ :identifiers_attributes ] [ :'0' ]
163+ return hash unless id_hash [ :value ] . blank?
164+
165+ hash . delete ( :identifiers_attributes )
166+ hash
167+ end
168+
169+ # When updating, destroy the ORCID if it was blanked out on form
170+ def process_orcid_for_update ( hash :)
171+ return hash unless hash [ :identifiers_attributes ] . present?
172+
173+ id_hash = hash [ :identifiers_attributes ] [ :'0' ]
174+ return hash unless id_hash [ :value ] . blank?
175+
176+ existing = @contributor . identifier_for_scheme ( scheme : 'orcid' )
177+ existing . destroy if existing . present?
178+ hash . delete ( :identifiers_attributes )
179+ hash
180+ end
181+
182+ # =============
183+ # = Callbacks =
184+ # =============
185+ def fetch_plan
186+ @plan = Plan . find_by ( id : params [ :plan_id ] )
187+ return true if @plan . present?
188+
189+ redirect_to root_path , alert : _ ( 'plan not found' )
190+ end
191+
192+ def fetch_contributor
193+ @contributor = Contributor . find_by ( id : params [ :id ] )
194+ return true if @contributor . present? &&
195+ @plan . contributors . include? ( @contributor )
196+
197+ redirect_to plan_contributors_path , alert : _ ( 'contributor not found' )
198+ end
199+
200+ # The following 2 methods address an issue with using Rails normal
201+ # accepts_nested_attributes_for on polymorphic relationships.
202+ #
203+ # Currently, when creating the underlying model, the `.valid?` method is
204+ # called prior to the `save`. This causes all `identifiers` to report that
205+ # the `identifiable_id` is nil. Because Rails forces the `belong_to` relation
206+ # to be present.
207+ #
208+ # To get around it we stash the identifiers during the creation step
209+ # and then save them after the model has been created
210+ #
211+ # Supposedly this is fixed in Rails 5+ by designating `optional: true`
212+ # on the `belong_to` side of the relationship
213+ def stash_orcid
214+ return false unless @contributor . identifiers . any?
215+
216+ @cached_orcid = @contributor . identifiers . first
217+ @contributor . identifiers = [ ]
218+ end
219+
220+ def save_orcid
221+ return true unless @cached_orcid . present?
222+
223+ @cached_orcid . identifiable = @contributor
224+ @cached_orcid . save
225+ @contributor . reload
226+ end
227+ end
228+ end
0 commit comments