rails/app/helpers/application_helper.rb
module ApplicationHelper
include Clipboard
include Pundit
def current_settings
@_settings ||= Admin::Settings.default_settings
end
def top_level_container_name
APP_CONFIG[:top_level_container_name] || "investigation"
end
#
# dom_for_id generates a dom id value for any object that returns an integer when sent an "id" message
#
# This helper is normally used with ActiveRecord objects.
#
# @model = Model.find(3)
# dom_id_for(@model) # => "model_3"
# dom_id_for(@model, :item) # => "item_model_3"
# dom_id_for(@model, :item, :textarea) # => "item_textarea_model_3"
#
# @scoped_model = OuterScope::InnerScope::Model.find(3)
# dom_id_for(@scoped_model) # => "outer_scope__inner_scope__model_3"
def dom_id_for(component, *optional_prefixes)
optional_prefixes.flatten!
optional_prefixes.compact! unless optional_prefixes.empty?
prefix = ''
optional_prefixes.each { |p| prefix << "#{p.to_s}_" }
class_name = component.class.name.underscore.clipboardify
if component.is_a?(ApplicationRecord)
id = component.id || Time.now.to_i
else
# this will be a temporary id, so it seems unlikely that these type of ids
# should be really be generated, however there are some parts of the code
# calling dom_id_for and passing a form object for example
id = component.object_id
end
id_string = id.to_s
"#{prefix}#{class_name}_#{id_string}"
end
def dom_class_for(component)
component.class.name.underscore
end
def short_name(name)
name.strip.downcase.gsub(/\W+/, '_')
end
# Sets the page title and outputs title if container is passed in.
# eg. <%= title('Hello World', :h2) %> will return the following:
# <h2>Hello World</h2> as well as setting the page title.
def title_tag(str, container = nil)
@page_title = str
content_tag(container, str) if container
end
# Outputs the corresponding flash message if any are set
def flash_messages
messages = []
%w(notice warning error).each do |msg|
messages << content_tag(:div, html_escape(flash[msg.to_sym]), :id => "flash-#{msg}") unless flash[msg.to_sym].blank?
end
messages
end
def labeled_check_box(form, field, name=field.to_s.humanize)
form.label(field, name) + "\n" + form.check_box(field)
end
# http://davidwparker.com/2008/11/12/simple-non-model-checkbox-in-rails/
def check_box_tag_new(name, value = "1", options = {})
html_options = { "type" => "checkbox", "name" => name, "id" => name, "value" => value }.update(options.stringify_keys)
unless html_options["check"].nil?
html_options["checked"] = "checked" if html_options["check"].to_i == 1
end
tag :input, html_options
end
def render_top_level_container_list_partial(locals)
container = top_level_container_name.pluralize
container_sym = top_level_container_name.pluralize.to_sym
container_class = top_level_container_name.classify.constantize
hide_print = locals.delete(:hide_print) || false
if container_class.respond_to?(:search_list)
render :partial => "#{container}/runnable_list", :locals => { container_sym => container_class.search_list(locals), :hide_print => hide_print }
else
render :partial => "#{container}/runnable_list", :locals => { container_sym => container_class.all, :hide_print => hide_print }
end
end
def top_level_container_name_as_class_string
container = top_level_container_name.pluralize
container_sym = top_level_container_name.pluralize.to_sym
container_class = top_level_container_name.classify
end
def render_partial_for(component,_opts={})
class_name = component.class.name.underscore
demodulized_class_name = component.class.name.delete_module.underscore_module
opts = {
:teacher_mode => false,
:substitute => nil,
:partial => 'show',
:locals => {}
}
opts.merge!(_opts)
teacher_mode = opts[:teacher_mode]
substitute = opts[:substitute]
partial = "#{class_name.pluralize}/#{opts[:partial]}"
locals = opts[:locals]
locals[demodulized_class_name.to_sym] = substitute ? substitute : component
locals[:teacher_mode] = teacher_mode
render :partial => partial, :locals => locals
end
def render_show_partial_for(component, _opts = {})
opts = {:teacher_mode => false, :substitute => nil}.merge(_opts)
render_partial_for(component, opts)
end
def render_edit_partial_for(component,opts={})
render_partial_for(component, {:partial => "remote_form"}.merge!(opts))
end
def edit_url_for(component, scope=false)
if scope
{ :controller => component.class.name.pluralize.underscore,
:action => :edit,
:id => component.id,
:scope_type => scope.class,
:scope_id =>scope.id}
else
{ :controller => component.class.name.pluralize.underscore,
:action => :edit,
:id => component.id,
:container_type => @container_type,
:container_id => @container_id }
end
end
def edit_menu_for(component, form, options={:omit_cancel => true}, scope=false)
component = (component.respond_to? :embeddable) ? component.embeddable : component
capture_haml do
haml_tag :div, :class => 'action_menu' do
haml_tag :div, :class => 'action_menu_header_left' do
haml_tag(:h3,{:class => 'menu'}) do
haml_concat title_for_component(component, :id_prefix => 'edit')
end
end
haml_tag :div, :class => 'action_menu_header_right' do
haml_tag :ul, {:class => 'menu'} do
#if (component.changeable?(current_visitor))
haml_tag(:li, {:class => 'menu'}) { haml_concat form.submit("Save") }
haml_tag(:li, {:class => 'menu'}) { haml_concat form.submit("Cancel") } unless options[:omit_cancel]
#end
end
end
end
end
end
def accordion_for(model, title, dom_prefix='', options={})
show_hide_text = options[:show_hide_text]
capture_haml do
haml_tag :div, :id => dom_id_for(model, dom_prefix), :class => 'accordion_container' do
haml_tag :div, :class => 'accordion_name' do
haml_concat title
end
if show_hide_text
haml_tag :div, :id => dom_id_for(model, "#{dom_prefix}_toggle"), :class => 'accordion_toggle_closed accordion_toggle' do
haml_tag :span, :class => "accordion_show_hide_text" do
haml_concat show_hide_text
end
end
else
haml_tag :div, :id => dom_id_for(model, "#{dom_prefix}_toggle"), :class => 'accordion_toggle_closed accordion_toggle'
end
unless options[:usage_count].blank?
haml_tag :div, :class => 'accordion_count' do
haml_concat options[:usage_count]
end
end
haml_tag :div, :class => 'empty_break'
haml_tag :div, :id => dom_id_for(model, "#{dom_prefix}_content"), :class => 'accordion_content', :style=>'display: none;' do
if block_given?
yield
end
end
end
end
end
def sort_dropdown(selected,keep = [])
selected ||= Search::Alphabetical
default_options = [ [Search::Oldest, "Oldest"], [Search::Newest, "Newest"], [Search::Alphabetical, "Alphabetical"], [Search::Popularity, "Popularity"] ]
sort_options = (keep.size > 0 ? default_options.select{|o| keep.include?(o[0]) } : default_options)
select nil, :sort_order, sort_options, {:selected => selected, :include_blank => false }
end
def learner_report_link_for(learner, action='report', link_text='Report ', title=nil)
return "" unless learner.reportable?
reportable_display_name = learner.class.display_name.downcase
action_string = action.gsub('_', ' ')
name = learner.name
url = polymorphic_url(learner, :action => action)
if title.nil?
title = "Display a #{action_string} for the #{reportable_display_name}: '#{name}' in a new browser window."
end
link_to(link_text, url, :target => '_blank', :title => title)
end
def report_link_for(reportable, link_text='Report ', title=nil)
return "" if reportable.respond_to?('reportable?') && !reportable.reportable?
url = report_portal_offering_path(reportable.id)
link_to link_text, url, :target => '_blank', :title => title
end
def activation_toggle_link_for(activatable, action='activate', link_text='Activate', title=nil)
activatable_display_name = activatable.class.display_name.downcase
action_string = action.gsub('_', ' ')
url = polymorphic_url(activatable, :action => action)
if title.nil?
title = "#{action_string} the #{activatable_display_name}: '#{activatable.name}'."
end
link_to(link_text, url, :title => title)
end
def edit_link_for(component, params={})
component_display_name = component.class.display_name.downcase
name = component.name
link_text = params.delete(:link_text) || "edit "
url = polymorphic_url(component, :action => :edit, :params => params)
link_button("edit.png", url) +
link_to(link_text, url,
:title => "edit the #{component_display_name}: '#{name}'")
end
def duplicate_link_for(component, params={})
component_display_name = component.class.display_name.downcase
text = params[:text] || 'duplicate'
name = component.name
url = polymorphic_url(component, :action => :duplicate, :params => params)
link_button("itsi_copy.png", url,
:title => "copy the #{component_display_name}: '#{name}'") +
link_to(text, url)
end
def print_link_for(component, params={})
component_display_name = component.class.display_name.downcase
name = component.name
link_text = params.delete(:link_text) || "print #{component_display_name}"
if params[:teacher_mode]
link_text = "#{link_text} (with notes) "
end
params.merge!({:print => true})
url = polymorphic_url(component,:params => params)
link_button("print.png", url, :title => "print the #{component_display_name}: '#{name}'") +
link_to(link_text,url, :target => '_blank')
end
def link_to_container(container, options={})
link_to name_for_component(container, options), container, :class => 'container_link'
end
def title_for_component(component, options={})
title = name_for_component(component, options)
id = dom_id_for(component, options[:id_prefix], :title)
if ::Rails.env == "development" || current_visitor.has_role?('admin')
"<span id=#{id} class='component_title'>#{title}</span><span class='dev_note'> #{link_to(component.id, component)}</span>".html_safe
else
"<span id=#{id} class='component_title'>#{title}</span>".html_safe
end
end
def name_for_component(component, options={})
if options[:display_name]
return options[:display_name]
end
name = ''
unless options[:hide_component_name]
if component.class.respond_to? :display_name
name << component.class.display_name
else
name << component.class.name.humanize
end
if component.respond_to? :display_type
name = "#{component.display_type} #{name}"
end
name << ': '
end
default_name = ''
if component.class.respond_to?(:default_value)
default_name = component.class.default_value('name')
end
name << case
when component.id.nil? then "(new)"
when component.name == default_name then ''
when component.name then component.name
else ''
end
end
def sessions_learner_stat(learner)
'n/a'
end
def report_details_for_learner(learner, opts = {})
options = { :omit_delete => true, :omit_edit => true, :hide_component_name => true, :type => :open_responses, :correctable => false }
options.update(opts)
capture_haml do
haml_tag :div, :class => 'action_menu' do
haml_tag :div, :class => 'action_menu_header_left' do
haml_concat title_for_component(learner, options)
end
end
end
end
def percent(count,max,precision = 1)
return 0 if max < 1
raw = (count/max.to_f)*100
result = (raw*(10**precision)).round/(10**precision).to_f
end
def percent_str(count, max, precision = 1)
return "" if max < 1
number_to_percentage(percent(count,max,precision), :precision => precision)
end
def menu_for_learner(learner, opts = {})
options = { :omit_delete => true, :omit_edit => true, :hide_component_name => true }
options.update(opts)
capture_haml do
haml_tag :div, :class => 'action_menu' do
haml_tag :div, :class => 'action_menu_activity_options' do
haml_concat learner_report_link_for(learner, 'report', 'Report')
end
haml_tag :div, :class => 'action_menu_activity_title' do
haml_concat title_for_component(learner, options)
haml_tag :span, :class => 'tiny' do
haml_concat sessions_learner_stat(learner)
end
end
end
end
end
def lara_report_link(offering)
if offering.runnable.kind_of?(ExternalActivity)
url = offering.runnable.url
uri = URI.parse(url)
learners = offering.learner_ids.join(",")
students = offering.learners.map { |l| "#{l.id}:#{l.user.login}" }.join(",")
students = URI.escape(students)
learners = URI.escape(learners)
report_url = "#{uri.scheme}://#{uri.host}:#{uri.port}/runs/details?learners=#{learners}&students=#{students}"
haml_concat " | "
haml_concat link_to("LARA Run report", report_url, {:target => "_blank"} )
end
end
def link_button(image,url,options={})
defaults = {
:class => 'rollover'
}
options = defaults.merge(options)
link_to image_tag(image, :alt=>options[:title]) , url, options
end
def tab_for(component, options={})
if(options[:active])
"<li id=#{dom_id_for(component, :tab)} class='tab active'>#{link_to component.name, component, :class => 'active'}</li>".html_safe
else
"<li id=#{dom_id_for(component, :tab)} class='tab'>#{link_to component.name, component}</li>".html_safe
end
end
# expects styles to contain space seperated list of style classes.
def style_for_teachers(component,style_classes=[])
if (for_teacher_only?(component))
style_classes << 'teacher_only' # funny, just adding a style text
end
return style_classes
end
def style_for_item(component,style_classes=[])
style_classes << 'item' << 'selectable' << 'item_selectable'
if (component.respond_to? 'changeable?') && (component.changeable?(current_visitor))
style_classes << 'movable'
end
style_classes = style_for_teachers(component,style_classes)
return style_classes.join(" ")
end
def simple_div_helper_that_yields
capture_haml do
haml_tag :div, :class => 'simple_div' do
if block_given?
haml_concat yield
end
end
end
end
def already_rendered?(thing)
return @render_scope_additional_objects && @render_scope_additional_objects.include?(thing)
end
def mark_rendered(thing)
@render_scope_additional_objects ||= []
@render_scope_additional_objects << thing
end
def in_render_scope?(thing)
return true if thing == nil
if already_rendered?(thing)
return true
end
if @render_scope
if @render_scope.respond_to?("page_elements")
embeddables = @render_scope.page_elements.collect{|pe| pe.embeddable}.uniq
if embeddables.include?(thing)
return true
end
end
end
return false
end
def render_scoped_reference(thing)
return "" if thing == nil
if in_render_scope?(thing)
capture_haml do
haml_tag :object, :refid => ot_refid_for(thing)
end
else
render_show_partial_for(thing)
end
end
#
# is a component viewable only by teacher?
# cascading logic.
# TODO: generic container-based method-forwarding mechanism
#
def for_teacher_only?(thing)
if (thing.respond_to?("teacher_only?") && thing.teacher_only?)
return true;
end
if thing.respond_to? :parent
while thing = thing.parent
if thing.respond_to? :teacher_only?
if thing.teacher_only?
return true
end
end
end
end
return false
end
def render_project_info
unless @rendered_project_info
render_themed_partial("home/project_info")
@rendered_project_info = true
end
end
def add_top_menu_item(link)
@top_menu_items ||= []
@top_menu_items << link
end
def runnable_list(options)
Investigation.search_list(options)
end
def students_in_class(all_students)
all_students.to_a.compact.uniq.sort{|a,b| (a.user ? [a.first_name, a.last_name] : ["",""]) <=> (b.user ? [b.first_name, b.last_name] : ["",""])}
end
# Welcome
# = "#{current_visitor.name}."
# - unless current_visitor.anonymous?
# = link_to 'Preferences', preferences_user_path(current_visitor)
# \/
# = link_to 'Logout', logout_path
# - else
# = link_to 'Login', login_path
# \/
# = link_to 'Sign Up', pick_signup_path
# - if @original_user.has_role?('admin', 'manager')
# \/
# = link_to 'Switch', switch_user_path(current_visitor)
def login_line(options = {})
opts = {
:welcome => "Welcome",
:login => "Login",
:signup => "Sign up",
:logout => "Logout",
:prefs => "Preferences",
:guest => false,
:name_method => "name"
}
opts.merge!(options)
message = ""
if current_visitor.anonymous?
if opts[:guest]
message += "#{opts[:welcome]} #{opts[:guest]} "
end
message += link_to opts[:login], login_path
message += " / "
message += link_to opts[:signup], 'javascript:Portal.openSignupModal();'
else
message += "#{opts[:welcome]} #{current_visitor.send(opts[:name_method])} "
message += link_to opts[:prefs], preferences_user_path(current_visitor)
message += " / "
message += link_to opts[:logout], logout_path
if @original_user.has_role?('admin','manager')
message += " "
message += link_to 'Switch', switch_user_path(current_visitor)
end
end
message
end
def settings_for(key)
Admin::Settings.settings_for(key)
end
# this appears to not be used in master right now
def current_user_can_author
return true if current_visitor.has_role? "author"
if settings_for(:teachers_can_author)
return true unless current_visitor.portal_teacher.nil?
end
# TODO add aditional can-author conditions
return false
end
# Rails 3.0 way of switching the format for a block of code
# see: http://stackoverflow.com/questions/339130/how-do-i-render-a-partial-of-a-different-format-in-rails
def with_format(format, &block)
old_formats = formats
self.formats = [format]
block.call
self.formats = old_formats
nil
end
# This fixes an error in polymorphic_url brought on because the Admin::Settings model name is plural.
def admin_settings_index_path(*args)
admin_settings_path
end
def class_link_for_user
if current_visitor.portal_teacher
if current_visitor.has_active_classes?
recent_activity_path
else
getting_started_path
end
elsif current_visitor.portal_student
my_classes_path
else
root_path
end
end
def current_user_home_path
if current_user.nil?
# anonymous home is the '/'
root_path
elsif BoolENV['RESEARCHER_REPORT_ONLY']
learner_report_path
elsif current_user.portal_student
my_classes_path
elsif APP_CONFIG[:recent_activity_on_login] && current_user.portal_teacher
# Teachers are redirected to the "Recent Activity" page
recent_activity_path
else
# this is a generic logged in user (not teacher or student)
getting_started_path
end
end
end