Class: ApplicationController

Inherits:
ActionController::Base
  • Object
show all
Defined in:
app/controllers/application_controller.rb

Direct Known Subclasses

ConcertoConfigController, ConcertoPluginsController, ContentsController, DashboardController, ErrorsController, FeedsController, FieldConfigsController, Frontend::ContentsController, Frontend::FieldsController, Frontend::ScreensController, Frontend::TemplatesController, GroupsController, KindsController, MediaController, MembershipsController, PagesController, ScreensController, SubmissionsController, SubscriptionsController, TemplatesController, UsersController

Instance Method Summary (collapse)

Instance Method Details

- (Object) after_sign_in_path_for(resource)

Redirect the user to the dashboard after signing in.



411
412
413
# File 'app/controllers/application_controller.rb', line 411

def (resource)
  dashboard_path
end

- (Object) allow_cors

Cross-Origin Resource Sharing for JS interfaces Browsers are very selective about allowing CORS when Authentication headers are present. They require us to use an origin by name, not *, and to specifically allow authorization credentials (twice).



402
403
404
405
406
407
408
# File 'app/controllers/application_controller.rb', line 402

def allow_cors
  origin = request.headers['origin']
  headers['Access-Control-Allow-Origin'] = origin || '*'
  headers['Access-Control-Allow-Methods'] = '*'
  headers['Access-Control-Allow-Credentials'] = 'true'
  headers['Access-Control-Allow-Headers'] = 'Authorization'
end

- (Object) allow_screen_if_unsecured(screen)

This method allows the Frontend to circumvent normal screen auth in order to support legacy unsecured screens. Should not be used outside the Frontend controllers.



60
61
62
63
64
65
# File 'app/controllers/application_controller.rb', line 60

def allow_screen_if_unsecured (screen)
  if screen.unsecured? || screen.auth_by_mac?
    @current_screen = screen
    @current_ability = nil
  end
end

- (Object) apply_relative_root

Ensure that any Relative URL Root applied by the webserver is available to engine routing logic. This should not be needed once we go to Rails 4, per Rails ticket #6933



270
271
272
# File 'app/controllers/application_controller.rb', line 270

def apply_relative_root
  Rails.application.routes.default_url_options[:script_name] = ENV['RAILS_RELATIVE_URL_ROOT']
end

- (Object) auth!(opts = {})

Authenticate using the current action and instance variables. If the instance variable is an Enumerable or ActiveRecord::Relation we remove anything that we cannot? from the array. If the instance variable is a single object, we raise CanCan::AccessDenied if we cannot? the object.

or raise if empty.

Options Hash (opts):

  • action (Symbol)

    The CanCan action to test.

  • object (Object)

    The object we should be testing.

  • allow_empty (Boolean) — default: true

    If we should allow an empty array.

  • new_exception (Boolean) — default: true

    Allow the user to the page if they can create new objects, regardless of the empty status.



287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# File 'app/controllers/application_controller.rb', line 287

