2011-02-01 09:57:29 +08:00
#
# Copyright (C) 2011 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Methods added to this helper will be available to all templates in the application.
module ApplicationHelper
include TextHelper
2014-02-05 04:53:04 +08:00
include HtmlTextHelper
2011-07-13 04:31:40 +08:00
include LocaleSelection
import ActiveModel::Serializers port and convert quizzes api to it
test plan:
- The quiz api should work like it normally does when you don't pass
an 'Accept: application/vnd.api+json' header.
- The quizzes index page and quiz edit page should work like they
always do.
- Testing the Quizzes API for "jsonapi" style:
- For all requests, you MUST have the "Accept" header set to
"application/vnd.api+json"
- Test all the endpoints (PUT, POST, GET, INDEX, DELETE) like you
normally would, except you'll need to format the data according to
the next few steps:
- For "POST" and "PUT" (create and update) requests, you should send
the data like: { "quizzes": [ { id: 1, title: "blah" } ]
- For all requests (except DELETE), you should get back a response
that looks like: { "quizzes": [ { quiz you requested } ]
- For the "delete" action, you should get a "no content" response
and the request should be successful
Change-Id: Ie91deaeb6772cbe52a0fc46a28ab93a4e3036061
Reviewed-on: https://gerrit.instructure.com/25997
Reviewed-by: Jacob Fugal <jacob@instructure.com>
Tested-by: Jenkins <jenkins@instructure.com>
QA-Review: Caleb Guanzon <cguanzon@instructure.com>
Product-Review: Stanley Stuart <stanley@instructure.com>
2013-12-05 03:06:32 +08:00
include Canvas :: LockExplanation
2011-11-24 03:52:38 +08:00
2013-03-20 05:05:36 +08:00
def context_user_name ( context , user )
2011-02-01 09:57:29 +08:00
return nil unless user
return user . short_name if ! context && user . respond_to? ( :short_name )
user_id = user
user_id = user . id if user . is_a? ( User ) || user . is_a? ( OpenObject )
2013-03-20 05:05:36 +08:00
Rails . cache . fetch ( [ 'context_user_name' , context , user_id ] . cache_key , { :expires_in = > 15 . minutes } ) do
user = user . respond_to? ( :short_name ) ? user : User . find ( user_id )
user . short_name || user . name
2011-02-01 09:57:29 +08:00
end
end
2011-11-24 03:52:38 +08:00
2011-02-01 09:57:29 +08:00
def keyboard_navigation ( keys )
2011-11-11 00:31:45 +08:00
# TODO: move this to JS, currently you have to know what shortcuts the JS has defined
# making it very likely this list will not reflect the key bindings
2011-02-01 09:57:29 +08:00
content = " <ul class='navigation_list' tabindex='-1'> \n "
keys . each do | hash |
content += " <li> \n "
2011-06-30 01:35:47 +08:00
content += " <span class='keycode'> #{ h ( hash [ :key ] ) } </span> \n "
2011-02-01 09:57:29 +08:00
content += " <span class='colon'>:</span> \n "
2011-06-30 01:35:47 +08:00
content += " <span class='description'> #{ h ( hash [ :description ] ) } </span> \n "
2011-02-01 09:57:29 +08:00
content += " </li> \n "
end
content += " </ul> "
2011-06-30 01:35:47 +08:00
content_for ( :keyboard_navigation ) { raw ( content ) }
2011-02-01 09:57:29 +08:00
end
2011-11-24 03:52:38 +08:00
2011-02-01 09:57:29 +08:00
def context_prefix ( code )
return " /{{ context_type_pluralized }}/{{ context_id }} " unless code
split = code . split ( / _ / )
id = split . pop
type = split . join ( '_' )
" / #{ type . pluralize } / #{ id } "
end
2011-11-24 03:52:38 +08:00
2011-02-01 09:57:29 +08:00
def cached_context_short_name ( code )
return nil unless code
@cached_context_names || = { }
@cached_context_names [ code ] || = Rails . cache . fetch ( [ 'short_name_lookup' , code ] . cache_key ) do
Context . find_by_asset_string ( code ) . short_name rescue " "
end
end
2011-05-20 05:39:43 +08:00
round avatars everywhere (almost)
fixes CNVS-10713, CNVS-10636
test plan
- ensure that every page that has avatars displays them in a
consistent, circular way
- ensure that the change didn't break the rest of the page
avatars were left square on purpose on these pages
- speedgrader header (too much work to circle)
- big pic on profile page (too big and interactive, needs
some proper ui design)
- profile pic selector (not really profile pics yet, just images)
- app review (error handling is tricky)
some pages where you might find avatars to check
- manage avatars (/account/:id/avatars)
- discussions index
- discussions show (don't forget threaded sub-entries!)
- profile page (/about/:id)
- group edit (i can't find this page, but it has code)
- course roster (/courses/:id/users)
- context roster (/groups/:id/users)
- old conversations (message list, message detail, people picker)
- new conversations
- gradebook2 (student column, submission comments)
- speed grader (top left student name, submission comments,
discussion assignment)
- app review (enable app center plugin, app rating comments)
- user show page (/users/:id)
- masquerade page (/users/:id/masquerade)
- masquerade footer (on the bottom of any page when you masquerade)
Change-Id: Ic01a4b44433aaf6254798d8267bf473a8bf85c2d
Reviewed-on: https://gerrit.instructure.com/28953
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Braden Anderson <banderson@instructure.com>
Product-Review: Joel Hough <joel@instructure.com>
QA-Review: Joel Hough <joel@instructure.com>
2014-02-06 07:23:59 +08:00
# don't use this anymore. circular avatars are the new hotness
def square_avatar_image ( user_or_id , width = 50 , opts = { } )
2012-08-22 01:28:31 +08:00
user_id = user_or_id . is_a? ( User ) ? user_or_id . id : user_or_id
user = user_or_id . is_a? ( User ) && user_or_id
2011-02-01 09:57:29 +08:00
if session [ " reported_ #{ user_id } " ]
2012-02-04 14:44:45 +08:00
image_tag " messages/avatar-50.png "
2011-02-01 09:57:29 +08:00
else
2012-08-22 01:28:31 +08:00
avatar_settings = @domain_root_account && @domain_root_account . settings [ :avatars ] || 'enabled'
image_url , alt_tag = Rails . cache . fetch ( Cacher . inline_avatar_cache_key ( user_id , avatar_settings ) ) do
if ! user && user_id . to_i > 0
user = User . find ( user_id )
end
if user
url = avatar_url_for_user ( user )
else
url = " messages/avatar-50.png "
end
alt = user ? user . short_name : ''
[ url , alt ]
end
2013-07-16 06:43:40 +08:00
image_tag ( image_url ,
:style = > " width: #{ width } px; min-height: #{ ( width / 1 . 6 ) . to_i } px; max-height: #{ ( width * 1 . 6 ) . to_i } px " ,
:alt = > alt_tag ,
:class = > Array ( opts [ :image_class ] ) . join ( ' ' ) )
2011-02-01 09:57:29 +08:00
end
end
2011-11-24 03:52:38 +08:00
round avatars everywhere (almost)
fixes CNVS-10713, CNVS-10636
test plan
- ensure that every page that has avatars displays them in a
consistent, circular way
- ensure that the change didn't break the rest of the page
avatars were left square on purpose on these pages
- speedgrader header (too much work to circle)
- big pic on profile page (too big and interactive, needs
some proper ui design)
- profile pic selector (not really profile pics yet, just images)
- app review (error handling is tricky)
some pages where you might find avatars to check
- manage avatars (/account/:id/avatars)
- discussions index
- discussions show (don't forget threaded sub-entries!)
- profile page (/about/:id)
- group edit (i can't find this page, but it has code)
- course roster (/courses/:id/users)
- context roster (/groups/:id/users)
- old conversations (message list, message detail, people picker)
- new conversations
- gradebook2 (student column, submission comments)
- speed grader (top left student name, submission comments,
discussion assignment)
- app review (enable app center plugin, app rating comments)
- user show page (/users/:id)
- masquerade page (/users/:id/masquerade)
- masquerade footer (on the bottom of any page when you masquerade)
Change-Id: Ic01a4b44433aaf6254798d8267bf473a8bf85c2d
Reviewed-on: https://gerrit.instructure.com/28953
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Braden Anderson <banderson@instructure.com>
Product-Review: Joel Hough <joel@instructure.com>
QA-Review: Joel Hough <joel@instructure.com>
2014-02-06 07:23:59 +08:00
def square_avatar ( user_or_id , context_code , width = 50 , opts = { } )
2012-08-22 01:28:31 +08:00
user_id = user_or_id . is_a? ( User ) ? user_or_id . id : user_or_id
2011-03-16 02:49:26 +08:00
if service_enabled? ( :avatars )
round avatars everywhere (almost)
fixes CNVS-10713, CNVS-10636
test plan
- ensure that every page that has avatars displays them in a
consistent, circular way
- ensure that the change didn't break the rest of the page
avatars were left square on purpose on these pages
- speedgrader header (too much work to circle)
- big pic on profile page (too big and interactive, needs
some proper ui design)
- profile pic selector (not really profile pics yet, just images)
- app review (error handling is tricky)
some pages where you might find avatars to check
- manage avatars (/account/:id/avatars)
- discussions index
- discussions show (don't forget threaded sub-entries!)
- profile page (/about/:id)
- group edit (i can't find this page, but it has code)
- course roster (/courses/:id/users)
- context roster (/groups/:id/users)
- old conversations (message list, message detail, people picker)
- new conversations
- gradebook2 (student column, submission comments)
- speed grader (top left student name, submission comments,
discussion assignment)
- app review (enable app center plugin, app rating comments)
- user show page (/users/:id)
- masquerade page (/users/:id/masquerade)
- masquerade footer (on the bottom of any page when you masquerade)
Change-Id: Ic01a4b44433aaf6254798d8267bf473a8bf85c2d
Reviewed-on: https://gerrit.instructure.com/28953
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Braden Anderson <banderson@instructure.com>
Product-Review: Joel Hough <joel@instructure.com>
QA-Review: Joel Hough <joel@instructure.com>
2014-02-06 07:23:59 +08:00
link_to ( square_avatar_image ( user_or_id , width , opts ) , " #{ context_prefix ( context_code ) } /users/ #{ user_id } " , :style = > 'z-index: 2; position: relative;' , :class = > 'avatar' )
2011-03-16 02:49:26 +08:00
end
2011-02-01 09:57:29 +08:00
end
2011-11-24 03:52:38 +08:00
2011-02-01 09:57:29 +08:00
def slugify ( text = " " )
text . gsub ( / [^ \ w] / , " _ " ) . downcase
end
2011-11-24 03:52:38 +08:00
2011-02-01 09:57:29 +08:00
def count_if_any ( count = nil )
if count && count > 0
" ( #{ count } ) "
else
" "
end
end
# Used to generate context_specific urls, as in:
# context_url(@context, :context_assignments_url)
# context_url(@context, :controller => :assignments, :action => :show)
def context_url ( context , * opts )
@context_url_lookup || = { }
2013-06-23 01:27:44 +08:00
context_name = url_helper_context_from_object ( context )
2011-02-16 06:28:47 +08:00
lookup = [ context ? context . id : nil , context_name , * opts ]
2011-02-01 09:57:29 +08:00
return @context_url_lookup [ lookup ] if @context_url_lookup [ lookup ]
res = nil
if opts . length > 1 || ( opts [ 0 ] . is_a? String ) || ( opts [ 0 ] . is_a? Symbol )
name = opts . shift . to_s
name = name . sub ( / context / , context_name )
opts . unshift context . id
opts . push ( { } ) unless opts [ - 1 ] . is_a? ( Hash )
ajax = opts [ - 1 ] . delete :ajax rescue nil
2011-03-10 00:11:22 +08:00
opts [ - 1 ] [ :only_path ] = true unless opts [ - 1 ] [ :only_path ] == false
2011-02-01 09:57:29 +08:00
res = self . send name , * opts
elsif opts [ 0 ] . is_a? Hash
opts = opts [ 0 ]
ajax = opts [ 0 ] . delete :ajax rescue nil
opts [ :only_path ] = true
opts [ " #{ context_name } _id " ] = context . id
res = self . url_for opts
else
res = context_name . to_s + opts . to_json . to_s
end
@context_url_lookup [ lookup ] = res
end
2011-08-21 08:16:40 +08:00
2013-09-21 15:17:14 +08:00
def full_url ( path )
uri = URI . parse ( request . url )
uri . path = ''
uri . query = ''
URI . join ( uri , path ) . to_s
end
2013-06-23 01:27:44 +08:00
def url_helper_context_from_object ( context )
( context ? context . class . base_ar_class : context . class ) . name . underscore
end
2013-09-07 04:45:08 +08:00
def message_user_path ( user , context = nil )
context = context || @context
context = nil unless context . is_a? ( Course )
conversations_path ( user_id : user . id , user_name : user . name ,
context_id : context . try ( :asset_string ) )
end
# Public: Determine if the currently logged-in user is an account or site admin.
#
# Returns a boolean.
def current_user_is_account_admin
[ @domain_root_account , Account . site_admin ] . map do | account |
account . membership_for_user ( @current_user )
end . any?
2011-08-21 08:16:40 +08:00
end
2011-11-24 03:52:38 +08:00
2011-02-01 09:57:29 +08:00
def hidden ( include_style = false )
2013-02-08 06:03:29 +08:00
include_style ? " style='display:none;' " . html_safe : " display: none; "
2011-02-01 09:57:29 +08:00
end
# Helper for easily checking vender/plugins/adheres_to_policy.rb
# policies from within a view. Caches the response, but basically
# user calls object.grants_right?(user, nil, action)
2011-08-16 06:34:57 +08:00
def can_do ( object , user , * actions )
2011-02-01 09:57:29 +08:00
return false unless object
if object . is_a? ( OpenObject ) && object . type
obj = object . temporary_instance
if ! obj
obj = object . type . classify . constantize . new
obj . instance_variable_set ( " @attributes " , object . instance_variable_get ( " @table " ) . with_indifferent_access )
obj . instance_variable_set ( " @new_record " , false )
object . temporary_instance = obj
end
2011-08-16 06:34:57 +08:00
return can_do ( obj , user , actions )
2011-02-01 09:57:29 +08:00
end
2011-08-16 06:34:57 +08:00
actions = Array ( actions ) . flatten
2011-02-01 09:57:29 +08:00
if ( object == @context || object . is_a? ( Course ) ) && user == @current_user
@context_all_permissions || = { }
@context_all_permissions [ object . asset_string ] || = object . grants_rights? ( user , session , nil )
2011-08-16 06:34:57 +08:00
return ! ( @context_all_permissions [ object . asset_string ] . keys & actions ) . empty?
2011-02-01 09:57:29 +08:00
end
@permissions_lookup || = { }
2011-08-16 06:34:57 +08:00
return true if actions . any? do | action |
lookup = [ object ? object . asset_string : nil , user ? user . id : nil , action ]
@permissions_lookup [ lookup ] if @permissions_lookup [ lookup ] != nil
end
2011-02-01 09:57:29 +08:00
begin
2011-08-16 06:34:57 +08:00
rights = object . grants_rights? ( user , session , * actions )
2011-02-01 09:57:29 +08:00
rescue = > e
logger . warn " #{ object . inspect } raised an error while granting rights. #{ e . inspect } " if logger
2011-08-16 06:34:57 +08:00
return false
end
res = false
rights . each do | action , value |
lookup = [ object ? object . asset_string : nil , user ? user . id : nil , action ]
@permissions_lookup [ lookup ] = value
res || = value
2011-02-01 09:57:29 +08:00
end
2011-08-16 06:34:57 +08:00
res
2011-02-01 09:57:29 +08:00
end
2011-11-24 03:52:38 +08:00
2011-02-01 09:57:29 +08:00
# Loads up the lists of files needed for the wiki_sidebar. Called from
# within the cached code so won't be loaded unless needed.
def load_wiki_sidebar
return if @wiki_sidebar_data
logger . warn " database lookups happening in view code instead of controller code for wiki sidebar (load_wiki_sidebar) "
@wiki_sidebar_data = { }
2012-09-28 23:52:32 +08:00
includes = [ :active_assignments , :active_discussion_topics , :active_quizzes , :active_context_modules ]
2013-03-20 00:41:22 +08:00
includes . each { | i | @wiki_sidebar_data [ i ] = @context . send ( i ) . limit ( 150 ) if @context . respond_to? ( i ) }
2011-02-01 09:57:29 +08:00
includes . each { | i | @wiki_sidebar_data [ i ] || = [ ] }
2013-03-20 00:41:22 +08:00
@wiki_sidebar_data [ :wiki_pages ] = @context . wiki . wiki_pages . active . order ( :title ) . limit ( 150 ) if @context . respond_to? ( :wiki )
2012-09-28 23:52:32 +08:00
@wiki_sidebar_data [ :wiki_pages ] || = [ ]
2013-01-03 06:48:52 +08:00
if can_do ( @context , @current_user , :manage_files )
@wiki_sidebar_data [ :root_folders ] = Folder . root_folders ( @context )
elsif @context . is_a? ( Course ) && ! @context . tab_hidden? ( Course :: TAB_FILES )
@wiki_sidebar_data [ :root_folders ] = Folder . root_folders ( @context ) . reject { | folder | folder . locked? || folder . hidden }
else
@wiki_sidebar_data [ :root_folders ] = [ ]
end
2011-02-01 09:57:29 +08:00
@wiki_sidebar_data
end
2011-11-24 03:52:38 +08:00
# js_block captures the content of what you pass it and render_js_blocks will
2011-02-01 09:57:29 +08:00
# render all of the blocks that were captured by js_block inside of a <script> tag
# if you are in the development environment it will also print out a javascript // comment
# that shows the file and line number of where this block of javascript came from.
2011-06-17 03:17:39 +08:00
def js_block ( options = { } , & block )
js_blocks << options . merge (
2011-02-01 09:57:29 +08:00
:file_and_line = > block . to_s ,
:contents = > capture ( & block )
2011-06-17 03:17:39 +08:00
)
2011-02-01 09:57:29 +08:00
end
def js_blocks ; @js_blocks || = [ ] ; end
def render_js_blocks
output = js_blocks . inject ( '' ) do | str , e |
# print file and line number for debugging in development mode.
value = " "
2013-10-05 00:47:54 +08:00
value << " <!-- BEGIN SCRIPT BLOCK FROM: " + e [ :file_and_line ] + " --> \n " if Rails . env . development?
2011-02-01 09:57:29 +08:00
value << e [ :contents ]
2013-10-05 00:47:54 +08:00
value << " <!-- END SCRIPT BLOCK FROM: " + e [ :file_and_line ] + " --> \n " if Rails . env . development?
2011-02-01 09:57:29 +08:00
str << value
end
raw ( output )
end
2012-03-03 02:11:34 +08:00
def hidden_dialog ( id , & block )
content = capture ( & block )
if ! Rails . env . production? && hidden_dialogs [ id ] && hidden_dialogs [ id ] != content
raise " Attempted to capture a hidden dialog with #{ id } and different content! "
end
2012-03-09 07:28:12 +08:00
hidden_dialogs [ id ] = capture ( & block )
2012-03-03 02:11:34 +08:00
end
def hidden_dialogs ; @hidden_dialogs || = { } ; end
def render_hidden_dialogs
2012-05-19 23:59:18 +08:00
output = hidden_dialogs . keys . sort . inject ( '' ) do | str , id |
str << " <div id=' #{ id } ' style='display: none;''> " << hidden_dialogs [ id ] << " </div> "
2012-03-03 02:11:34 +08:00
end
raw ( output )
end
2011-06-17 03:17:39 +08:00
class << self
attr_accessor :cached_translation_blocks
end
2013-04-18 04:36:55 +08:00
def include_js_translations?
! ! ( params [ :include_js_translations ] || use_optimized_js? )
end
2012-02-07 09:42:19 +08:00
# See `js_base_url`
def use_optimized_js?
if ENV [ 'USE_OPTIMIZED_JS' ] == 'true'
2012-05-23 03:09:30 +08:00
# allows overriding by adding ?debug_assets=1 or ?debug_js=1 to the url
# (debug_assets is also used by jammit => you'll get unpackaged css AND js)
! ( params [ :debug_assets ] || params [ :debug_js ] )
2012-02-07 09:42:19 +08:00
else
# allows overriding by adding ?optimized_js=1 to the url
2012-02-15 05:37:42 +08:00
params [ :optimized_js ] || false
2012-02-07 09:42:19 +08:00
end
end
2011-11-11 00:31:45 +08:00
# Determines the location from which to load JavaScript assets
#
# uses optimized:
# * when ENV['USE_OPTIMIZED_JS'] is true
# * or when ?optimized_js=true is present in the url. Run `rake js:build` to
# build the optimized files
#
# uses non-optimized:
# * when ENV['USE_OPTIMIZED_JS'] is false
# * or when ?debug_assets=true is present in the url
def js_base_url
2012-02-07 09:42:19 +08:00
use_optimized_js? ? '/optimized' : '/javascripts'
2011-11-11 00:31:45 +08:00
end
# Returns a <script> tag for each registered js_bundle
def include_js_bundles
2012-06-02 07:13:53 +08:00
paths = js_bundles . inject ( [ ] ) do | ary , ( bundle , plugin ) |
2012-02-19 05:04:57 +08:00
base_url = js_base_url
2012-06-02 07:13:53 +08:00
base_url += " /plugins/ #{ plugin } " if plugin
ary . concat ( Canvas :: RequireJs . extensions_for ( bundle , 'plugins/' ) ) unless use_optimized_js?
ary << " #{ base_url } /compiled/bundles/ #{ bundle } .js "
2012-02-19 05:04:57 +08:00
end
2011-11-11 00:31:45 +08:00
javascript_include_tag * paths
2011-02-01 09:57:29 +08:00
end
2012-02-22 05:58:05 +08:00
def include_css_bundles
unless jammit_css_bundles . empty?
2012-02-22 08:03:28 +08:00
bundles = jammit_css_bundles . map { | ( bundle , plugin ) | plugin ? " plugins_ #{ plugin } _ #{ bundle } " : bundle }
2012-11-30 04:52:24 +08:00
bundles << { :media = > 'all' }
2012-02-22 05:58:05 +08:00
include_stylesheets ( * bundles )
end
end
2013-08-27 03:22:46 +08:00
def include_common_stylesheets
include_stylesheets :vendor , :common , media : " all "
end
2011-02-01 09:57:29 +08:00
def section_tabs
@section_tabs || = begin
2011-11-24 03:52:38 +08:00
if @context
2012-06-08 01:07:07 +08:00
html = [ ]
2012-08-16 02:49:55 +08:00
tabs = Rails . cache . fetch ( [ @context , @current_user , @domain_root_account , " section_tabs_hash " , I18n . locale ] . cache_key ) do
basic lti navigation links
By properly configuring external tools (see
/spec/models/course_spec/rb:898 for examples) they can
be added as left-side navigation links to a course,
an account, or to the user profile section of Canvas.
testing notes:
- you have to manually set options on the external tool:
- for user navigation the tool needs to be created on the root account
with the following settings:
{:user_navigation => {:url => <url>, :text => <tab label>} }
(there are also some optional language options you can set using
the :labels attribute)
- for account navigation it's the same
- for course navigation it's the same, except with :course_navigation
there's also some additional options:
:visibility => <value> // public, members, admins
:default => <value> // disabled, enabled
test plan:
- configure a user navigation tool at the root account level,
make sure it shows up in the user's profile section
- configure a course navigation tool at the account level,
make sure it shows up in the course's navigation
- configure a course navigation tool at the course level,
make sure it shows up in the course's navigation
- make sure :default => 'disabled' course navigation tools don't
appear by default in the navigation, but can be enabled on
the course settings page
- make sure :visibility => 'members' only shows up for course members
- make sure :visibility => 'admins' only shows up for course admins
- configure an account navigation tool at the account level,
make sure it shows up in the account's navigation, and
any sub-account's navigation
Change-Id: I977da3c6b89a9e32b4cff4c2b6b221f8162782ff
Reviewed-on: https://gerrit.instructure.com/5427
Reviewed-by: Brian Whitmer <brian@instructure.com>
Tested-by: Hudson <hudson@instructure.com>
2011-08-18 13:49:01 +08:00
if @context . respond_to? ( :tabs_available ) && ! ( tabs = @context . tabs_available ( @current_user , :session = > session , :root_account = > @domain_root_account ) ) . empty?
2012-06-08 01:07:07 +08:00
tabs . select do | tab |
2013-12-18 05:21:40 +08:00
if ( tab [ :id ] == @context . class :: TAB_COLLABORATIONS rescue false )
2011-02-01 09:57:29 +08:00
tab [ :href ] && tab [ :label ] && Collaboration . any_collaborations_configured?
elsif ( tab [ :id ] == @context . class :: TAB_CONFERENCES rescue false )
tab [ :href ] && tab [ :label ] && feature_enabled? ( :web_conferences )
else
tab [ :href ] && tab [ :label ]
end
end
2012-06-08 01:07:07 +08:00
else
[ ]
end
end
2012-06-14 02:59:36 +08:00
return '' if tabs . empty?
2012-09-05 04:29:38 +08:00
html << '<nav role="navigation" aria-label="context"><ul id="section-tabs">'
2012-06-08 01:07:07 +08:00
tabs . each do | tab |
path = nil
if tab [ :args ]
path = send ( tab [ :href ] , * tab [ :args ] )
elsif tab [ :no_args ]
path = send ( tab [ :href ] )
else
path = send ( tab [ :href ] , @context )
2011-02-01 09:57:29 +08:00
end
2012-06-08 01:07:07 +08:00
hide = tab [ :hidden ] || tab [ :hidden_unused ]
2014-02-12 08:03:26 +08:00
class_name = tab [ :css_class ] . downcase . replace_whitespace ( " - " )
2012-06-08 01:07:07 +08:00
class_name += ' active' if @active_tab == tab [ :css_class ]
Fully adopt Bootstrap & update css to work with it, closes: #CNVS-1344
this commit does the following:
* upgrade bootstrap-sass gem to most recent version
* switches to using bootstrap's normalize.css and forms.css
which fixes a whole bunch of misformatting of how bootstrap
stuff is supposed to look, but changing those 2 affects
a lot of our old stylesheets.
* gets rid of unified_buttons.sass and just uses bootstraps buttons.
.ui-button @extends these because we still have to support .ui-button
for modals & buttonsets. but .button is no longer supported.
* a lot of css file reorganization (there's no more 'blue' and
'normal canvas', there's just canvas)
* a bunch of files had to be tweaked to look good with these changes.
test plan:
This change touches every page in canvas so, no kidding, we need to make
sure every page looks OK. In order to do that:
1. each sprint team needs to give a +1 after they make sure all the
pages in the features they are over look good.
2. the QA person on each team needs to look at the pages for their
teams features for a QA +1
things to look for specifically when testing:
* buttons: this gets rid of all those red 'cancel' links
that are actually buttons, make sure all the buttons you see
look right. if you see 2 plain gray buttons next to each other
like [Save] [Cancel], we should make the primary one blue (by
adding the .btn-primary class)
* Forms: a lot of this change has to do with how form elements look,
especially <select>s, <input>s and <label>s. look at the diffs
for the ones that have the most changes and make sure those look
good, but also check for the ones I missed and make sure those
look good too.
* and just random style changes, if something looks ugly or broken
(and it didn't before), we should fix that.
Also:
just use a link instead of a drop-menu for adding event from sidebar
we used to have a drop down menu for adding events
to cal2 from the sidebar where you'd hit a cog
and it'd ask you if you wanted to add an event or
an assignment. this just simplifies it to an add
icon.
this: http://cl.ly/image/133a2A3q3q1M
instead of: http://cl.ly/image/46463o2s3W0g
Change-Id: I384fe273934bca96bf28423afb1402c7792d8766
Reviewed-on: https://gerrit.instructure.com/15422
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Ryan Florence <ryanf@instructure.com>
QA-Review: Ryan Florence <ryanf@instructure.com>
2012-12-21 14:46:28 +08:00
html << " <li class='section #{ " section-tab-hidden " if hide } '> " + link_to ( tab [ :label ] , path , :class = > class_name ) + " </li> " if tab [ :href ]
2011-02-01 09:57:29 +08:00
end
2012-06-08 01:07:07 +08:00
html << " </ul></nav> "
html . join ( " " )
2011-02-01 09:57:29 +08:00
end
end
raw ( @section_tabs )
end
2011-11-24 03:52:38 +08:00
2011-02-01 09:57:29 +08:00
def sortable_tabs
basic lti navigation links
By properly configuring external tools (see
/spec/models/course_spec/rb:898 for examples) they can
be added as left-side navigation links to a course,
an account, or to the user profile section of Canvas.
testing notes:
- you have to manually set options on the external tool:
- for user navigation the tool needs to be created on the root account
with the following settings:
{:user_navigation => {:url => <url>, :text => <tab label>} }
(there are also some optional language options you can set using
the :labels attribute)
- for account navigation it's the same
- for course navigation it's the same, except with :course_navigation
there's also some additional options:
:visibility => <value> // public, members, admins
:default => <value> // disabled, enabled
test plan:
- configure a user navigation tool at the root account level,
make sure it shows up in the user's profile section
- configure a course navigation tool at the account level,
make sure it shows up in the course's navigation
- configure a course navigation tool at the course level,
make sure it shows up in the course's navigation
- make sure :default => 'disabled' course navigation tools don't
appear by default in the navigation, but can be enabled on
the course settings page
- make sure :visibility => 'members' only shows up for course members
- make sure :visibility => 'admins' only shows up for course admins
- configure an account navigation tool at the account level,
make sure it shows up in the account's navigation, and
any sub-account's navigation
Change-Id: I977da3c6b89a9e32b4cff4c2b6b221f8162782ff
Reviewed-on: https://gerrit.instructure.com/5427
Reviewed-by: Brian Whitmer <brian@instructure.com>
Tested-by: Hudson <hudson@instructure.com>
2011-08-18 13:49:01 +08:00
tabs = @context . tabs_available ( @current_user , :for_reordering = > true , :root_account = > @domain_root_account )
2011-11-24 03:52:38 +08:00
tabs . select do | tab |
2013-12-18 05:21:40 +08:00
if ( tab [ :id ] == @context . class :: TAB_COLLABORATIONS rescue false )
2011-02-01 09:57:29 +08:00
Collaboration . any_collaborations_configured?
elsif ( tab [ :id ] == @context . class :: TAB_CONFERENCES rescue false )
feature_enabled? ( :web_conferences )
else
tab [ :id ] != ( @context . class :: TAB_SETTINGS rescue nil )
end
end
end
2011-11-24 03:52:38 +08:00
2013-10-11 01:33:42 +08:00
def embedded_chat_quicklaunch_params
2013-11-23 07:11:48 +08:00
{
user_id : @current_user . id ,
course_id : @context . id ,
canvas_url : " #{ HostUrl . protocol } :// #{ HostUrl . default_host } " ,
tool_consumer_instance_guid : @context . root_account . lti_guid
}
2013-10-11 01:33:42 +08:00
end
2013-09-12 03:47:28 +08:00
def embedded_chat_url
chat_tool = active_external_tool_by_id ( 'chat' )
2013-09-21 15:17:14 +08:00
return unless chat_tool && chat_tool . url && chat_tool . custom_fields [ 'mini_view_url' ]
2013-09-12 03:47:28 +08:00
uri = URI . parse ( chat_tool . url )
2013-09-21 15:17:14 +08:00
uri . path = chat_tool . custom_fields [ 'mini_view_url' ]
2013-09-12 03:47:28 +08:00
uri . to_s
end
def embedded_chat_enabled
chat_tool = active_external_tool_by_id ( 'chat' )
2013-09-21 15:17:14 +08:00
chat_tool && chat_tool . url && chat_tool . custom_fields [ 'mini_view_url' ] && Canvas :: Plugin . value_to_boolean ( chat_tool . custom_fields [ 'embedded_chat_enabled' ] )
2013-09-12 03:47:28 +08:00
end
2013-09-06 15:05:47 +08:00
def embedded_chat_visible
@show_embedded_chat != false &&
! @embedded_view &&
! @body_class_no_headers &&
2013-10-01 00:43:12 +08:00
@current_user &&
2013-09-06 15:05:47 +08:00
@context . is_a? ( Course ) &&
2013-09-12 03:47:28 +08:00
embedded_chat_enabled &&
2013-09-06 15:05:47 +08:00
external_tool_tab_visible ( 'chat' )
end
2013-09-12 03:47:28 +08:00
def active_external_tool_by_id ( tool_id )
# don't use for groups. they don't have account_chain_ids
2013-09-06 15:05:47 +08:00
tool = @context . context_external_tools . active . find_by_tool_id ( tool_id )
2013-09-12 03:47:28 +08:00
return tool if tool
# account_chain_ids is in the order we need to search for tools
# unfortunately, the db will return an arbitrary one first.
# so, we pull all the tools (probably will only have one anyway) and look through them here
tools = ContextExternalTool . active . where ( :context_type = > 'Account' , :context_id = > @context . account_chain_ids , :tool_id = > tool_id ) . all
@context . account_chain_ids . each do | account_id |
tool = tools . find { | t | t . context_id == account_id }
return tool if tool
end
nil
end
def external_tool_tab_visible ( tool_id )
tool = active_external_tool_by_id ( tool_id )
2013-09-06 15:05:47 +08:00
return false unless tool
2013-10-01 00:43:12 +08:00
@context . tabs_available ( @current_user ) . find { | tc | tc [ :id ] == tool . asset_string } . present?
2013-09-06 15:05:47 +08:00
end
2011-02-01 09:57:29 +08:00
def license_help_link
@include_license_dialog = true
link_to ( image_tag ( 'help.png' ) , '#' , :class = > 'license_help_link no-hover' , :title = > " Help with content licensing " )
end
2011-11-24 03:52:38 +08:00
2011-02-01 09:57:29 +08:00
def equella_enabled?
2011-02-03 05:30:07 +08:00
@equella_settings || = @context . equella_settings if @context . respond_to? ( :equella_settings )
2011-03-19 22:08:21 +08:00
@equella_settings || = @domain_root_account . try ( :equella_settings )
2011-02-01 09:57:29 +08:00
! ! @equella_settings
end
def show_user_create_course_button ( user )
2011-08-17 02:12:48 +08:00
@domain_root_account . manually_created_courses_account . grants_rights? ( user , session , :create_courses , :manage_courses ) . values . any?
2011-02-01 09:57:29 +08:00
end
2011-08-10 06:09:17 +08:00
2012-08-01 03:31:39 +08:00
# Public: Create HTML for a sidebar button w/ icon.
#
# url - The url the button should link to.
# img - The path to an image (e.g. 'icon.png')
# label - The text to display on the button (should already be internationalized).
#
# Returns an HTML string.
def sidebar_button ( url , label , img = nil )
2012-11-09 13:59:16 +08:00
link_to ( url , :class = > 'btn button-sidebar-wide' ) do
2012-11-29 05:23:14 +08:00
img ? ( " <i class='icon- " + img + " '></i> " ) . html_safe + label : label
2012-08-01 03:31:39 +08:00
end
end
2011-02-01 09:57:29 +08:00
def hash_get ( hash , key , default = nil )
if hash
if hash [ key . to_s ] != nil
hash [ key . to_s ]
elsif hash [ key . to_sym ] != nil
hash [ key . to_sym ]
else
default
end
else
default
end
end
2011-11-24 03:52:38 +08:00
2011-02-01 09:57:29 +08:00
def safe_cache_key ( * args )
key = args . cache_key
if key . length > 200
key = Digest :: MD5 . hexdigest ( key )
end
key
end
2011-11-24 03:52:38 +08:00
2011-02-01 09:57:29 +08:00
def inst_env
2011-03-09 03:12:38 +08:00
global_inst_object = { :environment = > Rails . env }
{
2011-08-20 03:53:49 +08:00
:allowMediaComments = > Kaltura :: ClientV3 . config && @context . try_rescue ( :allow_media_comments? ) ,
2014-01-28 00:56:14 +08:00
:kalturaSettings = > Kaltura :: ClientV3 . config . try ( :slice , 'domain' , 'resource_domain' , 'rtmp_domain' , 'partner_id' , 'subpartner_id' , 'player_ui_conf' , 'player_cache_st' , 'kcw_ui_conf' , 'upload_ui_conf' , 'max_file_size_bytes' , 'do_analytics' , 'do_flash_var_test' ) ,
2011-08-20 03:53:49 +08:00
:equellaEnabled = > ! ! equella_enabled? ,
2013-10-05 04:02:49 +08:00
:googleAnalyticsAccount = > Setting . get ( 'google_analytics_key' , nil ) ,
2011-08-20 03:53:49 +08:00
:http_status = > @status ,
:error_id = > @error && @error . id ,
2011-11-24 03:52:38 +08:00
:disableGooglePreviews = > ! service_enabled? ( :google_docs_previews ) ,
2011-08-20 03:53:49 +08:00
:disableScribdPreviews = > ! feature_enabled? ( :scribd ) ,
2012-08-21 07:41:42 +08:00
:disableCrocodocPreviews = > ! feature_enabled? ( :crocodoc ) ,
2013-09-06 01:29:07 +08:00
:enableScribdHtml5 = > feature_enabled? ( :scribd_html5 ) ,
2011-08-20 03:53:49 +08:00
:logPageViews = > ! @body_class_no_headers ,
:maxVisibleEditorButtons = > 3 ,
:editorButtons = > editor_buttons ,
2011-03-09 03:12:38 +08:00
} . each do | key , value |
# dont worry about keys that are nil or false because in javascript: if (INST.featureThatIsUndefined ) { //won't happen }
global_inst_object [ key ] = value if value
2011-02-01 09:57:29 +08:00
end
global_inst_object
end
2011-11-24 03:52:38 +08:00
2011-08-20 03:53:49 +08:00
def editor_buttons
tools = [ ]
contexts = [ ]
contexts << @context if @context && @context . respond_to? ( :context_external_tools )
contexts += @context . account_chain if @context . respond_to? ( :account_chain )
contexts << @domain_root_account if @domain_root_account
2013-06-28 04:25:50 +08:00
return [ ] if contexts . empty?
2011-08-20 03:53:49 +08:00
Rails . cache . fetch ( ( [ 'editor_buttons_for' ] + contexts . uniq ) . cache_key ) do
2013-03-20 00:41:22 +08:00
tools = ContextExternalTool . active . having_setting ( 'editor_button' ) . where ( contexts . map { | context | " (context_type=' #{ context . class . base_class . to_s } ' AND context_id= #{ context . id } ) " } . join ( " OR " ) )
2011-08-20 03:53:49 +08:00
tools . sort_by ( & :id ) . map do | tool |
{
:name = > tool . label_for ( :editor_button , nil ) ,
:id = > tool . id ,
2013-04-10 00:48:51 +08:00
:url = > tool . editor_button ( :url ) ,
:icon_url = > tool . editor_button ( :icon_url ) ,
:width = > tool . editor_button ( :selection_width ) ,
:height = > tool . editor_button ( :selection_height )
2011-08-20 03:53:49 +08:00
}
end
end
end
2011-02-01 09:57:29 +08:00
def nbsp
raw ( " " )
end
2011-02-16 06:59:44 +08:00
2013-01-16 05:14:29 +08:00
def dataify ( obj , * attributes )
hash = obj . respond_to? ( :to_hash ) && obj . to_hash
res = " "
if ! attributes . empty?
attributes . each do | attribute |
res << %Q{ data- #{ h attribute } =" #{ h ( hash ? hash [ attribute ] : obj . send ( attribute ) ) } " }
end
elsif hash
res << hash . map { | key , value | %Q{ data- #{ h key } =" #{ h value } " } } . join ( " " )
end
raw ( " #{ res } " )
end
def inline_media_comment_link ( comment = nil )
if comment && comment . media_comment_id
raw %Q{ <a href=" # " class="instructure_inline_media_comment no-underline" #{ dataify ( comment , :media_comment_id , :media_comment_type ) } > </a> }
end
end
2011-02-16 06:59:44 +08:00
# translate a URL intended for an iframe into an alternative URL, if one is
# avavailable. Right now the only supported translation is for youtube
# videos. Youtube video pages can no longer be embedded, but we can translate
# the URL into the player iframe data.
def iframe ( src , html_options = { } )
uri = URI . parse ( src ) rescue nil
if uri
query = Rack :: Utils . parse_query ( uri . query )
if uri . host == 'www.youtube.com' && uri . path == '/watch' && query [ 'v' ] . present?
2011-03-09 02:19:33 +08:00
src = " //www.youtube.com/embed/ #{ query [ 'v' ] } "
2011-02-16 06:59:44 +08:00
html_options . merge! ( { :title = > 'Youtube video player' , :width = > 640 , :height = > 480 , :frameborder = > 0 , :allowfullscreen = > 'allowfullscreen' } )
end
end
content_tag ( 'iframe' , '' , { :src = > src } . merge ( html_options ) )
end
2011-04-22 01:23:00 +08:00
# returns a time object at 00:00:00 tomorrow
def tomorrow_at_midnight
2011-09-23 09:36:56 +08:00
1 . day . from_now . midnight
2011-04-22 01:23:00 +08:00
end
2011-11-24 03:52:38 +08:00
2011-05-19 12:49:12 +08:00
# you should supply :all_folders to avoid a db lookup on every iteration
def folders_as_options ( folders , opts = { } )
opts [ :indent_width ] || = 3
opts [ :depth ] || = 0
opts [ :options_so_far ] || = [ ]
2013-09-26 23:00:42 +08:00
if opts . has_key? ( :all_folders )
opts [ :sub_folders ] = opts . delete ( :all_folders ) . to_a . group_by { | f | f . parent_folder_id }
end
2011-05-19 06:58:19 +08:00
folders . each do | folder |
2011-05-19 12:49:12 +08:00
opts [ :options_so_far ] << %{ <option value=" #{ folder . id } " #{ 'selected' if opts [ :selected_folder_id ] == folder . id } > #{ " " * opts [ :indent_width ] * opts [ :depth ] } #{ " - " if opts [ :depth ] > 0 } #{ html_escape folder . name } </option> }
if opts [ :max_depth ] . nil? || opts [ :depth ] < opts [ :max_depth ]
2013-09-26 23:00:42 +08:00
child_folders = if opts [ :sub_folders ]
opts [ :sub_folders ] [ folder . id ] || [ ]
2013-06-14 03:07:04 +08:00
else
folder . active_sub_folders . by_position
end
2013-09-26 23:00:42 +08:00
folders_as_options ( child_folders , opts . merge ( { :depth = > opts [ :depth ] + 1 } ) ) if child_folders . any?
2011-05-19 12:49:12 +08:00
end
2011-05-19 06:58:19 +08:00
end
2011-05-19 12:49:12 +08:00
opts [ :depth ] == 0 ? raw ( opts [ :options_so_far ] . join ( " \n " ) ) : nil
2011-05-19 06:58:19 +08:00
end
2011-06-15 00:55:12 +08:00
# this little helper just allows you to do <% ot(...) %> and have it output the same as <%= t(...) %>. The upside though, is you can interpolate whole blocks of HTML, like:
# <% ot 'some_key', 'For %{a} select %{b}', :a => capture { %>
# <div>...</div>
# <% }, :b => capture { %>
# <select>...</select>
# <% } %>
def ot ( * args )
concat ( t ( * args ) )
end
2011-06-28 07:53:07 +08:00
2011-11-11 00:31:45 +08:00
def jt ( key , default , js_options = '{}' )
2012-09-27 04:00:47 +08:00
full_key = key =~ / \ A # / ? key . sub ( / \ A # / , '' ) : i18n_scope + '.' + key
2011-11-11 00:31:45 +08:00
translated_default = I18n . backend . send ( :lookup , I18n . locale , full_key ) || default # string or hash
raw " I18n.scoped( #{ i18n_scope . to_json } ).t( #{ key . to_json } , #{ translated_default . to_json } , #{ js_options } ) "
end
2011-06-28 07:53:07 +08:00
def join_title ( * parts )
parts . join ( t ( '#title_separator' , ': ' ) )
end
2011-08-05 00:28:36 +08:00
def cache ( name = { } , options = nil , & block )
unless options && options [ :no_locale ]
name = name . cache_key if name . respond_to? ( :cache_key )
name = name + " / #{ I18n . locale } " if name . is_a? ( String )
end
super
end
2011-09-27 03:08:41 +08:00
2011-09-28 04:06:52 +08:00
def map_courses_for_menu ( courses )
mapped = courses . map do | course |
2012-04-06 03:23:56 +08:00
term = course . enrollment_term . name if ! course . enrollment_term . default_term?
2011-11-01 03:42:10 +08:00
subtitle = ( course . primary_enrollment_state == 'invited' ?
before_label ( '#shared.menu_enrollment.labels.invited_as' , 'Invited as' ) :
before_label ( '#shared.menu_enrollment.labels.enrolled_as' , " Enrolled as " )
) + " " + Enrollment . readable_type ( course . primary_enrollment )
2011-09-28 04:06:52 +08:00
{
:longName = > " #{ course . name } - #{ course . short_name } " ,
:shortName = > course . name ,
2013-07-26 06:05:18 +08:00
:courseCode = > course . course_code ,
2011-11-08 03:10:20 +08:00
:href = > course_path ( course , :invitation = > course . read_attribute ( :invitation ) ) ,
2011-09-28 04:06:52 +08:00
:term = > term || nil ,
:subtitle = > subtitle ,
:id = > course . id
}
end
mapped
end
def menu_courses_locals
courses = @current_user . menu_courses
2011-11-24 03:52:38 +08:00
all_courses_count = @current_user . courses_with_primary_enrollment . size
2011-09-27 03:08:41 +08:00
{
2011-09-28 04:06:52 +08:00
:collection = > map_courses_for_menu ( courses ) ,
:collection_size = > all_courses_count ,
2011-09-27 03:08:41 +08:00
:more_link_for_over_max = > courses_path ,
2011-09-28 04:06:52 +08:00
:title = > t ( '#menu.my_courses' , " My Courses " ) ,
2014-01-24 08:37:46 +08:00
:link_text = > t ( '#layouts.menu.view_all_enrollments' , 'View all courses' ) ,
2012-01-28 11:59:18 +08:00
:edit = > t ( " # menu.customize " , " Customize " )
2011-09-27 03:08:41 +08:00
}
end
def menu_groups_locals
{
2011-09-29 06:27:13 +08:00
:collection = > @current_user . menu_data [ :group_memberships ] ,
:collection_size = > @current_user . menu_data [ :group_memberships_count ] ,
2011-09-27 03:08:41 +08:00
:partial = > " shared/menu_group_membership " ,
:max_to_show = > 8 ,
:more_link_for_over_max = > groups_path ,
:title = > t ( '#menu.current_groups' , " Current Groups " ) ,
2014-01-24 08:37:46 +08:00
:link_text = > t ( '#layouts.menu.view_all_groups' , 'View all groups' )
2011-09-27 03:08:41 +08:00
}
end
def menu_accounts_locals
{
2011-09-29 06:27:13 +08:00
:collection = > @current_user . menu_data [ :accounts ] ,
:collection_size = > @current_user . menu_data [ :accounts_count ] ,
2011-09-27 03:08:41 +08:00
:partial = > " shared/menu_account " ,
:max_to_show = > 8 ,
:more_link_for_over_max = > accounts_path ,
:title = > t ( '#menu.managed_accounts' , " Managed Accounts " ) ,
2014-01-24 08:37:46 +08:00
:link_text = > t ( '#layouts.menu.view_all_accounts' , 'View all accounts' )
2011-09-27 03:08:41 +08:00
}
end
def show_home_menu?
@current_user . set_menu_data ( session [ :enrollment_uuid ] )
[
2011-11-08 03:10:20 +08:00
@current_user . menu_courses ( session [ :enrollment_uuid ] ) ,
2013-11-01 00:42:26 +08:00
@current_user . all_accounts ,
2011-09-27 03:08:41 +08:00
@current_user . cached_current_group_memberships ,
@current_user . enrollments . ended
] . any? { | e | e . respond_to? ( :count ) && e . count > 0 }
end
2011-10-22 06:36:39 +08:00
def cache_if ( cond , * args )
if cond
cache ( * args ) { yield }
else
yield
end
end
2011-11-11 10:43:36 +08:00
def help_link
url = ( ( @domain_root_account && @domain_root_account . settings [ :support_url ] ) || ( Account . default && Account . default . settings [ :support_url ] ) )
2013-10-05 04:02:49 +08:00
show_feedback_link = Setting . get ( " show_feedback_link " , " false " ) == " true "
2011-11-11 10:43:36 +08:00
css_classes = [ ]
css_classes << " support_url " if url
css_classes << " help_dialog_trigger " if show_feedback_link
if url || show_feedback_link
2012-09-18 05:21:08 +08:00
link_to t ( '#links.help' , " Help " ) , url || '#' ,
:class = > css_classes . join ( " " ) ,
'data-track-category' = > " help system " ,
'data-track-label' = > 'help button'
2011-11-11 10:43:36 +08:00
end
end
sub-account branding; closes #9368
allow sub accounts to include their own global scripts and stylesheets. if
global includes are enabled on the root account, root account administrators
will have an option to enable them for immediate child accounts. those child
accounts can then choose to enable them for their sub-accounts, and so on down
the chain.
these includes are added to the page in order from highest to lowest account,
so sub-accounts are able to override styles added by their parents.
the logic for which styles to display on which pages is as follows:
- on account pages, include all styles in the chain from this account up to the
root account. this ensures that you can always see styles for account
X without any sub-account overrides on account X's page
- on course/group pages, include all styles in the chain from the account which
contains that course/group up to the root
- on the dashboard, calendar, user pages, and other pages that don't fall into
one of the above categories, we find the lowest account that contains all of
the current user's active classes + groups, and include styles from that
account up to the root
test plan:
- in a root account, create two sub-accounts, create courses in each of them,
and create 3 users, one enrolled only in the first course, one only in the
second course, and one enrolled in both courses.
- enable global includes on the root account (no sub-accounts yet) add files,
and make sure all three students see them.
- now enable sub-account includes, and add include files to each sub-account
- make sure both users in course 1 see include for sub-account 1
- make sure user 1 sees include for sub-account 1 on her dashboard, but user
3 does not.
Change-Id: I3d07d4bced39593f3084d5eac6ea3137666e319b
Reviewed-on: https://gerrit.instructure.com/12248
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
2012-07-10 05:30:16 +08:00
def account_context ( context )
if context . is_a? ( Account )
context
elsif context . is_a? ( Course ) || context . is_a? ( CourseSection )
account_context ( context . account )
elsif context . is_a? ( Group )
account_context ( context . context )
end
end
2012-08-19 02:37:31 +08:00
def get_global_includes
2012-08-19 07:36:51 +08:00
return @global_includes if defined? ( @global_includes )
2013-09-28 11:53:02 +08:00
@global_includes = [ ]
if @current_user && @current_user . enabled_theme != " default "
@global_includes << { :css = > " compiled/ #{ @current_user . enabled_theme } " }
end
@global_includes << Account . site_admin . global_includes_hash
2012-08-19 02:37:31 +08:00
@global_includes << @domain_root_account . global_includes_hash if @domain_root_account . present?
sub-account branding; closes #9368
allow sub accounts to include their own global scripts and stylesheets. if
global includes are enabled on the root account, root account administrators
will have an option to enable them for immediate child accounts. those child
accounts can then choose to enable them for their sub-accounts, and so on down
the chain.
these includes are added to the page in order from highest to lowest account,
so sub-accounts are able to override styles added by their parents.
the logic for which styles to display on which pages is as follows:
- on account pages, include all styles in the chain from this account up to the
root account. this ensures that you can always see styles for account
X without any sub-account overrides on account X's page
- on course/group pages, include all styles in the chain from the account which
contains that course/group up to the root
- on the dashboard, calendar, user pages, and other pages that don't fall into
one of the above categories, we find the lowest account that contains all of
the current user's active classes + groups, and include styles from that
account up to the root
test plan:
- in a root account, create two sub-accounts, create courses in each of them,
and create 3 users, one enrolled only in the first course, one only in the
second course, and one enrolled in both courses.
- enable global includes on the root account (no sub-accounts yet) add files,
and make sure all three students see them.
- now enable sub-account includes, and add include files to each sub-account
- make sure both users in course 1 see include for sub-account 1
- make sure user 1 sees include for sub-account 1 on her dashboard, but user
3 does not.
Change-Id: I3d07d4bced39593f3084d5eac6ea3137666e319b
Reviewed-on: https://gerrit.instructure.com/12248
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
2012-07-10 05:30:16 +08:00
if @domain_root_account . try ( :sub_account_includes? )
# get the deepest account to start looking for branding
2012-08-19 02:37:31 +08:00
if acct = account_context ( @context )
key = [ acct . id , 'account_context_global_includes' ] . cache_key
2012-08-19 07:36:51 +08:00
includes = Rails . cache . fetch ( key , :expires_in = > 15 . minutes ) do
2012-08-19 02:37:31 +08:00
acct . account_chain . reverse . map ( & :global_includes_hash )
end
@global_includes . concat ( includes )
elsif @current_user . present?
key = [ @domain_root_account . id , 'common_account_global_includes' , @current_user . id ] . cache_key
2012-08-19 07:36:51 +08:00
includes = Rails . cache . fetch ( key , :expires_in = > 15 . minutes ) do
2012-08-19 02:37:31 +08:00
@current_user . common_account_chain ( @domain_root_account ) . map ( & :global_includes_hash )
end
@global_includes . concat ( includes )
end
sub-account branding; closes #9368
allow sub accounts to include their own global scripts and stylesheets. if
global includes are enabled on the root account, root account administrators
will have an option to enable them for immediate child accounts. those child
accounts can then choose to enable them for their sub-accounts, and so on down
the chain.
these includes are added to the page in order from highest to lowest account,
so sub-accounts are able to override styles added by their parents.
the logic for which styles to display on which pages is as follows:
- on account pages, include all styles in the chain from this account up to the
root account. this ensures that you can always see styles for account
X without any sub-account overrides on account X's page
- on course/group pages, include all styles in the chain from the account which
contains that course/group up to the root
- on the dashboard, calendar, user pages, and other pages that don't fall into
one of the above categories, we find the lowest account that contains all of
the current user's active classes + groups, and include styles from that
account up to the root
test plan:
- in a root account, create two sub-accounts, create courses in each of them,
and create 3 users, one enrolled only in the first course, one only in the
second course, and one enrolled in both courses.
- enable global includes on the root account (no sub-accounts yet) add files,
and make sure all three students see them.
- now enable sub-account includes, and add include files to each sub-account
- make sure both users in course 1 see include for sub-account 1
- make sure user 1 sees include for sub-account 1 on her dashboard, but user
3 does not.
Change-Id: I3d07d4bced39593f3084d5eac6ea3137666e319b
Reviewed-on: https://gerrit.instructure.com/12248
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
2012-07-10 05:30:16 +08:00
end
2012-08-19 02:37:31 +08:00
@global_includes . uniq!
@global_includes . compact!
@global_includes
sub-account branding; closes #9368
allow sub accounts to include their own global scripts and stylesheets. if
global includes are enabled on the root account, root account administrators
will have an option to enable them for immediate child accounts. those child
accounts can then choose to enable them for their sub-accounts, and so on down
the chain.
these includes are added to the page in order from highest to lowest account,
so sub-accounts are able to override styles added by their parents.
the logic for which styles to display on which pages is as follows:
- on account pages, include all styles in the chain from this account up to the
root account. this ensures that you can always see styles for account
X without any sub-account overrides on account X's page
- on course/group pages, include all styles in the chain from the account which
contains that course/group up to the root
- on the dashboard, calendar, user pages, and other pages that don't fall into
one of the above categories, we find the lowest account that contains all of
the current user's active classes + groups, and include styles from that
account up to the root
test plan:
- in a root account, create two sub-accounts, create courses in each of them,
and create 3 users, one enrolled only in the first course, one only in the
second course, and one enrolled in both courses.
- enable global includes on the root account (no sub-accounts yet) add files,
and make sure all three students see them.
- now enable sub-account includes, and add include files to each sub-account
- make sure both users in course 1 see include for sub-account 1
- make sure user 1 sees include for sub-account 1 on her dashboard, but user
3 does not.
Change-Id: I3d07d4bced39593f3084d5eac6ea3137666e319b
Reviewed-on: https://gerrit.instructure.com/12248
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
2012-07-10 05:30:16 +08:00
end
2013-09-17 04:27:27 +08:00
def include_account_js ( options = { } )
2013-03-26 02:56:40 +08:00
return if params [ :global_includes ] == '0'
2013-09-17 04:27:27 +08:00
includes = get_global_includes . map do | global_include |
global_include [ :js ] if global_include [ :js ] . present?
2012-02-09 06:31:45 +08:00
end
2013-09-17 04:27:27 +08:00
includes . compact!
2012-02-09 06:31:45 +08:00
if includes . length > 0
2013-09-17 04:27:27 +08:00
if options [ :raw ]
includes . unshift ( " /optimized/vendor/jquery-1.7.2.js " )
javascript_include_tag ( includes )
else
str = <<-ENDSCRIPT
( function ( ) {
var inject = function ( src ) {
var s = document . createElement ( 'script' ) ;
s . src = src ;
s . type = 'text/javascript' ;
document . body . appendChild ( s ) ;
} ;
var srcs = #{includes.to_json};
require ( [ 'jquery' ] , function ( ) {
for ( var i = 0 , l = srcs . length ; i < l ; i + + ) {
inject ( srcs [ i ] ) ;
}
} ) ;
} ) ( ) ;
ENDSCRIPT
javascript_tag ( str )
end
2012-02-09 06:31:45 +08:00
end
end
2012-02-02 04:39:03 +08:00
sub-account branding; closes #9368
allow sub accounts to include their own global scripts and stylesheets. if
global includes are enabled on the root account, root account administrators
will have an option to enable them for immediate child accounts. those child
accounts can then choose to enable them for their sub-accounts, and so on down
the chain.
these includes are added to the page in order from highest to lowest account,
so sub-accounts are able to override styles added by their parents.
the logic for which styles to display on which pages is as follows:
- on account pages, include all styles in the chain from this account up to the
root account. this ensures that you can always see styles for account
X without any sub-account overrides on account X's page
- on course/group pages, include all styles in the chain from the account which
contains that course/group up to the root
- on the dashboard, calendar, user pages, and other pages that don't fall into
one of the above categories, we find the lowest account that contains all of
the current user's active classes + groups, and include styles from that
account up to the root
test plan:
- in a root account, create two sub-accounts, create courses in each of them,
and create 3 users, one enrolled only in the first course, one only in the
second course, and one enrolled in both courses.
- enable global includes on the root account (no sub-accounts yet) add files,
and make sure all three students see them.
- now enable sub-account includes, and add include files to each sub-account
- make sure both users in course 1 see include for sub-account 1
- make sure user 1 sees include for sub-account 1 on her dashboard, but user
3 does not.
Change-Id: I3d07d4bced39593f3084d5eac6ea3137666e319b
Reviewed-on: https://gerrit.instructure.com/12248
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
2012-07-10 05:30:16 +08:00
def include_account_css
2013-03-26 02:56:40 +08:00
return if params [ :global_includes ] == '0'
2012-08-19 02:37:31 +08:00
includes = get_global_includes . inject ( [ ] ) do | css_includes , global_include |
css_includes << global_include [ :css ] if global_include [ :css ] . present?
sub-account branding; closes #9368
allow sub accounts to include their own global scripts and stylesheets. if
global includes are enabled on the root account, root account administrators
will have an option to enable them for immediate child accounts. those child
accounts can then choose to enable them for their sub-accounts, and so on down
the chain.
these includes are added to the page in order from highest to lowest account,
so sub-accounts are able to override styles added by their parents.
the logic for which styles to display on which pages is as follows:
- on account pages, include all styles in the chain from this account up to the
root account. this ensures that you can always see styles for account
X without any sub-account overrides on account X's page
- on course/group pages, include all styles in the chain from the account which
contains that course/group up to the root
- on the dashboard, calendar, user pages, and other pages that don't fall into
one of the above categories, we find the lowest account that contains all of
the current user's active classes + groups, and include styles from that
account up to the root
test plan:
- in a root account, create two sub-accounts, create courses in each of them,
and create 3 users, one enrolled only in the first course, one only in the
second course, and one enrolled in both courses.
- enable global includes on the root account (no sub-accounts yet) add files,
and make sure all three students see them.
- now enable sub-account includes, and add include files to each sub-account
- make sure both users in course 1 see include for sub-account 1
- make sure user 1 sees include for sub-account 1 on her dashboard, but user
3 does not.
Change-Id: I3d07d4bced39593f3084d5eac6ea3137666e319b
Reviewed-on: https://gerrit.instructure.com/12248
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
2012-07-10 05:30:16 +08:00
css_includes
end
2012-08-19 02:37:31 +08:00
if includes . length > 0
includes << { :media = > 'all' }
stylesheet_link_tag * includes
end
sub-account branding; closes #9368
allow sub accounts to include their own global scripts and stylesheets. if
global includes are enabled on the root account, root account administrators
will have an option to enable them for immediate child accounts. those child
accounts can then choose to enable them for their sub-accounts, and so on down
the chain.
these includes are added to the page in order from highest to lowest account,
so sub-accounts are able to override styles added by their parents.
the logic for which styles to display on which pages is as follows:
- on account pages, include all styles in the chain from this account up to the
root account. this ensures that you can always see styles for account
X without any sub-account overrides on account X's page
- on course/group pages, include all styles in the chain from the account which
contains that course/group up to the root
- on the dashboard, calendar, user pages, and other pages that don't fall into
one of the above categories, we find the lowest account that contains all of
the current user's active classes + groups, and include styles from that
account up to the root
test plan:
- in a root account, create two sub-accounts, create courses in each of them,
and create 3 users, one enrolled only in the first course, one only in the
second course, and one enrolled in both courses.
- enable global includes on the root account (no sub-accounts yet) add files,
and make sure all three students see them.
- now enable sub-account includes, and add include files to each sub-account
- make sure both users in course 1 see include for sub-account 1
- make sure user 1 sees include for sub-account 1 on her dashboard, but user
3 does not.
Change-Id: I3d07d4bced39593f3084d5eac6ea3137666e319b
Reviewed-on: https://gerrit.instructure.com/12248
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
2012-07-10 05:30:16 +08:00
end
2012-03-24 04:47:02 +08:00
# this should be the same as friendlyDatetime in handlebars_helpers.coffee
def friendly_datetime ( datetime , opts = { } )
attributes = { :title = > datetime }
attributes [ :pubdate ] = true if opts [ :pubdate ]
2013-10-09 03:21:53 +08:00
if CANVAS_RAILS2 # see config/initializers/rails2.rb
content_tag_without_nil_return ( :time , attributes ) do
datetime_string ( datetime )
end
else
content_tag ( :time , attributes ) do
datetime_string ( datetime )
end
2012-03-24 04:47:02 +08:00
end
end
2013-03-20 03:25:59 +08:00
# render a link with a tooltip containing a summary of due dates
def multiple_due_date_tooltip ( assignment , user , opts = { } )
user || = @current_user
presenter = OverrideTooltipPresenter . new ( assignment , user , opts )
render 'shared/vdd_tooltip' , :presenter = > presenter
end
2012-02-02 04:39:03 +08:00
require 'digest'
# create a checksum of an array of objects' cache_key values.
# useful if we have a whole collection of objects that we want to turn into a
# cache key, so that we don't make an overly-long cache key.
# if you can avoid loading the list at all, that's even better, of course.
def collection_cache_key ( collection )
keys = collection . map { | element | element . cache_key }
Digest :: MD5 . hexdigest ( keys . join ( '/' ) )
end
2012-08-17 12:56:06 +08:00
2012-11-20 06:21:52 +08:00
def translated_due_date ( assignment )
2012-12-29 05:02:21 +08:00
if assignment . multiple_due_dates_apply_to? ( @current_user )
2012-11-20 06:21:52 +08:00
t ( '#due_dates.multiple_due_dates' , 'due: Multiple Due Dates' )
else
2012-12-29 05:02:21 +08:00
assignment = assignment . overridden_for ( @current_user )
if assignment . due_at
t ( '#due_dates.due_at' , 'due: %{assignment_due_date_time}' , {
:assignment_due_date_time = > datetime_string ( force_zone ( assignment . due_at ) )
} )
else
t ( '#due_dates.no_due_date' , 'due: No Due Date' )
end
2012-11-20 06:21:52 +08:00
end
end
2012-08-17 12:56:06 +08:00
def add_uri_scheme_name ( uri )
noSchemeName = ! uri . match ( / ^(.+): \/ \/ (.+) / )
uri = 'http://' + uri if noSchemeName
uri
end
2012-12-21 08:33:47 +08:00
def agree_to_terms
# may be overridden by a plugin
@agree_to_terms ||
2013-07-31 03:50:49 +08:00
t ( " # user.registration.agree_to_terms_and_privacy_policy " ,
" You agree to the *terms of use* and acknowledge the **privacy policy**. " ,
wrapper : {
'*' = > link_to ( '\1' , @domain_root_account . terms_of_use_url , target : '_blank' ) ,
'**' = > link_to ( '\1' , @domain_root_account . privacy_policy_url , target : '_blank' )
}
)
2012-12-21 08:33:47 +08:00
end
2011-02-01 09:57:29 +08:00
end