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

benwbrum / fromthepage / 26532161746

27 May 2026 06:57PM UTC coverage: 70.216% (+0.3%) from 69.883%
26532161746

Pull #5519

github

web-flow
Merge b2560b316 into ead4f0265
Pull Request #5519: Style Review Dashboard title as h2 heading

2522 of 4126 branches covered (61.12%)

Branch coverage included in aggregate %.

10263 of 14082 relevant lines covered (72.88%)

168.49 hits per line

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

41.75
/app/controllers/work_controller.rb
1
class WorkController < ApplicationController
1✔
2
  # require 'ftools'
3
  include ApplicationHelper
1✔
4
  include XmlSourceProcessor
1✔
5

6
  protect_from_forgery except: [:set_work_title,
1✔
7
                                   :set_work_description,
8
                                   :set_work_physical_description,
9
                                   :set_work_document_history,
10
                                   :set_work_permission_description,
11
                                   :set_work_location_of_composition,
12
                                   :set_work_author,
13
                                   :set_work_transcription_conventions]
14
  # tested
15
  before_action :authorized?, only: [
1✔
16
    :edit,
17
    :edit_tasks,
18
    :edit_metadata,
19
    :edit_privacy,
20
    :edit_danger,
21
    :pages_tab,
22
    :delete,
23
    :new,
24
    :create,
25
    :update,
26
    :edit_scribes,
27
    :add_scribe,
28
    :remove_scribe,
29
    :search_scribes
30
  ]
31

32
  # no layout if xhr request
33
  layout :dynamic_layout, only: [:new, :create, :configurable_printout, :edit_scribes, :remove_scribe]
1✔
34

35
  def metadata_overview_monitor
1✔
36
    @is_monitor_view = true
×
37
    render template: 'transcribe/monitor_view'
×
38
  end
39

40
  def configurable_printout
1✔
41
    @bulk_export = BulkExport.new
×
42
    @bulk_export.collection = @collection
×
43
    @bulk_export.work = @work
×
44
    @bulk_export.text_pdf_work = true
×
45
    @bulk_export.report_arguments['include_contributors'] = true
×
46
    @bulk_export.report_arguments['include_metadata'] = true
×
47
    @bulk_export.report_arguments['include_notes'] = true
×
48
    @bulk_export.report_arguments['preserve_linebreaks'] = false
×
49
  end
50

51
  def search
1✔
52
    @search_string = search_params[:term]
1✔
53

54
    @es_query = Elasticsearch::MultiQuery.new(
1✔
55
      query: @search_string,
56
      query_params: {
57
        mode: 'work',
58
        slug: @work.slug
59
      },
60
      page: params[:page] || 1,
61
      scope: search_params[:filter],
62
      user: current_user
63
    ).call
64

65
    @breadcrumb_scope = { work: true }
1✔
66
    @work_filter = @es_query.work_filter
1✔
67

68
    @search_results = @es_query.results
1✔
69
    @full_count = @es_query.total_count
1✔
70
    @type_counts = @es_query.type_counts
1✔
71
  end
72

73
  def describe
1✔
74
    @layout_mode = cookies[:transcribe_layout_mode] || @collection.default_orientation
×
75
    @metadata_array = JSON.parse(@work.metadata_description || '[]')
×
76
  end
77

78
  def needs_review_checkbox_checked
1✔
79
    params[:work] && params[:work]['needs_review'] == '1'
×
80
  end
81

82
  def save_description
1✔
83
    @field_cells = request.params[:fields]
×
84
    @metadata_array = @work.process_fields(@field_cells)
×
85
    @layout_mode = cookies[:transcribe_layout_mode] || @collection.default_orientation
×
86

87
    if params['save_to_incomplete'] && !needs_review_checkbox_checked
×
88
      @work.description_status = Work::DescriptionStatus::INCOMPLETE
×
89
    elsif params['save_to_needs_review'] || needs_review_checkbox_checked
×
90
      @work.description_status = Work::DescriptionStatus::NEEDS_REVIEW
×
91
    elsif (params['save_to_transcribed'] && !needs_review_checkbox_checked) || params['approve_to_transcribed']
×
92
      @work.description_status = Work::DescriptionStatus::DESCRIBED
×
93
    else
94
      # unexpected state
95
    end
96

97
    if @work.save
×
98
      # TODO record_description_deed(@work)
×
99
      if @work.saved_change_to_description_status?
×
100
        record_deed(@work, DeedType::DESCRIBED_METADATA, current_user)
×
101
      else
×
102
        record_deed(@work, DeedType::EDITED_METADATA, current_user)
