app/controllers/application_controller.rb
include ActionView::Helpers::DateHelper # required for time_ago_in_words()
include Pagy::Backend
class ApplicationController < ActionController::Base
protect_from_forgery unless: -> { is_dataurl_post }
layout 'application'
helper_method :current_user_session, :current_user, :prompt_login, :sidebar
before_action :set_locale
before_action :set_sentry_context
private
# allow limited CSRF from external apps submitting params[:dataurl_main_image] data
def is_dataurl_post
params[:controller] == "editor" && params[:action] == "post" && !params[:datauri_main_image].nil?
end
def set_sentry_context
Sentry.with_scope do |scope|
scope.set_user(id: session[:current_user_id])
scope.set_extras(params: params.to_unsafe_h)
scope.set_extras(url: request.url)
end
end
# non-Authlogic... homebrew
def prompt_login(message = I18n.t('application_controller.must_be_logged_in'))
flash[:warning] = message
redirect_to '/login'
end
def current_user_session
@current_user_session ||= UserSession.find
end
def current_user
unless defined?(@current_user)
@current_user = current_user_session&.record
end
# if banned or moderated:
if @current_user.try(:status) == 0
# Same effect as if the user clicked logout:
current_user_session.destroy
# Ensures no code will use old @current_user info. Treat the user
# as anonymous (until the login process sets @current_user again):
@current_user = nil
elsif @current_user.try(:status) == 5
# Tell the user they are banned. Fails b/c redirect to require below.
flash[:warning] = "The user '#{@current_user.username}' has been placed in moderation; please see <a href='https://#{request.host}/wiki/moderators'>our moderation policy</a> and contact <a href='mailto:moderators@#{request.host}?body=Please make sure to include your username and the email address you used to sign up for the site.'>moderators@#{request.host}</a> if you believe this is in error."
# Same effect as if the user clicked logout:
current_user_session.destroy
# Ensures no code will use old @current_user info. Treat the user
# as anonymous (until the login process sets @current_user again):
@current_user = nil
end
cookies.signed["user_token"] = nil
if @current_user
cookies.signed["user_token"] = @current_user.persistence_token
end
@current_user
end
def require_user
unless current_user
store_location
flash[:warning] ||= I18n.t('application_controller.must_be_logged_in_to_access')
redirect_to "/login?return_to=" + request.fullpath
false
end
return current_user
end
def require_no_user
if current_user
store_location
flash[:notice] = I18n.t('application_controller.must_be_logged_out_to_access')
url = URI.parse(home_url + '?return_to=' + CGI.escape(request.env['PATH_INFO'])).to_s
redirect_to url
false
end
end
def store_location
session[:return_to] = request.fullpath
end
def redirect_back_or_default(default)
redirect_to(session[:return_to] || default)
session[:return_to] = nil
end
def redirect_to_node_path?(node)
return false unless node.present? && node.type[/^redirect\|/] && node.status == 1
node = Node.find(node.type[/\|\d+/][1..-1])
redirect_to URI.parse(node.path).path, status: :moved_permanently
true
end
def alert_and_redirect_moderated
if @node.author.status == User::Status::BANNED && !(logged_in_as(['admin', 'moderator']))
flash[:error] = I18n.t('application_controller.author_has_been_banned')
redirect_to '/'
elsif @node.status == 4 && (logged_in_as(['admin', 'moderator']))
flash.now[:warning] = "First-time poster <a href='/profile/#{@node.author.name}'>#{@node.author.name}</a> submitted this #{time_ago_in_words(@node.created_at)} ago and it has not yet been approved by a moderator. <a class='btn btn-default btn-sm' href='/moderate/publish/#{@node.id}'>Approve</a> <a class='btn btn-default btn-sm' href='/moderate/spam/#{@node.id}'>Spam</a>"
elsif @node.status == 4 && current_user&.id == @node.author.id && !flash[:first_time_post]
flash.now[:warning] = "Thank you for contributing open research, and thanks for your patience while your post is approved by <a href='/wiki/moderation'>community moderators</a> and we'll email you when it is published. In the meantime, if you have more to contribute, feel free to do so."
elsif @node.status == 3 && (current_user&.is_coauthor?(@node) || current_user&.can_moderate?) && !flash[:first_time_post]
flash.now[:warning] = "This is a draft note. Once you're ready, click <a class='btn btn-success btn-xs' href='/notes/publish_draft/#{@node.id}'>Publish Draft</a> to make it public. You can share it with collaborators using this private link <a href='#{@node.draft_url(request.base_url)}'>#{@node.draft_url(request.base_url)}</a>"
elsif @node.status == 3 && (params[:token].nil? || (params[:token].present? && @node.slug.split('token:').last != params[:token]))
page_not_found
elsif @node.status != 1 && @node.status != 3 && current_user&.id != @node.author.id && !(logged_in_as(['admin', 'moderator']))
# if it's spam or a draft
# no notification; don't let people easily fish for existing draft titles; we should try to 404 it
redirect_to '/'
elsif @node.author.status == User::Status::MODERATED
flash.now[:warning] = "The user '#{@node.author.username}' has been placed <a href='https://#{request.host}/wiki/moderators'>in moderation</a> and will not be able to respond to comments."
end
end
# Check the locale set and adjust the locale accordingly
def set_locale
if cookies[:plots2_locale] && I18n.available_locales.include?(cookies[:plots2_locale].to_sym)
lang = cookies[:plots2_locale].to_sym
else
lang = http_accept_language.compatible_language_from(I18n.available_locales) || I18n.default_locale
cookies.permanent[:plots2_locale] = lang
end
I18n.locale = lang
end
def comments_node_and_path
@node = @comment.node
@path = params[:type] && params[:type] == 'question' ? @node.path(:question) : @node.path
end
# used for url redirects for friendly_id
# currently unused for issues discussed in https://github.com/publiclab/plots2/issues/691
def redirect_old_urls
# If an old id or a numeric id was used to find the record, then
# the request path will not match the notes path, and we should do
# a 301 redirect that uses the current friendly id.
if request.path != @node.path
redirect_to @node.path, status: :moved_permanently
end
end
def signed_in?
!current_user.nil?
end
def page_not_found
render file: "#{Rails.root}/public/404.html", layout: false, status: :not_found
end
# TODO: make less redundant with https://github.com/publiclab/plots2/blob/main/app/helpers/application_helper.rb#L3
def logged_in_as(roles)
return false unless current_user
has_valid_role = false
roles.each do |role|
has_valid_role = true if current_user.role == role
end
has_valid_role
end
end