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

benwbrum / fromthepage / 13528664866

25 Feb 2025 06:48PM UTC coverage: 59.626% (-2.2%) from 61.822%
13528664866

Pull #4471

github

web-flow
Merge 279229e31 into 3b7156fe3
Pull Request #4471: Search test

1548 of 3188 branches covered (48.56%)

Branch coverage included in aggregate %.

84 of 457 new or added lines in 18 files covered. (18.38%)

1 existing line in 1 file now uncovered.

7065 of 11257 relevant lines covered (62.76%)

78.7 hits per line

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

56.03
/app/controllers/collection_controller.rb
1
# handles administrative tasks for the collection object
2
class CollectionController < ApplicationController
1✔
3
  include ContributorHelper
1✔
4
  include AddWorkHelper
1✔
5
  include CollectionHelper
1✔
6
  include ElasticSearchable
1✔
7

8
  DEFAULT_WORKS_PER_PAGE = 15
1✔
9

10
  public :render_to_string
1✔
11

12
  protect_from_forgery except: [
1✔
13
    :set_collection_title,
14
    :set_collection_intro_block,
15
    :set_collection_footer_block
16
  ]
17

18
  edit_actions = [:edit, :edit_tasks, :edit_look, :edit_privacy, :edit_help, :edit_quality_control, :edit_danger]
1✔
19

20
  before_action :authorized?, only: [
1✔
21
    :new,
22
    :edit,
23
    :edit_tasks,
24
    :edit_look,
25
    :edit_privacy,
26
    :edit_help,
27
    :edit_quality_control,
28
    :edit_danger,
29
    :update,
30
    :blank_collection,
31
    :delete,
32
    :create,
33
    :edit_owners,
34
    :remove_owner,
35
    :add_owner,
36
    :edit_collaborators,
37
    :remove_collaborator,
38
    :add_collaborator,
39
    :edit_reviewers,
40
    :remove_reviewer,
41
    :add_reviewer,
42
    :new_mobile_user,
43
    :search_users
44
  ]
45
  before_action :review_authorized?, only: [:reviewer_dashboard, :works_to_review, :one_off_list, :recent_contributor_list, :user_contribution_list]
1✔
46
  before_action :set_collection, only: edit_actions + [:show, :update, :contributors, :new_work, :works_list, :needs_transcription_pages, :needs_review_pages, :start_transcribing]
1✔
47
  before_action :load_settings, only: [:upload, :edit_collaborators, :edit_owners, :block_users, :remove_owner, :remove_collaborator, :edit_reviewers, :remove_reviewer]
1✔
48
  before_action :permit_only_transcribed_works_flag, only: [:works_list]
1✔
49

50
  def search_users
1✔
51
    query = "%#{params[:term].to_s.downcase}%"
4✔
52
    user_type = (params[:user_type] || 'collaborator').to_sym
4✔
53

54
    owner_ids = @collection.owners.select(:id)
4✔
55
    blocked_user_ids = @collection.blocked_users.select(:id)
4✔
56
    reviewer_ids = @collection.reviewers.select(:id)
4✔
57
    collaborator_ids = @collection.collaborators.select(:id)
4✔
58

59
    excluded_ids = case user_type
4✔
60
                   when :owner
1✔
61
                     User.where(id: owner_ids).or(User.where(id: blocked_user_ids)).select(:id)
1✔
62
                   when :blocked
1✔
63
                     User.where(id: blocked_user_ids).or(User.where(id: owner_ids)).select(:id)
1✔
64
                   when :reviewer
1✔
65
                     reviewer_ids
1✔
66
                   else
67
                     # collaborator
1✔
68
                     collaborator_ids
1✔
69
                   end
70

71
    users = User.where('LOWER(real_name) LIKE :search OR LOWER(email) LIKE :search', search: query)
4✔
72
                .where.not(id: excluded_ids)
73
                .where.not(id: @collection.owner.id)
74
                .limit(100)
75

76
    render json: { results: users.map { |u| { text: "#{u.display_name} #{u.email}", id: u.id } } }
4✔
77
  end
78

79
  def reviewer_dashboard
1✔
80
    # works which have at least one page needing review
81
    @total_pages=@collection.pages.count
×
82
    @pages_needing_review=@collection.pages.where(status: :needs_review).count
×
83
    @transcribed_pages=@collection.pages.where(status: Page::NOT_INCOMPLETE_STATUSES).count
×
84
    @works_to_review = @collection.pages.where(status: :needs_review).pluck(:work_id).uniq.count
×
85
  end
86

87
  def works_to_review
1✔
88
    @works = @collection.works.joins(:work_statistic).includes(:notes, :pages).where.not('work_statistics.needs_review' => 0).reorder("works.title")
×
89
                        .paginate(:page => params[:page], :per_page => 15)
90
  end
