• 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

82.86
/app/controllers/application_controller.rb
1
class ApplicationController < ActionController::Base
1✔
2
  DEFAULT_PER_PAGE = 200
1✔
3

4
  protect_from_forgery with: :exception, except: [ :switch_locale, :saml ]
1✔
5

6
  before_action do
1✔
7
    if current_user && current_user.admin
1,668✔
8
      Rack::MiniProfiler.authorize_request
233✔
9
    end
10
  end
11

12
  before_action :load_objects_from_params
1✔
13
  before_action :store_current_location, unless: :devise_controller?
1✔
14
  before_action :load_html_blocks
1✔
15
  before_action :authorize_collection
1✔
16
  before_action :configure_permitted_parameters, if: :devise_controller?
1✔
17
  skip_before_action :verify_authenticity_token, if: (:devise_controller? && :codespaces_environment?)
1✔
18
  before_action :set_current_user_in_model
1✔
19
  before_action :masquerade_user!
1✔
20
  before_action :check_search_attempt
1✔
21
  after_action :track_action
1✔
22
  around_action :switch_locale
1✔
23

24
  layout :set_layout
1✔
25

26
  def switch_locale(&action)
1✔
27
    @dropdown_locales = I18n.available_locales.reject { |locale| locale.to_s.include? '-' }
12,984✔
28

29
    locale = nil
1,623✔
30

31
    # use user-record locale
32
    if user_signed_in? && !current_user.preferred_locale.blank?
1,623✔
33
      # the user has set their locale manually; use it.
×
34
      locale = current_user.preferred_locale
×
35
    end
36

37
    # if we can't find that, use session locale
38
    if locale.nil?
1,623!
39
      if session[:current_locale]
1,623!
40
        locale = session[:current_locale]
×
41
      end
42
    end
43

44
    # if we can't find that, use browser locale
45
    if locale.nil?
1,623!
46
      # the user might their locale set in the browser
1,623✔
47
      locale = http_accept_language.preferred_language_from(I18n.available_locales)
1,623✔
48
    end
49

50
    if locale.nil? || !I18n.available_locales.include?(locale.to_sym)
1,623✔
51
      # use the default if the above optiosn didn't work
1,234✔
52
      locale = I18n.default_locale
1,234✔
53
    end
54

55
    # append region to locale
56
    related_locales = http_accept_language.user_preferred_languages.select do |loc|
1,623✔
57
      loc.to_s.include?(locale.to_s) &&                              # is related to the chosen locale (is the locale, or is a regional version of it)
778✔
58
      I18n.available_locales.map { |e| e.to_s }.include?(loc.to_s) # is an available locale
5,446✔
59
    end
60

61
    unless related_locales.empty?
1,623✔
62
      # first preferred language from the related locales
389✔
63
      locale = http_accept_language.preferred_language_from(related_locales)
389✔
64
    end
65

66
    # execute the action with the locale
67
    I18n.with_locale(locale, &action)
1,623✔
68
  end
69

70
  # Set the current user in User
71
  def set_current_user_in_model
1✔
72
    Current.user = current_user
1,664✔
73
  end
74

75
  def current_user
1✔
76
    super || guest_user
33,590✔
77
  end
78

79
  # find the guest user account if a guest user session is currently active
80
  def guest_user
1✔
81
    User.find_by(id: session[:guest_user_id])
1,805✔
82
  end
83

84
  # when the user chooses to transcribe as guest, find guest user id or create new guest user
85
  def guest_transcription
1✔
86
    return head(:forbidden) unless GUEST_TRANSCRIPTION_ENABLED
3✔
87

88
    if check_recaptcha(model: @page, attribute: :errors)
2✔
89
      User.find(session[:guest_user_id].nil? ? session[:guest_user_id] = create_guest_user.id : session[:guest_user_id])
2!
90
      redirect_to controller: 'transcribe', action: 'display_page', page_id: @page.id
2✔
91
    else
92
      # TODO: Get some kind of flash notification on failure
×
93
      flash[:error] = t('layouts.application.recaptcha_validation_failed')
×
94
      flash.keep
×
NEW
95
      redirect_to controller: 'transcribe', action: 'guest', page_id: @page.id
×
96
    end
97
  end
98

99
  def create_guest_user
1✔
100
    user = User.new { |user| user.guest = true }
4✔
101
    user.email = "guest_#{Time.now.to_i}#{rand(99)}@example.com"
2✔
102
    user.save(validate: false)