×
103
      end
104

105
      flash[:notice] = t('.work_described')
×
106
      render :describe
×
107
    else
×
108
      render :describe
×
109
    end
110
  end
111

112
  def description_versions
1✔
113
    # @selected_version = @page_version.present? ? @page_version : @page.page_versions.first
114
    # @previous_version = params[:compare_version_id] ? PageVersion.find(params[:compare_version_id]) : @selected_version.prev
115
    selected_version_id = params[:metadata_description_version_id]
×
116
    if selected_version_id
×
117
      @selected_version= MetadataDescriptionVersion.find(selected_version_id)
×
118
    else
×
119
      @selected_version= @work.metadata_description_versions.first
×
120
    end
121
    # NB: Unlike in page versions (which are created when we first create the page), metadata description versions may be nil
122
    compare_version_id = params[:compare_version_id]
×
123
    if compare_version_id
×
124
      @previous_version = MetadataDescriptionVersion.find(compare_version_id)
×
125
    else
×
126
      if @selected_version.version_number > 1
×
127
        @previous_version = @work.metadata_description_versions.second
×
128
      else
×
129
        @previous_version = @selected_version
×
130
      end
131
    end
132
    # again, both may be blank here
133
  end
134

135
  def delete
1✔
136
    @result = Work::Delete.new(
2✔
137
      work: @work,
138
      user: current_user
139
    ).call
140

141
    respond_to(&:turbo_stream)
2✔
142
  end
143

144
  def edit
1✔
145
    @collections = current_user.collections
23✔
146
    @document_sets = @work.collection.document_sets
23✔
147
    @subjects_exist = @work.articles.any?
23✔
148

149
    @document_sets_options = @document_sets.map { |ds| [ds.title, ds.id] }
29✔
150
  end
151

152
  def edit_tasks
1✔
153
  end
154

155
  def edit_metadata
1✔
156
  end
157

158
  def edit_privacy
1✔
159
    @scribes = @work.scribes
3✔
160
  end
161

162
  def edit_ai
1✔
163
    calculate_counts
×
164
  end
165

166
  def edit_danger
1✔
167
  end
168

169
  def edit_scribes
1✔
170
    @scribes = @work.scribes
5✔
171
    @nonscribes = User.where.not(id: @scribes.pluck(:id)).limit(100)
5✔
172
  end
173

174
  def search_scribes
1✔
175
    query = "%#{params[:term].to_s.downcase}%"
×
176
    excluded_ids = @work.scribes.pluck(:id) + [@work.owner.id]
×
177
    users = User.where('LOWER(real_name) LIKE :search OR LOWER(email) LIKE :search', search: query)
×
178
                .where.not(id: excluded_ids)
179
                .limit(100)
180

181
    render json: { results: users.map { |u| { text: "#{u.display_name} #{u.email}", id: u.id } } }
×
182
  end
183

184
  def add_scribe
1✔
185
    scribe = User.find_by(id: params[:scribe_id])
2✔
186
    @work.scribes << scribe
2✔
187

188
    if scribe.notification.add_as_collaborator && SMTP_ENABLED
2✔
189
      begin
190
        UserMailer.work_collaborator(scribe, @work).deliver!
1✔
191
      # :nocov:
192
      rescue StandardError => e
193
        print "SMTP Failed: Exception: #{e.message}"
194
      end
195
      # :cov:
196
    end
197

198
    redirect_to work_edit_scribes_path(@collection, @work)
199
  end
200

201
  def remove_scribe
202
    scribe = User.find_by(id: params[:scribe_id])
203
    @work.scribes.delete(scribe)
204

205
    redirect_to work_edit_scribes_path(@collection, @work)
206
  end
207

208
  def update_work
209
    @work.update(work_params)
210
    redirect_to work_edit_path(work_id: @work.id)
211
  end
212

213
  # tested
214
  def create
215
    @work = Work.new
216
    @work.title = params[:work][:title]
217
    @work.collection_id = params[:work][:collection_id]
218
    @work.description = params[:work][:description]
219
    @work.owner = current_user
220
    @collections = current_user.all_owner_collections
221

222
    if @work.save
223
      record_deed(@work, DeedType::WORK_ADDED, work.owner)
224
      flash[:notice] = t('.work_created')
225
      ajax_redirect_to(work_pages_tab_path(work_id: @work.id, anchor: 'create-page'))
226
    else
227
      render :new
228
    end
229
  end
230

231
  def update
232
    work = Work.find(params[:id])
233
    @collection ||= work.collection
