• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pulibrary / pdc_describe / 1aaf6302-d8cf-4943-bb96-5e86951c32a3

pending completion
1aaf6302-d8cf-4943-bb96-5e86951c32a3

Pull #1079

circleci

Bess Sadler
Nil safe doi gsub
Pull Request #1079: Nil safe collection title

2 of 2 new or added lines in 1 file covered. (100.0%)

1777 of 2063 relevant lines covered (86.14%)

100.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.42
/app/controllers/works_controller.rb
1
# frozen_string_literal: true
2

3
require "nokogiri"
1✔
4
require "open-uri"
1✔
5

6
# rubocop:disable Metrics/ClassLength
7
class WorksController < ApplicationController
1✔
8
  include ERB::Util
1✔
9
  around_action :rescue_aasm_error, only: [:approve, :withdraw, :resubmit, :validate, :create]
1✔
10

11
  skip_before_action :authenticate_user!
1✔
12
  before_action :authenticate_user!, unless: :public_request?
1✔
13

14
  ##
15
  # Public requests are requests that do not require authentication.
16
  # This is to enable PDC Discovery to index approved content via the RSS feed and
17
  # .json calls to individual works without needing to log in as a user.
18
  # Note that only approved works can be fetched for indexing.
19
  def public_request?
1✔
20
    return true if action_name == "index" && request.format.symbol == :rss
236✔
21
    return true if action_name == "show" && request.format.symbol == :json && Work.find(params[:id]).state == "approved"
235✔
22
    false
234✔
23
  end
24

25
  def index
1✔
26
    @works = Work.all
2✔
27
    respond_to do |format|
2✔
28
      format.html
2✔
29
      format.rss { render layout: false }
3✔
30
    end
31
  end
32

33
  # Renders the "step 0" information page before creating a new dataset
34
  def new
1✔
35
    if wizard_mode?
19✔
36
      render "new_submission"
4✔
37
    else
38
      @work = Work.new(created_by_user_id: current_user.id, collection: current_user.default_collection)
15✔
39
    end
40
  end
41

42
  def create
1✔
43
    # TODO: We need to process files submitted by the user in this method.
44
    # We currently do not and therefore there are not saved to the work.
45
    # See https://github.com/pulibrary/pdc_describe/issues/1041
46
    @work = Work.new(created_by_user_id: current_user.id, collection_id: params_collection_id, user_entered_doi: params["doi"].present?)
17✔
47
    @work.resource = FormToResourceService.convert(params, @work)
17✔
48
    if @work.valid?
17✔
49
      @work.draft!(current_user)
15✔
50
      redirect_to work_url(@work), notice: "Work was successfully created."
15✔
51
    else
52
      render :new, status: :unprocessable_entity
2✔
53
    end
54
  end
55

56
  # Creates the new dataset
57
  def new_submission
1✔
58
    default_collection_id = current_user.default_collection.id
4✔
59
    work = Work.new(created_by_user_id: current_user.id, collection_id: default_collection_id)
4✔
60
    work.resource = FormToResourceService.convert(params, work)
4✔
61
    work.draft!(current_user)
4✔
62
    redirect_to edit_work_path(work, wizard: true)
4✔
63
  end
64

65
  ##
66
  # Show the information for the dataset with the given id
67
  # When requested as .json, return the internal json resource
68
  def show
1✔
69
    @work = Work.find(params[:id])
42✔
70
    @changes =  WorkActivity.changes_for_work(@work.id)
42✔
71
    @messages = WorkActivity.messages_for_work(@work.id)
42✔
72

73
    respond_to do |format|
42✔
74
      format.html do
42✔
75
        # Ensure that the Work belongs to a Collection
76
        @collection = @work.collection
41✔
77
        raise(Work::InvalidCollectionError, "The Work #{@work.id} does not belong to any Collection") unless @collection
41✔
78

79
        @can_curate = current_user.can_admin?(@collection)
40✔
80
        @work.mark_new_notifications_as_read(current_user.id)
40✔
81
      end
82
      format.json { render json: @work.to_json }
43✔
83
    end
84
  end
85