91

92
  def one_off_list
1✔
93
    @pages = @collection.pages_needing_review_for_one_off
×
94
  end
95

96
  def recent_contributor_list
1✔
97
    @unreviewed_users = @collection.never_reviewed_users
×
98
  end
99

100
  def user_contribution_list
1✔
101
    unless params[:quality_sampling_id].blank?
×
102
      @quality_sampling = QualitySampling.find(params[:quality_sampling_id])
×
103
    end
104
    @pages = @collection.pages.where(status: :needs_review).where(:last_editor_user_id => @user.id)
×
105
  end
106

107
  def approve_all
1✔
108
    @quality_sampling = QualitySampling.find(params[:quality_sampling_id])
×
109
    @pages = @collection.pages.where(status: :needs_review).where(:last_editor_user_id => @user.id)
×
110
    page_count = @pages.count
×
111
    @pages.update_all(status: :transcribed)
×
112
    @collection.works.each do |work|
×
113
      work.work_statistic.recalculate({ type: Page.statuses[:needs_review] }) if work.work_statistic
×
114
    end
115
    flash[:notice] = t('.approved_n_pages', page_count: page_count)
×
116
    redirect_to(collection_quality_sampling_path(@collection.owner, @collection, @quality_sampling))
×
117
  end
118

119
  def edit_buttons
1✔
120
    @prefer_html = @collection.editor_buttons.where(:prefer_html => true).exists?
×
121
  end
122

123
  def update_buttons
1✔
124
    @collection.editor_buttons.delete_all
×
125

126
    prefer_html = (params[:prefer_html] == 'true')
×
127

128
    EditorButton::BUTTON_MAP.keys.each do |key|
×
129
      if params[key] == "1"
×
130
        button_config = EditorButton.new
×
131
        button_config.key = key
×
132
        button_config.prefer_html = prefer_html
×
133
        button_config.collection = @collection
×
134
        button_config.save
×
135
      end
136
    end
137

138
    flash[:notice] = t('.editor_buttons_updated')
×
139
    ajax_redirect_to(edit_tasks_collection_path(@collection.owner, @collection))
×
140

141
  end
142

143
  def search # ElasticSearch version
1✔
NEW
144
    search_page = (search_params[:page] || 1).to_i
×
NEW
145
    @term = search_params[:term]
×
NEW
146
    @breadcrumb_scope={collection: true}
×
147

NEW
148
    page_size = 10
×
149

NEW
150
    if @collection.is_a?(Collection)
×
151
      query_config = {
NEW
152
        type: 'collection',
×
153
        coll_id: @collection.id
154
      }
NEW
155
      @collection_filter = @collection
×
156
    else
×
157
      query_config = {
NEW
158
        type: 'docset',
×
159
        docset_id: @collection.id
160
      }
NEW
161
      @docset_filter = @collection
×
162
    end
163

NEW
164
    search_data = elastic_search_results(
×
165
      @term,
166
      search_page,
167
      page_size,
168
      search_params[:filter],
169
      query_config
170
    )
171

NEW
172
    if search_data
×
NEW
173
      inflated_results = search_data[:inflated]
×
NEW
174
      @full_count = search_data[:full_count] # Used by All tab
×
NEW
175
      @type_counts = search_data[:type_counts]
×
176

177
      # Used for pagination, currently capped at 10k
178
      #
179
      # TODO: ES requires a scroll/search_after query for result sets larger
180
      #       than 10k.
181
      #
182
      #       To setup support we just need to add a composite tiebreaker field
183
      #       to the schemas
NEW
184
      @filtered_count = [ 10000, search_data[:filtered_count] ].min
×
185

186
      # Inspired by display controller search
NEW
187
      @search_string = "\"#{@term || ""}\""
×
NEW
188
      @search_results = WillPaginate::Collection.create(
×
189
        search_page,
190
        page_size,
191
        @filtered_count) do |pager|
NEW
192
          pager.replace(inflated_results)
×
193
        end
194
    end
195
  end
196

197
  def facets
1✔
198
    collection = Collection.find(params[:collection_id])
6✔
199
    @metadata_coverages = collection.metadata_coverages
6✔
200
  end
201

202
  def facet_search
1✔
203
    mc = @collection.metadata_coverages.where(key: params['facet_search']['label']).first
×
204
    first_year = params['facet_search']['date'].split.first.to_i
×
205
    last_year = params['facet_search']['date'].split.last.to_i
×
206
    order = params['facet_search']['date_order'].to_i
×
207
    years = (first_year..last_year).to_a
×
208
    date_order = "d#{order}"
×
209
    year = "w.work_facet.#{date_order}.year"
×
210

211
    facets = []
×
212

213
    @collection.works.each do |w|
×
214
      unless w.work_facet.nil?
×
215
        if years.include?(eval(year))
