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

benwbrum / fromthepage / 17387282326

01 Sep 2025 09:13PM UTC coverage: 64.405%. Remained the same
17387282326

push

github

web-flow
4857 - Require rubocop step in CI (#4858)

* 4857 - Require rubocop step in CI

* 4865 - Organize gemfiles

1790 of 3303 branches covered (54.19%)

Branch coverage included in aggregate %.

839 of 1497 new or added lines in 133 files covered. (56.05%)

43 existing lines in 29 files now uncovered.

7928 of 11786 relevant lines covered (67.27%)

103.82 hits per line

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

38.14
/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
    :pages_tab,
18
    :delete,
19
    :new,
20
    :create,
21
    :edit_scribes,
22
    :add_scribe,
23
    :remove_scribe,
24
    :search_scribes
25
  ]
26

27
  # no layout if xhr request
28
  layout Proc.new { |controller| controller.request.xhr? ? false : nil }, only: [ :new, :create, :configurable_printout, :edit_scribes, :remove_scribe ]
6!
29

30
  def metadata_overview_monitor
1✔
31
    @is_monitor_view = true
×
NEW
32
    render template: 'transcribe/monitor_view'
×
33
  end
34

35
  def configurable_printout
1✔
36
    @bulk_export = BulkExport.new
×
37
    @bulk_export.collection = @collection
×
38
    @bulk_export.work = @work
×
39
    @bulk_export.text_pdf_work = true
×
40
    @bulk_export.report_arguments['include_contributors'] = true
×
41
    @bulk_export.report_arguments['include_metadata'] = true
×
42
    @bulk_export.report_arguments['include_notes'] = true
×
43
    @bulk_export.report_arguments['preserve_linebreaks'] = false
×
44
  end
45

46
  def search
1✔
47
    @search_string = search_params[:term]
1✔
48

49
    @es_query = Elasticsearch::MultiQuery.new(
1✔
50
      query: @search_string,
51
      query_params: {
52
        mode: 'work',
53
        slug: @work.slug
54
      },
55
      page: params[:page] || 1,
56
      scope: search_params[:filter],
57
      user: current_user
58
    ).call
59

60
    @breadcrumb_scope = { work: true }
1✔
61
    @work_filter = @es_query.work_filter
1✔
62

63
    @search_results = @es_query.results
1✔
64
    @full_count = @es_query.total_count
1✔
65
    @type_counts = @es_query.type_counts
1✔
66
  end
67

68
  def describe
1✔
69
    @layout_mode = cookies[:transcribe_layout_mode] || @collection.default_orientation
×
70
    @metadata_array = JSON.parse(@work.metadata_description || '[]')
×
71
  end
72

73
  def needs_review_checkbox_checked
1✔
74
    params[:work] && params[:work]['needs_review'] == '1'
×
75
  end
76

77
  def save_description
1✔
78
    @field_cells = request.params[:fields]
×
79
    @metadata_array = @work.process_fields(@field_cells)
×
80
    @layout_mode = cookies[:transcribe_layout_mode] || @collection.default_orientation
×
81

82
    if params['save_to_incomplete'] && !needs_review_checkbox_checked
×
83
      @work.description_status = Work::DescriptionStatus::INCOMPLETE
×
84
    elsif params['save_to_needs_review'] || needs_review_checkbox_checked
×
85
      @work.description_status = Work::DescriptionStatus::NEEDS_REVIEW
×
86
    elsif (params['save_to_transcribed'] && !needs_review_checkbox_checked) || params['approve_to_transcribed']
×
87
      @work.description_status = Work::DescriptionStatus::DESCRIBED
×
88
    else
89
      # unexpected state
90
    end
91

92

93

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

102
      flash[:notice] = t('.work_described')
×
103
      render :describe
×
104
    else
×
105
      render :describe
×
106
    end
107
  end
108

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

132
  def delete
1✔
133
    @work.destroy
2✔
134
    redirect_to dashboard_owner_path
2✔
135
  end
136

137
  def edit
1✔
138
    @collections = current_user.all_owner_collections
34✔
139
    # set subjects to true if there are any articles/page_article_links
140
    @subjects = !@work.articles.blank?
34✔
141
    @scribes = @work.scribes
34✔
142
  end
143

144
  def edit_scribes
1✔
145
    @scribes = @work.scribes
5✔
146
    @nonscribes = User.where.not(id: @scribes.pluck(:id)).limit(100)
5✔
147
  end
148

149
  def search_scribes
1✔
150
    query = "%#{params[:term].to_s.downcase}%"
×
NEW
151
    excluded_ids = @work.scribes.pluck(:id) + [ @work.owner.id ]
×
152
    users = User.where('LOWER(real_name) LIKE :search OR LOWER(email) LIKE :search', search: query)
×
153
                .where.not(id: excluded_ids)
154
                .limit(100)
155

156
    render json: { results: users.map { |u| { text: "#{u.display_name} #{u.email}", id: u.id } } }
×
157
  end
158

159
  def add_scribe
1✔
160
    scribe = User.find_by(id: params[:scribe_id])
2✔
161
    @work.scribes << scribe
2✔
162

163
    if scribe.notification.add_as_collaborator && SMTP_ENABLED
2✔
164
      begin
165
        UserMailer.work_collaborator(scribe, @work).deliver!
1✔
166
      # :nocov:
167
      rescue StandardError => e
168
        print "SMTP Failed: Exception: #{e.message}"
169
      end
170
      # :cov:
171
    end
172

173
    redirect_to work_edit_scribes_path(@collection, @work)