86
  def file_list
1✔
87
    if params[:id] == "NONE"
65✔
88
      # This is a special case when we render the file list for a work being created
89
      # (i.e. it does not have an id just yet)
90
      render json: []
17✔
91
    else
92
      @work = Work.find(params[:id])
48✔
93
      render json: @work.uploads
48✔
94
    end
95
  end
96

97
  def resolve_doi
1✔
98
    @work = Work.find_by_doi(params[:doi])
×
99
    redirect_to @work
×
100
  end
101

102
  def resolve_ark
1✔
103
    @work = Work.find_by_ark(params[:ark])
×
104
    redirect_to @work
×
105
  end
106

107
  # GET /works/1/edit
108
  def edit
1✔
109
    @work = Work.find(params[:id])
16✔
110
    if current_user && @work.editable_by?(current_user)
16✔
111
      if @work.approved? && !@work.administered_by?(current_user)
14✔
112
        Honeybadger.notify("Can not edit work: #{@work.id} is approved but #{current_user} is not admin")
×
113
        redirect_to root_path, notice: I18n.t("works.uneditable.approved")
×
114
      else
115
        @uploads = @work.uploads
14✔
116
        @wizard_mode = wizard_mode?
14✔
117
        render "edit"
14✔
118
      end
119
    else
120
      Honeybadger.notify("Can not edit work: #{@work.id} is not editable by #{current_user}")
2✔
121
      redirect_to root_path, notice: I18n.t("works.uneditable.privs")
2✔
122
    end
123
  end
124

125
  def update
1✔
126
    @work = Work.find(params[:id])
10✔
127
    if current_user.blank? || !@work.editable_by?(current_user)
10✔
128
      Honeybadger.notify("Can not update work: #{@work.id} is not editable by #{current_user}")
×
129
      redirect_to root_path, notice: I18n.t("works.uneditable.privs")
×
130
    elsif !@work.editable_in_current_state?(current_user)
10✔
131
      Honeybadger.notify("Can not update work: #{@work.id} is not editable in current state by #{current_user}")
×
132
      redirect_to root_path, notice: I18n.t("works.uneditable.approved")
×
133
    else
134
      update_work
10✔
135
    end
136
  end
137

138
  # Prompt to select how to submit their files
139
  def attachment_select
1✔
140
    @work = Work.find(params[:id])
6✔
141
    @wizard_mode = true
6✔
142
  end
143

144
  # User selected a specific way to submit their files
145
  def attachment_selected
1✔
146
    @work = Work.find(params[:id])
5✔
147
    @wizard_mode = true
5✔
148
    @work.files_location = params["attachment_type"]
5✔
149
    @work.save!
5✔
150

151
    # create a directory for the work if the curator will need to move files by hand
152
    @work.s3_query_service.create_directory if @work.files_location != "file_upload"
5✔
153

154
    next_url = case @work.files_location
5✔
155
               when "file_upload"
156
                 work_file_upload_url(@work)
1✔
157
               when "file_cluster"
158
                 work_file_cluster_url(@work)
×
159
               else
160
                 work_file_other_url(@work)
4✔
161
               end
162
    redirect_to next_url
5✔
163
  end
164

165
  # Allow user to upload files directly
166
  def file_upload
1✔
167
    @work = Work.find(params[:id])
1✔
168
  end
169

170
  def file_uploaded
1✔
171
    @work = Work.find(params[:id])
1✔
172
    if pre_curation_uploads_param
1✔
173
      @work.pre_curation_uploads.attach(pre_curation_uploads_param)
×
174
      @work.save!
×
175
    end
176

177
    redirect_to(work_review_path)
1✔
178
  rescue StandardError => active_storage_error
179
    Rails.logger.error("Failed to attach the file uploads for the work #{@work.doi}: #{active_storage_error}")
×
180
    flash[:notice] = "Failed to attach the file uploads for the work #{@work.doi}: #{active_storage_error}. Please contact rdss@princeton.edu for assistance."
×
181

182
    redirect_to work_file_upload_path(@work)
×
183
  end
184

185
  # Allow user to indicate where their files are located in the PUL Research Cluster