×
216
          facets << w.work_facet
×
217
        end
218
      end
219
    end
220

221
    facet_ids = facets.pluck(:id)
×
222

223
    @works = Work.joins(:work_facet).where('work_facets.id in (?)', facet_ids).paginate(page: params[:page], :per_page => 10)
×
224
    @search = WorkSearch.new(params[:page])
×
225

226
    render :plain => @works.to_json(:methods => [:thumbnail])
×
227
  end
228

229
  def new_mobile_user
1✔
230
    # don't show popup again
231
    session[:new_mobile_user] = false
×
232
  end
233

234
  def show
1✔
235
    if current_user && CollectionBlock.find_by(collection_id: @collection.id, user_id: current_user.id).present?
117!
236
      flash[:error] = t('unauthorized_collection', :project => @collection.title)
×
237
      redirect_to user_profile_path(@collection.owner)
×
238
    else
117✔
239
      if @collection.alphabetize_works
117✔
240
        order_clause = 'works.title ASC'
117✔
241
      else
×
242
        order_clause = 'work_statistics.complete ASC, work_statistics.transcribed_percentage ASC, work_statistics.needs_review_percentage DESC'
×
243
      end
244

245
      @new_mobile_user = !!(session[:new_mobile_user])
117✔
246
      unless @collection.nil?
117✔
247
        if @collection.restricted
117✔
248
          if !user_signed_in? || !@collection.show_to?(current_user)
11✔
249
            flash[:error] = t('unauthorized_collection', :project => @collection.title)
1✔
250
            redirect_to user_profile_path(@collection.owner)
1✔
251
          end
252
        end
253

254
        # Coming from work title/metadata search
255
        if params[:search_attempt_id]
117!
256
          @search_attempt = SearchAttempt.find_by(id: params[:search_attempt_id])
×
257
          if session[:search_attempt_id] != @search_attempt.id
×
258
            session[:search_attempt_id] = @search_attempt.id
×
259
          end
260
          @works = @search_attempt.query_results.paginate(page: params[:page], per_page: 10)
×
261

117✔
262
        elsif (params[:works] == 'untranscribed')
117!
263
          ids = @collection.works.includes(:work_statistic).where.not(work_statistics: {complete: 100}).pluck(:id)
×
264
          @works = @collection.works.order_by_incomplete.where(id: ids).paginate(page: params[:page], per_page: 10)
×
265
          #show all works
117✔
266
        elsif (params[:works] == 'show')
117✔
267
          @works = @collection.works.joins(:work_statistic).reorder(order_clause).paginate(page: params[:page], per_page: 10)
4✔
268
          #hide incomplete works
113✔
269
        elsif params[:works] == 'hide' || (@collection.hide_completed)
113✔
270
          #find ids of completed translation works
91✔
271
          translation_ids = @collection.works.incomplete_translation.pluck(:id)
91✔
272
          #find ids of completed transcription works
273
          transcription_ids = @collection.works.incomplete_transcription.pluck(:id)
91✔
274
          #combine ids anduse to get works that aren't complete
275
          ids = translation_ids + transcription_ids
91✔
276

277
          if @collection.metadata_entry?
91✔
278
            description_ids = @collection.works.incomplete_description.pluck(:id)
10✔
279
            ids += description_ids
10✔
280
          end
281

282
          works = @collection.works.joins(:work_statistic).where(id: ids).reorder(order_clause).paginate(page: params[:page], per_page: 10)
91✔
283

284
          if works.empty?
91✔
285
            @works = @collection.works.joins(:work_statistic).reorder(order_clause).paginate(page: params[:page], per_page: 10)
7✔
286
          else
84✔
287
            @works = works
84✔
288
          end
289
        else
22✔
290
          @works = @collection.works.joins(:work_statistic).reorder(order_clause).paginate(page: params[:page], per_page: 10)
22✔
291
        end
292

293
        if @collection.facets_enabled?
117✔
294
          # construct the search object from the parameters
×
295
          @search = WorkSearch.new(params)
×
296
          @search.filter([:work, :collection_id]).value=@collection.id
×
297
          # the search results are WorkFacets, not works, so we need to fetch the works themselves
298
          facet_ids = @search.result.pluck(:id)
×
299
          @works = @collection.works.joins(:work_facet).where('work_facets.id in (?)', facet_ids).includes(:work_facet).paginate(page: params[:page], :per_page => @per_page) unless params[:search].is_a?(String)
×
300

301
          @date_ranges = []
×
302
          date_configs = @collection.facet_configs.where(input_type: 'date').where.not(order: nil).order(order: :asc)
×
303
          if date_configs.size > 0
×
304
            collection_facets = WorkFacet.joins(:work).where("works.collection_id = #{@collection.id}")
×
305
            date_configs.each do |facet_config|