174
  end
175

176
  def remove_scribe
177
    scribe = User.find_by(id: params[:scribe_id])
178
    @work.scribes.delete(scribe)
179

180
    redirect_to work_edit_scribes_path(@collection, @work)
181
  end
182

183
  def update_work
184
    @work.update(work_params)
185
    redirect_to work_edit_path(work_id: @work.id)
186
  end
187

188
  # tested
189
  def create
190
    @work = Work.new
191
    @work.title = params[:work][:title]
192
    @work.collection_id = params[:work][:collection_id]
193
    @work.description = params[:work][:description]
194
    @work.owner = current_user
195
    @collections = current_user.all_owner_collections
196

197
    if @work.save
198
      record_deed(@work, DeedType::WORK_ADDED, work.owner)
199
      flash[:notice] = t('.work_created')
200
      ajax_redirect_to(work_pages_tab_path(work_id: @work.id, anchor: 'create-page'))
201
    else
202
      render :new
203
    end
204
  end
205

206
  def update
207
    work = Work.find(params[:id])
208
    @collection ||= work.collection
209

210
    result = Work::Update.new(work: work, work_params: work_params).call
211

212
    @work = result.work
213
    if result.success?
214
      if result.original_collection_id != result.collection.id
215
        record_deed(@work, DeedType::WORK_ADDED, @work.owner)
216
        @collection = @work.collection
217
      end
218

219
      flash[:notice] = t('.work_updated')
220
      redirect_to edit_collection_work_path(@work.collection.owner, @collection, @work)
221
    else
222
      @scribes = @work.scribes
223
      @nonscribes = User.where.not(id: @scribes.select(:id))
224
      @collections = current_user.all_owner_collections
225
      @subjects = @work.articles.any?
226

227
      render :edit, status: :unprocessable_entity
228
    end
229
  end
230

231
  def revert
232
    work = Work.find_by(id: params[:work_id])
233
    work.update_attribute(:transcription_conventions, nil)
234
    render plain: work.collection.transcription_conventions
235
  end
236

237
  def update_featured_page
238
    @work.update(featured_page: params[:page_id])
239
    redirect_back fallback_location: @work
240
  end
241

242
  def document_sets_select
243
    document_sets = current_user.document_sets.where(collection_id: params[:collection_id])
244

245
    render partial: 'document_sets_select', locals: { document_sets: document_sets }
246
  end
247

248
  protected
249

250
  def record_deed(work, deed_type, user)
251
    deed = Deed.new
252
    deed.work = work
253
    deed.deed_type = deed_type
254
    deed.collection = work.collection
255
    deed.user = user
256
    deed.save!
257
    update_search_attempt_contributions
258
  end
259

260
  def show
261
    # Set meta information for work pages for better archival
262
    @page_title = "#{@work.title} - #{@collection.title}"
263
    @meta_description = "Historical document: #{@work.title}#{@work.author.present? ? " by #{@work.author}" : ""} in the #{@collection.title} collection. #{@work.description}".truncate(160)
264
    @meta_keywords = [ @work.title, @work.author, @collection.title, 'historical document', 'digital archive' ].compact.join(', ')
265

266
    # Generate structured data for work
267
    @structured_data = {
268
      '@context' => 'https://schema.org',
269
      '@type' => 'DigitalDocument',
270
      'name' => @work.title,
271
      'description' => @work.description,
272
      'inLanguage' => @collection.text_language || 'en',
273
      'isPartOf' => {
274
        '@type' => 'Collection',
275
        'name' => @collection.title,
276
        'description' => to_snippet(@collection.intro_block)
277
      },
278
      'url' => request.original_url,
279
      'dateModified' => @work.most_recent_deed_created_at&.iso8601,
280
      'publisher' => {
281
        '@type' => 'Organization',
282
        'name' => @collection.owner&.display_name || 'FromThePage'
283
      }
284
    }
285

286
    # Add optional fields conditionally
287
    @structured_data['author'] = @work.author if @work.author.present?
288
    @structured_data['dateCreated'] = @work.document_date if @work.document_date.present?
289

290
    # Add archival-friendly headers
291
    respond_to do |format|
292
      format.html do
293
        response.headers['X-Robots-Tag'] = 'index, follow, archive'
294
      end
295
    end
296
  end
297

298
  private
299

300
  def authorized?
301
    if !user_signed_in? || !current_user.owner
302
      ajax_redirect_to dashboard_path
303
    elsif @work && !current_user.like_owner?(@work)
304
      ajax_redirect_to dashboard_path
305
    end
306
  end
307

308
  def search_params
309
    params.permit(:term, :page, :filter, :work_id, :collection_id, :user_id)
310
  end
311

312
  def work_params
313
    params.require(:work).permit(
314
      :title,
315
      :description,
316
      :collection_id,
317
      :supports_translation,
318
      :slug,
319
      :ocr_correction,
320
      :transcription_conventions,
321
      :author,
322
      :recipient,
323
      :location_of_composition,
324
      :identifier,
325
      :pages_are_meaningful,
326
      :physical_description,
327
      :document_history,
328
      :permission_description,
329
      :translation_instructions,
330
      :scribes_can_edit_titles,
331
      :restrict_scribes,
332
      :picture,
333
      :genre,
334
      :source_location,
335
      :source_collection_name,
336
      :source_box_folder,
337
      :in_scope,
338
      :editorial_notes,
339
      :document_date,
340
      :term,
341
      document_set_ids: []
342
    )
343
  end
344
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