def auth!(opts = {})
  action_map = {
    'index' => :read,
    'show' => :read,
    'new' => :create,
    'edit' => :update,
    'create' => :create,
    'update' => :update,
    'destroy' => :delete,
  }

  test_action = (opts[:action] || action_map[action_name])
  allow_empty = true
  if !opts[:allow_empty].nil?
    allow_empty = opts[:allow_empty]
  end

  new_exception = true
  if !opts[:new_exception].nil?
    new_exception = opts[:new_exception]
  end

  var_name = controller_name
  if action_name != 'index'
    var_name = controller_name.singularize
  end
  object = (opts[:object] || instance_variable_get("@#{var_name}"))
  object_needs_replacement = false

  unless object.nil?
    if (object.is_a? ActiveRecord::Relation)
      # ActiveRecord::Relation will maintain ties back to the original query.
      # By replacing it with an array, we can make sure that it only ever
      # contains the items which have passed auth!.
      pagination = extract_pagination_from_relation(object)
      object = object.to_a
      object_needs_replacement = true
    end # Now continue as a normal Enumberable
    if (object.is_a? Enumerable)
      object = object.to_a # In case of a non-Array Enumerable
      object.delete_if {|o| cannot?(test_action, o)}
      if new_exception && object.empty?
        # Parent will be Object for Concerto, or the module for Plugins.
        new_parent = self.class.parent
        class_name =  controller_name.singularize.classify
        new_class = new_parent.const_get(class_name) if new_parent.const_defined?(class_name)
        new_object = new_class.new if !new_class.nil?
        return true if can?(:create, new_object)
      end
      if !allow_empty && object.empty?
        fake_cancan = Class.new.extend(CanCan::Ability)
        message ||= fake_cancan.unauthorized_message(test_action, object.class)
        raise CanCan::AccessDenied.new(message, test_action, object.class)
      end
      object = reapply_pagination(object, pagination) unless pagination.nil?
      if object_needs_replacement
        # Certain objects (Relations in particular) can't be authorized
        # by simply modifying them - they need to be replaced with new objects.
        instance_variable_set("@#{var_name}",object) if opts[:object].nil?
        return object if !opts[:object].nil?
      end
    else
      if cannot?(test_action, object)
        fake_cancan = Class.new.extend(CanCan::Ability)
        message ||= fake_cancan.unauthorized_message(test_action, object.class)
        raise CanCan::AccessDenied.new(message, test_action, object.class)
      end
    end
  end
end

- (Object) check_for_initial_install

If there are no users defined yet, redirect to create the first admin user



251
252
253
254
255
256
257
258
259
# File 'app/controllers/application_controller.rb', line 251

def check_for_initial_install
  #Don't do anything if a user is logged in
  unless user_signed_in?
    #if the flag set in the seeds file still isn't set to true and there are no users, let's do our thing
    if !User.exists? && !ConcertoConfig[:setup_complete]
      redirect_to new_user_registration_path
    end
  end
end

- (Object) compute_pending_moderation

Expose a instance variable counting the number of pending submissions a user can moderate. 0 indicates no pending submissions.



228
229
230
231
232
233
234
235
236
# File 'app/controllers/application_controller.rb', line 228

def compute_pending_moderation
  @pending_submissions_count = 0
  if user_signed_in?
    feeds = current_user.owned_feeds
    feeds.each do |f|
      @pending_submissions_count += f.submissions_to_moderate.count
    end
  end
end

- (Object) current_ability

Current Ability for CanCan authorization This matches CanCan's code but is here to be explicit, since we modify @current_ability below for plugins.



19
20
21
# File 'app/controllers/application_controller.rb', line 19

def current_ability
  @current_ability ||= ::Ability.new(current_accessor)
end

- (Object) current_accessor

Determine the current logged-in screen or user to be used for auth on the current action. Used by current_ability and use_plugin_ability



26
27
28
29
30
31
# File 'app/controllers/application_controller.rb', line 26

def current_accessor
  if @screen_api
    @current_accessor ||= current_screen
  end
  @current_accessor ||= current_user
end

- (Object) current_screen

current_screen finds the currently authenticated screen based on a cookie or HTTP basic auth sent with the request. Remember, this is only used on actions with the screen_api filter. On all other actions, screen auth is ignored and the current_accessor is the logged in user or anonymous.



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'app/controllers/application_controller.rb', line 38

def current_screen
  if @current_screen.nil?
    unless request.authorization.blank?
      (user,pass) = http_basic_user_name_and_password
      if user=="screen" and !pass.nil?
        @current_screen = Screen.find_by_screen_token(pass)
        if params.has_key? :request_cookie
          cookies.permanent[:concerto_screen_token] = pass
        end
      end
    end
    if @current_screen.nil? and cookies.has_key? :concerto_screen_token
      token = cookies[:concerto_screen_token]
      @current_screen = Screen.find_by_screen_token(token)
    end
  end
  @current_screen
end

- (Object) extract_pagination_from_relation(relation)

This method allows us to perform authorization on arrays (discarding items based on cancan results) and ensure that any requested pagination is applied to the authorized array. Note we can only help if the relation has not been frozen yet.



364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
# File 'app/controllers/application_controller.rb', line 364

