canvas-lms/app/helpers/application_helper.rb

860 lines
31 KiB
Ruby
Raw Normal View History

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
include HtmlTextHelper
include LocaleSelection
include Canvas::LockExplanation
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)
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-02-01 09:57:29 +08:00
def keyboard_navigation(keys)
AMD Conversion "Trivial" JavaScript / CoffeeScript changes -------------------------------------------------- For the most part, all javascript was simply wrapped in `require` or `define`. The dependencies were found with a script that matched regexes in the files, it errs on the side of listing too many dependencies, so its worth double checking each file's dependencies (over time, anyway). i18n API changes -------------------------------------------------- No longer have to do I18n.scoped calls, just list i18n as a dependency with the scope and it's imported already scoped require ['i18n!some_scope'], (I18n) -> I18n.t 'im_scoped', 'I'm scoped!' JS bundling now done with r.js, not Jammit -------------------------------------------------- We don't use jammit to bundle JS anymore. Simply list dependencies for your JS modules in the file and RequireJS handles the rest. To optimize the JavaScript, first make sure you have node.js 0.4.12+ installed and then run: $ rake js:build The app defaults to the optimized build in production. You can use non-optimized in production by putting ?debug_assets=true in the url just like before. You can also test the optimized JavaScript in development with ?optimized_js=true. Significant changes -------------------------------------------------- These files have "real" changes to them (unlike the JavaScript that is simply wrapped in require and define). Worth taking a really close look at: - app/helpers/application_helper.rb - app/views/layouts/application.html.erb - config/assets.yml - config/build.js - lib/handlebars/handlebars.rb - lib/i18n_extraction/js_extractor.rb - lib/tasks/canvas.rake - lib/tasks/i18n.rake - lib/tasks/js.rake Change-Id: I4bc5ecb1231f331aaded0fef2bcc1f3a9fe482a7 Reviewed-on: https://gerrit.instructure.com/6986 Tested-by: Hudson <hudson@instructure.com> Reviewed-by: Ryan Florence <ryanf@instructure.com>
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"
content += " <span class='keycode'>#{h(hash[:key])}</span>\n"
2011-02-01 09:57:29 +08:00
content += " <span class='colon'>:</span>\n"
content += " <span class='description'>#{h(hash[:description])}</span>\n"
2011-02-01 09:57:29 +08:00
content += " </li>\n"
end
content += "</ul>"
content_for(:keyboard_navigation) { raw(content) }
2011-02-01 09:57:29 +08:00
end
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-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-02-01 09:57:29 +08:00
def slugify(text="")
text.gsub(/[^\w]/, "_").downcase
end
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 ||= {}
context_name = url_helper_context_from_object(context)
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
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
def full_url(path)
uri = URI.parse(request.url)
uri.path = ''
uri.query = ''
URI.join(uri, path).to_s
end
def url_helper_context_from_object(context)
(context ? context.class.base_class : context.class).name.underscore
end
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?
end
2011-02-01 09:57:29 +08:00
def hidden(include_style=false)
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.
def can_do(object, user, *actions)
2011-02-01 09:57:29 +08:00
return false unless object
object.grants_any_right?(user, session, *actions)
2011-02-01 09:57:29 +08:00
end
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 = {}
includes = [:active_assignments, :active_discussion_topics, :active_quizzes, :active_context_modules]
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] ||= [] }
@wiki_sidebar_data[:wiki_pages] = @context.wiki.wiki_pages.active.order(:title).limit(150) if @context.respond_to?(:wiki)
@wiki_sidebar_data[:wiki_pages] ||= []
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
# 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.
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-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 = ""
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]
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
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
hidden_dialogs[id] = capture(&block)
end
def hidden_dialogs; @hidden_dialogs ||= {}; end
def render_hidden_dialogs
output = hidden_dialogs.keys.sort.inject('') do |str, id|
str << "<div id='#{id}' style='display: none;''>" << hidden_dialogs[id] << "</div>"
end
raw(output)
end
class << self
attr_accessor :cached_translation_blocks
end
def include_js_translations?
!!(params[:include_js_translations] || use_optimized_js?)
end
# See `js_base_url`
def use_optimized_js?
if ENV['USE_OPTIMIZED_JS'] == 'true'
# 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])
else
# allows overriding by adding ?optimized_js=1 to the url
params[:optimized_js] || false
end
end
AMD Conversion "Trivial" JavaScript / CoffeeScript changes -------------------------------------------------- For the most part, all javascript was simply wrapped in `require` or `define`. The dependencies were found with a script that matched regexes in the files, it errs on the side of listing too many dependencies, so its worth double checking each file's dependencies (over time, anyway). i18n API changes -------------------------------------------------- No longer have to do I18n.scoped calls, just list i18n as a dependency with the scope and it's imported already scoped require ['i18n!some_scope'], (I18n) -> I18n.t 'im_scoped', 'I'm scoped!' JS bundling now done with r.js, not Jammit -------------------------------------------------- We don't use jammit to bundle JS anymore. Simply list dependencies for your JS modules in the file and RequireJS handles the rest. To optimize the JavaScript, first make sure you have node.js 0.4.12+ installed and then run: $ rake js:build The app defaults to the optimized build in production. You can use non-optimized in production by putting ?debug_assets=true in the url just like before. You can also test the optimized JavaScript in development with ?optimized_js=true. Significant changes -------------------------------------------------- These files have "real" changes to them (unlike the JavaScript that is simply wrapped in require and define). Worth taking a really close look at: - app/helpers/application_helper.rb - app/views/layouts/application.html.erb - config/assets.yml - config/build.js - lib/handlebars/handlebars.rb - lib/i18n_extraction/js_extractor.rb - lib/tasks/canvas.rake - lib/tasks/i18n.rake - lib/tasks/js.rake Change-Id: I4bc5ecb1231f331aaded0fef2bcc1f3a9fe482a7 Reviewed-on: https://gerrit.instructure.com/6986 Tested-by: Hudson <hudson@instructure.com> Reviewed-by: Ryan Florence <ryanf@instructure.com>
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
use_optimized_js? ? '/optimized' : '/javascripts'
AMD Conversion "Trivial" JavaScript / CoffeeScript changes -------------------------------------------------- For the most part, all javascript was simply wrapped in `require` or `define`. The dependencies were found with a script that matched regexes in the files, it errs on the side of listing too many dependencies, so its worth double checking each file's dependencies (over time, anyway). i18n API changes -------------------------------------------------- No longer have to do I18n.scoped calls, just list i18n as a dependency with the scope and it's imported already scoped require ['i18n!some_scope'], (I18n) -> I18n.t 'im_scoped', 'I'm scoped!' JS bundling now done with r.js, not Jammit -------------------------------------------------- We don't use jammit to bundle JS anymore. Simply list dependencies for your JS modules in the file and RequireJS handles the rest. To optimize the JavaScript, first make sure you have node.js 0.4.12+ installed and then run: $ rake js:build The app defaults to the optimized build in production. You can use non-optimized in production by putting ?debug_assets=true in the url just like before. You can also test the optimized JavaScript in development with ?optimized_js=true. Significant changes -------------------------------------------------- These files have "real" changes to them (unlike the JavaScript that is simply wrapped in require and define). Worth taking a really close look at: - app/helpers/application_helper.rb - app/views/layouts/application.html.erb - config/assets.yml - config/build.js - lib/handlebars/handlebars.rb - lib/i18n_extraction/js_extractor.rb - lib/tasks/canvas.rake - lib/tasks/i18n.rake - lib/tasks/js.rake Change-Id: I4bc5ecb1231f331aaded0fef2bcc1f3a9fe482a7 Reviewed-on: https://gerrit.instructure.com/6986 Tested-by: Hudson <hudson@instructure.com> Reviewed-by: Ryan Florence <ryanf@instructure.com>
2011-11-11 00:31:45 +08:00
end
# Returns a <script> tag for each registered js_bundle
def include_js_bundles
js bundle extensions for plugins, bugfixes and DRYification allows plugins to have a bundle get auto-loaded with a corresponding canvas bundle. uses require.js' include mechanism in build.js (when optimized) and a rails helper tweak (when not optimized). this happens automatically based on the path, e.g. the foo plugin's "bundles/extensions/bar" will get automagically included whenever the regular "bar" bundle is required. removes the need for a plugin-level build.js, and auto-generates bundle module definitions in canvas' build.js (via erb). this handles all regular bundles both from canvas and plugins. also fixes plugins so that bundle dependencies get optimized. plugin paths are created automatically, so this means we can remove things like this from plugin bundles and specs: require.config paths: myplugin: "/plugins/myplugin/javascripts" test plan: 1. use canvas in development mode, it should work 2. use canvas in optimized JS mode, it should work i. confirm that all scripts are optimized 3. use canvas in development mode with plugins w/ JS, it should work 4. use canvas in optimized JS mode with plugins w/ JS, it should work i. confirm that all scripts are optimized 5. add a bundle extension in a plugin (e.g. create bundles/extensions/conversations in plugin foo) i. confirm that the extension code runs in development mode ii. confirm that the extension code runs in optimized JS mode Change-Id: If8507afdbabab4ae8966f7db79d9b0e2284034db Reviewed-on: https://gerrit.instructure.com/11238 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Jacob Fugal <jacob@instructure.com>
2012-06-02 07:13:53 +08:00
paths = js_bundles.inject([]) do |ary, (bundle, plugin)|
base_url = js_base_url
js bundle extensions for plugins, bugfixes and DRYification allows plugins to have a bundle get auto-loaded with a corresponding canvas bundle. uses require.js' include mechanism in build.js (when optimized) and a rails helper tweak (when not optimized). this happens automatically based on the path, e.g. the foo plugin's "bundles/extensions/bar" will get automagically included whenever the regular "bar" bundle is required. removes the need for a plugin-level build.js, and auto-generates bundle module definitions in canvas' build.js (via erb). this handles all regular bundles both from canvas and plugins. also fixes plugins so that bundle dependencies get optimized. plugin paths are created automatically, so this means we can remove things like this from plugin bundles and specs: require.config paths: myplugin: "/plugins/myplugin/javascripts" test plan: 1. use canvas in development mode, it should work 2. use canvas in optimized JS mode, it should work i. confirm that all scripts are optimized 3. use canvas in development mode with plugins w/ JS, it should work 4. use canvas in optimized JS mode with plugins w/ JS, it should work i. confirm that all scripts are optimized 5. add a bundle extension in a plugin (e.g. create bundles/extensions/conversations in plugin foo) i. confirm that the extension code runs in development mode ii. confirm that the extension code runs in optimized JS mode Change-Id: If8507afdbabab4ae8966f7db79d9b0e2284034db Reviewed-on: https://gerrit.instructure.com/11238 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Jacob Fugal <jacob@instructure.com>
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"
end
AMD Conversion "Trivial" JavaScript / CoffeeScript changes -------------------------------------------------- For the most part, all javascript was simply wrapped in `require` or `define`. The dependencies were found with a script that matched regexes in the files, it errs on the side of listing too many dependencies, so its worth double checking each file's dependencies (over time, anyway). i18n API changes -------------------------------------------------- No longer have to do I18n.scoped calls, just list i18n as a dependency with the scope and it's imported already scoped require ['i18n!some_scope'], (I18n) -> I18n.t 'im_scoped', 'I'm scoped!' JS bundling now done with r.js, not Jammit -------------------------------------------------- We don't use jammit to bundle JS anymore. Simply list dependencies for your JS modules in the file and RequireJS handles the rest. To optimize the JavaScript, first make sure you have node.js 0.4.12+ installed and then run: $ rake js:build The app defaults to the optimized build in production. You can use non-optimized in production by putting ?debug_assets=true in the url just like before. You can also test the optimized JavaScript in development with ?optimized_js=true. Significant changes -------------------------------------------------- These files have "real" changes to them (unlike the JavaScript that is simply wrapped in require and define). Worth taking a really close look at: - app/helpers/application_helper.rb - app/views/layouts/application.html.erb - config/assets.yml - config/build.js - lib/handlebars/handlebars.rb - lib/i18n_extraction/js_extractor.rb - lib/tasks/canvas.rake - lib/tasks/i18n.rake - lib/tasks/js.rake Change-Id: I4bc5ecb1231f331aaded0fef2bcc1f3a9fe482a7 Reviewed-on: https://gerrit.instructure.com/6986 Tested-by: Hudson <hudson@instructure.com> Reviewed-by: Ryan Florence <ryanf@instructure.com>
2011-11-11 00:31:45 +08:00
javascript_include_tag *paths
2011-02-01 09:57:29 +08:00
end
def include_css_bundles
unless jammit_css_bundles.empty?
a way for accounts to opt-in to new styles and users to high-contrast fixes CNVS-11426 The UI/UX team *really* wants to start doing some major style changes to the canvas interface. Because it is a very visual change and especially because we've let people hack the crap out of canvas styles with their own css override file, any changes we do need to be behind a feature flag so an institution can opt-in when they are ready. This commit does some trickery so we actually create 4 different versions of every stylesheet. one for each variant in: legacy_normal_contrast legacy_high_contrast new_styles_normal_contrast new_styles_high_contrast and then sets the $use_new_styles and $use_high_contrast sass variables accordingly in each. now, in any sass file, you can do things like: @if $use_new_styles { background-color: red; } @else { background-color: blue; } @if not $use_high_contrast{ font-size: 12px; } test plan: * in a production (minified assets) environment, ensure you see: <link href="/assets/common_legacy_normal_contrast.css... in the <head> of the page * go to the account settings page for the domain_root_account you are on * turn on the "Use New Styles" feature * now verify that <link href="/assets/common_new_styles_normal_contrast.css... is in the <head>. There should not be any visible differences because we do not have any styles that target specifically $use_new_styles yet. * now, go to /settings and turn on the user feature for "Use High Contrast Styles" * verify that <link href="/assets/common_new_styles_high_contrast.css.. is in the <head> * again, turning that on will not actually change the way anything looks, the next commit (g/32863) will take care of that Change-Id: I45ba4ddfe780c5c819fb995b61ebfc0a5325d418 Reviewed-on: https://gerrit.instructure.com/31881 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Ryan Shaw <ryan@instructure.com> Product-Review: Ryan Shaw <ryan@instructure.com> QA-Review: Ryan Shaw <ryan@instructure.com> Reviewed-by: Bracken Mosbacker <bracken@instructure.com>
2014-04-03 14:37:18 +08:00
bundles = jammit_css_bundles.map do |(bundle,plugin)|
bundle = variant_name_for(bundle)
plugin ? "plugins_#{plugin}_#{bundle}" : bundle
end
bundles << {:media => 'all'}
include_stylesheets(*bundles)
end
end
a way for accounts to opt-in to new styles and users to high-contrast fixes CNVS-11426 The UI/UX team *really* wants to start doing some major style changes to the canvas interface. Because it is a very visual change and especially because we've let people hack the crap out of canvas styles with their own css override file, any changes we do need to be behind a feature flag so an institution can opt-in when they are ready. This commit does some trickery so we actually create 4 different versions of every stylesheet. one for each variant in: legacy_normal_contrast legacy_high_contrast new_styles_normal_contrast new_styles_high_contrast and then sets the $use_new_styles and $use_high_contrast sass variables accordingly in each. now, in any sass file, you can do things like: @if $use_new_styles { background-color: red; } @else { background-color: blue; } @if not $use_high_contrast{ font-size: 12px; } test plan: * in a production (minified assets) environment, ensure you see: <link href="/assets/common_legacy_normal_contrast.css... in the <head> of the page * go to the account settings page for the domain_root_account you are on * turn on the "Use New Styles" feature * now verify that <link href="/assets/common_new_styles_normal_contrast.css... is in the <head>. There should not be any visible differences because we do not have any styles that target specifically $use_new_styles yet. * now, go to /settings and turn on the user feature for "Use High Contrast Styles" * verify that <link href="/assets/common_new_styles_high_contrast.css.. is in the <head> * again, turning that on will not actually change the way anything looks, the next commit (g/32863) will take care of that Change-Id: I45ba4ddfe780c5c819fb995b61ebfc0a5325d418 Reviewed-on: https://gerrit.instructure.com/31881 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Ryan Shaw <ryan@instructure.com> Product-Review: Ryan Shaw <ryan@instructure.com> QA-Review: Ryan Shaw <ryan@instructure.com> Reviewed-by: Bracken Mosbacker <bracken@instructure.com>
2014-04-03 14:37:18 +08:00
def variant_name_for(bundle_name)
if k12?
variant = '_k12'
elsif use_new_styles?
variant = '_new_styles'
else
variant = '_legacy'
end
a way for accounts to opt-in to new styles and users to high-contrast fixes CNVS-11426 The UI/UX team *really* wants to start doing some major style changes to the canvas interface. Because it is a very visual change and especially because we've let people hack the crap out of canvas styles with their own css override file, any changes we do need to be behind a feature flag so an institution can opt-in when they are ready. This commit does some trickery so we actually create 4 different versions of every stylesheet. one for each variant in: legacy_normal_contrast legacy_high_contrast new_styles_normal_contrast new_styles_high_contrast and then sets the $use_new_styles and $use_high_contrast sass variables accordingly in each. now, in any sass file, you can do things like: @if $use_new_styles { background-color: red; } @else { background-color: blue; } @if not $use_high_contrast{ font-size: 12px; } test plan: * in a production (minified assets) environment, ensure you see: <link href="/assets/common_legacy_normal_contrast.css... in the <head> of the page * go to the account settings page for the domain_root_account you are on * turn on the "Use New Styles" feature * now verify that <link href="/assets/common_new_styles_normal_contrast.css... is in the <head>. There should not be any visible differences because we do not have any styles that target specifically $use_new_styles yet. * now, go to /settings and turn on the user feature for "Use High Contrast Styles" * verify that <link href="/assets/common_new_styles_high_contrast.css.. is in the <head> * again, turning that on will not actually change the way anything looks, the next commit (g/32863) will take care of that Change-Id: I45ba4ddfe780c5c819fb995b61ebfc0a5325d418 Reviewed-on: https://gerrit.instructure.com/31881 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Ryan Shaw <ryan@instructure.com> Product-Review: Ryan Shaw <ryan@instructure.com> QA-Review: Ryan Shaw <ryan@instructure.com> Reviewed-by: Bracken Mosbacker <bracken@instructure.com>
2014-04-03 14:37:18 +08:00
use_high_contrast = @current_user && @current_user.prefers_high_contrast?
variant += use_high_contrast ? '_high_contrast' : '_normal_contrast'
"#{bundle_name}#{variant}"
end
def include_common_stylesheets
a way for accounts to opt-in to new styles and users to high-contrast fixes CNVS-11426 The UI/UX team *really* wants to start doing some major style changes to the canvas interface. Because it is a very visual change and especially because we've let people hack the crap out of canvas styles with their own css override file, any changes we do need to be behind a feature flag so an institution can opt-in when they are ready. This commit does some trickery so we actually create 4 different versions of every stylesheet. one for each variant in: legacy_normal_contrast legacy_high_contrast new_styles_normal_contrast new_styles_high_contrast and then sets the $use_new_styles and $use_high_contrast sass variables accordingly in each. now, in any sass file, you can do things like: @if $use_new_styles { background-color: red; } @else { background-color: blue; } @if not $use_high_contrast{ font-size: 12px; } test plan: * in a production (minified assets) environment, ensure you see: <link href="/assets/common_legacy_normal_contrast.css... in the <head> of the page * go to the account settings page for the domain_root_account you are on * turn on the "Use New Styles" feature * now verify that <link href="/assets/common_new_styles_normal_contrast.css... is in the <head>. There should not be any visible differences because we do not have any styles that target specifically $use_new_styles yet. * now, go to /settings and turn on the user feature for "Use High Contrast Styles" * verify that <link href="/assets/common_new_styles_high_contrast.css.. is in the <head> * again, turning that on will not actually change the way anything looks, the next commit (g/32863) will take care of that Change-Id: I45ba4ddfe780c5c819fb995b61ebfc0a5325d418 Reviewed-on: https://gerrit.instructure.com/31881 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Ryan Shaw <ryan@instructure.com> Product-Review: Ryan Shaw <ryan@instructure.com> QA-Review: Ryan Shaw <ryan@instructure.com> Reviewed-by: Bracken Mosbacker <bracken@instructure.com>
2014-04-03 14:37:18 +08:00
include_stylesheets variant_name_for(:vendor), variant_name_for(:common), media: "all"
end
2011-02-01 09:57:29 +08:00
def section_tabs
@section_tabs ||= begin
if @context
html = []
tabs = Rails.cache.fetch([@context, @current_user, @domain_root_account, Lti::NavigationCache.new(@domain_root_account), "section_tabs_hash", I18n.locale].cache_key, expires_in: 1.hour) 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?
tabs.select do |tab|
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
else
[]
end
end
return '' if tabs.empty?
inactive_element = "<span id='inactive_nav_link' class='screenreader-only'>#{I18n.t('* No content has been added')}</span>"
html << '<nav role="navigation" aria-label="context"><ul id="section-tabs">'
tabs.each do |tab|
path = nil
if tab[:args]
path = tab[:args].instance_of?(Array) ? send(tab[:href], *tab[:args]) : 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
hide = tab[:hidden] || tab[:hidden_unused]
class_name = tab[:css_class].downcase.replace_whitespace("-")
class_name += ' active' if @active_tab == tab[:css_class]
if hide
tab[:label] += inactive_element
end
if tab[:screenreader]
link = "<a href='#{path}' class='#{class_name}' aria-label='#{tab[:screenreader]}'>#{tab[:label]}</a>"
else
link = "<a href='#{path}' class='#{class_name}'>#{tab[:label]}</a>"
end
html << "<li class='section #{"section-tab-hidden" if hide }'>" + link + "</li>" if tab[:href]
2011-02-01 09:57:29 +08:00
end
html << "</ul></nav>"
html.join("")
2011-02-01 09:57:29 +08:00
end
end
raw(@section_tabs)
end
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)
tabs.select do |tab|
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
def embedded_chat_quicklaunch_params
{
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
}
end
def embedded_chat_url
chat_tool = active_external_tool_by_id('chat')
return unless chat_tool && chat_tool.url && chat_tool.custom_fields['mini_view_url']
uri = URI.parse(chat_tool.url)
uri.path = chat_tool.custom_fields['mini_view_url']
uri.to_s
end
def embedded_chat_enabled
chat_tool = active_external_tool_by_id('chat')
chat_tool && chat_tool.url && chat_tool.custom_fields['mini_view_url'] && Canvas::Plugin.value_to_boolean(chat_tool.custom_fields['embedded_chat_enabled'])
end
def embedded_chat_visible
@show_embedded_chat != false &&
!@embedded_view &&
!@body_class_no_headers &&
@current_user &&
@context.is_a?(Course) &&
embedded_chat_enabled &&
external_tool_tab_visible('chat')
end
def active_external_tool_by_id(tool_id)
# don't use for groups. they don't have account_chain_ids
tool = @context.context_external_tools.active.where(tool_id: tool_id).first
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, :tool_id => tool_id).all
@context.account_chain.each do |account|
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)
return false unless tool
@context.tabs_available(@current_user).find {|tc| tc[:id] == tool.asset_string}.present?
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-02-01 09:57:29 +08:00
def equella_enabled?
@equella_settings ||= @context.equella_settings if @context.respond_to?(:equella_settings)
@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)
@domain_root_account.manually_created_courses_account.grants_any_right?(user, session, :create_courses, :manage_courses)
2011-02-01 09:57:29 +08:00
end
# 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)
link_to(url, :class => 'btn button-sidebar-wide') do
img ? ("<i class='icon-" + img + "'></i> ").html_safe + label : label
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-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-02-01 09:57:29 +08:00
def inst_env
global_inst_object = { :environment => Rails.env }
{
:allowMediaComments => CanvasKaltura::ClientV3.config && @context.try_rescue(:allow_media_comments?),
:kalturaSettings => CanvasKaltura::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', 'use_alt_record_widget', 'hide_rte_button', 'js_uploader'),
:equellaEnabled => !!equella_enabled?,
:googleAnalyticsAccount => Setting.get('google_analytics_key', nil),
:http_status => @status,
:error_id => @error && @error.id,
:disableGooglePreviews => !service_enabled?(:google_docs_previews),
:disableScribdPreviews => !feature_enabled?(:scribd),
:disableCrocodocPreviews => !feature_enabled?(:crocodoc),
:enableScribdHtml5 => feature_enabled?(:scribd_html5),
:enableHtml5FirstVideos => @domain_root_account.feature_enabled?(:html5_first_videos),
:logPageViews => !@body_class_no_headers,
:maxVisibleEditorButtons => 3,
:editorButtons => editor_buttons,
pandapub: add plugin settings, a simple client, and add to INST Adds a PandaPub client and settings UI. This commit doesn't add any new functionality or feature that use PandaPub - just the plumbing. running and configuring PandaPub: * Start up a local PandaPub instance. Easiest way is with Docker: docker run --rm -ti -p 49000:3000 -e ADMIN_USERNAME=admin \ -e ADMIN_PASSWORD=password zwily/pandapub:latest * Log into PandaPub at: http://$(boot2docker ip 2>/dev/null):49000/admin with admin/password. * Create a new application named "canvas" * Create a new key with an expiration date in the future. Record the key_id and secret. * Log into Canvas and go to PandaPub plugin settings: base_url: http://{DOCKER IP}:49000/ application_id: <id from created application> key_id: <id from created key> key_secret: <secret from created key> * In your PandaPub admin section, open the "Console" tab for your created application. In the "Subscribe" section, enter "**" in the input box, and hit "Subscribe". This will show you the first 50 events that arrive for the canvas application you just created. test plan for settings: * Verify that the /plugins/pandapub settings page works, saving, disabling, etc. test plan for client via console: * Set up and configure PandaPub in Canvas * Open a Rails console * Run this command to post a pandapub message: CanvasPandaPub.post_update("/public/foo", {"a" => 1}) * Verify that the message showed up in your PandaPub console. Change-Id: Ifddcbd335293c2a29f532b1e5fd44c23c8b910c4 Reviewed-on: https://gerrit.instructure.com/40311 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Jacob Fugal <jacob@instructure.com> QA-Review: Sean Lewis <slewis@instructure.com> Product-Review: Zach Wily <zach@instructure.com>
2014-09-06 05:06:10 +08:00
:pandaPubSettings => CanvasPandaPub::Client.config.try(:slice, 'push_url', 'application_id'),
}.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
2011-02-01 09:57:29 +08:00
global_inst_object
end
def editor_buttons
return [] if @context.is_a?(Group)
contexts = []
contexts << @context if @context && @context.respond_to?(:context_external_tools)
contexts += @context.account_chain if @context.respond_to?(:account_chain)
return [] if contexts.empty?
Rails.cache.fetch((['editor_buttons_for'] + contexts.uniq).cache_key) do
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 "))
tools.sort_by(&:id).map do |tool|
{
:name => tool.label_for(:editor_button, nil),
:id => tool.id,
:url => tool.editor_button(:url),
:icon_url => tool.editor_button(:icon_url),
:width => tool.editor_button(:selection_width),
:height => tool.editor_button(:selection_height)
}
end
end
end
2011-02-01 09:57:29 +08:00
def nbsp
raw("&nbsp;")
end
Use html5 to show videos by default using a library called mediaElement.js, try to show all kaltura video and audio using html5 and fall back on flash if needed. Test Plan: * in all the different browsers (including mobile safari/chrome) * try recording webcam videos (as homework submission, as content in a wiki page, etc) * view those same pages * view recorded submission in speedgrader use the playlist urls to get kaltura urls This will get CDN-configured links from Kaltura instead of direct Kaltura links. It also allows for the case where a MediaObject does not exist. When one is requested, it will be created in the database and details fetched. Test Plan for cdn urls: * Make sure kaltura is enabled * Open your web inspector's network tab * Browse to a recorded media file in Canvas, and play it * Ensure that the file is streamed from kaltura's configured CDN (s3 in the case of instructure). Test Plan for nonexistent MediaObjects: * Make sure kaltura is enabled * In a wiki page, record a media comment * Destroy that MediaObject from the database (use Inspect Element to find the media_id which looks like 0_1234abcd) * Reload the page, and verify that you can still view the video fix error when viewing a video file that has an unknown source type There are some videos that have valid sources that don't have an extension for some reason. This fixes handling of those correctly. There was also a bug that would sort by bitrate as strings, instead of as integers. The sort function was refactored out and tests written to ensure that's fixed. Test Plan * find a video in kaltura that has an asset with no extension, and make sure you can view that video in a wiki page refs #CNVS-324 Change-Id: I8ff24a94b8af11fc29b84e45545f8a8b639eeaff Reviewed-on: https://gerrit.instructure.com/16824 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Bracken Mosbacker <bracken@instructure.com> QA-Review: Adam Phillipps <adam@instructure.com>
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)} >&nbsp;</a>}
end
end
# 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?
src = "//www.youtube.com/embed/#{query['v']}"
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
# returns a time object at 00:00:00 tomorrow
def tomorrow_at_midnight
1.day.from_now.midnight
end
# 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] ||= []
if opts.has_key?(:all_folders)
opts[:sub_folders] = opts.delete(:all_folders).to_a.group_by{|f| f.parent_folder_id}
end
folders.each do |folder|
opts[:options_so_far] << %{<option value="#{folder.id}" #{'selected' if opts[:selected_folder_id] == folder.id}>#{"&nbsp;" * opts[:indent_width] * opts[:depth]}#{"- " if opts[:depth] > 0}#{html_escape folder.name}</option>}
if opts[:max_depth].nil? || opts[:depth] < opts[:max_depth]
child_folders = if opts[:sub_folders]
opts[:sub_folders][folder.id] || []
else
folder.active_sub_folders.by_position
end
folders_as_options(child_folders, opts.merge({:depth => opts[:depth] + 1})) if child_folders.any?
end
end
opts[:depth] == 0 ? raw(opts[:options_so_far].join("\n")) : nil
end
# 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
def join_title(*parts)
parts.join(t('#title_separator', ': '))
end
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
def map_courses_for_menu(courses)
mapped = courses.map do |course|
term = course.enrollment_term.name if !course.enrollment_term.default_term?
role = Role.get_role_by_id(course.primary_enrollment_role_id) || Enrollment.get_built_in_role_for_type(course.primary_enrollment_type)
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")
) + " " + role.label
{
:longName => "#{course.name} - #{course.short_name}",
:shortName => course.name,
:courseCode => course.course_code,
:href => course_path(course, :invitation => course.read_attribute(:invitation)),
:term => term || nil,
:subtitle => subtitle,
:id => course.id
}
end
mapped
end
def menu_courses_locals
courses = @current_user.menu_courses
all_courses_count = @current_user.courses_with_primary_enrollment.size
{
:collection => map_courses_for_menu(courses),
:collection_size => all_courses_count,
:more_link_for_over_max => courses_path,
:title => t('#menu.my_courses', "My Courses"),
:link_text => t('#layouts.menu.view_all_or_customize', 'View All or Customize'),
:edit => t("#menu.customize", "Customize")
}
end
def menu_groups_locals
{
:collection => @current_user.menu_data[:group_memberships],
:collection_size => @current_user.menu_data[:group_memberships_count],
:partial => "shared/menu_group_membership",
:max_to_show => 8,
:more_link_for_over_max => groups_path,
:title => t('#menu.current_groups', "Current Groups"),
:link_text => t('#layouts.menu.view_all_groups', 'View all groups')
}
end
def menu_accounts_locals
{
:collection => @current_user.menu_data[:accounts],
:collection_size => @current_user.menu_data[:accounts_count],
:partial => "shared/menu_account",
:max_to_show => 8,
:more_link_for_over_max => accounts_path,
:title => t('#menu.managed_accounts', "Managed Accounts"),
:link_text => t('#layouts.menu.view_all_accounts', 'View all accounts')
}
end
def cache_if(cond, *args)
if cond
cache(*args) { yield }
else
yield
end
end
def help_link
url = ((@domain_root_account && @domain_root_account.settings[:support_url]) || (Account.default && Account.default.settings[:support_url]))
show_feedback_link = Setting.get("show_feedback_link", "false") == "true"
css_classes = []
css_classes << "support_url" if url
css_classes << "help_dialog_trigger" if show_feedback_link
if url || show_feedback_link
link_to t('#links.help', "Help"), url || '#',
:class => css_classes.join(" "),
'data-track-category' => "help system",
'data-track-label' => 'help button'
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
def get_global_includes
return @global_includes if defined?(@global_includes)
Convert 'high-contrast' from user pref to feature flag fixes: CNVS-11426 a few months ago we started trying to enable users to turn on some "high contrast" css changes. The way we did that was with a override css file that would go and override existing style rules to set them to new values. the main difference here is that it is done at the *source of where the variable is declared* instead of *after the fact, overriding what was there* this will make things a lot easier to maintain. for example, see the part where instead of having a huge selector to override a bunch of things that look like links to be darker, we just go and set the $linkColor sass variable to be a little bit darker. test plan: * before you check out this code, go to /settings and turn on the 'high_contrast' user theme. * run the db migration in this commit * now go to /settings and make sure that the 'high contrast' feature is enabled. * verify that with that feature enabled, when you go to /styleguide you should see higher contrast buttons, link text, and backgrounds * it wont be exactly the same as when you had the 'high contrast' theme enabled because some of the high contrast things weren't super important (specifically, the darker grey borders on some elements). but the most important changes, making the contrast between text and the background around it, should be there. this essentially reverts: "adds high-contrast theme user preference" b232dcf44804b49c380a733adacdb50db5636e58. this also makes jmadsen's change to make the green alert bar a little darker (to comply with contrast) in g/31881 apply across the board to all of our green things so they are all compliant. (in addition, now if they have high contrast enabled, it will be even darker green) Change-Id: Iee7d81bb5160e86f1900dbf4a249364b1ddfa7f1 Reviewed-on: https://gerrit.instructure.com/32863 Reviewed-by: Cody Cutrer <cody@instructure.com> Reviewed-by: Jeremy Stanley <jeremy@instructure.com> Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Ryan Florence <ryanf@instructure.com> QA-Review: Nathan Rogowski <nathan@instructure.com> Product-Review: Ryan Shaw <ryan@instructure.com>
2014-04-04 06:03:05 +08:00
@global_includes = [Account.site_admin.global_includes_hash]
@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
if acct = account_context(@context)
key = [acct.id, 'account_context_global_includes'].cache_key
includes = Rails.cache.fetch(key, :expires_in => 15.minutes) do
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
includes = Rails.cache.fetch(key, :expires_in => 15.minutes) do
@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
@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
def include_account_js(options = {})
return if params[:global_includes] == '0'
includes = get_global_includes.map do |global_include|
global_include[:js] if global_include[:js].present?
end
includes.compact!
if includes.length > 0
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
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 include_account_css
return if params[:global_includes] == '0' || @domain_root_account.try(:feature_enabled?, :use_new_styles)
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
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
# this should be the same as friendlyDatetime in handlebars_helpers.coffee
def friendly_datetime(datetime, opts={}, attributes={})
attributes[:pubdate] = true if opts[:pubdate]
context = opts[:context]
tag_type = opts.fetch(:tag_type, :time)
if datetime.present?
attributes['data-html-tooltip-title'] ||= context_sensitive_datetime_title(datetime, context, just_text: true)
attributes['data-tooltip'] ||= 'top'
end
content_tag(tag_type, attributes) do
datetime_string(datetime)
end
end
def context_sensitive_datetime_title(datetime, context, options={})
just_text = options.fetch(:just_text, false)
default_text = options.fetch(:default_text, "")
return default_text unless datetime.present?
local_time = datetime_string(datetime)
text = local_time
if context.present?
course_time = datetime_string(datetime, :event, nil, false, context.time_zone)
if course_time != local_time
text = "#{h I18n.t('#helpers.local', "Local")}: #{h local_time}<br>#{h I18n.t('#helpers.course', "Course")}: #{h course_time}".html_safe
end
end
return text if just_text
"data-tooltip data-html-tooltip-title=\"#{text}\"".html_safe
end
# 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
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
def translated_due_date(assignment)
if assignment.multiple_due_dates_apply_to?(@current_user)
t('#due_dates.multiple_due_dates', 'due: Multiple Due Dates')
else
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
end
end
def add_uri_scheme_name(uri)
noSchemeName = !uri.match(/^(.+):\/\/(.+)/)
uri = 'http://' + uri if noSchemeName
uri
end
def agree_to_terms
# may be overridden by a plugin
@agree_to_terms ||
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', terms_of_use_url, target: '_blank'),
'**' => link_to('\1', privacy_policy_url, target: '_blank')
}
)
end
def dashboard_url(opts={})
return super(opts) if opts[:login_success] || opts[:become_user_id]
custom_dashboard_url || super(opts)
end
def dashboard_path(opts={})
return super(opts) if opts[:login_success] || opts[:become_user_id]
custom_dashboard_url || super(opts)
end
def custom_dashboard_url
url = @domain_root_account.settings[:dashboard_url]
if url.present?
url += "?current_user_id=#{@current_user.id}" if @current_user
url
end
end
def include_custom_meta_tags
if @meta_tags.present?
@meta_tags.
map{ |meta_attrs| tag("meta", meta_attrs) }.
join("\n").
html_safe
end
end
2011-02-01 09:57:29 +08:00
end