186
  def file_cluster
1✔
187
    @work = Work.find(params[:id])
×
188
  end
189

190
  # Allow user to indicate where their files are located in the WWW
191
  def file_other
1✔
192
    @work = Work.find(params[:id])
4✔
193
  end
194

195
  def review
1✔
196
    @work = Work.find(params[:id])
5✔
197
    if request.method == "POST"
5✔
198
      @work.location_notes = params["location_notes"]
4✔
199
      @work.save!
4✔
200
    end
201
  end
202

203
  def validate
1✔
204
    @work = Work.find(params[:id])
20✔
205
    @work.submission_notes = params["submission_notes"]
20✔
206
    @uploads = @work.uploads
20✔
207
    @wizard_mode = true
20✔
208
    @work.complete_submission!(current_user)
20✔
209
    redirect_to user_url(current_user)
19✔
210
  end
211

212
  def approve
1✔
213
    @work = Work.find(params[:id])
4✔
214
    @work.approve!(current_user)
4✔
215
    redirect_to work_path(@work)
4✔
216
  end
217

218
  def withdraw
1✔
219
    @work = Work.find(params[:id])
×
220
    @work.withdraw!(current_user)
×
221
    redirect_to work_path(@work)
×
222
  end
223

224
  def resubmit
1✔
225
    @work = Work.find(params[:id])
×
226
    @work.resubmit!(current_user)
×
227
    redirect_to work_path(@work)
×
228
  end
229

230
  def assign_curator
1✔
231
    work = Work.find(params[:id])
×
232
    work.change_curator(params[:uid], current_user)
×
233
    if work.errors.count > 0
×
234
      render json: { errors: work.errors.map(&:type) }, status: :bad_request
×
235
    else
236
      render json: {}
×
237
    end
238
  rescue => ex
239
    Rails.logger.error("Error changing curator for work: #{work.id}. Exception: #{ex.message}")
×
240
    render json: { errors: ["Cannot save dataset"] }, status: :bad_request
×
241
  end
242

243
  def add_message
1✔
244
    work = Work.find(params[:id])
×
245
    if params["new-message"].present?
×
246
      new_message_param = params["new-message"]
×
247
      sanitized_new_message = html_escape(new_message_param)
×
248

249
      work.add_message(sanitized_new_message, current_user.id)
×
250
    end
251
    redirect_to work_path(id: params[:id])
×
252
  end
253

254
  def add_provenance_note
1✔
255
    work = Work.find(params[:id])
×
256
    if params["new-provenance-note"].present?
×
257
      new_date = params["new-provenance-date"]
×
258
      new_note = html_escape(params["new-provenance-note"])
×
259

260
      work.add_provenance_note(new_date, new_note, current_user.id)
×
261
    end
262
    redirect_to work_path(id: params[:id])
×
263
  end
264

265
  # Outputs the Datacite XML representation of the work
266
  def datacite
1✔
267
    work = Work.find(params[:id])
×
268
    render xml: work.to_xml
×
269
  end
270

271
  def datacite_validate
1✔
272
    @errors = []
×
273
    @work = Work.find(params[:id])
×
274
    datacite_xml = Nokogiri::XML(@work.to_xml)
×
275
    schema_location = Rails.root.join("config", "schema")
×
276
    Dir.chdir(schema_location) do
×
277
      xsd = Nokogiri::XML::Schema(File.read("datacite_4_4.xsd"))
×
278
      xsd.validate(datacite_xml).each do |error|
×
279
        @errors << error
×
280
      end
281
    end
282
  end
283

284
  def readme_select
1✔
285
    @work = Work.find(params[:id])
7✔
286
    readme = Readme.new(@work)
7✔
287
    @readme = readme.file_name
7✔
288
    @wizard = true
7✔
289
  end
290

291
  def readme_uploaded
1✔
292
    @work = Work.find(params[:id])
6✔
293
    @wizard = true
6✔
294
    readme = Readme.new(@work)
6✔
295
    readme_error = readme.attach(readme_file_param)
6✔
296
    if readme_error.nil?