234

235
    @result = Work::Update.new(work: work, work_params: work_params).call
236

237
    @work = @result.work
238

239
    if @result.success? && @result.original_collection_id != @result.collection.id
240
      record_deed(@work, DeedType::WORK_ADDED, @work.owner)
241
      @collection = @work.collection
242
    end
243

244
    respond_to do |format|
245
      template = case params[:scope]
246
      when 'edit_tasks'
247
        'work/update_tasks'
248
      when 'edit_metadata'
249
        'work/update_metadata'
250
      when 'edit_privacy'
251
        @scribes = @work.scribes
252
        'work/update_privacy'
253
      else
254
        @collections = current_user.collections
255
        @document_sets = @collection.document_sets
256
        @subjects_exist = @work.articles.any?
257

258
        @document_sets_options = @document_sets.map { |ds| [ds.title, ds.id] }
259
        'work/update_general'
260
      end
261

262
      format.turbo_stream { render template }
263
    end
264
  end
265

266
  def update_featured_page
267
    @work.update(featured_page: params[:page_id])
268
    redirect_back fallback_location: @work
269
  end
270

271
  def document_sets_select
272
    document_sets = current_user.document_sets.where(collection_id: params[:collection_id])
273

274
    render partial: 'document_sets_select', locals: { document_sets: document_sets }
275
  end
276

277
  protected
278

279
  def record_deed(work, deed_type, user)
280
    deed = Deed.new
281
    deed.work = work
282
    deed.deed_type = deed_type
283
    deed.collection = work.collection
284
    deed.user = user
285
    deed.save!
286
    update_search_attempt_contributions
287
  end
288

289
  def show
290
    # Set meta information for work pages for better archival
291
    @page_title = "#{@work.title} - #{@collection.title}"
292
    @meta_description = "Historical document: #{@work.title}#{@work.author.present? ? " by #{@work.author}" : ""} in the #{@collection.title} collection. #{@work.description}".truncate(160)
293
    @meta_keywords = [@work.title, @work.author, @collection.title, 'historical document', 'digital archive'].compact.join(', ')
294

295
    # Generate structured data for work
296
    @structured_data = {
297
      '@context' => 'https://schema.org',
298
      '@type' => 'DigitalDocument',
299
      'name' => @work.title,
300
      'description' => @work.description,
301
      'inLanguage' => @collection.text_language || 'en',
302
      'isPartOf' => {
303
        '@type' => 'Collection',
304
        'name' => @collection.title,
305
        'description' => to_snippet(@collection.intro_block)
306
      },
307
      'url' => request.original_url,
308
      'dateModified' => @work.most_recent_deed_created_at&.iso8601,
309
      'publisher' => {
310
        '@type' => 'Organization',
311
        'name' => @collection.owner&.display_name || 'FromThePage'
312
      }
313
    }
314

315
    # Add optional fields conditionally
316
    @structured_data['author'] = @work.author if @work.author.present?
317
    @structured_data['dateCreated'] = @work.document_date if @work.document_date.present?
318

319
    # Add archival-friendly headers
320
    respond_to do |format|
321
      format.html do
322
        response.headers['X-Robots-Tag'] = 'index, follow, archive'
323
      end
324
    end
325
  end
326

327
  private
328

329
  def authorized?
330
    if !user_signed_in? || !current_user.owner
331
      ajax_redirect_to dashboard_path
332
    elsif @work && !current_user.like_owner?(@work)
333
      ajax_redirect_to dashboard_path
334
    end
335
  end
336

337
  def search_params
338
    params.permit(:term, :page, :filter, :work_id, :collection_id, :user_id)
339
  end
340

341
  def work_params
342
    params.require(:work).permit(
343
      :title,
344
      :description,
345
      :collection_id,
346
      :supports_translation,
347
      :slug,
348
      :ocr_correction,
349
      :transcription_conventions,
350
      :author,
351
      :recipient,
352
      :location_of_composition,
353
      :identifier,
354
      :pages_are_meaningful,
355
      :physical_description,
356
      :document_history,
357
      :permission_description,
358
      :translation_instructions,
359
      :scribes_can_edit_titles,
360
      :restrict_scribes,
361
      :picture,
362
      :genre,
363
      :source_location,
364
      :source_collection_name,
365
      :source_box_folder,
366
      :in_scope,
367
      :editorial_notes,
368
      :document_date,
369
      :term,
370
      document_set_ids: []
371
    )
372
  end
373

374
  def dynamic_layout
375
    request.xhr? ? false : 'application'
376
  end
377
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