×
306
              facet_attr = [:d0,:d1,:d2][facet_config.order]
×
307

308
              selection_values = @works.map{|w| w.work_facet.send(facet_attr)}.reject{|v| v.nil?}
×
309
              collection_values = collection_facets.map{|work_facet| work_facet.send(facet_attr)}.reject{|v| v.nil?}
×
310

311
              @date_ranges << {
×
312
                :facet => facet_attr,
313
                :max => collection_values.max.year,
314
                :min => collection_values.min.year,
315
                :top => selection_values.max.year,
316
                :bottom => selection_values.min.year
317
              }
318
            end
319
          end
320

321
          if params[:search_attempt_id]
×
322
            @search_attempt = SearchAttempt.find_by(id: params[:search_attempt_id])
×
323
            if session[:search_attempt_id] != @search_attempt.id
×
324
              session[:search_attempt_id] = @search_attempt.id
×
325
            end
326
            @works = @search_attempt.query_results.paginate(page: params[:page], per_page: 10)
×
327
          elsif (params[:works] == 'untranscribed')
×
328
            ids = @collection.works.includes(:work_statistic).where.not(work_statistics: {complete: 100}).pluck(:id)
×
329
            @works = @collection.works.order_by_incomplete.where(id: ids).paginate(page: params[:page], per_page: 10)
×
330
            #show all works
×
331
          elsif (params[:works] == 'show')
×
332
            @works = @collection.works.joins(:work_statistic).reorder(order_clause).paginate(page: params[:page], per_page: 10)
×
333
            #hide incomplete works
×
334
          elsif params[:works] == 'hide' || (@collection.hide_completed)
×
335
            #find ids of completed translation works
×
336
            translation_ids = @collection.works.incomplete_translation.pluck(:id)
×
337
            #find ids of completed transcription works
338
            transcription_ids = @collection.works.incomplete_transcription.pluck(:id)
×
339
            #combine ids anduse to get works that aren't complete
340
            ids = translation_ids + transcription_ids
×
341

342
            if @collection.metadata_entry?
×
343
              description_ids = @collection.works.incomplete_description.pluck(:id)
×
344
              ids += description_ids
×
345
            end
346

347
            works = @collection.works.joins(:work_statistic).where(id: ids).reorder(order_clause).paginate(page: params[:page], per_page: 10)
×
348

349
            if works.empty?
×
350
              @works = @collection.works.joins(:work_statistic).reorder(order_clause).paginate(page: params[:page], per_page: 10)
×
351
            else
×
352
              @works = works
×
353
            end
354
          else
×
355
            @works = @collection.works.joins(:work_statistic).reorder(order_clause).paginate(page: params[:page], per_page: 10)
×
356
          end
357

358
          if @collection.facets_enabled?
×
359
            # construct the search object from the parameters
×
360
            @search = WorkSearch.new(params)
×
361
            @search.filter([:work, :collection_id]).value=@collection.id
×
362
            # the search results are WorkFacets, not works, so we need to fetch the works themselves
363
            facet_ids = @search.result.pluck(:id)
×
364
            @works = @collection.works.joins(:work_facet).where('work_facets.id in (?)', facet_ids).includes(:work_facet).paginate(page: params[:page], :per_page => @per_page) unless params[:search].is_a?(String)
×
365

366
            @date_ranges = []
×
367
            date_configs = @collection.facet_configs.where(input_type: 'date').where.not(order: nil).order(order: :asc)
×
368
            if date_configs.size > 0
×
369
              collection_facets = WorkFacet.joins(:work).where("works.collection_id = #{@collection.id}")
×
370
              date_configs.each do |facet_config|
×
371
                facet_attr = [:d0,:d1,:d2][facet_config.order]
×
372

373
                selection_values = @works.map{|w| w.work_facet.send(facet_attr)}.reject{|v| v.nil?}
×
374
                collection_values = collection_facets.map{|work_facet| work_facet.send(facet_attr)}.reject{|v| v.nil?}
×
375

376
                @date_ranges << {
×
377
                  :facet => facet_attr,
378
                  :max => collection_values.max.year,
379
                  :min => collection_values.min.year,
380
                  :top => selection_values.max.year,
381
                  :bottom => selection_values.min.year
382
                }
383
              end
384
            end
385
          end
386
        end
387
      else
×
388
        redirect_to "/404"
×
389
      end
390
    end
391
  end
392

393
  def add_owner
1✔
394
    unless @user.owner
4✔
395
      @user.owner = true
3✔
396
      @user.account_type = "Staff"
3✔
397
      @user.save!
3✔
398
    end
399
    @collection.owners << @user
4✔
400
    @user.notification.owner_stats = true
4✔
401
    @user.notification.save!
4✔
402
    if @user.notification.add_as_owner
4✔
403
      send_email(@user, @collection)