def extract_pagination_from_relation(relation)
  if relation.loaded?
    # Can't do anything once the query has been executed.
    puts "loaded."
    return nil
  elsif relation.singleton_class.include? Kaminari::PageScopeMethods
   # This relation has had Kaminari's .page() method applied
   page = relation.current_page
   per = relation.limit_value
   offset = relation.offset_value
   # Reset the relation to the pre-pagination query as best we can.
   # If Kaminari's padding() method was used, the padding will be
   # applied before auth.
   relation.limit_value = nil
   relation.offset_value = [0,offset-(page-1)*per].max
   return {:page => page, :per => per}
  else
   # Not paginated, we don't need to do anything.
   return nil
  end
end

- (Object) http_basic_user_name_and_password



67
68
69
# File 'app/controllers/application_controller.rb', line 67

def http_basic_user_name_and_password
  ActionController::HttpAuthentication::Basic.user_name_and_password(request)
end

- (Object) precompile_error_catch



131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'app/controllers/application_controller.rb', line 131

def precompile_error_catch
  require 'yaml'
  concerto_base_config = YAML.load_file("./config/concerto.yml")
  if concerto_base_config['compile_production_assets'] == true
    if File.exist?('public/assets/manifest.yml') == false && Rails.env.production?
      precompile_status = system("env RAILS_ENV=production bundle exec rake assets:precompile")
      if precompile_status == true
        restart_webserver()
      else
        raise t(:asset_precomp_failed)
      end
    end
  end
end

- (Model?) process_notification(ar_instance, pa_params, options = {})

Record and send notification of an activity.



199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'app/controllers/application_controller.rb', line 199

def process_notification(ar_instance, pa_params, options = {})
  return nil if ar_instance.nil? || !ar_instance.respond_to?('create_activity')

  options[:params] ||= {}
  options[:params].merge!(pa_params) unless pa_params.nil?
  activity = ar_instance.create_activity(options)
  # form the actionmailer method name by combining the class name with the action being performed (e.g. "submission_update")
  am_string = "#{ar_instance.class.name.downcase}_#{options[:action]}"
  # If ActivityMailer can find a method by the formulated name, pass in the activity (everything we know about what was done)
  if ActivityMailer.respond_to?(am_string) && (options[:recipient].nil? || options[:owner].nil? || options[:recipient] != options[:owner])
    #fulfilling bamnet's expansive notification ambitions via metaprogramming since 2013
    begin
      ActivityMailer.send(am_string, activity).deliver
    #make an effort to catch all mail-related exceptions after sending the mail - IOError will catch anything for sendmail, SMTP for the rest
    rescue IOError, Net::SMTPAuthenticationError, Net::SMTPServerBusy, Net::SMTPSyntaxError, Net::SMTPFatalError, Net::SMTPUnknownError => e
      Rails.logger.debug "Mail delivery failed at #{Time.now.to_s} for #{options[:recipient]}: #{e.message}"
      ConcertoConfig.first.create_activity :action => :system_notification, :params => {:message => t(:smtp_send_error)}
    rescue OpenSSL::SSL::SSLError => e
      Rails.logger.debug "Mail delivery failed at #{Time.now.to_s} for #{options[:recipient]}: #{e.message} -- might need to disable SSL Verification in settings"
      ConcertoConfig.first.create_activity :action => :system_notification, :params => {:message => t(:smtp_send_error_ssl)}
    end
  end

  activity
end

- (Object) reapply_pagination(arr, pagination)



386
387
388
389
390
391
392
393
394
395
396
# File 'app/controllers/application_controller.rb', line 386

def reapply_pagination(arr, pagination)
  if pagination.is_a? Hash
    page = pagination[:page]
    per = pagination[:per]
    if page.is_a? Numeric and per.is_a? Numeric
     return Kaminari.paginate_array(arr).page(page).per(per)
    end
  end
  # If there was no pagination, we need to do nothing.
  return arr
end

- (Object) restart_webserver



93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'app/controllers/application_controller.rb', line 93

def restart_webserver
  unless webserver_supports_restart?
    flash[:notice] = t(:wont_write_restart_txt)
    return false
  end
  begin
    File.open("tmp/restart.txt", "w") {}
    return true
  rescue
    #generally a write permission error
    flash[:notice] = t(:cant_write_restart_txt)
    return false
  end