2✔
103
    user
2✔
104
  end
105

106
  def remove_col_id
1✔
107
    # if there's a col_id set, needs to be removed to prevent breadcrumb issues
108
    if session[:col_id]
269✔
109
      session[:col_id] = nil
9✔
110
    end
111
  end
112

113
  # See ActionController::RequestForgeryProtection for details
114
  # Uncomment the :secret if you're not using the cookie session store
115
  # protect_from_forgery :secret => 'I Hate InvalidAuthenticityToken'
116
  rescue_from ActiveRecord::RecordNotFound do |e|
1✔
117
    bad_record_id(e)
2✔
118
  end
119

120
  def load_objects_from_params
1✔
121
    # this needs to be ordered from the specific to the
122
    # general, so that parent_id will load the appropriate
123
    # object without being overridden by child_id.parent
124
    # whenever both are specified on the parameters
125

126
    if params[:article_id]
1,636✔
127
      @article = Article.find(params[:article_id])
57✔
128
      if session[:col_id] != nil
57✔
129
        @collection = set_friendly_collection(session[:col_id])
14✔
130
        session[:col_id] = nil
14✔
131
      else
43✔
132
        @collection = @article.collection
43✔
133
      end
134
    end
135
    if params[:page_id]
1,636✔
136
      @page = Page.find(params[:page_id])
368✔
137
      @work = @page.work
368✔
138
      if session[:col_id] != nil
368✔
139
        @collection = set_friendly_collection(session[:col_id])
131✔
140
        session[:col_id] = nil
131✔
141
      else
237✔
142
        @collection = @page.collection
237✔
143
      end
144
    end
145
    if params[:work_id]
1,636✔
146
      @work = Work.friendly.find(params[:work_id])
409✔
147
      @collection = @work.collection
409✔
148
    end
149
    if params[:document_set_id]
1,636✔
150
      @document_set = DocumentSet.friendly.find(params[:document_set_id])
13✔
151
      @collection = @document_set.collection
13✔
152
    end
153
    if params[:collection_id]
1,636✔
154
      @collection = set_friendly_collection(params[:collection_id])
794✔
155
    end
156

157
    if params[:user_id]
1,636✔
158
      @user = User.friendly.find(params[:user_id])
73✔
159
    end
160

161
    # category stuff may be orthogonal to collections and articles
162
    if params[:category_id]
1,634✔
163
      @category = Category.find(params[:category_id])
6✔
164
    end
165

166
    # consider loading work and collection from the versions
167
    if params[:page_version_id]
1,634!
168
      @page_version = PageVersion.find(params[:page_version_id])
×
169
      @page = @page_version.page
×
170
      @work = @page.work
×
171
      @collection = @work.collection
×
172
    end
173
    if params[:article_version_id]
1,634!
174
      @article_version = ArticleVersion.find(params[:article_version_id])
×
175
      @article = @article_version.article
×
176
      @collection = @article.collection
×
177
    end
178
    if params[:collection_ids]
1,634!
179
      @collection_ids = params[:collection_ids]
×
180
    end
181

182

183
    if self.class.module_parent == Thredded && @collection
1,634✔
184
      Thredded::Engine.routes.default_url_options = { user_slug: @collection.owner.slug, collection_id: @collection.slug }
1✔
185
    else
1,633✔
186
      Thredded::Engine.routes.default_url_options = { user_slug: 'nil', collection_id: 'nil' }
1,633✔
187
    end
188
  end
189

190
  def set_friendly_collection(id)
1✔
191
    if Collection.friendly.exists?(id)
939✔
192
      @collection = Collection.friendly.find(id)
820✔
193
    elsif DocumentSet.friendly.exists?(id)
119✔
194
      @collection = DocumentSet.friendly.find(id)
118✔
195
    elsif !DocumentSet.find_by(slug: id).nil?
1!
196
      @collection = DocumentSet.find_by(slug: id)
✔
197
    elsif !Collection.find_by(slug: id).nil?
1!
198
      @collection = Collection.find_by(slug: id)
×
199
    end
200

201
    # check to make sure URLs haven't gotten scrambled
202
    if @work
939✔
203
      if @work.collection != @collection
589✔
204
        # this could be a document set or a bad collection
65✔
205
        unless @collection.is_a? DocumentSet
65!
206
          @collection = @work.collection
×
207
        end
208
      end
209
    end
210
    @collection
939✔
211
  end
212