3✔
404
    end
405
    redirect_to collection_edit_owners_path(@collection)
4✔
406
  end
407

408
  def remove_owner
1✔
409
    @collection.owners.delete(@user)
2✔
410
    redirect_to collection_edit_owners_path(@collection)
2✔
411
  end
412

413
  def remove_block_user
1✔
414
    collection_block = CollectionBlock.find_by(collection_id: @collection.id, user_id: @user.id)
×
415
    collection_block.destroy if collection_block
×
416
    redirect_to collection_block_users_path(@collection)
×
417
  end
418

419
  def block_users
1✔
420
    @list_of_blocked_users = @collection.blocked_users
×
421
    render layout: false
×
422
  end
423

424
  def add_collaborator
1✔
425
    collaborator = User.find_by(id: params[:collaborator_id])
2✔
426
    @collection.collaborators << collaborator
2✔
427
    send_email(collaborator, @collection) if collaborator.notification.add_as_collaborator
2✔
428

429
    redirect_to collection_edit_collaborators_path(@collection)
2✔
430
  end
431

432
  def remove_collaborator
1✔
433
    collaborator = User.find_by(id: params[:collaborator_id])
2✔
434
    @collection.collaborators.delete(collaborator)
2✔
435

436
    redirect_to collection_edit_collaborators_path(@collection)
2✔
437
  end
438

439
  def add_reviewer
1✔
440
    @collection.reviewers << @user
×
441
    if @user.notification.add_as_collaborator
×
442
      send_email(@user, @collection)
×
443
    end
444
    redirect_to collection_edit_reviewers_path(@collection)
×
445
  end
446

447
  def add_block_user
1✔
448
    CollectionBlock.create(collection_id: @collection.id, user_id: @user.id)
×
449
    redirect_to collection_block_users_path(@collection)
×
450
  end
451

452
  def remove_reviewer
1✔
453
    @collection.reviewers.delete(@user)
×
454
    redirect_to collection_edit_reviewers_path(@collection)
×
455
  end
456

457
  def send_email(user, collection)
1✔
458
    if SMTP_ENABLED
4✔
459
      begin
460
        UserMailer.collection_collaborator(user, collection).deliver!
4✔
461
      # :nocov:
462
      rescue StandardError => e
463
        print "SMTP Failed: Exception: #{e.message}"
464
      end
465
      # :nocov:
466
    end
467
  end
468

469
  def publish_collection
1✔
470
    @collection.restricted = false
×
471
    @collection.save!
×
472
    redirect_back fallback_location: edit_privacy_collection_path(@collection.owner, @collection)
×
473
  end
474

475
  def toggle_collection_active(is_active)
1✔
476
    # Register New Deed for In/Active
477
    deed = Deed.new
×
478
    deed.collection = @collection
×
479
    deed.user = current_user
×
480
    if is_active
×
481
      deed.deed_type = DeedType::COLLECTION_ACTIVE
×
482
    else
×
483
      deed.deed_type = DeedType::COLLECTION_INACTIVE
×
484
    end
485
    deed.save!
×
486
  end
487

488
  def restrict_collection
1✔
489
    @collection.restricted = true
×
490
    @collection.save!
×
491
    redirect_back fallback_location: edit_privacy_collection_path(@collection.owner, @collection)
×
492
  end
493

494
  def restrict_transcribed
1✔
495
    @collection.works.joins(:work_statistic).where('work_statistics.complete' => 100, :restrict_scribes => false).update_all(restrict_scribes: true)
×
496
    redirect_back fallback_location: edit_privacy_collection_path(@collection.owner, @collection)
×
497
  end
498

499
  def enable_fields
1✔
500
    @collection.field_based = true
×
501
    @collection.voice_recognition = false
×
502
    @collection.language = nil
×
503
    @collection.save!
×
504
    redirect_to collection_edit_fields_path(@collection.owner, @collection)
×
505
  end
506

507
  def delete
1✔
508
    @collection.destroy
4✔
509

510
    flash[:notice] = t('.notice')
4✔
511
    redirect_to dashboard_owner_path
4✔
512
  end
513

514
  def new
1✔
515
    @collection = Collection.new
9✔
516
  end
517

518
  def edit
1✔
519
    @tags_options = Tag.where(canonical: true)
50✔
520
    @selected_tags = @collection.tags.pluck(:id)
50✔
521
  end
522

523
  def edit_tasks
1✔
524
    flash.now[:info] = t('.alert') if @collection.field_based && !@collection.transcription_fields.present?
17✔
525
  end
526

527
  def edit_look
1✔
528
    # Edit look form
529
  end
530

531
  def edit_privacy
1✔
532
    @main_owner = @collection.owner
9✔
533
    @collaborators = @collection.collaborators
9✔
534
    @owners = User.where(id: @main_owner.id).or(User.where(id: @collection.owners.select(:id)))
9✔
535
    @blocked_users = @collection.blocked_users
9✔
536
  end
537

538
  def edit_help
1✔
539
    @works_with_custom_conventions = @collection.works
4✔
540
                                                .includes(collection: :owner)
541
                                                .where.not(transcription_conventions: nil)
542
  end
543

544
  def edit_quality_control
1✔
545
    @reviewers = @collection.reviewers
2✔
546
  end
547

548
  def edit_danger
1✔
549
    # Edit danger form
550
  end
551

552
  def update
1✔
553
    @result = Collection::Update.new(
31✔
554
      collection: @collection,
555
      collection_params: collection_params,
556
      user: current_user
557
    ).call
558

559
    @collection = @result.collection
31✔
560

561
    respond_to do |format|
31✔
562
      template = case params[:scope]
31✔
563
                 when 'edit_tasks'
9✔
564
                   'collection/update_tasks'
9✔
565
                 when 'edit_look'
9✔
566
                   'collection/update_look'
9✔
567
                 when 'edit_privacy'
3✔
568
                   @main_owner = @collection.owner
3✔
569
                   @collaborators = @collection.collaborators
3✔
570
                   @owners = User.where(id: @main_owner.id).or(User.where(id: @collection.owners.select(:id)))
3✔
571
                   @blocked_users = @collection.blocked_users
3✔
572
                   'collection/update_privacy'
3✔
573
                 when 'edit_help'
2✔
574
                   @works_with_custom_conventions = @collection.works
2✔
575
                                                               .includes(collection: :owner)
576
                                                               .where.not(transcription_conventions: nil)
577
                   'collection/update_help'
2✔
578
                 when 'edit_quality_control'
2✔
579
                   @reviewers = @collection.reviewers
2✔
580
                   'collection/update_quality_control'
2✔
581
                 when 'edit_danger'
3✔
582
                   'collection/update_danger'
3✔
583
                 else
584
                   # edit
3✔
585
                   @tags_options = Tag.where(canonical: true)
3✔
586
                   @selected_tags = @collection.tags.pluck(:id)
3✔
587
                   'collection/update_general'
3✔
588
                 end
589

590
      format.turbo_stream { render template }
62✔
591
    end
592
  end
593

594
  # tested
595
  def create
1✔
596
    @collection = Collection.new
7✔
597
    @collection.title = params[:collection][:title]
7✔
598
    @collection.intro_block = params[:collection][:intro_block]
7✔
599
    if current_user.account_type != "Staff"
7✔
600
      @collection.owner = current_user
6✔
601
    else
1✔
602
      extant_collection = current_user.collections.detect { |c| c.owner.account_type != "Staff" }
2✔
603
      @collection.owner = extant_collection.owner
1✔
604
      @collection.owners << current_user
1✔
605
    end
606
    if @collection.save
7✔
607
      flash[:notice] = t('.notice')
7✔
608
      if request.referrer.include?('sc_collections')
7!
609
        session[:iiif_collection] = @collection.id
×
610
        ajax_redirect_to(request.referrer)
×
611
      else
7✔
612
        ajax_redirect_to({ controller: 'dashboard', action: 'startproject', collection_id: @collection.id })
7✔
613
      end
614
    else
×
615
      render action: 'new'
×
616
    end
617
  end
618

619
  def add_work_to_collection
1✔
620
    logger.debug("DEBUG collection1=#{@collection}")
×
621
    set_collection_for_work(@collection, @work)
×
622
    logger.debug("DEBUG collection2=#{@collection}")
×
623
    #redirect_to action: 'edit', collection_slug: @collection.slug
624
    redirect_to action: 'edit', collection_id: @collection.id
×
625
  end
626

627
  def remove_work_from_collection
1✔
628
    set_collection_for_work(nil, @work)
×
629
    #redirect_to action: 'edit', collection_slug: @collection.slug
630
    redirect_to action: 'edit', collection_id: @collection.id
×
631
  end
632

633
  def new_work
1✔
634
    @work = Work.new
1✔
635
    @work.collection = @collection
1✔
636
    @document_upload = DocumentUpload.new
1✔
637
    @document_upload.collection=@collection
1✔
638
    @universe_collections = ScCollection.universe
1✔
639
    @sc_collections = ScCollection.all
1✔
640
  end
641

642
  def contributors
1✔
643
    #Get the start and end date params from date picker, if none, set defaults
644
    start_date = params[:start_date]
2✔
645
    end_date = params[:end_date]
2✔
646

647
    if start_date == nil
2!
648
      start_date = 1.week.ago
2✔
649
      end_date = DateTime.now.utc
2✔
650
    end
651

652
    start_date = start_date.to_datetime.beginning_of_day
2✔
653
    end_date = end_date.to_datetime.end_of_day
2✔
654

655
    @start_deed = start_date.strftime("%b %d, %Y")
2✔
656
    @end_deed = end_date.strftime("%b %d, %Y")
2✔
657

658
    new_contributors(@collection, start_date, end_date)
2✔
659
    @stats = @collection.get_stats_hash(start_date, end_date)
2✔
660
  end
661

662
  def blank_collection
1✔
663
    @result = Collection::Blankout.new(collection: @collection).call
2✔
664

665
    flash[:notice] = t('.notice')
2✔
666
    redirect_to collection_path(@result.collection.owner, @result.collection)
2✔
667
  end
668

669
  def works_list
1✔
670
    filtered_works
11✔
671

672
    respond_to do |format|
11✔
673
      format.html
11✔
674
      format.turbo_stream
11✔
675
    end
676
  end
677

678
  def needs_transcription_pages
1✔
679
    work_ids = @collection.works.pluck(:id)
1✔
680
    @review='transcription'
1✔
681
    @pages = Page.where(work_id: work_ids).joins(:work).merge(Work.unrestricted).needs_transcription.order(work_id: :asc, position: :asc).paginate(page: params[:page], per_page: 10)
1✔
682
    @count = @pages.count
1✔
683
    @incomplete_pages = Page.where(work_id: work_ids).joins(:work).merge(Work.unrestricted).needs_completion.order(work_id: :asc, position: :asc).paginate(page: params[:page], per_page: 10)
1✔
684
    @incomplete_count = @incomplete_pages.count
1✔
685
    @heading = t('.pages_need_transcription')
1✔
686
  end
687

688
  def needs_review_pages
1✔
689
    work_ids = @collection.works.pluck(:id)
1✔
690
    @review='review'
1✔
691
    @pages = Page.where(work_id: work_ids).joins(:work).merge(Work.unrestricted).review.paginate(page: params[:page], per_page: 10)
1✔
692
    @heading = t('.pages_need_review')
1✔
693
  end
694

695
  def needs_metadata_works
1✔
696
    if params['need_review']
×
697
      @works = @collection.works.where(description_status: "needsreview")
×
698
    else
×
699
      @works = @collection.works.where(description_status: ["incomplete", "undescribed"])
×
700
    end
701
  end
702

703
  def start_transcribing
1✔
704
    page = find_untranscribed_page
1✔
705
    if page.nil?
1!
706
      flash[:notice] = t('.notice')
×
707
      redirect_to collection_path(@collection.owner, @collection)
×
708
    else
1✔
709
      if !user_signed_in?
1✔
710
        redirect_to collection_guest_page_path(page.collection.owner, page.collection, page.work, page)
1✔
711
      else
×
712
        redirect_to collection_transcribe_page_path(page.collection.owner, page.collection, page.work, page)
×
713
      end
714
    end
715
  end
716

717
  def enable_ocr
1✔
718
    @collection.enable_ocr
1✔
719
    flash[:notice] = t('.notice')
1✔
720
    redirect_back fallback_location: edit_tasks_collection_path(@collection.owner, @collection)
1✔
721
  end
722

723
  def disable_ocr
1✔
724
    @collection.disable_ocr
1✔
725
    flash[:notice] = t('.notice')
1✔
726
    redirect_back fallback_location: edit_tasks_collection_path(@collection.owner, @collection)
1✔
727
  end
728

729
  def email_link
1✔
730
    if SMTP_ENABLED
×
731
      begin
×
732
        UserMailer.new_mobile_user(current_user, @collection).deliver!
×
733
      rescue StandardError => e
734
        log_smtp_error(e, current_user)
×
735
      end
736
    end
737
    flash[:notice] = "Email sent."
×
738
    ajax_redirect_to(collection_path(@collection.owner, @collection))
×
739
  end
740

741
  private
1✔
742

743
  def authorized?
1✔
744
    unless user_signed_in?
194✔
745
      ajax_redirect_to dashboard_path
1✔
746
      return
1✔
747
    end
748

749
    if @collection && !current_user.like_owner?(@collection)
193✔
750
      ajax_redirect_to dashboard_path
1✔
751
    end
752
  end
753

754
  def review_authorized?
1✔
755
    unless user_signed_in? && current_user.can_review?(@collection)
×
756
      redirect_to new_user_session_path
×
757
    end
758
  end
759

760
  def set_collection
1✔
761
    unless @collection
274✔
762
      if Collection.friendly.exists?(params[:id])
224✔
763
        @collection = Collection.friendly.find(params[:id])
204✔
764
      elsif DocumentSet.friendly.exists?(params[:id])
20✔
765
        @collection = DocumentSet.friendly.find(params[:id])
20!
766
      elsif !DocumentSet.find_by(slug: params[:id]).nil?
×
767
        @collection = DocumentSet.find_by(slug: params[:id])
×
768
      elsif !Collection.find_by(slug: params[:id]).nil?
×
769
        @collection = Collection.find_by(slug: params[:id])
×
770
      end
771
    end
772
  end
773

774
  def set_collection_for_work(collection, work)
1✔
775
    # first update the id on the work
776
    work.collection = collection
×
777
    work.save!
×
778
    # then update the id on the articles
779
    # consider moving this to the work model?
780
    for article in work.articles
×
781
      article.collection = collection
×
782
      article.save!
×
783
    end
784
  end
785

786
  def updated_fields_hash
1✔
787
    @collection.changed.to_h {|field| [field, @collection.send(field)]}
×
788
  end
789

790
  def collection_params
1✔
791
    params.require(:collection).permit(
31✔
792
      :title,
793
      :slug,
794
      :intro_block,
795
      :transcription_conventions,
796
      :help,
797
      :link_help,
798
      :subjects_disabled,
799
      :subjects_enabled,
800
      :review_type,
801
      :hide_completed,
802
      :text_language,
803
      :default_orientation,
804
      :voice_recognition,
805
      :picture,
806
      :user_download,
807
      :enable_spellcheck,
808
      :messageboards_enabled,
809
      :facets_enabled,
810
      :supports_document_sets,
811
      :api_access,
812
      :data_entry_type,
813
      :field_based,
814
      :is_active,
815
      :search_attempt_id,
816
      :alphabetize_works,
817
      :restricted,
818
      tags: []
819
    )
820
  end
821

822
  def load_settings
1✔
823
    @main_owner = @collection.owner
20✔
824
    @owners = ([@main_owner] + @collection.owners).sort_by(&:display_name)
20✔
825
    @works_not_in_collection = current_user.owner_works - @collection.works
20✔
826
    @collaborators = @collection.collaborators
20✔
827
    @reviewers = @collection.reviewers
20✔
828
    @blocked_users = @collection.blocked_users.sort_by(&:display_name)
20✔
829

830
    collection_owner_ids = @owners.pluck(:id)
20✔
831
    @nonowners = User.where.not(id: collection_owner_ids).order(:display_name).limit(100)
20✔
832
    @noncollaborators = User.where.not(id: @collaborators.pluck(:id) + collection_owner_ids).order(:display_name).limit(100)
20✔
833
    @nonreviewers = User.where.not(id: @reviewers.pluck(:id) + collection_owner_ids).order(:display_name).limit(100)
20✔
834

835
    @collaborators = @collaborators.sort_by(&:display_name)
20✔
836
    @reviewers = @reviewers.sort_by(&:display_name)
20✔
837
  end
838

839
  def permit_only_transcribed_works_flag
1✔
840
    params.permit(:only_transcribed)
11✔
841
  end
842

843
  def filtered_works
1✔
844
    @sorting = (params[:sort] || 'title').to_sym
11✔
845
    @ordering = (params[:order] || 'ASC').downcase.to_sym
11✔
846
    @ordering = [:asc, :desc].include?(@ordering) ? @ordering : :desc
11!
847

848
    works_scope = @collection.works.includes(:work_statistic, :deeds)
11✔
849
    if params[:show] == 'need_transcription'
11✔
850
      works_scope = works_scope.joins(:work_statistic)
1✔
851
                               .where('work_statistics.transcribed_percentage < ?', 100)
852
                               .where('work_statistics.transcribed_percentage < ?', 100)
853
                               .where('work_statistics.needs_review = ?', 0)
854
    end
855

856
    if params[:search]
11✔
857
      query = "%#{params[:search].to_s.downcase}%"
3✔
858
      works_scope = works_scope.where(
3✔
859
        'LOWER(works.title) LIKE :search OR LOWER(works.searchable_metadata) like :search',
860
        search: "%#{query}%"
861
      )
862
    end
863

864
    case @sorting
11✔
865
    when :activity
3✔
866
      works_scope = works_scope.left_joins(:deeds)
3✔
867
                               .reorder(Arel.sql("COALESCE((SELECT created_at FROM deeds WHERE deeds.work_id = works.id ORDER BY created_at DESC LIMIT 1), works.created_on) #{@ordering}"))
868
    when :collaboration
1✔
869
      works_scope = works_scope.reorder(restrict_scribes: @ordering)
1✔
870
    else
7✔
871
      works_scope = works_scope.reorder(title: @ordering)
7✔
872
    end
873

874
    works_scope = works_scope.distinct.paginate(page: params[:page], per_page: DEFAULT_WORKS_PER_PAGE)
11✔
875
    @works = works_scope
11✔
876
  end
877

878
  def search_params
1✔
NEW
879
    params.permit(:term, :page, :filter, :collection_id, :user_id)
×
880
  end
881

882

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

© 2026 Coveralls, Inc