6✔
297
      redirect_to work_attachment_select_url(@work)
6✔
298
    else
299
      flash[:notice] = readme_error
×
300
      redirect_to work_readme_select_url(@work)
×
301
    end
302
  end
303

304
  private
1✔
305

306
    def work_params
1✔
307
      params[:work] || params
20✔
308
    end
309

310
    def patch_params
1✔
311
      return {} unless params.key?(:patch)
14✔
312

313
      params[:patch]
8✔
314
    end
315

316
    def pre_curation_uploads_param
1✔
317
      return if patch_params.nil?
1✔
318

319
      patch_params[:pre_curation_uploads_added]
1✔
320
    end
321

322
    def readme_file_param
1✔
323
      return if patch_params.nil?
6✔
324

325
      patch_params[:readme_file]
6✔
326
    end
327

328
    def rescue_aasm_error
1✔
329
      yield
41✔
330
    rescue AASM::InvalidTransition => error
331
      message = error.message
1✔
332
      if @work.errors.count > 0
1✔
333
        message = @work.errors.to_a.join(", ")
1✔
334
      end
335
      Honeybadger.notify("Invalid #{@work.current_transition}: #{error.message} errors: #{message}")
1✔
336
      @errors = ["Cannot #{@work.current_transition}: #{message}"]
1✔
337
      render error_action, status: :unprocessable_entity
1✔
338
    end
339

340
    def error_action
1✔
341
      if action_name == "create"
1✔
342
        :new
×
343
      elsif action_name == "validate"
1✔
344
        :edit
1✔
345
      else
346
        :show
×
347
      end
348
    end
349

350
    def wizard_mode?
1✔
351
      params[:wizard] == "true"
43✔
352
    end
353

354
    def update_work
1✔
355
      @wizard_mode = wizard_mode?
10✔
356
      upload_service = WorkUploadsEditService.new(@work, current_user)
10✔
357
      if @work.approved?
10✔
358
        upload_keys = work_params[:deleted_uploads] || []
×
359
        deleted_uploads = upload_service.find_post_curation_uploads(upload_keys: upload_keys)
×
360

361
        return head(:forbidden) unless deleted_uploads.empty?
×
362
      else
363
        @work = upload_service.update_precurated_file_list(added_files_param, deleted_files_param)
10✔
364
      end
365

366
      process_updates
10✔
367
    end
368

369
    def update_params
1✔
370
      {
371
        collection_id: params_collection_id,
20✔
372
        resource: FormToResourceService.convert(params, @work)
373
      }
374
    end
375

376
    def added_files_param
1✔
377
      Array(work_params[:pre_curation_uploads_added])
10✔
378
    end
379

380
    def deleted_files_param
1✔
381
      deleted_count = (work_params["deleted_files_count"] || "0").to_i
10✔
382
      (1..deleted_count).map { |i| work_params["deleted_file_#{i}"] }.select(&:present?)
10✔
383
    end
384

385
    def process_updates
1✔
386
      resource_before = @work.resource
10✔
387
      if @work.update(update_params)
10✔
388

389
        resource_compare = ResourceCompareService.new(resource_before, update_params[:resource])
10✔
390
        @work.log_changes(resource_compare, current_user.id)
10✔
391

392
        if @wizard_mode
10✔
393
          redirect_to work_readme_select_url(@work)
6✔
394
        else
395
          redirect_to work_url(@work), notice: "Work was successfully updated."
4✔
396
        end
397
      else
398
        @uploads = @work.uploads
×
399
        render :edit, status: :unprocessable_entity
×
400
      end
401
    end
402

403
    def params_collection_id
1✔
404
      # Do not allow a nil for the collection id
405
      @params_collection_id ||= begin
37✔
406
        collection_id = params[:collection_id]
27✔
407
        if collection_id.blank?
27✔
408
          collection_id = current_user.default_collection.id
×
409
          Honeybadger.notify("We got a nil collection as part of the parameters #{params} #{request}")
×
410
        end
411
        collection_id
27✔
412
      end
413
    end
414
end
415
# rubocop:enable Metrics/ClassLength
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc