<commit_before>#
# Copyright (C) 2011 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#

require 'atom'

class Account < ActiveRecord::Base
  include Context
  include OutcomeImportContext
  include Pronouns

  INSTANCE_GUID_SUFFIX = 'canvas-lms'

  include Workflow
  include BrandConfigHelpers
  belongs_to :parent_account, :class_name => 'Account'
  belongs_to :root_account, :class_name => 'Account'

  has_many :courses
  has_many :all_courses, :class_name => 'Course', :foreign_key => 'root_account_id'
  has_one :terms_of_service, :dependent => :destroy
  has_one :terms_of_service_content, :dependent => :destroy
  has_many :group_categories, -> { where(deleted_at: nil) }, as: :context, inverse_of: :context
  has_many :all_group_categories, :class_name => 'GroupCategory', foreign_key: 'root_account_id', inverse_of: :root_account
  has_many :groups, :as => :context, :inverse_of => :context
  has_many :all_groups, class_name: 'Group', foreign_key: 'root_account_id', inverse_of: :root_account
  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 :grading_period_groups, inverse_of: :root_account, dependent: :destroy
  has_many :grading_periods, through: :grading_period_groups
  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 :active_account_users, -> { active }, class_name: 'AccountUser'
  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 => :active_account_users
  has_many :pseudonyms, -> { preload(:user) }, inverse_of: :account
  has_many :role_overrides, :as => :context, :inverse_of => :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, :inverse_of => :context, :dependent => :destroy
  has_many :active_assignments, -> { where("assignments.workflow_state<>'deleted'") }, as: :context, inverse_of: :context, class_name: 'Assignment'
  has_many :folders, -> { order('folders.name') }, as: :context, inverse_of: :context, dependent: :destroy
  has_many :active_folders, -> { where("folder.workflow_state<>'deleted'").order('folders.name') }, class_name: 'Folder', as: :context, inverse_of: :context
  has_many :developer_keys
  has_many :developer_key_account_bindings, inverse_of: :account, dependent: :destroy
  has_many :authentication_providers,
           -> { order(:position) },
           inverse_of: :account,
           extend: AuthenticationProvider::FindWithType

  has_many :account_reports, inverse_of: :account
  has_many :grading_standards, -> { where("workflow_state<>'deleted'") }, as: :context, inverse_of: :context
  has_many :assessment_question_banks, -> { preload(:assessment_questions, :assessment_question_bank_users) }, as: :context, inverse_of: :context
  has_many :assessment_questions, :through => :assessment_question_banks
  has_many :roles
  has_many :all_roles, :class_name => 'Role', :foreign_key => 'root_account_id'
  has_many :progresses, :as => :context, :inverse_of => :context
  has_many :content_migrations, :as => :context, :inverse_of => :context
  has_many :sis_batch_errors, foreign_key: :root_account_id, inverse_of: :root_account
  has_one :outcome_proficiency, dependent: :destroy

  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

  include LearningOutcomeContext
  include RubricContext

  has_many :context_external_tools, -> { order(:name) }, as: :context, inverse_of: :context, dependent: :destroy
  has_many :error_reports
  has_many :announcements, :class_name => 'AccountNotification'
  has_many :alerts, -> { preload(:criteria) }, as: :context, inverse_of: :context
  has_many :user_account_associations
  has_many :report_snapshots
  has_many :external_integration_keys, :as => :context, :inverse_of => :context, :dependent => :destroy
  has_many :shared_brand_configs
  belongs_to :brand_config, foreign_key: "brand_config_md5"

  before_validation :verify_unique_sis_source_id
  before_save :ensure_defaults
  before_create :enable_sis_imports, if: :root_account?
  after_save :update_account_associations_if_changed

  before_save :setup_cache_invalidation
  after_save :invalidate_caches_if_changed
  after_update :clear_special_account_cache_if_special

  after_update :clear_cached_short_name, :if => :saved_change_to_name?

  after_create :create_default_objects

  serialize :settings, Hash
  include TimeZoneHelper

  time_zone_attribute :default_time_zone, default: "America/Denver"
  def default_time_zone
    if read_attribute(:default_time_zone) || root_account?
      super
    else
      root_account.default_time_zone
    end
  end
  alias_method :time_zone, :default_time_zone

  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 :workflow_state, presence: true
  validate :no_active_courses, if: lambda { |a| a.workflow_state_changed? && !a.active? }
  validate :no_active_sub_accounts, if: lambda { |a| a.workflow_state_changed? && !a.active? }

  include StickySisFields
  are_sis_sticky :name, :parent_account_id

  include FeatureFlags
  def feature_flag_cache
    MultiCache.cache
  end

  def default_locale(recurse = false)
    result = read_attribute(:default_locale)
    if recurse
      result ||= Rails.cache.fetch(['default_locale', self.global_id].cache_key) do
        parent_account.default_locale(true) if parent_account
      end
    end
    result = nil unless I18n.locale_available?(result)
    result
  end

  def resolved_outcome_proficiency
    outcome_proficiency || parent_account&.resolved_outcome_proficiency
  end

  include ::Account::Settings
  include ::Csp::AccountHelper

  # these settings either are or could be easily added to
  # the account settings page<commit_msg>cache parent locale for account object<commit_after>#
# Copyright (C) 2011 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#

require 'atom'

class Account < ActiveRecord::Base
  include Context
  include OutcomeImportContext
  include Pronouns

  INSTANCE_GUID_SUFFIX = 'canvas-lms'

  include Workflow
  include BrandConfigHelpers
  belongs_to :parent_account, :class_name => 'Account'
  belongs_to :root_account, :class_name => 'Account'

  has_many :courses
  has_many :all_courses, :class_name => 'Course', :foreign_key => 'root_account_id'
  has_one :terms_of_service, :dependent => :destroy
  has_one :terms_of_service_content, :dependent => :destroy
  has_many :group_categories, -> { where(deleted_at: nil) }, as: :context, inverse_of: :context
  has_many :all_group_categories, :class_name => 'GroupCategory', foreign_key: 'root_account_id', inverse_of: :root_account
  has_many :groups, :as => :context, :inverse_of => :context
  has_many :all_groups, class_name: 'Group', foreign_key: 'root_account_id', inverse_of: :root_account
  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 :grading_period_groups, inverse_of: :root_account, dependent: :destroy
  has_many :grading_periods, through: :grading_period_groups
  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 :active_account_users, -> { active }, class_name: 'AccountUser'
  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 => :active_account_users
  has_many :pseudonyms, -> { preload(:user) }, inverse_of: :account
  has_many :role_overrides, :as => :context, :inverse_of => :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, :inverse_of => :context, :dependent => :destroy
  has_many :active_assignments, -> { where("assignments.workflow_state<>'deleted'") }, as: :context, inverse_of: :context, class_name: 'Assignment'
  has_many :folders, -> { order('folders.name') }, as: :context, inverse_of: :context, dependent: :destroy
  has_many :active_folders, -> { where("folder.workflow_state<>'deleted'").order('folders.name') }, class_name: 'Folder', as: :context, inverse_of: :context
  has_many :developer_keys
  has_many :developer_key_account_bindings, inverse_of: :account, dependent: :destroy
  has_many :authentication_providers,
           -> { order(:position) },
           inverse_of: :account,
           extend: AuthenticationProvider::FindWithType

  has_many :account_reports, inverse_of: :account
  has_many :grading_standards, -> { where("workflow_state<>'deleted'") }, as: :context, inverse_of: :context
  has_many :assessment_question_banks, -> { preload(:assessment_questions, :assessment_question_bank_users) }, as: :context, inverse_of: :context
  has_many :assessment_questions, :through => :assessment_question_banks
  has_many :roles
  has_many :all_roles, :class_name => 'Role', :foreign_key => 'root_account_id'
  has_many :progresses, :as => :context, :inverse_of => :context
  has_many :content_migrations, :as => :context, :inverse_of => :context
  has_many :sis_batch_errors, foreign_key: :root_account_id, inverse_of: :root_account
  has_one :outcome_proficiency, dependent: :destroy

  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

  include LearningOutcomeContext
  include RubricContext

  has_many :context_external_tools, -> { order(:name) }, as: :context, inverse_of: :context, dependent: :destroy
  has_many :error_reports
  has_many :announcements, :class_name => 'AccountNotification'
  has_many :alerts, -> { preload(:criteria) }, as: :context, inverse_of: :context
  has_many :user_account_associations
  has_many :report_snapshots
  has_many :external_integration_keys, :as => :context, :inverse_of => :context, :dependent => :destroy
  has_many :shared_brand_configs
  belongs_to :brand_config, foreign_key: "brand_config_md5"

  before_validation :verify_unique_sis_source_id
  before_save :ensure_defaults
  before_create :enable_sis_imports, if: :root_account?
  after_save :update_account_associations_if_changed

  before_save :setup_cache_invalidation
  after_save :invalidate_caches_if_changed
  after_update :clear_special_account_cache_if_special

  after_update :clear_cached_short_name, :if => :saved_change_to_name?

  after_create :create_default_objects

  serialize :settings, Hash
  include TimeZoneHelper

  time_zone_attribute :default_time_zone, default: "America/Denver"
  def default_time_zone
    if read_attribute(:default_time_zone) || root_account?
      super
    else
      root_account.default_time_zone
    end
  end
  alias_method :time_zone, :default_time_zone

  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 :workflow_state, presence: true
  validate :no_active_courses, if: lambda { |a| a.workflow_state_changed? && !a.active? }
  validate :no_active_sub_accounts, if: lambda { |a| a.workflow_state_changed? && !a.active? }

  include StickySisFields
  are_sis_sticky :name, :parent_account_id

  include FeatureFlags
  def feature_flag_cache
    MultiCache.cache
  end

  def default_locale(recurse = false)
    result = read_attribute(:default_locale)
    if recurse && !result && parent_account
      unless instance_variable_defined?(:@cached_parent_locale)
        @cached_parent_locale = Rails.cache.fetch(['default_locale', self.global_id].cache_key) do
          parent_account.default_locale(true)
        end
      end
      result = @cached_parent_locale
    end
    result = nil unless I18n.locale_available?(result)
    result
  end

  def resolved_outcome_proficiency
    outcome_proficiency || parent_account&.resolved_outcome_proficiency
  end

  include ::Account::Settings
  include ::Csp::AccountHelper

  # these settings either are or could be easily added to
  # the account settings page
{'fraction_non_alphanumeric': 0.10525316455696203, 'fraction_numerical': 0.001139240506329114, 'mean_word_length': 5.735294117647059, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 129, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2932085', 'n_tokens_mistral': 5113, 'n_tokens_neox': 5018, 'n_words': 1183}