213
  def bad_record_id(e)
1✔
214
    logger.error("Bad record ID exception for params=#{params.inspect}")
2✔
215
    logger.error(e.backtrace[2])
2✔
216
    if @collection
2!
NEW
217
      redirect_to controller: 'collection', action: 'show', collection_id: @collection.id
×
218
    else
2✔
219
      redirect_to '/404'
2✔
220
    end
221

222
    nil
223
  end
224

225
  def load_html_blocks
1✔
226
    @html_blocks = {}
1,625✔
227
    page_blocks = PageBlock.where(controller: controller_name, view: action_name)
1,625✔
228
    page_blocks.each do |b|
1,625✔
229
      if b && b.html
804✔
230
        begin
81✔
231
          b.rendered_html = render_to_string(inline: b.html)
81✔
232
        rescue StandardError => _e
233
          b.rendered_html = ''
×
234
        end
235
      else
723✔
236
        b.rendered_html = ''
723✔
237
      end
238
      @html_blocks[b.tag] = b
804✔
239
    end
240
  end
241

242
  def authorize_collection
1✔
243
    return unless @collection
1,659✔
244
    if self.class.module_parent.name == 'Thredded'
900✔
245
      unless @collection.messageboards_enabled
1!
NEW
246
        flash[:error] = t('message_boards_are_disabled', project: @collection.title)
×
247
        redirect_to main_app.user_profile_path(@collection.owner)
×
248
      end
249
    end
250

251
    return unless @collection.restricted
900✔
252
    return if params[:controller] == 'iiif'
40!
253

254
    unless @collection.show_to?(current_user)
40✔
255
      # second chance?
2✔
256
      unless set_fallback_collection
2!
257
        flash[:error] = t('unauthorized_collection', project: @collection.title)
2✔
258
        redirect_to main_app.user_profile_path(@collection.owner)
2✔
259
      end
260
    end
261
  end
262

263
  def set_fallback_collection
1✔
264
    if @work && @work.collection.supports_document_sets
2✔
265
      alternative_set = @work.document_sets.unrestricted.first
2✔
266
      if alternative_set
2!
267
        @collection = alternative_set
×
268
        true
×
269
      else
2✔
270
        false
2✔
271
      end
272
    else
×
273
      false
×
274
    end
275
  end
276

277
  def configure_permitted_parameters
1✔
278
    devise_parameter_sanitizer.permit(:sign_up) { |u| u.permit(:login, :email, :password, :password_confirmation, :display_name, :owner, :paid_date, :activity_email) }
73✔
279
    devise_parameter_sanitizer.permit(:sign_in) { |u| u.permit(:login_id, :login, :email, :password, :remember_me) }
95✔
280
    devise_parameter_sanitizer.permit(:account_update) { |u| u.permit(:login, :email, :password, :current_password, :password_confirmation, :real_name) }
73✔
281
  end
282

283
  # Redirect to admin or owner dashboard after sign in
284
  # Always send admins to admin dashboard
285
  # Everyone else should go back to where they came from if their previous page is set
286
  # Otherwise owners should go to their dashboards
287
  # And everyone else should go to user dashboard/watchlist
288
  def after_sign_in_path_for(resource)
1✔
289
    if current_user.admin
18✔
290
      admin_path
4✔
291
    elsif !session[:user_return_to].blank? && session[:user_return_to] != '/' && !session[:user_return_to].include?('/landing')
14✔
292
      session[:user_return_to]
2✔
293
    elsif current_user.owner
12✔
294
      if current_user.all_owner_collections.any?
4✔
295
        dashboard_owner_path
2✔
296
      else
2✔
297
        dashboard_startproject_path
2✔
298
      end
8✔
299
    elsif current_user.deeds.any?
8✔
300
      dashboard_watchlist_path
3✔
301
    else
5✔
302
      landing_page_path
5✔
303
    end
304
  end
305

306
  # destroy guest user session if a user signs out, then redirect to root path
307
  def after_sign_out_path_for(resource)
1✔
308
    if session[:guest_user_id]
×
309
      session[:guest_user_id] = nil
×
310
    end
311
    root_path
×
312
  end
313

314
  # Wrapper around redirect_to for modal ajax forms
315
  def ajax_redirect_to(options = {}, response_status = {})
1✔
316
    if request.xhr?
51✔
317
      head :created, location: url_for(options)
8✔
318
    else
43✔
319
      redirect_to options, response_status
43✔
320
    end
321
  end
322

323
  private
1✔
324

325
  def set_layout
1✔
326
    request.xhr? ? false : nil
2,326✔
327
  end
328

329
  def per_page
1✔
330
    return @per_page if defined?(@per_page)
27✔
331

332
    @per_page = if params[:per_page].present?
14✔
333
                  params[:per_page].to_i
3✔
334
    else
11✔
335
                  default_per_page
11✔
336
    end
337

338
    @per_page
14✔
339
  end
340

341
  def default_per_page
1✔
342
    # Override in controller for custom defaults
343
    DEFAULT_PER_PAGE
11✔
344
  end
345
end
346

347
def page_params(page)
1✔
348
  if @collection
843✔
349
    collection = @collection
795✔
350
  else
48✔
351
    collection = page.work.access_object(current_user) || page.work.collection
48✔
352
  end
353

354
  if page.status_new?
843✔
355
    if user_signed_in?
468✔
356
      collection_transcribe_page_path(page.work.collection.owner, collection, page.work, page)
378✔
357
    else
90✔
358
      collection_guest_page_path(page.work.collection.owner, collection, page.work, page)
90✔
359
    end
360
  else
375✔
361
    collection_display_page_path(page.work.collection.owner, collection, page.work, page)
375✔
362
  end
363
end
364

365
def track_action
1✔
366
  extras = {}
1,624✔
367
  if @collection
1,624✔
368
    if @collection.is_a? DocumentSet
1,124✔
369
      extras[:document_set_id] = @collection.id
120✔
370
      extras[:document_set_title] = @collection.title
120✔
371
      extras[:collection_id] = @collection.collection.id
120✔
372
      extras[:collection_title] = @collection.collection.title
120✔
373
    else
1,004✔
374
      extras[:collection_id] = @collection.id
1,004✔
375
      extras[:collection_title] = @collection.title
1,004✔
376
    end
377
  end
378
  extras[:work_id] = @work.id if @work
1,624✔
379
  extras[:work_title] = @work.title if @work
1,624✔
380
  extras[:page_id] = @page.id if @page
1,624✔
381
  extras[:page_title] = @page.title if @page
1,624✔
382
  extras[:article_id] = @article.id if @article
1,624✔
383
  extras[:article_title] = @article.title if @article
1,624✔
384
  ahoy.track("#{controller_name}##{action_name}", extras) unless action_name == 'still_editing'
1,624✔
385
end
386

387
def check_api_access
1✔
388
  if (defined? @collection) && @collection
4✔
389
    if @collection.restricted && !@collection.api_access
2!
390
      if @api_user.nil? || !(@api_user.like_owner?(@collection))
×
NEW
391
        render status: 403, plain: 'This collection is private.  The collection owner must enable API access to it or make it public for it to appear.'
×
392
      end
393
    end
394
  end
395
end
396

397
def set_api_user
1✔
398
  authenticate_with_http_token do |token, options|
16✔
399
    @api_user = User.find_by(api_key: token)
12✔
400
  end
401
end
402

403
def check_search_attempt
1✔
404
  if session[:search_attempt_id]
1,664!
NEW
405
    your_profile = controller_name == 'user' && @user == current_user
×
NEW
406
    if [ 'dashboard', 'static' ].include?(controller_name) || your_profile
×
UNCOV
407
      session[:search_attempt_id] = nil
×
408
    end
409
  end
410
end
411

412
def update_search_attempt_contributions
1✔
413
  if session[:search_attempt_id]
73!
414
    search_attempt = SearchAttempt.find(session[:search_attempt_id])
×
415
    search_attempt.increment!(:contributions)
×
416
  end
417
end
418

419
def update_search_attempt_user(user, session_var)
1✔
420
  if session_var[:search_attempt_id]
491!
421
    search_attempt = SearchAttempt.find(session_var[:search_attempt_id])
×
422
    search_attempt.user = user
×
423
    search_attempt.owner = user.owner
×
424
    search_attempt.save
×
425
  end
426
end
427

428
private
1✔
429

430
def store_current_location
1✔
431
  store_location_for(:user, request.url)
1,296✔
432
end
433

434
def check_recaptcha(options)
1✔
435
  return verify_recaptcha(options) if RECAPTCHA_ENABLED
2!
436

437
  true
2✔
438
end
439

440
def codespaces_environment?
1✔
441
  Rails.env.development? && ENV['CODESPACES'] == 'true'
525✔
442
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