canvas-lms/app/models/account.rb

1567 lines
59 KiB
Ruby
Raw Normal View History

2011-02-01 09:57:29 +08:00
#
# Copyright (C) 2011 - 2014 Instructure, Inc.
2011-02-01 09:57:29 +08:00
#
# 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/>.
#
require 'atom'
2011-02-01 09:57:29 +08:00
class Account < ActiveRecord::Base
include Context
attr_accessible :name, :turnitin_account_id, :turnitin_shared_secret,
:turnitin_host, :turnitin_comments, :turnitin_pledge,
2011-02-01 09:57:29 +08:00
:default_time_zone, :parent_account, :settings, :default_storage_quota,
:default_storage_quota_mb, :storage_quota, :ip_filters, :default_locale,
A new way of doing css/sass & New Canvas Theme Editor what this does: * Changes the way we generate css so we are able to generate custom css for people that use the theme editor. * Sets everything up so we can push all of our static assets (js, fonts, css, images, etc) to s3 pre-deploy and serve them from cloudfront. Yay! faster canvas for everyone! * as part of that, this enables the rails asset pipeline just so we can use it to put md5s in our urls. we don't use it for any of the coffeescript/sass/sprockets transformer stuff. * adds a new "Theme editor" functionality (only for people that have have the use-new-styles feature flag turned on) where an admin for an account can pick their own colors/images for all the users at their account/school. * when the user is done saving things in theme editor, it will, in a delayed job, generate all the css with against the variables that user specified and push it to s3 so it will be available to anyone else that requests it. (the delayed job will shell out to a node.js executable called `brandable_css`). * ability to pick an existing shared theme and to reset to blank theme. closes: CNVS-19685 * gets rid of jammit. test plan: (this is exaustive, so not every person has to do every step but we should make sure at least someone does each of these things. maybe as part of the review add a comment if you have done one of these bulletpoints) * before you check this out, compile all css and copy the public/stylsheets_compiled directory somewhere. after you check out this code and regenerate all the css. make sure there are no significant changes to the css output. (we updated the versions of node-sass and autoprefixer that we use so we want to make sure they don't change things in a way we weren't expecting) * make sure the way we load css for handlebars templates still works. eg: if there is a handlebars template at app/views/jst/some/template.handlebars if there is also a scss file at app/stylesheets/jst/some/template.scss then that stylesheet should get loaded when that template is rendered * check out the code and run migrations. browse around canvas, make sure css and js files load correctly as before. * cody, jacob, or someone on queso: look at the db migrations and make sure everything looks good and that I am handling sharding correctly. * verify that both rake canvas:compile_assets and guard, works as well as `node_modules/.bin/brandable_css` (note: if you have "node_modules/.bin" in your PATH (which you should), it will also work with just `brandable_css`) * verify that passing the --watch option to `.bin/node_modules/brandable_css` works and picks up changes to sass files, images, fonts, or any other resource that goes into a css file. and that it only recompiles the css files that actually depend on that file. * go to https://github.com/ryankshaw/brandable_css and check out the code there. that is what is actually doing the sass compiling * create a config/canvas_cdn.yml file and add aws access creds and an s3 bucket and cdn hostname (for testing, you can use the credentials for instructure_uploads_engineering from https://gollum.instructure.com/OtherServiceTestAccounts ). for a test cdn hostname you can use https://diu0rq5m1weh1.cloudfront.net. that is a cloudfront bucket I set up on my personal account that points to instructure_uploads_engineering * run rake canvas:compile_assets again, this time, at the end, you should see it run the assets:precompile task that puts md5s in filenames and, gzipps them, and copys them to public/assets. then you should see it run canvas:cdn:upload_to_s3 (look at log/development.log for progress), which pushes everything to s3. closes: CNVS-17333 CNVS-17430 CNVS-17337 * try out the theme editor: turn on new styles, go to accounts/x (where x is the @domain root acount you are testing from) and click the "theme editor" button on the right side of the page. that should take you to a page that has the ability to pick colors/images on the left side and preview your changes in an iframe on the right closes: CNVS-19360 CNVS-20551 * test the "preview", "save", "reset", and "choose existing" functionality closes: CNVS-17339 CNVS-17338 CNVS-19685 * make sure that the themeeditor works both if you have config/canvas_cdn.yml set up and enabled as well as if you don't. if it is enabled, you should see it push the css for just that new brand config to s3 when you hit preview, and the css should be accessible from the cdn you configured. Change-Id: Ie0a812d04f5eeb40e7df7e71941ff63ea51a4d22 Reviewed-on: https://gerrit.instructure.com/53873 Tested-by: Jenkins QA-Review: Jeremy Putnam <jeremyp@instructure.com> Reviewed-by: Jacob Fugal <jacob@instructure.com> Product-Review: Ryan Shaw <ryan@instructure.com>
2015-02-12 03:51:05 +08:00
:default_user_storage_quota_mb, :default_group_storage_quota_mb, :integration_id, :brand_config_md5
2011-02-01 09:57:29 +08:00
INSTANCE_GUID_SUFFIX = 'canvas-lms'
2011-02-01 09:57:29 +08:00
include Workflow
subaccount branding (creation and trickle down) fixes CNVS-21426 changes theme_editor routes to nest under accounts show correct brand_config for subaccount be able to theme subaccounts parent account changes trickle down to subs theme editor default values are that of parent account add not in preview text that subaccounts dont show test plan: simple creation - create a sub-account - brand it differently from parent - if you go to courses belonging to this subaccount, it gets their css - if you go to courses belonging to the parent you get the parent css - if you go to a user dashboard you get the parent branding (maybe we can add smarts to this later in case you only are in the subaccount?) trickle down - rebrand two values on the parent - one that the subaccount explicitly sets - and one that doesnt - apply this config on the parent - wait a little and go to the child account - its theme inherits values for the parent, but... if it explicitly sets those values itself then it doesnt inherit it - go to the editor for the subaccount - the placeholder values are those of the parent account (or the canvas default if the parent hasnt set it) - test trickle down with a sub-account and a sub-sub-account still to do on different patch sets: - account settings to allow subaccount branding - show the progress of all the child compilation Change-Id: Iaddba7036f564965427807c2fd8b0a6a5d524366 Reviewed-on: https://gerrit.instructure.com/61285 Reviewed-by: Rob Orton <rob@instructure.com> QA-Review: August Thornton <august@instructure.com> Tested-by: Jenkins Product-Review: Ryan Shaw <ryan@instructure.com> Product-Review: Colleen Palmer <colleen@instructure.com>
2015-08-15 02:42:55 +08:00
include BrandConfigHelpers
2011-02-01 09:57:29 +08:00
belongs_to :parent_account, :class_name => 'Account'
belongs_to :root_account, :class_name => 'Account'
authenticates_many :pseudonym_sessions
has_many :courses
2011-02-01 09:57:29 +08:00
has_many :all_courses, :class_name => 'Course', :foreign_key => 'root_account_id'
has_many :group_categories, -> { where(deleted_at: nil) }, as: :context
has_many :all_group_categories, :class_name => 'GroupCategory', :as => :context
has_many :groups, :as => :context
has_many :all_groups, :class_name => 'Group', :foreign_key => 'root_account_id'
has_many :all_group_memberships, source: 'group_memberships', through: :all_groups
has_many :enrollment_terms, :foreign_key => 'root_account_id'
has_many :active_enrollment_terms, -> { where("enrollment_terms.workflow_state<>'deleted'") }, class_name: 'EnrollmentTerm', foreign_key: 'root_account_id'
has_many :enrollments, -> { where("enrollments.type<>'StudentViewEnrollment'") }, foreign_key: 'root_account_id'
has_many :all_enrollments, :class_name => 'Enrollment', :foreign_key => 'root_account_id'
has_many :sub_accounts, -> { where("workflow_state<>'deleted'") }, class_name: 'Account', foreign_key: 'parent_account_id'
has_many :all_accounts, -> { order(:name) }, class_name: 'Account', foreign_key: 'root_account_id'
has_many :account_users, :dependent => :destroy
has_many :course_sections, :foreign_key => 'root_account_id'
has_many :sis_batches
has_many :abstract_courses, :class_name => 'AbstractCourse', :foreign_key => 'account_id'
has_many :root_abstract_courses, :class_name => 'AbstractCourse', :foreign_key => 'root_account_id'
has_many :users, :through => :account_users
has_many :pseudonyms, -> { preload(:user) }
2011-02-01 09:57:29 +08:00
has_many :role_overrides, :as => :context
has_many :course_account_associations
has_many :child_courses, -> { where(course_account_associations: { depth: 0 }) }, through: :course_account_associations, source: :course
has_many :attachments, :as => :context, :dependent => :destroy
has_many :active_assignments, -> { where("assignments.workflow_state<>'deleted'") }, as: :context, class_name: 'Assignment'
has_many :folders, -> { order('folders.name') }, as: :context, dependent: :destroy
has_many :active_folders, -> { where("folder.workflow_state<>'deleted'").order('folders.name') }, class_name: 'Folder', as: :context
has_many :developer_keys
has_many :authentication_providers,
-> { order(:position) },
extend: AccountAuthorizationConfig::FindWithType,
class_name: "AccountAuthorizationConfig"
2011-02-01 09:57:29 +08:00
has_many :account_reports
has_many :grading_standards, -> { where("workflow_state<>'deleted'") }, as: :context
has_many :assessment_questions, :through => :assessment_question_banks
has_many :assessment_question_banks, -> { preload(:assessment_questions, :assessment_question_bank_users) }, as: :context
has_many :roles
has_many :all_roles, :class_name => 'Role', :foreign_key => 'root_account_id'
has_many :progresses, :as => :context
has_many :content_migrations, :as => :context
add grading period group model add GradingPeriodGroup, and change associations between GradingPeriods, GradingPeriodGroups, Courses, and Accounts. also adjust the grading periods controller to account for addition of grading period groups closes CNVS-16538 test plan: -run bundle exec rake db:migrate, and bundle exec rake db:migrate RAILS_ENV=test -verify the migrations successfully run -open the rails console in sandbox: bundle exec rails c -s -create a course, a few grading periods, and a grading period group. Add the grading periods to the group. Assign the grading period group to the course. $ course = Course.create $ grading_period1 = GradingPeriod.create(weight: 25.0, start_date: Time.zone.now, end_date: 2.days.from_now) $ grading_period2 = GradingPeriod.create(weight: 30.0, start_date: Time.zone.now, end_date: 2.days.from_now) $ grading_period_group = GradingPeriodGroup.create() $ grading_period_group.grading_periods << grading_period1 $ grading_period_group.grading_periods << grading_period2 $ grading_period_group.course = course -verify the associations are working as expected, i.e. a GradingPeriodGroup has GradingPeriods, a GradingPeriod belongs to a GradingPeriodGroup, and a GradingPeriodGroup belongs to a course or account. $ grading_period_group.grading_periods #should return an array containing grading_period1 and grading_period2 $ grading_period1.grading_period_group #should return grading_period_group $ grading_period2.grading_period_group #should return grading_period_group $ grading_period_group.course #should return course $ grading_period_group.account #should return nil (should not throw error) Change-Id: I9d7465431dabd2afa18e7a8a33706b9a78a94cd1 Reviewed-on: https://gerrit.instructure.com/43512 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Josh Simpson <jsimpson@instructure.com> QA-Review: Amber Taniuchi <amber@instructure.com> Reviewed-by: Jacob Fugal <jacob@instructure.com> Product-Review: Spencer Olson <solson@instructure.com>
2014-10-30 02:42:41 +08:00
has_many :grading_period_groups, dependent: :destroy
has_many :grading_periods, through: :grading_period_groups
def inherited_assessment_question_banks(include_self = false, *additional_contexts)
sql = []
conds = []
contexts = additional_contexts + account_chain
contexts.delete(self) unless include_self
contexts.each { |c|
sql << "context_type = ? AND context_id = ?"
conds += [c.class.to_s, c.id]
}
conds.unshift(sql.join(" OR "))
AssessmentQuestionBank.where(conds)
end
learning outcomes refactor This list is *NOT* complete, some items may have snuck in that I forgot to note, and/or some of the noted items may not be completely functional yet. Specs need to be written around a lot of this, other specs will no doubt need to be fixed. Some things, particularly around LearningOutcomeGroups will need data migrations that aren't there yet. * remove LearningOutcome.non_rubric_outcomes? and replace with false where invoked * remove LearningOutcome.enabled? and replace with true where invoked * remove never-taken branches * remove the shared/aligned_outcomes partial and it's supporting javascript, since it's now empty * remove js handler for add_outcome_alignment_link and supporting method since it only occurred in never-taken branches * mix LearningOutcomeContext into Course and Account * replace LearningOutcomeGroup.default_for(context) with LearningOutcomeContext#root_outcome_group * rename LearningOutcome#content_tags to LearningOutcome#alignments * rename LearningOutcomeGroup#content_tags to LearningOutcomeGroup#child_links, and properly restrict * remove ContentTag[Alignment]#rubric_association_id, add ContentTag[Alignment]#has_rubric_association? that looks at the presence of the content's rubric_association_id * condition off the assignment having a rubric_association rather than filtering tags by has_rubric_association (which just looks back at the assignment). all or none of the assignment's alignments are forced to have the association (via the assignment). this was true in practice before, is now codified (and more efficient) * rename AssessmentQuestionBank#learning_outcome_tags to AssessmentQuestionBank#learning_outcome_alignments * rename Assignment#learning_outcome_tags to Assignment#learning_outcome_alignments * rename Rubric#learning_outcome_tags to Rubric#learning_outcome_alignments * move/rename (Course|Account)#learning_outcome_tags to LearningOutcomeContext#learning_outcome_links * move/rename Account#learning_outcomes (corrected) and Course#learning_outcomes to LearningOutcomeContext#linked_learning_outcomes * move/rename Account#created_learning_outcomes and Course#created_learning_outcomes to LearningOutcomeContext#created_learning_outcomes * clarify and correct usage of linked_learning_outcomes vs. created_learning_outcomes * move/rename (Account|Account)#learning_outcome_groups to LearningOutcomeContext#learning_outcome_groups * remove unused Account#associated_learning_outcomes * just remove one link to a learning outcome when deleting * merge Account#has_outcomes?, Course#has_outcomes? and Course#has_outcomes into LearningOutcomeContext#has_outcomes?, add a use in Context#active_record_types * kill LearningOutcomeGroup#root_learning_outcome_group (unused) * rename LearningOutcomeResult#content_tag to LearningOutcomeResult#alignment * kill unused (and broken) OutcomesController#add_outcome_group * kill unused OutcomesController#update_outcomes_for_asset * kill unused OutcomesController#outcomes_for_asset * remove unused (outside specs, correct specs) AssessmentQuestionBank#outcomes= * remove unused ContentTag#learning_outcome_content * replace ContentTag.learning_outcome_tags_for(asset) (only ever called with asset=an assignment) with call to Assignment#learning_outcome_alignments * remove unused ContentTag.not_rubric * remove (now) unused ContentTag.include_outcome * remove unused LearningOutcome#learning_outcome_group_associations * avoid explicit use of ContentTag in outcome-related specs * replace LearningOutcomeGroup#learning_outcome_tags with LearningOutcomeGroup#child_outcome_links (and only use for outcome links; not tags for child groups) * split ContentTag#create_outcome_result into Submission#create_outcome_result, QuizSubmission#create_outcome_result, and RubricAssessment#create_outcome_result. fix some bugs along the way * refactor ContentTag.outcome_tags_for_banks and some code from QuizSubmission#(track_outcomes|update_outcomes_for_assessment_questions) into QuizSubmission#questions_and_alignments * refactor RubricAssociation#update_outcome_relations and Rubric#update_alignments into LearningOutcome.update_alignments * don't use ContentTag#rubric_association with outcome alignments; use the tag's content's rubric_association in its place (they should have been equal anyways) * refactor LearningOutcome.available_in_context and @context.root_outcome_group.sorted_all_outcomes (only time sorted_all_outcomes is used) into LearningOutcomeContext#available_outcomes and LearningOutcomeContext#available_outcome * overhaul LearningOutcomeGroup#sorted_content and rename to LearningOutcomeGroup#sorted_children. it not returns ContentTags (outcome links) and LearningOutcomeGroups, vs. LearningOutcomes and LearningOutcomeGroups; fix usages appropriately * fix UI for arranging/deleting outcome links and groups within a group to refer to the outcome link rather than the outcome Change-Id: I85d99f2634f7206332cb1f5d5ea575b428988d4b Reviewed-on: https://gerrit.instructure.com/12590 Reviewed-by: Jacob Fugal <jacob@instructure.com> Tested-by: Jacob Fugal <jacob@instructure.com>
2012-07-13 01:16:13 +08:00
include LearningOutcomeContext
include RubricContext
learning outcomes refactor This list is *NOT* complete, some items may have snuck in that I forgot to note, and/or some of the noted items may not be completely functional yet. Specs need to be written around a lot of this, other specs will no doubt need to be fixed. Some things, particularly around LearningOutcomeGroups will need data migrations that aren't there yet. * remove LearningOutcome.non_rubric_outcomes? and replace with false where invoked * remove LearningOutcome.enabled? and replace with true where invoked * remove never-taken branches * remove the shared/aligned_outcomes partial and it's supporting javascript, since it's now empty * remove js handler for add_outcome_alignment_link and supporting method since it only occurred in never-taken branches * mix LearningOutcomeContext into Course and Account * replace LearningOutcomeGroup.default_for(context) with LearningOutcomeContext#root_outcome_group * rename LearningOutcome#content_tags to LearningOutcome#alignments * rename LearningOutcomeGroup#content_tags to LearningOutcomeGroup#child_links, and properly restrict * remove ContentTag[Alignment]#rubric_association_id, add ContentTag[Alignment]#has_rubric_association? that looks at the presence of the content's rubric_association_id * condition off the assignment having a rubric_association rather than filtering tags by has_rubric_association (which just looks back at the assignment). all or none of the assignment's alignments are forced to have the association (via the assignment). this was true in practice before, is now codified (and more efficient) * rename AssessmentQuestionBank#learning_outcome_tags to AssessmentQuestionBank#learning_outcome_alignments * rename Assignment#learning_outcome_tags to Assignment#learning_outcome_alignments * rename Rubric#learning_outcome_tags to Rubric#learning_outcome_alignments * move/rename (Course|Account)#learning_outcome_tags to LearningOutcomeContext#learning_outcome_links * move/rename Account#learning_outcomes (corrected) and Course#learning_outcomes to LearningOutcomeContext#linked_learning_outcomes * move/rename Account#created_learning_outcomes and Course#created_learning_outcomes to LearningOutcomeContext#created_learning_outcomes * clarify and correct usage of linked_learning_outcomes vs. created_learning_outcomes * move/rename (Account|Account)#learning_outcome_groups to LearningOutcomeContext#learning_outcome_groups * remove unused Account#associated_learning_outcomes * just remove one link to a learning outcome when deleting * merge Account#has_outcomes?, Course#has_outcomes? and Course#has_outcomes into LearningOutcomeContext#has_outcomes?, add a use in Context#active_record_types * kill LearningOutcomeGroup#root_learning_outcome_group (unused) * rename LearningOutcomeResult#content_tag to LearningOutcomeResult#alignment * kill unused (and broken) OutcomesController#add_outcome_group * kill unused OutcomesController#update_outcomes_for_asset * kill unused OutcomesController#outcomes_for_asset * remove unused (outside specs, correct specs) AssessmentQuestionBank#outcomes= * remove unused ContentTag#learning_outcome_content * replace ContentTag.learning_outcome_tags_for(asset) (only ever called with asset=an assignment) with call to Assignment#learning_outcome_alignments * remove unused ContentTag.not_rubric * remove (now) unused ContentTag.include_outcome * remove unused LearningOutcome#learning_outcome_group_associations * avoid explicit use of ContentTag in outcome-related specs * replace LearningOutcomeGroup#learning_outcome_tags with LearningOutcomeGroup#child_outcome_links (and only use for outcome links; not tags for child groups) * split ContentTag#create_outcome_result into Submission#create_outcome_result, QuizSubmission#create_outcome_result, and RubricAssessment#create_outcome_result. fix some bugs along the way * refactor ContentTag.outcome_tags_for_banks and some code from QuizSubmission#(track_outcomes|update_outcomes_for_assessment_questions) into QuizSubmission#questions_and_alignments * refactor RubricAssociation#update_outcome_relations and Rubric#update_alignments into LearningOutcome.update_alignments * don't use ContentTag#rubric_association with outcome alignments; use the tag's content's rubric_association in its place (they should have been equal anyways) * refactor LearningOutcome.available_in_context and @context.root_outcome_group.sorted_all_outcomes (only time sorted_all_outcomes is used) into LearningOutcomeContext#available_outcomes and LearningOutcomeContext#available_outcome * overhaul LearningOutcomeGroup#sorted_content and rename to LearningOutcomeGroup#sorted_children. it not returns ContentTags (outcome links) and LearningOutcomeGroups, vs. LearningOutcomes and LearningOutcomeGroups; fix usages appropriately * fix UI for arranging/deleting outcome links and groups within a group to refer to the outcome link rather than the outcome Change-Id: I85d99f2634f7206332cb1f5d5ea575b428988d4b Reviewed-on: https://gerrit.instructure.com/12590 Reviewed-by: Jacob Fugal <jacob@instructure.com> Tested-by: Jacob Fugal <jacob@instructure.com>
2012-07-13 01:16:13 +08:00
has_many :context_external_tools, -> { order(:name) }, as: :context, dependent: :destroy
2011-02-01 09:57:29 +08:00
has_many :error_reports
has_many :announcements, :class_name => 'AccountNotification'
has_many :alerts, -> { preload(:criteria) }, as: :context
has_many :user_account_associations
has_many :report_snapshots
has_many :external_integration_keys, :as => :context, :dependent => :destroy
A new way of doing css/sass & New Canvas Theme Editor what this does: * Changes the way we generate css so we are able to generate custom css for people that use the theme editor. * Sets everything up so we can push all of our static assets (js, fonts, css, images, etc) to s3 pre-deploy and serve them from cloudfront. Yay! faster canvas for everyone! * as part of that, this enables the rails asset pipeline just so we can use it to put md5s in our urls. we don't use it for any of the coffeescript/sass/sprockets transformer stuff. * adds a new "Theme editor" functionality (only for people that have have the use-new-styles feature flag turned on) where an admin for an account can pick their own colors/images for all the users at their account/school. * when the user is done saving things in theme editor, it will, in a delayed job, generate all the css with against the variables that user specified and push it to s3 so it will be available to anyone else that requests it. (the delayed job will shell out to a node.js executable called `brandable_css`). * ability to pick an existing shared theme and to reset to blank theme. closes: CNVS-19685 * gets rid of jammit. test plan: (this is exaustive, so not every person has to do every step but we should make sure at least someone does each of these things. maybe as part of the review add a comment if you have done one of these bulletpoints) * before you check this out, compile all css and copy the public/stylsheets_compiled directory somewhere. after you check out this code and regenerate all the css. make sure there are no significant changes to the css output. (we updated the versions of node-sass and autoprefixer that we use so we want to make sure they don't change things in a way we weren't expecting) * make sure the way we load css for handlebars templates still works. eg: if there is a handlebars template at app/views/jst/some/template.handlebars if there is also a scss file at app/stylesheets/jst/some/template.scss then that stylesheet should get loaded when that template is rendered * check out the code and run migrations. browse around canvas, make sure css and js files load correctly as before. * cody, jacob, or someone on queso: look at the db migrations and make sure everything looks good and that I am handling sharding correctly. * verify that both rake canvas:compile_assets and guard, works as well as `node_modules/.bin/brandable_css` (note: if you have "node_modules/.bin" in your PATH (which you should), it will also work with just `brandable_css`) * verify that passing the --watch option to `.bin/node_modules/brandable_css` works and picks up changes to sass files, images, fonts, or any other resource that goes into a css file. and that it only recompiles the css files that actually depend on that file. * go to https://github.com/ryankshaw/brandable_css and check out the code there. that is what is actually doing the sass compiling * create a config/canvas_cdn.yml file and add aws access creds and an s3 bucket and cdn hostname (for testing, you can use the credentials for instructure_uploads_engineering from https://gollum.instructure.com/OtherServiceTestAccounts ). for a test cdn hostname you can use https://diu0rq5m1weh1.cloudfront.net. that is a cloudfront bucket I set up on my personal account that points to instructure_uploads_engineering * run rake canvas:compile_assets again, this time, at the end, you should see it run the assets:precompile task that puts md5s in filenames and, gzipps them, and copys them to public/assets. then you should see it run canvas:cdn:upload_to_s3 (look at log/development.log for progress), which pushes everything to s3. closes: CNVS-17333 CNVS-17430 CNVS-17337 * try out the theme editor: turn on new styles, go to accounts/x (where x is the @domain root acount you are testing from) and click the "theme editor" button on the right side of the page. that should take you to a page that has the ability to pick colors/images on the left side and preview your changes in an iframe on the right closes: CNVS-19360 CNVS-20551 * test the "preview", "save", "reset", and "choose existing" functionality closes: CNVS-17339 CNVS-17338 CNVS-19685 * make sure that the themeeditor works both if you have config/canvas_cdn.yml set up and enabled as well as if you don't. if it is enabled, you should see it push the css for just that new brand config to s3 when you hit preview, and the css should be accessible from the cdn you configured. Change-Id: Ie0a812d04f5eeb40e7df7e71941ff63ea51a4d22 Reviewed-on: https://gerrit.instructure.com/53873 Tested-by: Jenkins QA-Review: Jeremy Putnam <jeremyp@instructure.com> Reviewed-by: Jacob Fugal <jacob@instructure.com> Product-Review: Ryan Shaw <ryan@instructure.com>
2015-02-12 03:51:05 +08:00
belongs_to :brand_config, foreign_key: "brand_config_md5"
2011-02-01 09:57:29 +08:00
before_validation :verify_unique_sis_source_id
2011-02-01 09:57:29 +08:00
before_save :ensure_defaults
after_save :update_account_associations_if_changed
before_save :setup_cache_invalidation
after_save :invalidate_caches_if_changed
after_create :default_enrollment_term
after_create :enable_canvas_authentication
2011-02-01 09:57:29 +08:00
serialize :settings, Hash
include TimeZoneHelper
time_zone_attribute :default_time_zone, default: "America/Denver"
def default_time_zone_with_root_account
if read_attribute(:default_time_zone) || root_account?
default_time_zone_without_root_account
else
root_account.default_time_zone
end
end
alias_method_chain :default_time_zone, :root_account
alias_method :time_zone, :default_time_zone
2011-02-01 09:57:29 +08:00
validates_locale :default_locale, :allow_nil => true
validates_length_of :name, :maximum => maximum_string_length, :allow_blank => true
validate :account_chain_loop, :if => :parent_account_id_changed?
validate :validate_auth_discovery_url
validates_presence_of :workflow_state
include StickySisFields
are_sis_sticky :name
feature flags infrastructure and API test plan: - install the test_features plugin (since no real features exist yet) - render and consult the feature flags documentation - have a test environment with a root account, sub-account, course in sub-account, and user - Use the "list features" endpoint as a root account admin (with no site admin privileges), on the root account context, and confirm that hidden features do not show up - Use the "list features" endpoint as a site admin user, on the root account context, and confirm that hidden features show up - Use the "list features" endpoint on the site admin account and confirm the hidden features show up - Use the "set feature flag" endpoint on a hidden feature on site admin and ensure the feature becomes visible in all root accounts - Use the "set feature flag endpoint" on a hidden feature on a single root account, and ensure the feature becomes visible to that root account and not others - Confirm that root_opt_in features appear "Off" by default in root accounts, after being "Allowed" in code or site admin - Confirm a feature flag that is set to "on" or "off" (vs. "allowed") cannot be overridden in a lower context (and the API returns locked=true for them) - Confirm that setting locking_account_id requires admin rights in the locking account - Confirm that a feature flag with locking_account_id cannot be changed without admin rights in the locking account (e.g., set a feature flag on a course, locked with the root account's id, and make sure a teacher who is not an account admin can't change it) - Confirm feature flags can be deleted with the "remove feature flag" endpoint (and they are only deleted where they are defined, not when called on an object that inherits a flag) Change-Id: I3e12e23b4454889b6e8b263f1315e82d8f2ada52 Reviewed-on: https://gerrit.instructure.com/25502 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Matt Fairbourn <mfairbourn@instructure.com> Product-Review: Matt Goodwin <mattg@instructure.com> Reviewed-by: Zach Pendleton <zachp@instructure.com>
2013-10-22 23:28:26 +08:00
include FeatureFlags
def default_locale(recurse = false)
result = read_attribute(:default_locale)
result ||= parent_account.default_locale(true) if recurse && parent_account
result = nil unless I18n.locale_available?(result)
result
end
include ::Account::Settings
2011-02-01 09:57:29 +08:00
# these settings either are or could be easily added to
# the account settings page
add_setting :sis_app_token, :root_only => true
add_setting :sis_app_url, :root_only => true
add_setting :sis_default_grade_export, :boolean => true, :default => false, :inheritable => true
add_setting :global_includes, :root_only => true, :boolean => true, :default => false
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
add_setting :global_javascript, :condition => :allow_global_includes
add_setting :global_stylesheet, :condition => :allow_global_includes
get sub account branding and custom css/includes working fixes: CNVS-24787 fixes: CNVS-23964 fixes: CNVS-23957 - Handle parent account custom css/js for new_styles test plan: * set up a root account, child account, and grandchild account * use theme editor to set a custom css/js file for each (eg: for css `* {color:red}` and for js 'console.log("from grandchild")` * make a course & a group in the grandchild account * load a page in that course and group and make sure you see grandchild account's branding, and root's, child's, and then grandchild's css loaded on the page (grandchild should be loaded last so you see it's css effects override root or child's and you should see the console.log from root then child then grandchild) * view a page in "child". it should have root and child's css/js but not grandchild * as a user that only has enrollments (account associations) in "child", go to the dashboard. you should see css/js for both root and child but not grandchild fixes: CNVS-25051 Opening Theme Editor for sub-accounts shows incorrect theme preview test plan: * Go to a sub-account in theme editor and change settings so the Branding is different and save. * the preview on the right should reflect your changes both after you "apply" and "save" (and not just show the preview of the root account's branding) fixes: CNVS-23406 - global JS and CSS files are being included when Global CSS/JavaScript includes is false test plan: * go to /accounts/self/, and go to theme editor and upload a css_override * see that that css is loaded on pages * back in root account settings disable Global CSS/JavaScript includes * check that the css is no longer loaded. * do the same thing checking a subaccount's custom css fixes: CNVS-25558 - load whole chain of custom css/js in native app api requests test plan: * make api request for a wiki page in course in a subaccount that has custom css/js within a root account that also has custom css/js * you should see both the root account's css/js and the child account's returned in the response to test grandchild js issue jeremyp found: * go to theme editor for a grandchild account * choose a js override file (like: `console.log('first')`) * preview & apply * you should see "first" in console * go back to theme editor, pick a new file (like: `console.log('second')`) * preview & apply * you should only see "second" in console. not "first" Change-Id: I8d9047948f5da94be41e0205844629a170f980af Reviewed-on: https://gerrit.instructure.com/68249 Reviewed-by: Simon Williams <simon@instructure.com> QA-Review: Jeremy Putnam <jeremyp@instructure.com> Tested-by: Jenkins Product-Review: Ryan Shaw <ryan@instructure.com>
2015-12-05 00:57:07 +08:00
add_setting :sub_account_includes, :condition => :use_new_styles_or_allow_global_includes, :boolean => true, :default => false
2011-02-01 09:57:29 +08:00
add_setting :error_reporting, :hash => true, :values => [:action, :email, :url, :subject_param, :body_param], :root_only => true
add_setting :custom_help_links, :root_only => true
2011-02-01 09:57:29 +08:00
add_setting :prevent_course_renaming_by_teachers, :boolean => true, :root_only => true
refactor views for unified AAC administration closes CNVS-20076 First, pull a presenter out of the AAC index This thing needs more flexibility before introducing multiple OAuth connectors. This commit adds a couple characterization specs for the AAC controller, then drives out a presenter to pull as much logic and config out of the nested views as possible. Then, this commit refactors the previously-somewhat-bespoke-and-presumptive sac configuration into a workflow that shows each aac in turn according to it's type, creating forms for each type at the bottom, and showing the relevant form for a new one based on selection on the right. Have regression tested in the browser to the level of CRUD functionality, but also deserves solid QA for SSO functionality post-configuration. DONE: -successful CAS creation/editing/deletion -successful LDAP creation/editing/deletion -proper differentiation between LDAP primary and secondary -proper SAML creation/editing -move away from "update_all" deprecated endpoint -Selenium Spec fixes -ensure discovery URL and debugging workflows for saml -remove duplication from views -tear down old JS workflow -apply appropriate tests for new behavior -remove presenter methods that are no longer valuable -Moved change_password_url and login_handle_name -up to account settings, removed them from AACs, and built -migrations to manage the transition. -Found and fixed all references to change_password_url on AACs -Found and fixes all references to login_handle_name on AACs -add datafixup for migrating AAC data to account settings -unify repetative individual files into single form delcarations \o/ -remove old SAML editing js -Make sure SAML still works -Make LDAP partial flow just like SAML/CAS -Unify position information across all types -update "acts_as_list" to support STI classes -move discovery URL into account auth form -remove discover URL js management -Unify form generation between new/existing aacs -deprecate discovery url API endpoints -update docs for authorization settings to deprecate their usage in AAC api and redirect their values to current settings for now -make delete links non-js-y to stop this silly page refresh on api completion -make form submissions actually submit the form rather than do this silly page refresh on api completion -See if anything needs “Edit Details” button, remove if not -Wire up removing account settings by blanking out form -Removed "cancel" button from form because fields are always open -placate gergich -Test removing config info -Test population fixup on real data -write docs for authorization settings -fix existing specs -fix routing and docs to not break doc generation -fix stupid jenkins task that thinks it can’t see controls -re-fix selenium -fix saml debugging workflow -write tests for acts_as_list behavior -write tests for authorization settings -remove auth_info types of things -clean up and unify styles where possible TEST PLAN: Regression test creating/deleting/editing and logging in with SSO solutions for CAS, LDAP, and SAML. Should be no functional behavior modification, though workflow will be a little more unified between the 3 currently supported types (each one will require using the menu in the right sidebar to add a new AAC). Also test setting and deleting account settings through the form underneath the configs when there are AACs in existence. Finally, make sure that the SAML Debugging workflow still works. Change-Id: I448db10185512d1b9469c2a425be0a3bcf9e6ebf Reviewed-on: https://gerrit.instructure.com/53448 Tested-by: Jenkins Reviewed-by: Cody Cutrer <cody@instructure.com> QA-Review: August Thornton <august@instructure.com> Product-Review: Ethan Vizitei <evizitei@instructure.com>
2015-04-28 05:23:04 +08:00
add_setting :login_handle_name, root_only: true
add_setting :change_password_url, root_only: true
add_setting :unknown_user_url, root_only: true
add_setting :fft_registration_url, root_only: true
add_setting :restrict_student_future_view, :boolean => true, :default => false, :inheritable => true
add_setting :restrict_student_past_view, :boolean => true, :default => false, :inheritable => true
refactor user creation/invitations closes #5833 fixes #5573, #5572, #5753 * communication channels are now only unique within a single user * UserList changes * Always resolve pseudonym#unique_ids * Support looking up by SMS CCs * Option to either require e-mails match an existing CC, or e-mails that don't match a Pseudonym will always be returned unattached (relying on better merging behavior to not have a gazillion accounts created) * Method to return users, creating new ones (*without* a Pseudonym) if necessary. (can't create with a pseudonym, since Pseudonym#unique_id is still unique, I can't have multiple outstanding users with the same unique_id) * EnrollmentsFromUserList is mostly gutted, now using UserList's functionality directy. * Use UserList for adding account admins, removing the now unused Account#add_admin => User#find_by_email/User#assert_by_email codepath * Update UsersController#create to not worry about duplicate communication channels * Remove AccountsController#add_user, and just use UsersController#create * Change SIS::UserImporter to send out a merge opportunity e-mail if a conflicting CC is found (but still create the CC) * In /profile, don't worry about conflicting CCs (the CC confirmation process will now allow merging) * Remove CommunicationChannelsController#try_merge and #merge * For the non-simple case of CoursesController#enrollment_invitation redirect to /register (CommunicationsChannelController#confirm) * Remove CoursesController#transfer_enrollment * Move PseudonymsController#registration_confirmation to CommunicationChannelsController#confirm (have to be able to register an account without a Pseudonym yet) * Fold the old direct confirm functionality in, if there are no available merge opportunities * Allow merging the new account with the currently logged in user * Allow changing the Pseudonym#unique_id when registering a new account (since there might be conflicts) * Display a list of merge opportunities based on conflicting communication channels * Provide link(s) to log in as the other user, redirecting back to the registration page after login is complete (to complete the merge as the current user) * Remove several assert_* methods that are no longer needed * Update PseudonymSessionsController a bit to deal with the new way of dealing with conflicting CCs (especially CCs from LDAP), and to redirect back to the registration/confirmation page when attempting to do a merge * Expose the open_registration setting; use it to control if inviting users to a course is able to create new users Change-Id: If2f38818a71af656854d3bf8431ddbf5dcb84691 Reviewed-on: https://gerrit.instructure.com/6149 Tested-by: Hudson <hudson@instructure.com> Reviewed-by: Jacob Fugal <jacob@instructure.com>
2011-10-13 04:30:48 +08:00
add_setting :teachers_can_create_courses, :boolean => true, :root_only => true, :default => false
add_setting :students_can_create_courses, :boolean => true, :root_only => true, :default => false
add_setting :restrict_quiz_questions, :boolean => true, :root_only => true, :default => false
refactor user creation/invitations closes #5833 fixes #5573, #5572, #5753 * communication channels are now only unique within a single user * UserList changes * Always resolve pseudonym#unique_ids * Support looking up by SMS CCs * Option to either require e-mails match an existing CC, or e-mails that don't match a Pseudonym will always be returned unattached (relying on better merging behavior to not have a gazillion accounts created) * Method to return users, creating new ones (*without* a Pseudonym) if necessary. (can't create with a pseudonym, since Pseudonym#unique_id is still unique, I can't have multiple outstanding users with the same unique_id) * EnrollmentsFromUserList is mostly gutted, now using UserList's functionality directy. * Use UserList for adding account admins, removing the now unused Account#add_admin => User#find_by_email/User#assert_by_email codepath * Update UsersController#create to not worry about duplicate communication channels * Remove AccountsController#add_user, and just use UsersController#create * Change SIS::UserImporter to send out a merge opportunity e-mail if a conflicting CC is found (but still create the CC) * In /profile, don't worry about conflicting CCs (the CC confirmation process will now allow merging) * Remove CommunicationChannelsController#try_merge and #merge * For the non-simple case of CoursesController#enrollment_invitation redirect to /register (CommunicationsChannelController#confirm) * Remove CoursesController#transfer_enrollment * Move PseudonymsController#registration_confirmation to CommunicationChannelsController#confirm (have to be able to register an account without a Pseudonym yet) * Fold the old direct confirm functionality in, if there are no available merge opportunities * Allow merging the new account with the currently logged in user * Allow changing the Pseudonym#unique_id when registering a new account (since there might be conflicts) * Display a list of merge opportunities based on conflicting communication channels * Provide link(s) to log in as the other user, redirecting back to the registration page after login is complete (to complete the merge as the current user) * Remove several assert_* methods that are no longer needed * Update PseudonymSessionsController a bit to deal with the new way of dealing with conflicting CCs (especially CCs from LDAP), and to redirect back to the registration/confirmation page when attempting to do a merge * Expose the open_registration setting; use it to control if inviting users to a course is able to create new users Change-Id: If2f38818a71af656854d3bf8431ddbf5dcb84691 Reviewed-on: https://gerrit.instructure.com/6149 Tested-by: Hudson <hudson@instructure.com> Reviewed-by: Jacob Fugal <jacob@instructure.com>
2011-10-13 04:30:48 +08:00
add_setting :no_enrollments_can_create_courses, :boolean => true, :root_only => true, :default => false
add_setting :allow_sending_scores_in_emails, :boolean => true, :root_only => true
2011-02-01 09:57:29 +08:00
add_setting :support_url, :root_only => true
add_setting :self_enrollment
add_setting :equella_endpoint
add_setting :equella_teaser
add_setting :enable_alerts, :boolean => true, :root_only => true
add_setting :enable_eportfolios, :boolean => true, :root_only => true
add_setting :users_can_edit_name, :boolean => true, :root_only => true
add_setting :open_registration, :boolean => true, :root_only => true
add_setting :show_scheduler, :boolean => true, :root_only => true, :default => false
add_setting :enable_profiles, :boolean => true, :root_only => true, :default => false
add_setting :enable_manage_groups2, :boolean => true, :root_only => true, :default => true
multi-factor authentication closes #9532 test plan: * enable optional MFA, and check the following: * normal log in should not be affected * you can enroll in MFA from your profile page * you can re-enroll in MFA from your profile page * you can disable MFA from your profile page * MFA can be reset by an admin on your user page * when enrolled, you are asked for verification code after username/password when logging in * you can't access any other part of the site directly until until entering your verification code * enable required MFA, and check the following * when not enrolled in MFA, and you log in, you are forced to enroll * you cannot disable MFA from your profile page * you can re-enroll in MFA from your profile page * an admin (other than himself) can reset MFA from the user page * for enrolling in MFA * use Google Authenticator and scan the QR code; you should have 30-seconds or so of extra leeway to enter your code * having no SMS communication channels on your profile, the enrollment page should just have a form to add a new phone * having one or more SMS communication channels on your profile, the enrollment page should list them, or allow you to create a new one (and switch back) * having more than one SMS communication channel on your profile, the enrollment page should remember which one you have selected after you click "send" * an unconfirmed SMS channel should go to confirmed when it's used to enroll in MFA * you should not be able to go directly to /login/otp to enroll if you used "Remember me" token to log in * MFA login flow * if configured with SMS, it should send you an SMS after you put in your username/password; you should have about 5 minutes of leeway to put it in * if you don't check "remember computer" checkbox, you should have to enter a verification code each time you log in * if you do check it, you shouldn't have to enter your code anymore (for three days). it also shouldn't SMS you a verification code each time you log in * setting MFA to required for admins should make it required for admins, optional for other users * with MFA enabled, directly go to /login/otp after entering username/password but before entering a verification code; it should send you back to the main login page * if you enrolled via SMS, you should not be able to remove that SMS from your profile * there should not be a reset MFA link on a user page if they haven't enrolled * test a login or required enrollment sequence with CAS and/or SAML Change-Id: I692de7405bf7ca023183e717930ee940ccf0d5e6 Reviewed-on: https://gerrit.instructure.com/12700 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Brian Palmer <brianp@instructure.com>
2012-08-03 05:17:50 +08:00
add_setting :mfa_settings, :root_only => true
add_setting :admins_can_change_passwords, :boolean => true, :root_only => true, :default => false
ui enabling admins to search notifications adds account setting and new user permission fixes CNVS-4726 Testing Notes: ============ * To enable... * Account Settings, check Feature "Admins can view notifications" * Account permission under "Account Roles" becomes available under "Admin Tools" group. Check "View notifications" * appears under account "Admin Tools" sidebar area on "View Notifications" tab. (EX: /accounts/[account_id]/admin_tools) * Verify "View Notifications" tab does not appear if either account setting or user permission is disabled. (For AccountAdmins) * Verify a SiteAdmin is able to access the feature. * Verify that notifications are returned and displayed for a selected user and date range. * Verify it displays "No messages found" when user doesn't exist or the user exists but the date range doesn't return data. * Verify that the user_id is required when searching * Verify that the "To Date" cannot be before the "From Date". * Verify an invalid date like the word "couch" gets ignored and the actually used date/time is displayed in the overview text description. * Verify searching by dates when either or both are left blank and when both are used. * Verify that the messages automatically fetch more when you scroll down. * Verify that before the results, it displays the user's name and the dates used for the results. Change-Id: I9d2689b4760af57bbc2d15fd7d50610dcf593a7e Reviewed-on: https://gerrit.instructure.com/18629 Tested-by: Jenkins <jenkins@instructure.com> Product-Review: Marc LeGendre <marc@instructure.com> QA-Review: Marc LeGendre <marc@instructure.com> Reviewed-by: Mark Ericksen <marke@instructure.com>
2013-03-15 03:36:27 +08:00
add_setting :admins_can_view_notifications, :boolean => true, :root_only => true, :default => false
add_setting :outgoing_email_default_name
add_setting :external_notification_warning, :boolean => true, :default => false
# Terms of Use and Privacy Policy settings for the root account
add_setting :terms_changed_at, :root_only => true
add_setting :account_terms_required, :root_only => true, :boolean => true, :default => true
# When a user is invited to a course, do we let them see a preview of the
# course even without registering? This is part of the free-for-teacher
# account perks, since anyone can invite anyone to join any course, and it'd
# be nice to be able to see the course first if you weren't expecting the
# invitation.
add_setting :allow_invitation_previews, :boolean => true, :root_only => true, :default => false
add_setting :large_course_rosters, :boolean => true, :root_only => true, :default => false
add_setting :edit_institution_email, :boolean => true, :root_only => true, :default => true
add_setting :js_kaltura_uploader, :boolean => true, :root_only => true, :default => false
restrict google doc submissions by domain fixes CNVS-8949 test plan: * from a rails console, run the following: account = Account.default account.settings[:google_docs_domain] = 'example.com' account.save! * as a student, configure your google docs integration from your profile page; * attempt to submit an assignment that allows file uploads using the "google docs" tab of the submissions box; * verify that a message is displayed informing you that cannot use a google doc because your domain is incorrect; * as another user who shares a course with the first student, create a google doc collaboration and add the first student to it; * verify that the first student cannot join the collaboration; * change the account :gmail_domain setting to 'gmail.com' and verify that both assignment submissions and collaborations work as expected; * verify that if you delete the gmail domain account setting, both google doc submissions and collaborations work as expected for users with google docs integration configured. note: users added to a collaboration without a valid google docs address will receive a notification about the collaboration, but will not be able to join it. this is pre-existing behavior. Change-Id: I26f164f253710819214ccf5f2250b74b91a6774b Reviewed-on: https://gerrit.instructure.com/26436 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Zach Pendleton <zachp@instructure.com> Product-Review: Zach Pendleton <zachp@instructure.com> QA-Review: Zach Pendleton <zachp@instructure.com>
2013-11-19 03:41:10 +08:00
add_setting :google_docs_domain, root_only: true
add_setting :dashboard_url, root_only: true
add_setting :product_name, root_only: true
add_setting :author_email_in_notifications, boolean: true, root_only: true, default: false
add_setting :include_students_in_global_survey, boolean: true, root_only: true, default: false
add_setting :trusted_referers, root_only: true
get sub account branding and custom css/includes working fixes: CNVS-24787 fixes: CNVS-23964 fixes: CNVS-23957 - Handle parent account custom css/js for new_styles test plan: * set up a root account, child account, and grandchild account * use theme editor to set a custom css/js file for each (eg: for css `* {color:red}` and for js 'console.log("from grandchild")` * make a course & a group in the grandchild account * load a page in that course and group and make sure you see grandchild account's branding, and root's, child's, and then grandchild's css loaded on the page (grandchild should be loaded last so you see it's css effects override root or child's and you should see the console.log from root then child then grandchild) * view a page in "child". it should have root and child's css/js but not grandchild * as a user that only has enrollments (account associations) in "child", go to the dashboard. you should see css/js for both root and child but not grandchild fixes: CNVS-25051 Opening Theme Editor for sub-accounts shows incorrect theme preview test plan: * Go to a sub-account in theme editor and change settings so the Branding is different and save. * the preview on the right should reflect your changes both after you "apply" and "save" (and not just show the preview of the root account's branding) fixes: CNVS-23406 - global JS and CSS files are being included when Global CSS/JavaScript includes is false test plan: * go to /accounts/self/, and go to theme editor and upload a css_override * see that that css is loaded on pages * back in root account settings disable Global CSS/JavaScript includes * check that the css is no longer loaded. * do the same thing checking a subaccount's custom css fixes: CNVS-25558 - load whole chain of custom css/js in native app api requests test plan: * make api request for a wiki page in course in a subaccount that has custom css/js within a root account that also has custom css/js * you should see both the root account's css/js and the child account's returned in the response to test grandchild js issue jeremyp found: * go to theme editor for a grandchild account * choose a js override file (like: `console.log('first')`) * preview & apply * you should see "first" in console * go back to theme editor, pick a new file (like: `console.log('second')`) * preview & apply * you should only see "second" in console. not "first" Change-Id: I8d9047948f5da94be41e0205844629a170f980af Reviewed-on: https://gerrit.instructure.com/68249 Reviewed-by: Simon Williams <simon@instructure.com> QA-Review: Jeremy Putnam <jeremyp@instructure.com> Tested-by: Jenkins Product-Review: Ryan Shaw <ryan@instructure.com>
2015-12-05 00:57:07 +08:00
def use_new_styles_or_allow_global_includes?
feature_enabled?(:use_new_styles) || allow_global_includes?
end
2011-02-01 09:57:29 +08:00
def settings=(hash)
@invalidate_settings_cache = true
2011-02-01 09:57:29 +08:00
if hash.is_a?(Hash)
hash.each do |key, val|
if account_settings_options && account_settings_options[key.to_sym]
opts = account_settings_options[key.to_sym]
if (opts[:root_only] && !self.root_account?) || (opts[:condition] && !self.send("#{opts[:condition]}?".to_sym))
2011-02-01 09:57:29 +08:00
settings.delete key.to_sym
elsif opts[:hash]
new_hash = {}
if val.is_a?(Hash)
val.each do |inner_key, inner_val|
inner_key = inner_key.to_sym
if opts[:values].include?(inner_key)
if opts[:inheritable] && (inner_key == :locked || (inner_key == :value && opts[:boolean]))
new_hash[inner_key] = Canvas::Plugin.value_to_boolean(inner_val)
else
new_hash[inner_key] = inner_val.to_s
end
2011-02-01 09:57:29 +08:00
end
end
end
settings[key.to_sym] = new_hash.empty? ? nil : new_hash
elsif opts[:boolean]
settings[key.to_sym] = Canvas::Plugin.value_to_boolean(val)
2011-02-01 09:57:29 +08:00
else
settings[key.to_sym] = val.to_s
end
end
end
end
# prune nil or "" hash values to save space in the DB.
settings.reject! { |_, value| value.nil? || value == "" }
2011-02-01 09:57:29 +08:00
settings
end
def product_name
settings[:product_name] || t("#product_name", "Canvas")
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 allow_global_includes?
get sub account branding and custom css/includes working fixes: CNVS-24787 fixes: CNVS-23964 fixes: CNVS-23957 - Handle parent account custom css/js for new_styles test plan: * set up a root account, child account, and grandchild account * use theme editor to set a custom css/js file for each (eg: for css `* {color:red}` and for js 'console.log("from grandchild")` * make a course & a group in the grandchild account * load a page in that course and group and make sure you see grandchild account's branding, and root's, child's, and then grandchild's css loaded on the page (grandchild should be loaded last so you see it's css effects override root or child's and you should see the console.log from root then child then grandchild) * view a page in "child". it should have root and child's css/js but not grandchild * as a user that only has enrollments (account associations) in "child", go to the dashboard. you should see css/js for both root and child but not grandchild fixes: CNVS-25051 Opening Theme Editor for sub-accounts shows incorrect theme preview test plan: * Go to a sub-account in theme editor and change settings so the Branding is different and save. * the preview on the right should reflect your changes both after you "apply" and "save" (and not just show the preview of the root account's branding) fixes: CNVS-23406 - global JS and CSS files are being included when Global CSS/JavaScript includes is false test plan: * go to /accounts/self/, and go to theme editor and upload a css_override * see that that css is loaded on pages * back in root account settings disable Global CSS/JavaScript includes * check that the css is no longer loaded. * do the same thing checking a subaccount's custom css fixes: CNVS-25558 - load whole chain of custom css/js in native app api requests test plan: * make api request for a wiki page in course in a subaccount that has custom css/js within a root account that also has custom css/js * you should see both the root account's css/js and the child account's returned in the response to test grandchild js issue jeremyp found: * go to theme editor for a grandchild account * choose a js override file (like: `console.log('first')`) * preview & apply * you should see "first" in console * go back to theme editor, pick a new file (like: `console.log('second')`) * preview & apply * you should only see "second" in console. not "first" Change-Id: I8d9047948f5da94be41e0205844629a170f980af Reviewed-on: https://gerrit.instructure.com/68249 Reviewed-by: Simon Williams <simon@instructure.com> QA-Review: Jeremy Putnam <jeremyp@instructure.com> Tested-by: Jenkins Product-Review: Ryan Shaw <ryan@instructure.com>
2015-12-05 00:57:07 +08:00
if root_account?
global_includes?
else
root_account.try(:sub_account_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
def global_includes_hash
includes = {}
if allow_global_includes?
includes = {}
includes[:js] = settings[:global_javascript] if settings[:global_javascript].present?
includes[:css] = settings[:global_stylesheet] if settings[:global_stylesheet].present?
end
includes.present? ? includes : nil
end
multi-factor authentication closes #9532 test plan: * enable optional MFA, and check the following: * normal log in should not be affected * you can enroll in MFA from your profile page * you can re-enroll in MFA from your profile page * you can disable MFA from your profile page * MFA can be reset by an admin on your user page * when enrolled, you are asked for verification code after username/password when logging in * you can't access any other part of the site directly until until entering your verification code * enable required MFA, and check the following * when not enrolled in MFA, and you log in, you are forced to enroll * you cannot disable MFA from your profile page * you can re-enroll in MFA from your profile page * an admin (other than himself) can reset MFA from the user page * for enrolling in MFA * use Google Authenticator and scan the QR code; you should have 30-seconds or so of extra leeway to enter your code * having no SMS communication channels on your profile, the enrollment page should just have a form to add a new phone * having one or more SMS communication channels on your profile, the enrollment page should list them, or allow you to create a new one (and switch back) * having more than one SMS communication channel on your profile, the enrollment page should remember which one you have selected after you click "send" * an unconfirmed SMS channel should go to confirmed when it's used to enroll in MFA * you should not be able to go directly to /login/otp to enroll if you used "Remember me" token to log in * MFA login flow * if configured with SMS, it should send you an SMS after you put in your username/password; you should have about 5 minutes of leeway to put it in * if you don't check "remember computer" checkbox, you should have to enter a verification code each time you log in * if you do check it, you shouldn't have to enter your code anymore (for three days). it also shouldn't SMS you a verification code each time you log in * setting MFA to required for admins should make it required for admins, optional for other users * with MFA enabled, directly go to /login/otp after entering username/password but before entering a verification code; it should send you back to the main login page * if you enrolled via SMS, you should not be able to remove that SMS from your profile * there should not be a reset MFA link on a user page if they haven't enrolled * test a login or required enrollment sequence with CAS and/or SAML Change-Id: I692de7405bf7ca023183e717930ee940ccf0d5e6 Reviewed-on: https://gerrit.instructure.com/12700 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Brian Palmer <brianp@instructure.com>
2012-08-03 05:17:50 +08:00
def mfa_settings
settings[:mfa_settings].try(:to_sym) || :disabled
end
def non_canvas_auth_configured?
authentication_providers.active.where("auth_type<>'canvas'").exists?
end
def canvas_authentication_provider
@canvas_ap ||= authentication_providers.active.where(auth_type: 'canvas').first
end
def canvas_authentication?
!!canvas_authentication_provider
end
def enable_canvas_authentication
return unless root_account?
# for migrations creating a new db
return unless AccountAuthorizationConfig.columns_hash.key?('workflow_state')
return if authentication_providers.active.where(auth_type: 'canvas').exists?
authentication_providers.create!(auth_type: 'canvas')
end
def open_registration?
!!settings[:open_registration] && canvas_authentication?
end
def self_registration?
canvas_authentication_provider.try(:jit_provisioning?)
end
def self_registration_type
canvas_authentication_provider.try(:self_registration)
end
def self_registration_allowed_for?(type)
return false unless self_registration?
return false if self_registration_type != 'all' && type != self_registration_type
true
end
def enable_self_registration
canvas_authentication_provider.update_attribute(:self_registration, true)
end
def terms_required?
Setting.get('terms_required', 'true') == 'true' && root_account.account_terms_required?
end
def require_acceptance_of_terms?(user)
soc2_start_date = Setting.get('SOC2_start_date', Time.new(2015, 5, 16, 0, 0, 0).utc).to_datetime
return false if !terms_required?
return true if user.nil? || user.new_record?
terms_changed_at = settings[:terms_changed_at]
last_accepted = user.preferences[:accepted_terms]
# make sure existing users are grandfathered in
return false if terms_changed_at.nil? && user.registered? && user.created_at < soc2_start_date
return false if last_accepted && (terms_changed_at.nil? || last_accepted > terms_changed_at)
true
end
def ip_filters=(params)
filters = {}
require 'ipaddr'
params.each do |key, str|
ips = []
vals = str.split(/,/)
vals.each do |val|
ip = IPAddr.new(val) rescue nil
# right now the ip_filter column on quizzes is just a string,
# so it has a max length. I figure whatever we set it to this
# setter should at the very least limit stored values to that
# length.
ips << val if ip && val.length <= 255
end
filters[key] = ips.join(',') unless ips.empty?
end
settings[:ip_filters] = filters
end
2011-02-01 09:57:29 +08:00
def ensure_defaults
self.uuid ||= CanvasSlug.generate_securish_uuid
self.lti_guid ||= "#{self.uuid}:#{INSTANCE_GUID_SUFFIX}" if self.respond_to?(:lti_guid)
self.root_account_id ||= self.parent_account.root_account_id if self.parent_account
self.root_account_id ||= self.parent_account_id
self.parent_account_id ||= self.root_account_id
Account.invalidate_cache(self.id) if self.id
true
2011-02-01 09:57:29 +08:00
end
def verify_unique_sis_source_id
return true unless self.sis_source_id
return true if !root_account_id_changed? && !sis_source_id_changed?
if self.root_account?
self.errors.add(:sis_source_id, t('#account.root_account_cant_have_sis_id', "SIS IDs cannot be set on root accounts"))
return false
end
scope = root_account.all_accounts.where(sis_source_id: self.sis_source_id)
scope = scope.where("id<>?", self) unless self.new_record?
return true unless scope.exists?
self.errors.add(:sis_source_id, t('#account.sis_id_in_use', "SIS ID \"%{sis_id}\" is already in use", :sis_id => self.sis_source_id))
false
end
2011-02-01 09:57:29 +08:00
def update_account_associations_if_changed
send_later_if_production(:update_account_associations) if self.parent_account_id_changed? || self.root_account_id_changed?
2011-02-01 09:57:29 +08:00
end
2011-02-01 09:57:29 +08:00
def equella_settings
endpoint = self.settings[:equella_endpoint] || self.equella_endpoint
if !endpoint.blank?
2011-02-01 09:57:29 +08:00
OpenObject.new({
:endpoint => endpoint,
:default_action => self.settings[:equella_action] || 'selectOrAdd',
:teaser => self.settings[:equella_teaser]
2011-02-01 09:57:29 +08:00
})
else
nil
2011-02-01 09:57:29 +08:00
end
end
2011-02-01 09:57:29 +08:00
def settings
result = self.read_attribute(:settings)
return result if result
return write_attribute(:settings, {}) unless frozen?
{}.freeze
2011-02-01 09:57:29 +08:00
end
def domain
HostUrl.context_host(self)
end
def self.find_by_domain(domain)
self.default if HostUrl.default_host == domain
end
def root_account?
!self.root_account_id
end
def root_account_with_self
return self if self.root_account?
root_account_without_self
end
alias_method_chain :root_account, :self
def sub_accounts_as_options(indent = 0, preloaded_accounts = nil)
unless preloaded_accounts
preloaded_accounts = {}
self.root_account.all_accounts.active.each do |account|
(preloaded_accounts[account.parent_account_id] ||= []) << account
end
end
res = [[("&nbsp;&nbsp;" * indent).html_safe + self.name, self.id]]
if preloaded_accounts[self.id]
preloaded_accounts[self.id].each do |account|
res += account.sub_accounts_as_options(indent + 1, preloaded_accounts)
end
2011-02-01 09:57:29 +08:00
end
res
end
def users_visible_to(user)
self.grants_right?(user, :read) ? self.all_users : self.all_users.none
end
2011-02-01 09:57:29 +08:00
def users_name_like(query="")
@cached_users_name_like ||= {}
@cached_users_name_like[query] ||= self.fast_all_users.name_like(query)
end
def associated_courses
if root_account?
all_courses
else
shard.activate do
Course.where("EXISTS (?)", CourseAccountAssociation.where(account_id: self).where("course_id=courses.id"))
end
end
end
def associated_user?(user)
user_account_associations.where(user_id: user).exists?
end
def fast_course_base(opts)
columns = "courses.id, courses.name, courses.workflow_state, courses.course_code, courses.sis_source_id, courses.enrollment_term_id"
associated_courses = self.associated_courses.active
associated_courses = associated_courses.with_enrollments if opts[:hide_enrollmentless_courses]
associated_courses = associated_courses.for_term(opts[:term]) if opts[:term].present?
associated_courses = yield associated_courses if block_given?
associated_courses.limit(opts[:limit]).active_first.select(columns).to_a
end
def fast_all_courses(opts={})
2011-02-01 09:57:29 +08:00
@cached_fast_all_courses ||= {}
@cached_fast_all_courses[opts] ||= self.fast_course_base(opts)
2011-02-01 09:57:29 +08:00
end
2011-02-01 09:57:29 +08:00
def all_users(limit=250)
@cached_all_users ||= {}
@cached_all_users[limit] ||= User.of_account(self).limit(limit)
2011-02-01 09:57:29 +08:00
end
2011-02-01 09:57:29 +08:00
def fast_all_users(limit=nil)
@cached_fast_all_users ||= {}
@cached_fast_all_users[limit] ||= self.all_users(limit).active.select("users.id, users.updated_at, users.name, users.sortable_name").order_by_sortable_name
2011-02-01 09:57:29 +08:00
end
def users_not_in_groups(groups, opts={})
scope = User.active.joins(:user_account_associations).
where(user_account_associations: {account_id: self}).
where(Group.not_in_group_sql_fragment(groups.map(&:id))).
select("users.id, users.name")
scope = scope.select(opts[:order]).order(opts[:order]) if opts[:order]
scope
2011-02-01 09:57:29 +08:00
end
def courses_name_like(query="", opts={})
opts[:limit] ||= 200
@cached_courses_name_like ||= {}
@cached_courses_name_like[[query, opts]] ||= self.fast_course_base(opts) {|q| q.name_like(query)}
2011-02-01 09:57:29 +08:00
end
self enrollment refactor to facilitate CN integration fixes #CNVS-1119, potentially supersedes https://gerrit.instructure.com/14501 with a little work. simpler flow that is more consistent with FFT signup. whether you click the "join course" button (popup) or go to the join url, the workflow is the same: 1. if you are authenticated, you just click the enroll button. 2. if you are not authenticated, you can either: 1. enter your (canvas/ldap) credentials and submit to join the course. 2. register and join the course (single form). you will then be dropped on the course dashboard in the pre_registered state just like a /register signup (you have to follow the link in your email to set a password). note that if open registration is turned off, option 2.2 is not available. other items of interest: * fix CSRF vulnerabilities where you can enroll authenticated users in open courses, or un-enroll them if you know their enrollment's UUID * move to shorter course-id-less route (w/ join code) * reuse UserController#create * handy openAsDialog behavior and embedded view mode * better json support in PseudonymSessionsController#create * extract markdown helper from mt * show "you need to confirm your email" popup when you land on the course page the first time (already showed on dashboard) test plan: 1. test the authenticated/unauthenticated scenarios above, for both the popup and join pages 2. regression test of /registration forms Change-Id: I0d8351695356d437bdbba72cb66c23ed268b0d1a Reviewed-on: https://gerrit.instructure.com/15902 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Joe Tanner <joe@instructure.com> QA-Review: Jon Jensen <jon@instructure.com>
2012-12-07 14:28:37 +08:00
def self_enrollment_course_for(code)
all_courses.
where(:self_enrollment_code => code).
self enrollment refactor to facilitate CN integration fixes #CNVS-1119, potentially supersedes https://gerrit.instructure.com/14501 with a little work. simpler flow that is more consistent with FFT signup. whether you click the "join course" button (popup) or go to the join url, the workflow is the same: 1. if you are authenticated, you just click the enroll button. 2. if you are not authenticated, you can either: 1. enter your (canvas/ldap) credentials and submit to join the course. 2. register and join the course (single form). you will then be dropped on the course dashboard in the pre_registered state just like a /register signup (you have to follow the link in your email to set a password). note that if open registration is turned off, option 2.2 is not available. other items of interest: * fix CSRF vulnerabilities where you can enroll authenticated users in open courses, or un-enroll them if you know their enrollment's UUID * move to shorter course-id-less route (w/ join code) * reuse UserController#create * handy openAsDialog behavior and embedded view mode * better json support in PseudonymSessionsController#create * extract markdown helper from mt * show "you need to confirm your email" popup when you land on the course page the first time (already showed on dashboard) test plan: 1. test the authenticated/unauthenticated scenarios above, for both the popup and join pages 2. regression test of /registration forms Change-Id: I0d8351695356d437bdbba72cb66c23ed268b0d1a Reviewed-on: https://gerrit.instructure.com/15902 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Joe Tanner <joe@instructure.com> QA-Review: Jon Jensen <jon@instructure.com>
2012-12-07 14:28:37 +08:00
first
end
2011-02-01 09:57:29 +08:00
def file_namespace
Shard.birth.activate { "account_#{self.root_account.id}" }
2011-02-01 09:57:29 +08:00
end
2011-02-01 09:57:29 +08:00
def self.account_lookup_cache_key(id)
['_account_lookup4', id].cache_key
2011-02-01 09:57:29 +08:00
end
2011-02-01 09:57:29 +08:00
def self.invalidate_cache(id)
return unless id
birth_id = Shard.relative_id_for(id, Shard.current, Shard.birth)
Shard.birth.activate do
Rails.cache.delete(account_lookup_cache_key(birth_id)) if birth_id
end
rescue
2011-02-01 09:57:29 +08:00
nil
end
def setup_cache_invalidation
@invalidations = []
unless self.new_record?
@invalidations += ['default_storage_quota', 'current_quota'] if self.try_rescue(:default_storage_quota_changed?)
@invalidations << 'default_group_storage_quota' if self.try_rescue(:default_group_storage_quota_changed?)
end
end
def invalidate_caches_if_changed
@invalidations ||= []
@invalidations += Account.inheritable_settings if @invalidate_settings_cache
if @invalidations.present?
shard.activate do
@invalidations.each do |key|
Rails.cache.delete([key, self.global_id].cache_key)
end
Account.send_later_if_production(:invalidate_inherited_caches, self, @invalidations)
end
end
end
def self.invalidate_inherited_caches(parent_account, keys)
parent_account.shard.activate do
account_ids = Account.sub_account_ids_recursive(parent_account.id)
account_ids.each do |id|
global_id = Shard.global_id_for(id)
keys.each do |key|
Rails.cache.delete([key, global_id].cache_key)
end
end
end
end
def self.default_storage_quota
Setting.get('account_default_quota', 500.megabytes.to_s).to_i
end
2011-02-01 09:57:29 +08:00
def quota
return storage_quota if read_attribute(:storage_quote)
return self.class.default_storage_quota if root_account?
shard.activate do
Rails.cache.fetch(['current_quota', self.global_id].cache_key) do
self.parent_account.default_storage_quota
end
2011-02-01 09:57:29 +08:00
end
end
2011-02-01 09:57:29 +08:00
def default_storage_quota
return super if read_attribute(:default_storage_quota)
return self.class.default_storage_quota if root_account?
shard.activate do
@default_storage_quota ||= Rails.cache.fetch(['default_storage_quota', self.global_id].cache_key) do
parent_account.default_storage_quota
end
end
end
def default_storage_quota_mb
default_storage_quota / 1.megabyte
end
def default_storage_quota_mb=(val)
self.default_storage_quota = val.try(:to_i).try(:megabytes)
2011-02-01 09:57:29 +08:00
end
2011-02-01 09:57:29 +08:00
def default_storage_quota=(val)
val = val.to_f
val = nil if val <= 0
# If the value is the same as the inherited value, then go
# ahead and blank it so it keeps using the inherited value
if parent_account && parent_account.default_storage_quota == val
val = nil
end
write_attribute(:default_storage_quota, val)
end
def default_user_storage_quota
read_attribute(:default_user_storage_quota) ||
User.default_storage_quota
end
def default_user_storage_quota=(val)
val = val.to_i
val = nil if val == User.default_storage_quota || val <= 0
write_attribute(:default_user_storage_quota, val)
end
def default_user_storage_quota_mb
default_user_storage_quota / 1.megabyte
end
def default_user_storage_quota_mb=(val)
self.default_user_storage_quota = val.try(:to_i).try(:megabytes)
end
def default_group_storage_quota
return super if read_attribute(:default_group_storage_quota)
return Group.default_storage_quota if root_account?
shard.activate do
Rails.cache.fetch(['default_group_storage_quota', self.global_id].cache_key) do
self.parent_account.default_group_storage_quota
end
end
end
def default_group_storage_quota=(val)
val = val.to_i
if (val == Group.default_storage_quota) || (val <= 0) ||
(self.parent_account && self.parent_account.default_group_storage_quota == val)
val = nil
end
write_attribute(:default_group_storage_quota, val)
end
def default_group_storage_quota_mb
default_group_storage_quota / 1.megabyte
end
def default_group_storage_quota_mb=(val)
self.default_group_storage_quota = val.try(:to_i).try(:megabytes)
end
2011-02-01 09:57:29 +08:00
def turnitin_shared_secret=(secret)
return if secret.blank?
self.turnitin_crypted_secret, self.turnitin_salt = Canvas::Security.encrypt_password(secret, 'instructure_turnitin_secret_shared')
end
2011-02-01 09:57:29 +08:00
def turnitin_shared_secret
return nil unless self.turnitin_salt && self.turnitin_crypted_secret
Canvas::Security.decrypt_password(self.turnitin_crypted_secret, self.turnitin_salt, 'instructure_turnitin_secret_shared')
end
def self.account_chain(starting_account_id)
chain = []
if (starting_account_id.is_a?(Account))
chain << starting_account_id
starting_account_id = starting_account_id.parent_account_id
end
if starting_account_id
if ActiveRecord::Base.configurations[Rails.env]['adapter'] == 'postgresql'
chain.concat(Shard.shard_for(starting_account_id).activate do
Account.find_by_sql(<<-SQL)
WITH RECURSIVE t AS (
SELECT * FROM #{Account.quoted_table_name} WHERE id=#{Shard.local_id_for(starting_account_id).first}
UNION
SELECT accounts.* FROM #{Account.quoted_table_name} INNER JOIN t ON accounts.id=t.parent_account_id
)
SELECT * FROM t
SQL
end)
else
account = Account.find(starting_account_id)
chain << account
while account.parent_account
account = account.parent_account
chain << account
end
end
2011-02-01 09:57:29 +08:00
end
chain
end
def self.add_site_admin_to_chain!(chain)
chain << Account.site_admin unless chain.last.site_admin?
chain
end
def account_chain(include_site_admin: false)
@account_chain ||= Account.account_chain(self)
result = @account_chain.dup
Account.add_site_admin_to_chain!(result) if include_site_admin
result
2011-02-01 09:57:29 +08:00
end
def account_chain_loop
# this record hasn't been saved to the db yet, so if the the chain includes
# this account, it won't point to the new parent yet, and should still be
# valid
if self.parent_account.account_chain.include?(self)
errors.add(:parent_account_id,
"Setting account #{self.sis_source_id || self.id}'s parent to #{self.parent_account.sis_source_id || self.parent_account_id} would create a loop")
end
end
# returns all sub_accounts recursively as far down as they go, in id order
# because this uses a custom sql query for postgresql, we can't use a normal
# named scope, so we pass the limit and offset into the method instead and
# build our own query string
def sub_accounts_recursive(limit, offset)
if ActiveRecord::Base.configurations[Rails.env]['adapter'] == 'postgresql'
Account.find_by_sql([<<-SQL, self.id, limit.to_i, offset.to_i])
WITH RECURSIVE t AS (
SELECT * FROM #{Account.quoted_table_name}
WHERE parent_account_id = ? AND workflow_state <>'deleted'
UNION
SELECT accounts.* FROM #{Account.quoted_table_name}
INNER JOIN t ON accounts.parent_account_id = t.id
WHERE accounts.workflow_state <>'deleted'
)
SELECT * FROM t ORDER BY parent_account_id, id LIMIT ? OFFSET ?
SQL
else
account_descendents = lambda do |id|
as = Account.where(:parent_account_id => id).active.order(:id)
as.empty? ?
[] :
as << as.map { |a| account_descendents.call(a.id) }
end
account_descendents.call(id).flatten[offset, limit]
end
end
def self.sub_account_ids_recursive(parent_account_id)
if connection.adapter_name == 'PostgreSQL'
sql = "
WITH RECURSIVE t AS (
SELECT id, parent_account_id FROM #{Account.quoted_table_name}
WHERE parent_account_id = #{parent_account_id} AND workflow_state <> 'deleted'
UNION
SELECT accounts.id, accounts.parent_account_id FROM #{Account.quoted_table_name}
INNER JOIN t ON accounts.parent_account_id = t.id
WHERE accounts.workflow_state <> 'deleted'
)
SELECT id FROM t"
Account.find_by_sql(sql).map(&:id)
else
account_descendants = lambda do |ids|
as = Account.where(:parent_account_id => ids).active.pluck(:id)
as + account_descendants.call(as)
end
account_descendants.call([parent_account_id])
end
end
def associated_accounts
self.account_chain
end
2011-02-01 09:57:29 +08:00
def membership_for_user(user)
self.account_users.where(user_id: user).first if user
2011-02-01 09:57:29 +08:00
end
def available_custom_account_roles(include_inactive=false)
available_custom_roles(include_inactive).for_accounts
Add new permissions management ui (role overrides) When editing permissions for an account, course or admin section the UI has changed to use drop downs instead of a 6 state check box. It has also been switched to use 100% backbonejs to handle creating and editing roles. This works with the roles api and allows you to create custom roles for courses. fixes #CNVS-1165 Test Plan Apply this test in two places. The Site Admin and a custom university. 1. Go to the "permissions" tab 2. Notice permissions are available per role in a table. ------------ Adding/Removing Roles -------------- 1. When under the "Account Role" tab click "Add Role" 2. Enter a role name and click "Create/Add" 3. A new role should appear automatically. 4. You should be able to delete this role by clicking the x next to it's name. ------------ Editing Permissions ---------------- 1. Go to a role in the permissions tab. 2. Try to edit one of it's permissions. It should have a drop down with options to select permissions. You should be able to enable/disable or set to default and lock the permission. Read only permissions cannot be clicked on. 3. Buttons with default set should have a special "default" class added to the button representing its permission. ------------ Organization of Roles -------------- 1. In the course role tab, add a few roles with different base types (use the dropwdown) 2. Roles should be grouped together by base role type. ------------ Automatic Saving ------------------ 1. Change a permission on a role 2. The role should automatically save the the permission after selecting the option you want Change-Id: I343afc36b85183e5913c8eef6111ea2c5ae62726 Reviewed-on: https://gerrit.instructure.com/16323 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Bracken Mosbacker <bracken@instructure.com> QA-Review: Adam Phillipps <adam@instructure.com>
2012-12-27 04:09:58 +08:00
end
def available_account_roles(include_inactive=false, user = nil)
account_roles = available_custom_account_roles(include_inactive)
account_roles << Role.get_built_in_role('AccountAdmin')
if user
account_roles.select! { |role| au = account_users.new; au.role_id = role.id; au.grants_right?(user, :create) }
end
account_roles
2011-02-01 09:57:29 +08:00
end
def available_custom_course_roles(include_inactive=false)
available_custom_roles(include_inactive).for_courses
end
def available_course_roles(include_inactive=false)
course_roles = available_custom_course_roles(include_inactive)
course_roles += Role.built_in_course_roles
course_roles
end
def available_custom_roles(include_inactive=false)
@role_chain_ids ||= self.account_chain.map(&:id)
scope = Role.where(:account_id => @role_chain_ids)
scope = include_inactive ? scope.not_deleted : scope.active
scope
end
def available_roles(include_inactive=false)
available_account_roles(include_inactive) + available_course_roles(include_inactive)
end
def get_account_role_by_name(role_name)
role = get_role_by_name(role_name)
return role if role && role.account_role?
end
def get_course_role_by_name(role_name)
role = get_role_by_name(role_name)
return role if role && role.course_role?
end
def get_role_by_name(role_name)
if role = Role.get_built_in_role(role_name)
return role
end
self.shard.activate do
role_scope = Role.not_deleted.where(:name => role_name)
if self.class.connection.adapter_name == 'PostgreSQL'
role_scope = role_scope.where("account_id = ? OR
account_id IN (
WITH RECURSIVE t AS (
SELECT id, parent_account_id FROM #{Account.quoted_table_name} WHERE id = ?
UNION
SELECT accounts.id, accounts.parent_account_id FROM #{Account.quoted_table_name} INNER JOIN t ON accounts.id=t.parent_account_id
)
SELECT id FROM t
)", self.id, self.id)
else
role_scope = role_scope.where(:account_id => self.account_chain.map(&:id))
end
role_scope.first
end
end
def get_role_by_id(role_id)
role = Role.get_role_by_id(role_id)
return role if valid_role?(role)
end
def valid_role?(role)
role && (role.built_in? || (self.id == role.account_id) || self.account_chain.map(&:id).include?(role.account_id))
end
def login_handle_name_is_customized?
refactor views for unified AAC administration closes CNVS-20076 First, pull a presenter out of the AAC index This thing needs more flexibility before introducing multiple OAuth connectors. This commit adds a couple characterization specs for the AAC controller, then drives out a presenter to pull as much logic and config out of the nested views as possible. Then, this commit refactors the previously-somewhat-bespoke-and-presumptive sac configuration into a workflow that shows each aac in turn according to it's type, creating forms for each type at the bottom, and showing the relevant form for a new one based on selection on the right. Have regression tested in the browser to the level of CRUD functionality, but also deserves solid QA for SSO functionality post-configuration. DONE: -successful CAS creation/editing/deletion -successful LDAP creation/editing/deletion -proper differentiation between LDAP primary and secondary -proper SAML creation/editing -move away from "update_all" deprecated endpoint -Selenium Spec fixes -ensure discovery URL and debugging workflows for saml -remove duplication from views -tear down old JS workflow -apply appropriate tests for new behavior -remove presenter methods that are no longer valuable -Moved change_password_url and login_handle_name -up to account settings, removed them from AACs, and built -migrations to manage the transition. -Found and fixed all references to change_password_url on AACs -Found and fixes all references to login_handle_name on AACs -add datafixup for migrating AAC data to account settings -unify repetative individual files into single form delcarations \o/ -remove old SAML editing js -Make sure SAML still works -Make LDAP partial flow just like SAML/CAS -Unify position information across all types -update "acts_as_list" to support STI classes -move discovery URL into account auth form -remove discover URL js management -Unify form generation between new/existing aacs -deprecate discovery url API endpoints -update docs for authorization settings to deprecate their usage in AAC api and redirect their values to current settings for now -make delete links non-js-y to stop this silly page refresh on api completion -make form submissions actually submit the form rather than do this silly page refresh on api completion -See if anything needs “Edit Details” button, remove if not -Wire up removing account settings by blanking out form -Removed "cancel" button from form because fields are always open -placate gergich -Test removing config info -Test population fixup on real data -write docs for authorization settings -fix existing specs -fix routing and docs to not break doc generation -fix stupid jenkins task that thinks it can’t see controls -re-fix selenium -fix saml debugging workflow -write tests for acts_as_list behavior -write tests for authorization settings -remove auth_info types of things -clean up and unify styles where possible TEST PLAN: Regression test creating/deleting/editing and logging in with SSO solutions for CAS, LDAP, and SAML. Should be no functional behavior modification, though workflow will be a little more unified between the 3 currently supported types (each one will require using the menu in the right sidebar to add a new AAC). Also test setting and deleting account settings through the form underneath the configs when there are AACs in existence. Finally, make sure that the SAML Debugging workflow still works. Change-Id: I448db10185512d1b9469c2a425be0a3bcf9e6ebf Reviewed-on: https://gerrit.instructure.com/53448 Tested-by: Jenkins Reviewed-by: Cody Cutrer <cody@instructure.com> QA-Review: August Thornton <august@instructure.com> Product-Review: Ethan Vizitei <evizitei@instructure.com>
2015-04-28 05:23:04 +08:00
self.login_handle_name.present?
end
refactor views for unified AAC administration closes CNVS-20076 First, pull a presenter out of the AAC index This thing needs more flexibility before introducing multiple OAuth connectors. This commit adds a couple characterization specs for the AAC controller, then drives out a presenter to pull as much logic and config out of the nested views as possible. Then, this commit refactors the previously-somewhat-bespoke-and-presumptive sac configuration into a workflow that shows each aac in turn according to it's type, creating forms for each type at the bottom, and showing the relevant form for a new one based on selection on the right. Have regression tested in the browser to the level of CRUD functionality, but also deserves solid QA for SSO functionality post-configuration. DONE: -successful CAS creation/editing/deletion -successful LDAP creation/editing/deletion -proper differentiation between LDAP primary and secondary -proper SAML creation/editing -move away from "update_all" deprecated endpoint -Selenium Spec fixes -ensure discovery URL and debugging workflows for saml -remove duplication from views -tear down old JS workflow -apply appropriate tests for new behavior -remove presenter methods that are no longer valuable -Moved change_password_url and login_handle_name -up to account settings, removed them from AACs, and built -migrations to manage the transition. -Found and fixed all references to change_password_url on AACs -Found and fixes all references to login_handle_name on AACs -add datafixup for migrating AAC data to account settings -unify repetative individual files into single form delcarations \o/ -remove old SAML editing js -Make sure SAML still works -Make LDAP partial flow just like SAML/CAS -Unify position information across all types -update "acts_as_list" to support STI classes -move discovery URL into account auth form -remove discover URL js management -Unify form generation between new/existing aacs -deprecate discovery url API endpoints -update docs for authorization settings to deprecate their usage in AAC api and redirect their values to current settings for now -make delete links non-js-y to stop this silly page refresh on api completion -make form submissions actually submit the form rather than do this silly page refresh on api completion -See if anything needs “Edit Details” button, remove if not -Wire up removing account settings by blanking out form -Removed "cancel" button from form because fields are always open -placate gergich -Test removing config info -Test population fixup on real data -write docs for authorization settings -fix existing specs -fix routing and docs to not break doc generation -fix stupid jenkins task that thinks it can’t see controls -re-fix selenium -fix saml debugging workflow -write tests for acts_as_list behavior -write tests for authorization settings -remove auth_info types of things -clean up and unify styles where possible TEST PLAN: Regression test creating/deleting/editing and logging in with SSO solutions for CAS, LDAP, and SAML. Should be no functional behavior modification, though workflow will be a little more unified between the 3 currently supported types (each one will require using the menu in the right sidebar to add a new AAC). Also test setting and deleting account settings through the form underneath the configs when there are AACs in existence. Finally, make sure that the SAML Debugging workflow still works. Change-Id: I448db10185512d1b9469c2a425be0a3bcf9e6ebf Reviewed-on: https://gerrit.instructure.com/53448 Tested-by: Jenkins Reviewed-by: Cody Cutrer <cody@instructure.com> QA-Review: August Thornton <august@instructure.com> Product-Review: Ethan Vizitei <evizitei@instructure.com>
2015-04-28 05:23:04 +08:00
def login_handle_name_with_inference
if login_handle_name_is_customized?
refactor views for unified AAC administration closes CNVS-20076 First, pull a presenter out of the AAC index This thing needs more flexibility before introducing multiple OAuth connectors. This commit adds a couple characterization specs for the AAC controller, then drives out a presenter to pull as much logic and config out of the nested views as possible. Then, this commit refactors the previously-somewhat-bespoke-and-presumptive sac configuration into a workflow that shows each aac in turn according to it's type, creating forms for each type at the bottom, and showing the relevant form for a new one based on selection on the right. Have regression tested in the browser to the level of CRUD functionality, but also deserves solid QA for SSO functionality post-configuration. DONE: -successful CAS creation/editing/deletion -successful LDAP creation/editing/deletion -proper differentiation between LDAP primary and secondary -proper SAML creation/editing -move away from "update_all" deprecated endpoint -Selenium Spec fixes -ensure discovery URL and debugging workflows for saml -remove duplication from views -tear down old JS workflow -apply appropriate tests for new behavior -remove presenter methods that are no longer valuable -Moved change_password_url and login_handle_name -up to account settings, removed them from AACs, and built -migrations to manage the transition. -Found and fixed all references to change_password_url on AACs -Found and fixes all references to login_handle_name on AACs -add datafixup for migrating AAC data to account settings -unify repetative individual files into single form delcarations \o/ -remove old SAML editing js -Make sure SAML still works -Make LDAP partial flow just like SAML/CAS -Unify position information across all types -update "acts_as_list" to support STI classes -move discovery URL into account auth form -remove discover URL js management -Unify form generation between new/existing aacs -deprecate discovery url API endpoints -update docs for authorization settings to deprecate their usage in AAC api and redirect their values to current settings for now -make delete links non-js-y to stop this silly page refresh on api completion -make form submissions actually submit the form rather than do this silly page refresh on api completion -See if anything needs “Edit Details” button, remove if not -Wire up removing account settings by blanking out form -Removed "cancel" button from form because fields are always open -placate gergich -Test removing config info -Test population fixup on real data -write docs for authorization settings -fix existing specs -fix routing and docs to not break doc generation -fix stupid jenkins task that thinks it can’t see controls -re-fix selenium -fix saml debugging workflow -write tests for acts_as_list behavior -write tests for authorization settings -remove auth_info types of things -clean up and unify styles where possible TEST PLAN: Regression test creating/deleting/editing and logging in with SSO solutions for CAS, LDAP, and SAML. Should be no functional behavior modification, though workflow will be a little more unified between the 3 currently supported types (each one will require using the menu in the right sidebar to add a new AAC). Also test setting and deleting account settings through the form underneath the configs when there are AACs in existence. Finally, make sure that the SAML Debugging workflow still works. Change-Id: I448db10185512d1b9469c2a425be0a3bcf9e6ebf Reviewed-on: https://gerrit.instructure.com/53448 Tested-by: Jenkins Reviewed-by: Cody Cutrer <cody@instructure.com> QA-Review: August Thornton <august@instructure.com> Product-Review: Ethan Vizitei <evizitei@instructure.com>
2015-04-28 05:23:04 +08:00
self.login_handle_name
elsif self.delegated_authentication?
AccountAuthorizationConfig.default_delegated_login_handle_name
else
AccountAuthorizationConfig.default_login_handle_name
end
2011-02-01 09:57:29 +08:00
end
2011-02-01 09:57:29 +08:00
def self_and_all_sub_accounts
@self_and_all_sub_accounts ||= Account.where("root_account_id=? OR parent_account_id=?", self, self).pluck(:id).uniq + [self.id]
2011-02-01 09:57:29 +08:00
end
2011-02-01 09:57:29 +08:00
workflow do
state :active
state :deleted
end
def account_users_for(user)
return [] unless user
@account_users_cache ||= {}
if self == Account.site_admin
shard.activate do
@account_users_cache[user.global_id] ||= begin
all_site_admin_account_users_hash = MultiCache.fetch("all_site_admin_account_users3") do
# this is a plain ruby hash to keep the cached portion as small as possible
self.account_users.inject({}) { |result, au| result[au.user_id] ||= []; result[au.user_id] << [au.id, au.role_id]; result }
end
(all_site_admin_account_users_hash[user.id] || []).map do |(id, role_id)|
au = AccountUser.new
au.id = id
au.account = Account.site_admin
au.user = user
au.role_id = role_id
au.readonly!
au
end
end
end
else
@account_chain_ids ||= self.account_chain(:include_site_admin => true).map { |a| a.active? ? a.id : nil }.compact
@account_users_cache[user.global_id] ||= Shard.partition_by_shard(@account_chain_ids) do |account_chain_ids|
if account_chain_ids == [Account.site_admin.id]
Account.site_admin.account_users_for(user)
else
AccountUser.where(:account_id => account_chain_ids, :user_id => user).to_a
end
end
end
@account_users_cache[user.global_id] ||= []
@account_users_cache[user.global_id]
end
# returns all account users for this entire account tree
def all_account_users_for(user)
raise "must be a root account" unless self.root_account?
Shard.partition_by_shard(account_chain(include_site_admin: true).uniq) do |accounts|
next unless user.associated_shards.include?(Shard.current)
AccountUser.eager_load(:account).where("user_id=? AND (root_account_id IN (?) OR account_id IN (?))", user, accounts, accounts)
end
end
2011-02-01 09:57:29 +08:00
set_policy do
enrollment_types = RoleOverride.enrollment_type_labels.map { |role| role[:name] }
RoleOverride.permissions.each do |permission, details|
add support for applying role overrides to just self or just descendants * wrap RoleOverride#permission_for with enabled_for? that also takes a context of where the permission is being applied, and recalculates its enabled-ness relative to that context; use that for checking account admin and enrollment permissions * refactor User#can_masquerade to properly check for descendant permissions test plan: * create a custom role in site admin. give it permission to manage permissions * in script/console, find that override and set apply_to_self=false * add a user to that role, and login as that user * the user should not be able to change permissions in site admin * the user should be able to change permissions in the default account * add another role in site admin. give it permission to manage permissions * in script/console, find the override and set apply_to_self=true, apply_to_descendants=false * add another user to that role, and login as that user * the user should be able to change permissions in site admin * the user should not be able to change permissions in the default account * the first user should not be able to masquerade as the second user and vice versa * an Account Admin should be able to masquerade as either user * create a custom role in the default account, give it permission to manage permissions, and add a user to that role * the first user should be able to masquerade as the new user; the second user should not be able to masquerade as the new user * general regression tests on permissions and masquerading Change-Id: I20a1183b7dfec419634a92cda498f245187060ef Reviewed-on: https://gerrit.instructure.com/15896 Reviewed-by: Cody Cutrer <cody@instructure.com> QA-Review: Cody Cutrer <cody@instructure.com> Tested-by: Cody Cutrer <cody@instructure.com>
2012-12-07 07:15:53 +08:00
given { |user| self.account_users_for(user).any? { |au| au.has_permission_to?(self, permission) && (!details[:if] || send(details[:if])) } }
can permission
can :create_courses if permission == :manage_courses
next unless details[:account_only]
((details[:available_to] | details[:true_for]) & enrollment_types).each do |role_name|
given { |user|
user && RoleOverride.permission_for(self, permission, Role.get_built_in_role(role_name))[:enabled] &&
self.course_account_associations.joins("INNER JOIN #{Enrollment.quoted_table_name} ON course_account_associations.course_id=enrollments.course_id").
where("enrollments.type=? AND enrollments.workflow_state IN ('active', 'completed') AND user_id=?", role_name, user).first &&
(!details[:if] || send(details[:if])) }
can permission
end
2011-02-01 09:57:29 +08:00
end
given { |user| !self.account_users_for(user).empty? }
can :read and can :manage and can :update and can :delete and can :read_outcomes
given { |user|
result = false
if !root_account.site_admin? && user
scope = root_account.enrollments.active.where(user_id: user)
result = root_account.teachers_can_create_courses? &&
scope.where(:type => ['TeacherEnrollment', 'DesignerEnrollment']).exists?
result ||= root_account.students_can_create_courses? &&
scope.where(:type => ['StudentEnrollment', 'ObserverEnrollment']).exists?
result ||= root_account.no_enrollments_can_create_courses? &&
!scope.exists?
end
result
}
can :create_courses
# any logged in user can read global outcomes, but must be checked against the site admin
given{ |user| self.site_admin? && user }
can :read_global_outcomes
# any user with an association to this account can read the outcomes in the account
given{ |user| user && self.user_account_associations.where(user_id: user).exists? }
can :read_outcomes
# any user with an admin enrollment in one of the courses can read
given { |user| user && self.courses.where(:id => user.enrollments.admin.pluck(:course_id)).exists? }
can :read
2011-02-01 09:57:29 +08:00
end
alias_method :destroy_permanently!, :destroy
2011-02-01 09:57:29 +08:00
def destroy
self.workflow_state = 'deleted'
self.deleted_at = Time.now.utc
2011-02-01 09:57:29 +08:00
save!
end
2011-02-01 09:57:29 +08:00
def to_atom
Atom::Entry.new do |entry|
entry.title = self.name
entry.updated = self.updated_at
entry.published = self.created_at
entry.links << Atom::Link.new(:rel => 'alternate',
2011-02-01 09:57:29 +08:00
:href => "/accounts/#{self.id}")
end
end
2011-02-01 09:57:29 +08:00
def default_enrollment_term
return @default_enrollment_term if @default_enrollment_term
if self.root_account?
@default_enrollment_term = self.enrollment_terms.active.where(name: EnrollmentTerm::DEFAULT_TERM_NAME).first_or_create
end
2011-02-01 09:57:29 +08:00
end
2011-02-01 09:57:29 +08:00
def context_code
raise "DONT USE THIS, use .short_name instead" unless Rails.env.production?
2011-02-01 09:57:29 +08:00
end
2011-02-01 09:57:29 +08:00
def short_name
name
end
self enrollment refactor to facilitate CN integration fixes #CNVS-1119, potentially supersedes https://gerrit.instructure.com/14501 with a little work. simpler flow that is more consistent with FFT signup. whether you click the "join course" button (popup) or go to the join url, the workflow is the same: 1. if you are authenticated, you just click the enroll button. 2. if you are not authenticated, you can either: 1. enter your (canvas/ldap) credentials and submit to join the course. 2. register and join the course (single form). you will then be dropped on the course dashboard in the pre_registered state just like a /register signup (you have to follow the link in your email to set a password). note that if open registration is turned off, option 2.2 is not available. other items of interest: * fix CSRF vulnerabilities where you can enroll authenticated users in open courses, or un-enroll them if you know their enrollment's UUID * move to shorter course-id-less route (w/ join code) * reuse UserController#create * handy openAsDialog behavior and embedded view mode * better json support in PseudonymSessionsController#create * extract markdown helper from mt * show "you need to confirm your email" popup when you land on the course page the first time (already showed on dashboard) test plan: 1. test the authenticated/unauthenticated scenarios above, for both the popup and join pages 2. regression test of /registration forms Change-Id: I0d8351695356d437bdbba72cb66c23ed268b0d1a Reviewed-on: https://gerrit.instructure.com/15902 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Joe Tanner <joe@instructure.com> QA-Review: Jon Jensen <jon@instructure.com>
2012-12-07 14:28:37 +08:00
# can be set/overridden by plugin to enforce email pseudonyms
attr_accessor :email_pseudonyms
def password_policy
Canvas::PasswordPolicy.default_policy.merge(settings[:password_policy] || {})
end
def delegated_authentication?
authentication_providers.active.first.is_a?(AccountAuthorizationConfig::Delegated)
end
def forgot_password_external_url
refactor views for unified AAC administration closes CNVS-20076 First, pull a presenter out of the AAC index This thing needs more flexibility before introducing multiple OAuth connectors. This commit adds a couple characterization specs for the AAC controller, then drives out a presenter to pull as much logic and config out of the nested views as possible. Then, this commit refactors the previously-somewhat-bespoke-and-presumptive sac configuration into a workflow that shows each aac in turn according to it's type, creating forms for each type at the bottom, and showing the relevant form for a new one based on selection on the right. Have regression tested in the browser to the level of CRUD functionality, but also deserves solid QA for SSO functionality post-configuration. DONE: -successful CAS creation/editing/deletion -successful LDAP creation/editing/deletion -proper differentiation between LDAP primary and secondary -proper SAML creation/editing -move away from "update_all" deprecated endpoint -Selenium Spec fixes -ensure discovery URL and debugging workflows for saml -remove duplication from views -tear down old JS workflow -apply appropriate tests for new behavior -remove presenter methods that are no longer valuable -Moved change_password_url and login_handle_name -up to account settings, removed them from AACs, and built -migrations to manage the transition. -Found and fixed all references to change_password_url on AACs -Found and fixes all references to login_handle_name on AACs -add datafixup for migrating AAC data to account settings -unify repetative individual files into single form delcarations \o/ -remove old SAML editing js -Make sure SAML still works -Make LDAP partial flow just like SAML/CAS -Unify position information across all types -update "acts_as_list" to support STI classes -move discovery URL into account auth form -remove discover URL js management -Unify form generation between new/existing aacs -deprecate discovery url API endpoints -update docs for authorization settings to deprecate their usage in AAC api and redirect their values to current settings for now -make delete links non-js-y to stop this silly page refresh on api completion -make form submissions actually submit the form rather than do this silly page refresh on api completion -See if anything needs “Edit Details” button, remove if not -Wire up removing account settings by blanking out form -Removed "cancel" button from form because fields are always open -placate gergich -Test removing config info -Test population fixup on real data -write docs for authorization settings -fix existing specs -fix routing and docs to not break doc generation -fix stupid jenkins task that thinks it can’t see controls -re-fix selenium -fix saml debugging workflow -write tests for acts_as_list behavior -write tests for authorization settings -remove auth_info types of things -clean up and unify styles where possible TEST PLAN: Regression test creating/deleting/editing and logging in with SSO solutions for CAS, LDAP, and SAML. Should be no functional behavior modification, though workflow will be a little more unified between the 3 currently supported types (each one will require using the menu in the right sidebar to add a new AAC). Also test setting and deleting account settings through the form underneath the configs when there are AACs in existence. Finally, make sure that the SAML Debugging workflow still works. Change-Id: I448db10185512d1b9469c2a425be0a3bcf9e6ebf Reviewed-on: https://gerrit.instructure.com/53448 Tested-by: Jenkins Reviewed-by: Cody Cutrer <cody@instructure.com> QA-Review: August Thornton <august@instructure.com> Product-Review: Ethan Vizitei <evizitei@instructure.com>
2015-04-28 05:23:04 +08:00
self.change_password_url
end
def auth_discovery_url=(url)
self.settings[:auth_discovery_url] = url
end
def auth_discovery_url
self.settings[:auth_discovery_url]
end
refactor views for unified AAC administration closes CNVS-20076 First, pull a presenter out of the AAC index This thing needs more flexibility before introducing multiple OAuth connectors. This commit adds a couple characterization specs for the AAC controller, then drives out a presenter to pull as much logic and config out of the nested views as possible. Then, this commit refactors the previously-somewhat-bespoke-and-presumptive sac configuration into a workflow that shows each aac in turn according to it's type, creating forms for each type at the bottom, and showing the relevant form for a new one based on selection on the right. Have regression tested in the browser to the level of CRUD functionality, but also deserves solid QA for SSO functionality post-configuration. DONE: -successful CAS creation/editing/deletion -successful LDAP creation/editing/deletion -proper differentiation between LDAP primary and secondary -proper SAML creation/editing -move away from "update_all" deprecated endpoint -Selenium Spec fixes -ensure discovery URL and debugging workflows for saml -remove duplication from views -tear down old JS workflow -apply appropriate tests for new behavior -remove presenter methods that are no longer valuable -Moved change_password_url and login_handle_name -up to account settings, removed them from AACs, and built -migrations to manage the transition. -Found and fixed all references to change_password_url on AACs -Found and fixes all references to login_handle_name on AACs -add datafixup for migrating AAC data to account settings -unify repetative individual files into single form delcarations \o/ -remove old SAML editing js -Make sure SAML still works -Make LDAP partial flow just like SAML/CAS -Unify position information across all types -update "acts_as_list" to support STI classes -move discovery URL into account auth form -remove discover URL js management -Unify form generation between new/existing aacs -deprecate discovery url API endpoints -update docs for authorization settings to deprecate their usage in AAC api and redirect their values to current settings for now -make delete links non-js-y to stop this silly page refresh on api completion -make form submissions actually submit the form rather than do this silly page refresh on api completion -See if anything needs “Edit Details” button, remove if not -Wire up removing account settings by blanking out form -Removed "cancel" button from form because fields are always open -placate gergich -Test removing config info -Test population fixup on real data -write docs for authorization settings -fix existing specs -fix routing and docs to not break doc generation -fix stupid jenkins task that thinks it can’t see controls -re-fix selenium -fix saml debugging workflow -write tests for acts_as_list behavior -write tests for authorization settings -remove auth_info types of things -clean up and unify styles where possible TEST PLAN: Regression test creating/deleting/editing and logging in with SSO solutions for CAS, LDAP, and SAML. Should be no functional behavior modification, though workflow will be a little more unified between the 3 currently supported types (each one will require using the menu in the right sidebar to add a new AAC). Also test setting and deleting account settings through the form underneath the configs when there are AACs in existence. Finally, make sure that the SAML Debugging workflow still works. Change-Id: I448db10185512d1b9469c2a425be0a3bcf9e6ebf Reviewed-on: https://gerrit.instructure.com/53448 Tested-by: Jenkins Reviewed-by: Cody Cutrer <cody@instructure.com> QA-Review: August Thornton <august@instructure.com> Product-Review: Ethan Vizitei <evizitei@instructure.com>
2015-04-28 05:23:04 +08:00
def login_handle_name=(handle_name)
self.settings[:login_handle_name] = handle_name
end
def login_handle_name
self.settings[:login_handle_name]
end
def change_password_url=(change_password_url)
self.settings[:change_password_url] = change_password_url
end
def change_password_url
self.settings[:change_password_url]
end
def unknown_user_url=(unknown_user_url)
self.settings[:unknown_user_url] = unknown_user_url
end
def unknown_user_url
self.settings[:unknown_user_url]
end
def validate_auth_discovery_url
return if self.settings[:auth_discovery_url].blank?
begin
value, uri = CanvasHttp.validate_url(self.settings[:auth_discovery_url])
self.auth_discovery_url = value
rescue URI::Error, ArgumentError
errors.add(:discovery_url, t('errors.invalid_discovery_url', "The discovery URL is not valid" ))
end
end
2011-02-01 09:57:29 +08:00
def find_courses(string)
self.all_courses.select{|c| c.name.match(string) }
end
2011-02-01 09:57:29 +08:00
def find_users(string)
self.pseudonyms.map{|p| p.user }.select{|u| u.name.match(string) }
end
class << self
def special_accounts
@special_accounts ||= {}
end
def special_account_ids
@special_account_ids ||= {}
end
def special_account_timed_cache
@special_account_timed_cache ||= TimedCache.new(-> { Setting.get('account_special_account_cache_time', 60).to_i.seconds.ago }) do
special_accounts.clear
end
end
def clear_special_account_cache!(force = false)
special_account_timed_cache.clear(force)
end
def define_special_account(key, name = nil)
name ||= key.to_s.titleize
instance_eval <<-RUBY
def self.#{key}(force_create = false)
get_special_account(:#{key}, #{name.inspect}, force_create)
end
RUBY
end
2011-02-01 09:57:29 +08:00
end
define_special_account(:default, 'Default Account')
define_special_account(:site_admin)
2011-02-01 09:57:29 +08:00
# an opportunity for plugins to load some other stuff up before caching the account
def precache
end
2011-02-01 09:57:29 +08:00
class ::Canvas::AccountCacheError < StandardError; end
def self.find_cached(id)
birth_id = Shard.relative_id_for(id, Shard.current, Shard.birth)
Shard.birth.activate do
Rails.cache.fetch(account_lookup_cache_key(birth_id)) do
begin
account = Account.find(birth_id)
rescue ActiveRecord::RecordNotFound => e
raise ::Canvas::AccountCacheError, e.message
end
account.precache
account
end
end
end
def self.get_special_account(special_account_type, default_account_name, force_create = false)
Shard.birth.activate do
account = special_accounts[special_account_type]
unless account
special_account_id = special_account_ids[special_account_type] ||= Setting.get("#{special_account_type}_account_id", nil)
begin
account = special_accounts[special_account_type] = Account.find_cached(special_account_id) if special_account_id
rescue ::Canvas::AccountCacheError
raise unless Rails.env.test?
end
end
# another process (i.e. selenium spec) may have changed the setting
unless account
special_account_id = Setting.get("#{special_account_type}_account_id", nil)
if special_account_id && special_account_id != special_account_ids[special_account_type]
special_account_ids[special_account_type] = special_account_id
account = special_accounts[special_account_type] = Account.where(id: special_account_id).first
end
end
if !account && default_account_name && ((!special_account_id && !Rails.env.production?) || force_create)
t '#account.default_site_administrator_account_name', 'Site Admin'
t '#account.default_account_name', 'Default Account'
account = special_accounts[special_account_type] = Account.new(:name => default_account_name)
account.save!
Setting.set("#{special_account_type}_account_id", account.id)
special_account_ids[special_account_type] = account.id
end
account
end
2011-02-01 09:57:29 +08:00
end
def site_admin?
self == Account.site_admin
2011-02-01 09:57:29 +08:00
end
def display_name
self.name
end
# Updates account associations for all the courses and users associated with this account
def update_account_associations
self.shard.activate do
account_chain_cache = {}
all_user_ids = Set.new
# make sure to use the non-associated_courses associations
# to catch courses that didn't ever have an association created
scopes = if root_account?
[all_courses,
associated_courses.
where("root_account_id<>?", self)]
else
[courses,
associated_courses.
where("courses.account_id<>?", self)]
end
# match the "batch" size in Course.update_account_associations
scopes.each do |scope|
scope.select([:id, :account_id]).find_in_batches(:batch_size => 500) do |courses|
all_user_ids.merge Course.update_account_associations(courses, :skip_user_account_associations => true, :account_chain_cache => account_chain_cache)
end
end
# Make sure we have all users with existing account associations.
all_user_ids.merge self.user_account_associations.pluck(:user_id)
if root_account?
all_user_ids.merge self.pseudonyms.active.pluck(:user_id)
end
# Update the users' associations as well
User.update_account_associations(all_user_ids.to_a, :account_chain_cache => account_chain_cache)
end
2011-02-01 09:57:29 +08:00
end
2011-02-01 09:57:29 +08:00
# this will take an account and make it a sub_account of
# itself. Also updates all it's descendant accounts to point to
# the correct root account, and updates the pseudonyms to
# points to the new root account as well.
def consume_account(account)
account.all_accounts.each do |sub_account|
sub_account.root_account = self.root_account
2011-02-01 09:57:29 +08:00
sub_account.save!
end
account.parent_account = self
account.root_account = self.root_account
2011-02-01 09:57:29 +08:00
account.save!
account.pseudonyms.each do |pseudonym|
pseudonym.account = self.root_account
2011-02-01 09:57:29 +08:00
pseudonym.save!
end
end
2011-02-01 09:57:29 +08:00
def course_count
self.courses.active.count
2011-02-01 09:57:29 +08:00
end
2011-02-01 09:57:29 +08:00
def sub_account_count
self.sub_accounts.active.count
end
def user_count
self.user_account_associations.count
end
2011-02-01 09:57:29 +08:00
def current_sis_batch
if (current_sis_batch_id = self.read_attribute(:current_sis_batch_id)) && current_sis_batch_id.present?
self.sis_batches.where(id: current_sis_batch_id).first
end
2011-02-01 09:57:29 +08:00
end
2011-02-01 09:57:29 +08:00
def turnitin_settings
return @turnitin_settings if defined?(@turnitin_settings)
if self.turnitin_account_id.present? && self.turnitin_shared_secret.present?
@turnitin_settings = [self.turnitin_account_id, self.turnitin_shared_secret, self.turnitin_host]
2011-02-01 09:57:29 +08:00
else
@turnitin_settings = self.parent_account.try(:turnitin_settings)
2011-02-01 09:57:29 +08:00
end
end
2011-02-01 09:57:29 +08:00
def closest_turnitin_pledge
if self.turnitin_pledge && !self.turnitin_pledge.empty?
self.turnitin_pledge
else
res = self.parent_account.try(:closest_turnitin_pledge)
res ||= t('#account.turnitin_pledge', "This assignment submission is my own, original work")
2011-02-01 09:57:29 +08:00
end
end
2011-02-01 09:57:29 +08:00
def closest_turnitin_comments
if self.turnitin_comments && !self.turnitin_comments.empty?
self.turnitin_comments
else
self.parent_account.try(:closest_turnitin_comments)
2011-02-01 09:57:29 +08:00
end
end
def self_enrollment_allowed?(course)
if !settings[:self_enrollment].blank?
!!(settings[:self_enrollment] == 'any' || (!course.sis_source_id && settings[:self_enrollment] == 'manually_created'))
else
!!(parent_account && parent_account.self_enrollment_allowed?(course))
end
end
def allow_self_enrollment!(setting='any')
settings[:self_enrollment] = setting
self.save!
end
2011-02-01 09:57:29 +08:00
TAB_COURSES = 0
TAB_STATISTICS = 1
TAB_PERMISSIONS = 2
TAB_SUB_ACCOUNTS = 3
TAB_TERMS = 4
TAB_AUTHENTICATION = 5
TAB_USERS = 6
TAB_OUTCOMES = 7
TAB_RUBRICS = 8
TAB_SETTINGS = 9
TAB_FACULTY_JOURNAL = 10
TAB_SIS_IMPORT = 11
TAB_GRADING_STANDARDS = 12
TAB_QUESTION_BANKS = 13
TAB_ADMIN_TOOLS = 17
TAB_SEARCH = 18
# site admin tabs
TAB_PLUGINS = 14
TAB_JOBS = 15
TAB_DEVELOPER_KEYS = 16
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
def external_tool_tabs(opts)
tools = ContextExternalTool.active.find_all_for(self, :account_navigation)
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
tools.sort_by(&:id).map do |tool|
{
:id => tool.asset_string,
:label => tool.label_for(:account_navigation, opts[:language] || I18n.locale),
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
:css_class => tool.asset_string,
:visibility => tool.account_navigation(:visibility),
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
:href => :account_external_tool_path,
:external => true,
:args => [self.id, tool.id]
}
end
end
2011-02-01 09:57:29 +08:00
def tabs_available(user=nil, opts={})
manage_settings = user && self.grants_right?(user, :manage_account_settings)
if root_account.site_admin?
tabs = []
tabs << { :id => TAB_USERS, :label => t('#account.tab_users', "Users"), :css_class => 'users', :href => :account_users_path } if user && self.grants_right?(user, :read_roster)
tabs << { :id => TAB_PERMISSIONS, :label => t('#account.tab_permissions', "Permissions"), :css_class => 'permissions', :href => :account_permissions_path } if user && self.grants_right?(user, :manage_role_overrides)
tabs << { :id => TAB_SUB_ACCOUNTS, :label => t('#account.tab_sub_accounts', "Sub-Accounts"), :css_class => 'sub_accounts', :href => :account_sub_accounts_path } if manage_settings
tabs << { :id => TAB_AUTHENTICATION, :label => t('#account.tab_authentication', "Authentication"), :css_class => 'authentication', :href => :account_authentication_providers_path } if root_account? && manage_settings
tabs << { :id => TAB_PLUGINS, :label => t("#account.tab_plugins", "Plugins"), :css_class => "plugins", :href => :plugins_path, :no_args => true } if root_account? && self.grants_right?(user, :manage_site_settings)
tabs << { :id => TAB_JOBS, :label => t("#account.tab_jobs", "Jobs"), :css_class => "jobs", :href => :jobs_path, :no_args => true } if root_account? && self.grants_right?(user, :view_jobs)
tabs << { :id => TAB_DEVELOPER_KEYS, :label => t("#account.tab_developer_keys", "Developer Keys"), :css_class => "developer_keys", :href => :developer_keys_path, :no_args => true } if root_account? && self.grants_right?(user, :manage_developer_keys)
else
tabs = []
if feature_enabled?(:course_user_search)
tabs << { :id => TAB_SEARCH, :label => t("Search"), :css_class => 'search', :href => :account_path } if user && (grants_right?(user, :read_course_list) || grants_right?(user, :read_roster))
else
tabs << { :id => TAB_COURSES, :label => t('#account.tab_courses', "Courses"), :css_class => 'courses', :href => :account_path } if user && self.grants_right?(user, :read_course_list)
tabs << { :id => TAB_USERS, :label => t('#account.tab_users', "Users"), :css_class => 'users', :href => :account_users_path } if user && self.grants_right?(user, :read_roster)
end
tabs << { :id => TAB_STATISTICS, :label => t('#account.tab_statistics', "Statistics"), :css_class => 'statistics', :href => :statistics_account_path } if user && self.grants_right?(user, :view_statistics)
tabs << { :id => TAB_PERMISSIONS, :label => t('#account.tab_permissions', "Permissions"), :css_class => 'permissions', :href => :account_permissions_path } if user && self.grants_right?(user, :manage_role_overrides)
if user && self.grants_right?(user, :manage_outcomes)
tabs << { :id => TAB_OUTCOMES, :label => t('#account.tab_outcomes', "Outcomes"), :css_class => 'outcomes', :href => :account_outcomes_path }
tabs << { :id => TAB_RUBRICS, :label => t('#account.tab_rubrics', "Rubrics"), :css_class => 'rubrics', :href => :account_rubrics_path }
end
reactify grading standards page convert grading standards page to React code, and add a tab for Grading Periods when MGP feature flag is turned on. closes CNVS-15966 test plan: 1) As an admin, visit the 'Account' home page ( for example, http://localhost:3000/accounts/1). Go to settings and turn the feature flag on for 'Multiple Grading Periods' 2) Click on the link on the left-hand side that says 'Grading' 3) Verify that there are now 2 tabs - one for 'Grading Periods', and one for 'Grading Schemes'. There should not be any content in the 'Grading Periods' sectiion (this will be added in a later ticket). 4) Click on the 'Grading Schemes' tab. Verify that the tab is fully functional, i.e. you can create, edit, and delete grading schemes. Follow this link to a Jing video for more detail on what needs to be QA'd here (there is a lot). (http://screencast.com/t/nsiCVsMjcdF).I changed the behavior slightly from how it is portrayed in the video. Now, the values will auto-recalculate when input is changed, as opposed to changing when focus is lost on the input. 5) Verify when you hover over the '+' icon to add a new row to the scheme, a line appears below the current row to show where the new row will be inserted. 6) Verify that clicking "Add grading scheme" and then clicking "Cancel" removes the new grading scheme, rather than resetting the values of the scheme to default values (this behavior is different from what was shown in the video, and was changed after a talk with Product). 7) Check all functionality covered in the video is also accessible. Change-Id: Iff61acf917bce297f7943ac758862fa56eed275c Reviewed-on: https://gerrit.instructure.com/44667 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Josh Simpson <jsimpson@instructure.com> QA-Review: Amber Taniuchi <amber@instructure.com> Product-Review: Spencer Olson <solson@instructure.com>
2014-11-20 06:29:55 +08:00
tabs << { :id => TAB_GRADING_STANDARDS, :label => t('#account.tab_grading_standards', "Grading"), :css_class => 'grading_standards', :href => :account_grading_standards_path } if user && self.grants_right?(user, :manage_grades)
tabs << { :id => TAB_QUESTION_BANKS, :label => t('#account.tab_question_banks', "Question Banks"), :css_class => 'question_banks', :href => :account_question_banks_path } if user && self.grants_right?(user, :manage_assignments)
tabs << { :id => TAB_SUB_ACCOUNTS, :label => t('#account.tab_sub_accounts', "Sub-Accounts"), :css_class => 'sub_accounts', :href => :account_sub_accounts_path } if manage_settings
tabs << { :id => TAB_FACULTY_JOURNAL, :label => t('#account.tab_faculty_journal', "Faculty Journal"), :css_class => 'faculty_journal', :href => :account_user_notes_path} if self.enable_user_notes && user && self.grants_right?(user, :manage_user_notes)
tabs << { :id => TAB_TERMS, :label => t('#account.tab_terms', "Terms"), :css_class => 'terms', :href => :account_terms_path } if self.root_account? && manage_settings
tabs << { :id => TAB_AUTHENTICATION, :label => t('#account.tab_authentication', "Authentication"), :css_class => 'authentication', :href => :account_authentication_providers_path } if self.root_account? && manage_settings
tabs << { :id => TAB_SIS_IMPORT, :label => t('#account.tab_sis_import', "SIS Import"), :css_class => 'sis_import', :href => :account_sis_import_path } if self.root_account? && self.allow_sis_import && user && self.grants_right?(user, :manage_sis)
tabs << { :id => TAB_DEVELOPER_KEYS, :label => t("#account.tab_developer_keys", "Developer Keys"), :css_class => "developer_keys", :href => :account_developer_keys_path, account_id: root_account.id } if root_account? && root_account.grants_right?(user, :manage_developer_keys)
end
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 += external_tool_tabs(opts)
tabs += Lti::MessageHandler.lti_apps_tabs(self, [Lti::ResourcePlacement::ACCOUNT_NAVIGATION], opts)
tabs << { :id => TAB_ADMIN_TOOLS, :label => t('#account.tab_admin_tools', "Admin Tools"), :css_class => 'admin_tools', :href => :account_admin_tools_path } if can_see_admin_tools_tab?(user)
tabs << { :id => TAB_SETTINGS, :label => t('#account.tab_settings', "Settings"), :css_class => 'settings', :href => :account_settings_path }
tabs.delete_if{ |t| t[:visibility] == 'admins' } unless self.grants_right?(user, :manage)
2011-02-01 09:57:29 +08:00
tabs
end
def can_see_admin_tools_tab?(user)
return false if !user || root_account.site_admin?
admin_tool_permissions = RoleOverride.manageable_permissions(self).find_all{|p| p[1][:admin_tool]}
admin_tool_permissions.any? do |p|
self.grants_right?(user, p.first)
end
end
2011-02-01 09:57:29 +08:00
def is_a_context?
true
end
def help_links
Canvas::Help.default_links + (settings[:custom_help_links] || [])
end
def set_service_availability(service, enable)
service = service.to_sym
raise "Invalid Service" unless AccountServices.allowable_services[service]
allowed_service_names = (self.allowed_services || "").split(",").compact
if allowed_service_names.count > 0 && ![ '+', '-' ].include?(allowed_service_names[0][0,1])
# This account has a hard-coded list of services, so handle accordingly
allowed_service_names.reject! { |flag| flag.match("^[+-]?#{service}$") }
allowed_service_names << service if enable
else
allowed_service_names.reject! { |flag| flag.match("^[+-]?#{service}$") }
if enable
# only enable if it is not enabled by default
allowed_service_names << "+#{service}" unless AccountServices.default_allowable_services[service]
else
# only disable if it is not enabled by default
allowed_service_names << "-#{service}" if AccountServices.default_allowable_services[service]
end
end
@allowed_services_hash = nil
self.allowed_services = allowed_service_names.empty? ? nil : allowed_service_names.join(",")
end
def enable_service(service)
set_service_availability(service, true)
end
def disable_service(service)
set_service_availability(service, false)
end
2011-02-01 09:57:29 +08:00
def allowed_services_hash
return @allowed_services_hash if @allowed_services_hash
account_allowed_services = AccountServices.default_allowable_services
2011-02-01 09:57:29 +08:00
if self.allowed_services
allowed_service_names = self.allowed_services.split(",").compact
2011-02-01 09:57:29 +08:00
if allowed_service_names.count > 0
unless [ '+', '-' ].member?(allowed_service_names[0][0,1])
# This account has a hard-coded list of services, so we clear out the defaults
account_allowed_services = { }
end
2011-02-01 09:57:29 +08:00
allowed_service_names.each do |service_switch|
if service_switch =~ /\A([+-]?)(.*)\z/
flag = $1
service_name = $2.to_sym
2011-02-01 09:57:29 +08:00
if flag == '-'
account_allowed_services.delete(service_name)
else
account_allowed_services[service_name] = AccountServices.allowable_services[service_name]
2011-02-01 09:57:29 +08:00
end
end
end
end
end
@allowed_services_hash = account_allowed_services
end
# if expose_as is nil, all services exposed in the ui are returned
# if it's :service or :setting, then only services set to be exposed as that type are returned
def self.services_exposed_to_ui_hash(expose_as = nil, current_user = nil, account = nil)
if expose_as
AccountServices.allowable_services.reject { |_, setting| setting[:expose_to_ui] != expose_as }
else
AccountServices.allowable_services.reject { |_, setting| !setting[:expose_to_ui] }
end.reject { |_, setting| setting[:expose_to_ui_proc] && !setting[:expose_to_ui_proc].call(current_user, account) }
end
2011-02-01 09:57:29 +08:00
def service_enabled?(service)
service = service.to_sym
case service
when :none
self.allowed_services_hash.empty?
else
self.allowed_services_hash.has_key?(service)
end
end
def self.all_accounts_for(context)
if context.respond_to?(:account)
context.account.account_chain
elsif context.respond_to?(:parent_account)
context.account_chain
else
[]
end
end
2011-02-01 09:57:29 +08:00
def self.serialization_excludes; [:uuid]; end
2011-02-01 09:57:29 +08:00
# This could be much faster if we implement a SQL tree for the account tree
# structure.
def find_child(child_id)
child_id = child_id.to_i
child_ids = self.class.connection.select_values("SELECT id FROM accounts WHERE parent_account_id = #{self.id}").map(&:to_i)
until child_ids.empty?
if child_ids.include?(child_id)
return self.class.find(child_id)
end
child_ids = self.class.connection.select_values("SELECT id FROM accounts WHERE parent_account_id IN (#{child_ids.join(",")})").map(&:to_i)
end
return false
end
def manually_created_courses_account
return self.root_account.manually_created_courses_account unless self.root_account?
display_name = t('#account.manually_created_courses', "Manually-Created Courses")
acct = manually_created_courses_account_from_settings
if acct.blank?
transaction do
lock!
acct = manually_created_courses_account_from_settings
acct ||= self.sub_accounts.where(name: display_name).first_or_create! # for backwards compatibility
if acct.id != self.settings[:manually_created_courses_account_id]
self.settings[:manually_created_courses_account_id] = acct.id
self.save!
end
end
end
acct
end
def manually_created_courses_account_from_settings
acct_id = self.settings[:manually_created_courses_account_id]
acct = self.sub_accounts.where(id: acct_id).first if acct_id.present?
acct = nil if acct.present? && acct.root_account_id != self.id
acct
end
private :manually_created_courses_account_from_settings
def trusted_account_ids
return [] if !root_account? || self == Account.site_admin
[ Account.site_admin.id ]
end
def trust_exists?
false
end
introduce preferred user list search mode closes #7059, #7411 preferred is a compromise between open registration and closed registration. if a single user is found, that user is found without introducing any temporary user to allow user disambiguation. if no users are found, or multiple users are found, it's the same behavior as open registration. as part of this, the "only search existing users" checkbox has been removed. instead, preferred searching will be used in the following cases: * adding an admin, and open registration is enabled * adding an admin, open registration is disabled, and the user has permission to create new users * adding a user to a course, open registration is enabled, and delegated authentication is in use * adding a user to a course, open registration is disabled, and the user has permission to create new users the notable case that preferred searching is not used, and the user would want to use search existing users is open registration enabled, and adding a user to a course. in this case, a temporary user will be created instead of sending the invitation to the existing user. however, the end user experience is identical (invitation sent to e-mail, invitation visible in the same locations in the UI), with the small exception that the end user gets the opportunity to create a separate account if desired (but not preferred). test plan: * go through each of the cases above for a user that doesn't exist yet, a single matching user exists, or multiple users exist. it should behave as described above Change-Id: Idbc7fe23bfc7430b4cd25f7981f5dc08b9e4ffda Reviewed-on: https://gerrit.instructure.com/8966 Tested-by: Hudson <hudson@instructure.com> Reviewed-by: Brian Palmer <brianp@instructure.com>
2012-02-24 06:24:48 +08:00
def user_list_search_mode_for(user)
return :preferred if self.root_account.open_registration?
return :preferred if self.root_account.grants_right?(user, :manage_user_logins)
:closed
end
scope :root_accounts, -> { where(:root_account_id => nil) }
scope :processing_sis_batch, -> { where("accounts.current_sis_batch_id IS NOT NULL").order(:updated_at) }
scope :name_like, lambda { |name| where(wildcard('accounts.name', name)) }
scope :active, -> { where("accounts.workflow_state<>'deleted'") }
def canvas_network_enabled?
false
end
def change_root_account_setting!(setting_name, new_value)
root_account.settings[setting_name] = new_value
don't show speedgrader for unpublished assignments and quizzes Hides SpeedGrader links and redirects to the show page for an assignment or quiz that is "unpublished" and its context is draft state enabled. test plan: - Make sure you have draft state enabled for your account. - Create an assignment and a quiz. Ensure the following: - The SpeedGrader link is not visible on page load when the item is unpublished. - The SpeedGrader link is visible on page load when the item is published. - For the assignment show page only: - Toggling the item's published state using the publish button should show/hide the SpeedGrader link appropriately. - Republish the quiz and assignment. Save their SpeedGrader urls somewhere handy. Now unpublish both of them again. Try to visit each url. You should be redirected back to the item's show page with a friendly flash message indicating that SpeedGrader is only available for published assignments. - Create a discussion topic that can be graded. - Unpublish it from the assignments index page. - On its show page, you should not see a link to speedgrader under admin links. - Republish it. - On its show page, you should see a link to speedgrader under admin links. - Clicking "publish" on the assignments index page should toggle the published status on the quiz show page, and vice versa. Also creates a useful "enable_draft!" method on the Account model for an easy way to enable draft state on an account rather than repeating the setup code over and over again in various places. fixes CNVS-7194 Change-Id: If3880c0439f3a6e63f59417c55e0e0f617c3c5ce Reviewed-on: https://gerrit.instructure.com/23805 QA-Review: Myller de Araujo <myller@instructure.com> Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Simon Williams <simon@instructure.com> Product-Review: Stanley Stuart <stanley@instructure.com>
2013-08-27 05:57:41 +08:00
root_account.save!
end
Bookmarker = BookmarkedCollection::SimpleBookmarker.new(Account, :name, :id)
def format_referer(referer_url)
begin
referer = URI(referer_url || '')
rescue URI::Error
return
end
return unless referer.host
referer_with_port = "#{referer.scheme}://#{referer.host}"
referer_with_port += ":#{referer.port}" unless referer.port == (referer.scheme == 'https' ? 443 : 80)
referer_with_port
end
def trusted_referers=(value)
self.settings[:trusted_referers] = unless value.blank?
value.split(',').map { |referer_url| format_referer(referer_url) }.compact.join(',')
end
end
def trusted_referer?(referer_url)
return if !self.settings.has_key?(:trusted_referers) || self.settings[:trusted_referers].blank?
if referer_with_port = format_referer(referer_url)
self.settings[:trusted_referers].split(',').include?(referer_with_port)
end
end
Register Parents and Add Observees when configured for SAML authentication Fixes PFS-1084 Parent Registration: When a Saml config is designated for Parent Registration the parent signing up will be redirected to a Saml login page where they will log in with their child's credentials. After login the child user's Saml session will be ended and the parent registration process will complete. Parent Adding Student: When a Saml config is designated for Parent Registration the parent adding another observee will be redirected to a Saml login page where they will log in with their child's credentials. After login the child user's Saml session will be ended and the observee creation process will complete. --------------------------------------- TEST PLAN: SETUP: 1) In your account settings check the box for 'Self Registration' (and either of the sub-options) 2) Add the following users to your account (these will be the students): billyjoel eltonjohn 3) In Authentication Settings add a SAML authentication service and enter the following fields (I've set up a remote SAML Idp): IdP Entity ID: http://107.170.212.143/saml2/idp/metadata.php Log On URL: http://107.170.212.143/simplesaml/saml2/idp/SSOService.php Log Out URL: http://107.170.212.143/simplesaml/saml2/idp/SingleLogoutService.php Certificate Fingerprint: 9C:11:68:93:95:CD:18:01:EC:52:2B:9E:22:7F:73:55:ED:6D:82:D4 Parent Registration: check TEST: Parent Registration: * Go to '/login/canvas' * Click on the signup banner * sign up as a parent for billyjoel or eltonjohn (on SAML login page the password for either user is: tantrum) Add Student: * Log in as a parent user w/ a Canvas Auth login * Go to '/profile/observees' * Add Student 'billyjoel' or 'eltonjohn' Authentication Settings (new parent reg checkbox): * Go to Authentication Settings * Add a second SAML config * check the parent registration checkbox - it should warn that selection will deselect the other and in fact do so upon save. - the selected config is the one used for parent reg/add student --------------------------------------- Change-Id: Ief83b604fc252c88dbb912c56de65d8620fe802f Reviewed-on: https://gerrit.instructure.com/49691 Tested-by: Jenkins QA-Review: August Thornton <august@instructure.com> Reviewed-by: Jacob Fugal <jacob@instructure.com> Product-Review: Ethan Vizitei <evizitei@instructure.com>
2015-03-03 01:41:17 +08:00
def parent_registration?
authentication_providers.where(parent_registration: true).exists?
Register Parents and Add Observees when configured for SAML authentication Fixes PFS-1084 Parent Registration: When a Saml config is designated for Parent Registration the parent signing up will be redirected to a Saml login page where they will log in with their child's credentials. After login the child user's Saml session will be ended and the parent registration process will complete. Parent Adding Student: When a Saml config is designated for Parent Registration the parent adding another observee will be redirected to a Saml login page where they will log in with their child's credentials. After login the child user's Saml session will be ended and the observee creation process will complete. --------------------------------------- TEST PLAN: SETUP: 1) In your account settings check the box for 'Self Registration' (and either of the sub-options) 2) Add the following users to your account (these will be the students): billyjoel eltonjohn 3) In Authentication Settings add a SAML authentication service and enter the following fields (I've set up a remote SAML Idp): IdP Entity ID: http://107.170.212.143/saml2/idp/metadata.php Log On URL: http://107.170.212.143/simplesaml/saml2/idp/SSOService.php Log Out URL: http://107.170.212.143/simplesaml/saml2/idp/SingleLogoutService.php Certificate Fingerprint: 9C:11:68:93:95:CD:18:01:EC:52:2B:9E:22:7F:73:55:ED:6D:82:D4 Parent Registration: check TEST: Parent Registration: * Go to '/login/canvas' * Click on the signup banner * sign up as a parent for billyjoel or eltonjohn (on SAML login page the password for either user is: tantrum) Add Student: * Log in as a parent user w/ a Canvas Auth login * Go to '/profile/observees' * Add Student 'billyjoel' or 'eltonjohn' Authentication Settings (new parent reg checkbox): * Go to Authentication Settings * Add a second SAML config * check the parent registration checkbox - it should warn that selection will deselect the other and in fact do so upon save. - the selected config is the one used for parent reg/add student --------------------------------------- Change-Id: Ief83b604fc252c88dbb912c56de65d8620fe802f Reviewed-on: https://gerrit.instructure.com/49691 Tested-by: Jenkins QA-Review: August Thornton <august@instructure.com> Reviewed-by: Jacob Fugal <jacob@instructure.com> Product-Review: Ethan Vizitei <evizitei@instructure.com>
2015-03-03 01:41:17 +08:00
end
def parent_auth_type
return nil unless parent_registration?
parent_registration_aac.auth_type
end
def parent_registration_aac
authentication_providers.where(parent_registration: true).first
Register Parents and Add Observees when configured for SAML authentication Fixes PFS-1084 Parent Registration: When a Saml config is designated for Parent Registration the parent signing up will be redirected to a Saml login page where they will log in with their child's credentials. After login the child user's Saml session will be ended and the parent registration process will complete. Parent Adding Student: When a Saml config is designated for Parent Registration the parent adding another observee will be redirected to a Saml login page where they will log in with their child's credentials. After login the child user's Saml session will be ended and the observee creation process will complete. --------------------------------------- TEST PLAN: SETUP: 1) In your account settings check the box for 'Self Registration' (and either of the sub-options) 2) Add the following users to your account (these will be the students): billyjoel eltonjohn 3) In Authentication Settings add a SAML authentication service and enter the following fields (I've set up a remote SAML Idp): IdP Entity ID: http://107.170.212.143/saml2/idp/metadata.php Log On URL: http://107.170.212.143/simplesaml/saml2/idp/SSOService.php Log Out URL: http://107.170.212.143/simplesaml/saml2/idp/SingleLogoutService.php Certificate Fingerprint: 9C:11:68:93:95:CD:18:01:EC:52:2B:9E:22:7F:73:55:ED:6D:82:D4 Parent Registration: check TEST: Parent Registration: * Go to '/login/canvas' * Click on the signup banner * sign up as a parent for billyjoel or eltonjohn (on SAML login page the password for either user is: tantrum) Add Student: * Log in as a parent user w/ a Canvas Auth login * Go to '/profile/observees' * Add Student 'billyjoel' or 'eltonjohn' Authentication Settings (new parent reg checkbox): * Go to Authentication Settings * Add a second SAML config * check the parent registration checkbox - it should warn that selection will deselect the other and in fact do so upon save. - the selected config is the one used for parent reg/add student --------------------------------------- Change-Id: Ief83b604fc252c88dbb912c56de65d8620fe802f Reviewed-on: https://gerrit.instructure.com/49691 Tested-by: Jenkins QA-Review: August Thornton <august@instructure.com> Reviewed-by: Jacob Fugal <jacob@instructure.com> Product-Review: Ethan Vizitei <evizitei@instructure.com>
2015-03-03 01:41:17 +08:00
end
2011-02-01 09:57:29 +08:00
end