end

- (Object) screen_api

Call this with a before filter to indicate that the current action should be treated as a Screen API page. On Screen API pages, the current logged-in screen (if there is one) is used instead of the current user. For non-screen API pages, it is impossible for a screen to view the page (though that may change).



89
90
91
# File 'app/controllers/application_controller.rb', line 89

def screen_api
  @screen_api=true
end

- (Object) set_locale



242
243
244
245
246
247
248
# File 'app/controllers/application_controller.rb', line 242

def set_locale
  if user_signed_in? && current_user.locale != ""
    session[:locale] = current_user.locale
  end

  I18n.locale = session[:locale] || I18n.default_locale
end

- (Object) set_time_zone(&block)



108
109
110
111
112
113
114
# File 'app/controllers/application_controller.rb', line 108

def set_time_zone(&block)
  if user_signed_in? && !current_user.time_zone.nil?
    Time.use_zone(current_user.time_zone, &block)
  else
    Time.use_zone(ConcertoConfig[:system_time_zone], &block)
  end
end

- (Object) set_version



238
239
240
# File 'app/controllers/application_controller.rb', line 238

def set_version
  require 'concerto/version'
end

- (Object) sign_in_screen(screen)



71
72
73
74
# File 'app/controllers/application_controller.rb', line 71

def (screen)
  token = screen.generate_screen_token!
  cookies.permanent[:concerto_screen_token]=token
end

- (Object) sign_out_screen



76
77
78
79
80
81
82
# File 'app/controllers/application_controller.rb', line 76

def sign_out_screen
  if !current_screen.nil?
    current_screen.clear_screen_token!
    @current_screen = nil
  end
  cookies.permanent[:concerto_screen_token]=""
end

- (Object) switch_to_main_app_ability

Revert to the main app ability after using a plugin ability (if it was defined). Used by ConcertoPlugin for rendering hooks, and by use_plugin_ability block above.



186
187
188
# File 'app/controllers/application_controller.rb', line 186

def switch_to_main_app_ability
  @current_ability = @main_app_ability # it is okay if this is nil
end

- (Object) switch_to_plugin_ability(mod)

Store the current ability (if defined) and switch to the ability class for the specified plugin, if it has one. Always call switch_to_main_app_ability when done. Used by ConcertoPlugin for rendering hooks, and by use_plugin_ability block above.



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'app/controllers/application_controller.rb', line 159

def switch_to_plugin_ability(mod)
  @main_app_ability = @current_ability
  @plugin_abilities = @plugin_abilities || {}
  mod_sym = mod.name.to_sym
  if @plugin_abilities[mod_sym].nil?
    begin
      ability = (mod.name+"::Ability").constantize
    rescue
      ability = nil
    end
    if ability.nil?
      # Presumably this plugin doesn't define its own rules, no biggie
      logger.warn "ConcertoPlugin: use_plugin_ability: "+
        "No Ability found for "+mod.name
    else
      @plugin_abilities[mod_sym] ||= ability.new(current_accessor)
      @current_ability = @plugin_abilities[mod_sym]
    end
  else
    @current_ability = @plugin_abilities[mod_sym]
  end
end

- (Object) use_plugin_ability(mod, &block)

Allow views in the main application to do authorization checks for plugins.



148
149
150
151
152
# File 'app/controllers/application_controller.rb', line 148

def use_plugin_ability(mod, &block)
  switch_to_plugin_ability(mod)
  yield
  switch_to_main_app_ability
end

- (Boolean) webserver_supports_restart?



116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'app/controllers/application_controller.rb', line 116

def webserver_supports_restart?
  #add any webservers that don't support tmp/restart.txt to this array
  no_restart_txt = ["webrick"]
  no_restart_txt.each do |w|
    #check if the server environment contains a webserver that doesn't support restart.txt
    #This is NOT foolproof - a webserver may elect not to send this
    server_match = /\S*#{w}/.match(env['SERVER_SOFTWARE'].to_s.downcase)
    if server_match.nil?
      return true
    else
      return false
    end
  end
end