Noosfero/noosfero

View on GitHub
app/models/environment.rb

Summary

Maintainability
F
6 days
Test Coverage
# A Environment is like a website to be hosted in the platform. It may
# contain multiple Profile's and can be identified by several different
# domains.
class Environment < ApplicationRecord
  attr_accessible :name, :is_default, :signup_welcome_text_subject,
                  :signup_welcome_text_body, :terms_of_use,
                  :message_for_disabled_enterprise, :news_amount_by_folder,
                  :default_language, :languages, :description,
                  :organization_approval_method, :enabled_plugins,
                  :enabled_features, :redirection_after_login,
                  :redirection_after_signup, :contact_email, :theme,
                  :reports_lower_bound, :noreply_email,
                  :signup_welcome_screen_body, :members_whitelist_enabled,
                  :members_whitelist, :highlighted_news_amount,
                  :portal_news_amount, :area_news_show_not_highlighted, :date_format, :signup_intro,
                  :enable_feed_proxy, :http_feed_proxy, :https_feed_proxy,
                  :disable_feed_ssl, :layout_template, :boxes_attributes

  has_many :users

  # allow roles use
  def self.dangerous_attribute_method?(name)
    false
  end

  has_many :tasks, dependent: :destroy, as: "target"
  has_many :search_terms, as: :context
  has_many :email_templates, foreign_key: :owner_id
  has_many :custom_fields, dependent:  :destroy
  has_many :person_custom_fields, -> { where(customized_type: "Person") }, class_name: "CustomField"
  has_many :community_custom_fields, -> { where(customized_type: "Community") }, class_name: "CustomField"
  has_many :enterprise_custom_fields, -> { where(customized_type: "Enterprise") }, class_name: "CustomField"
  has_many :push_subscriptions

  IDENTIFY_SCRIPTS = /(php[0-9s]?|[sp]htm[l]?|pl|py|cgi|rb)/ unless const_defined?(:IDENTIFY_SCRIPTS)

  validates_inclusion_of :date_format,
                         in: ["numbers_with_year", "numbers",
                              "month_name_with_year", "month_name",
                              "past_time"],
                         if: :date_format

  def self.verify_filename(filename)
    filename += ".txt" if File.extname(filename) =~ IDENTIFY_SCRIPTS
    filename
  end

  NUMBER_OF_BOXES = 4

  PERMISSIONS["Environment"] = {
    "view_environment_admin_panel" => N_("View environment admin panel"),
    "edit_environment_features" => N_("Edit environment features"),
    "edit_environment_design" => N_("Edit environment design"),
    "manage_environment_categories" => N_("Manage environment categories"),
    "manage_environment_roles" => N_("Manage environment roles"),
    "manage_environment_validators" => N_("Manage environment validators"),
    "manage_environment_users" => N_("Manage environment users"),
    "manage_environment_organizations" => N_("Manage environment organizations"),
    "manage_environment_templates" => N_("Manage environment templates"),
    "manage_environment_licenses" => N_("Manage environment licenses"),
    "manage_environment_trusted_sites" => N_("Manage environment trusted sites"),
    "manage_environment_kinds" => N_("Manage environment kinds"),
    "manage_environment_captcha" => N_("Manage environment captcha"),
    "edit_appearance" => N_("Edit appearance"),
    "edit_raw_html_block" => N_("Edit Raw HTML block"),
    "manage_email_templates" => N_("Manage Email Templates"),
  }

  module Roles
    def self.admin(env_id)
      Role.find_by(key: "environment_administrator", environment_id: env_id)
    end
  end

  after_create :create_roles
  def create_roles
    Role.create!(
      key: "environment_administrator",
      name: N_("Environment Administrator"),
      environment: self,
      permissions: PERMISSIONS[Environment.name].keys + PERMISSIONS[Profile.name].keys
    )
    Role.create!(
      key: "profile_admin",
      name: N_("Profile Administrator"),
      environment: self,
      permissions: PERMISSIONS[Profile.name].keys
    )
    # members for enterprises, communities etc
    Role.create!(
      key: "profile_member",
      name: N_("Member"),
      environment: self,
      permissions: [
        "invite_members",
      ]
    )
    # moderators for enterprises, communities etc
    Role.create!(
      key: "profile_moderator",
      name: N_("Moderator"),
      environment: self,
      permissions: [
        "manage_memberships",
        "edit_profile_design",
        "manage_products",
        "manage_friends",
        "perform_task",
        "view_tasks"
      ]
    )
  end

  def add_admin(user)
    self.affiliate(user, Environment::Roles.admin(self.id))
  end

  def remove_admin(user)
    self.disaffiliate(user, Environment::Roles.admin(self.id))
  end

  def admins
    admin_role = Environment::Roles.admin(self)
    return [] if admin_role.blank?

    Person.members_of(self).where "role_assignments.role_id = ?", admin_role.id
  end

  # returns the available features for a Environment, in the form of a
  # hash, with pairs in the form <tt>'feature_name' => 'Feature name'</tt>.
  def self.available_features
    {
      "disable_asset_articles" => _("Disable search for articles "),
      "disable_asset_enterprises" => _("Disable search for enterprises"),
      "disable_asset_people" => _("Disable search for people"),
      "disable_asset_communities" => _("Disable search for communities"),
      "disable_asset_events" => _("Disable search for events"),
      "disable_categories" => _("Disable categories"),
      "disable_header_and_footer" => _("Disable header/footer editing by users"),
      "disable_gender_icon" => _("Disable gender icon"),
      "disable_categories_menu" => _("Disable the categories menu"),
      "disable_select_city_for_contact" => _("Disable state/city select for contact form"),
      "disable_contact_person" => _("Disable contact for people"),
      "disable_contact_community" => _("Disable contact for groups/communities"),
      "forbid_destroy_profile" => _("Forbid users of removing profiles"),

      "enterprise_registration" => _("Enterprise registration"),
      "enterprise_activation" => _("Enable activation of enterprises"),
      "enterprises_are_disabled_when_created" => _("Enterprises are disabled when created"),
      "enterprises_are_validated_when_created" => _("Enterprises are validated when created"),

      "media_panel" => _("Media panel in WYSIWYG editor"),
      "select_preferred_domain" => _("Select preferred domains per profile"),
      "use_portal_community" => _("Use the portal as news source for front page"),
      "user_themes" => _("Allow users to create their own themes"),
      "search_in_home" => _("Display search form in home page"),

      "cant_change_homepage" => _("Don't allow users to change which article to use as homepage"),
      "display_header_footer_explanation" => _("Display explanation about header and footer"),
      "articles_dont_accept_comments_by_default" => _("Articles don't accept comments by default"),
      "organizations_are_moderated_by_default" => _("Organizations have moderated publication by default"),
      "enable_profile_url_change" => _("Allow profiles to change their URL"),
      "admin_must_approve_new_communities" => _("Admin must approve creation of communities"),
      "admin_must_approve_new_users" => _("Admin must approve registration of new users"),
      "show_balloon_with_profile_links_when_clicked" => _("Show a balloon with profile links when a profile image is clicked"),
      "xmpp_chat" => _("XMPP/Jabber based chat"),
      "show_zoom_button_on_article_images" => _("Show a zoom link on all article images"),
      "skip_new_user_email_confirmation" => _("Skip e-mail confirmation for new users"),
      "send_welcome_email_to_new_users" => _("Send welcome e-mail to new users"),
      "allow_change_of_redirection_after_login" => _("Allow users to set the page to redirect after login"),
      "display_my_communities_on_user_menu" => _("Display on menu the list of communities the user can manage"),
      "display_my_enterprises_on_user_menu" => _("Display on menu the list of enterprises the user can manage"),
      "restrict_to_members" => _("Show content only to members"),

      "enable_appearance" => _("Enable appearance editing by users"),
    }
  end

  def self.login_redirection_options
    {
      "keep_on_same_page" => _("Stays on the same page the user was before login."),
      "site_homepage" => _("Redirects the user to the environment homepage."),
      "domain_root" => _("Redirects the user to the current domain root."),
      "user_profile_page" => _("Redirects the user to his profile page."),
      "user_homepage" => _("Redirects the user to his homepage."),
      "user_control_panel" => _("Redirects the user to his control panel."),
      "custom_url" => _("Specify the URL to redirect to:"),
    }
  end
  validates_inclusion_of :redirection_after_login, in: Environment.login_redirection_options.keys, allow_nil: true

  def self.signup_redirection_options
    {
      "keep_on_same_page" => _("Stays on the same page the user was before signup."),
      "site_homepage" => _("Redirects the user to the environment homepage."),
      "domain_root" => _("Redirects the user to the current domain root."),
      "user_profile_page" => _("Redirects the user to his profile page."),
      "user_homepage" => _("Redirects the user to his homepage."),
      "user_control_panel" => _("Redirects the user to his control panel."),
      "welcome_page" => _("Redirects the user to the environment welcome page.")
    }
  end
  validates_inclusion_of :redirection_after_signup, in: Environment.signup_redirection_options.keys, allow_nil: true

  # #################################################
  # Relationships and applied behaviour
  # #################################################

  extend ActsAsHavingBoxes::ClassMethods
  acts_as_having_boxes

  after_create do |env|
    NUMBER_OF_BOXES.times do
      env.boxes << Box.new
    end

    # main area
    env.boxes[0].blocks << MainBlock.new

    # "left" area
    env.boxes[1].blocks << LoginBlock.new
    env.boxes[1].blocks << RecentDocumentsBlock.new

    # "right" area
    env.boxes[2].blocks << CommunitiesBlock.new(limit: 6)
  end

  # One Environment can be reached by many domains
  has_many :domains, as: :owner
  has_many :profiles, dependent: :destroy

  has_many :organizations
  has_many :enterprises
  has_many :people
  has_many :communities
  has_many :licenses

  has_many :categories
  has_many :display_categories, -> {
    order("display_color").where("display_color is not null and parent_id is null")
  }, class_name: "Category"

  has_many :regions
  has_many :states
  has_many :cities

  has_many :roles, dependent: :destroy
  has_many :kinds

  has_many :mailings, class_name: "EnvironmentMailing", foreign_key: :source_id, as: "source"

  acts_as_accessible

  def superior_intances
    [self, nil]
  end
  # #################################################
  # Attributes
  # #################################################

  # store the Environment settings as YAML-serialized Hash.
  extend ActsAsHavingSettings::ClassMethods
  acts_as_having_settings field: :settings

  # introduce and explain to users something about the signup
  settings_items :signup_intro, type: String

  # the environment's terms of use: every user must accept them before registering.
  settings_items :terms_of_use, type: String

  # the environment's terms of enterprise use: every enterprise member must accept them before
  # registering or activating enterprises.
  settings_items :terms_of_enterprise_use, type: String

  # returns the approval method used for this environment. Possible values are:
  #
  # Defaults to <tt>:admim</tt>.
  settings_items :organization_approval_method, type: Symbol, default: :admin

  # Whether this environment should force having 'www.' in its domain name or
  # not. Defaults to false.
  #
  # Sets the value of #force_www. <tt>value</tt> must be a boolean.
  #
  # See also #default_hostname
  settings_items :force_www, default: false

  settings_items :message_for_friend_invitation, type: String
  def message_for_friend_invitation
    settings[:message_for_member_invitation] || InviteFriend.mail_template
  end

  settings_items :message_for_member_invitation, type: String
  def message_for_member_invitation
    settings[:message_for_member_invitation] || InviteMember.mail_template
  end

  settings_items :activation_blocked_text, type: String
  settings_items :message_for_disabled_enterprise, type: String,
                                                   default: _("This enterprise needs to be enabled.")

  settings_items :contact_phone, type: String
  settings_items :address, type: String
  settings_items :city, type: String
  settings_items :state, type: String
  settings_items :country_name, type: String
  settings_items :lat, type: Float
  settings_items :lng, type: Float
  settings_items :postal_code, type: String
  settings_items :location, type: String

  alias_method :zip_code=, :postal_code
  alias_method :zip_code, :postal_code

  settings_items :layout_template, type: String, default: "default"
  settings_items :homepage, type: String
  settings_items :description, type: String, default: '<div style="text-align: center"><a href="http://noosfero.org/"><img src="/images/noosfero-network.png" alt="Noosfero"/></a></div>'
  settings_items :local_docs, type: Array, default: []
  settings_items :news_amount_by_folder, type: Integer, default: 4
  settings_items :highlighted_news_amount, type: Integer, default: 2
  settings_items :portal_news_amount, type: Integer, default: 5
  settings_items :area_news_show_not_highlighted, type: :boolean, default: false
  settings_items :help_message_to_add_enterprise, type: String, default: ""
  settings_items :tip_message_enterprise_activation_question, type: String, default: ""

  settings_items :currency_iso_unit, type: String, default: "USD"
  settings_items :currency_unit, type: String, default: "$"
  settings_items :currency_separator, type: String, default: "."
  settings_items :currency_delimiter, type: String, default: ","

  settings_items :trusted_sites_for_iframe, type: Array, default: %w[
    developer.myspace.com
    itheora.org
    maps.google.com
    platform.twitter.com
    player.vimeo.com
    stream.softwarelivre.org
    tv.softwarelivre.org
    www.facebook.com
    www.flickr.com
    www.gmodules.com
    www.youtube.com
    openstreetmap.org
  ] + ("a".."z").map { |i| "#{i}.yimg.com" }

  settings_items :enabled_plugins, type: Array, default: Noosfero::Plugin.all

  settings_items :search_hints, type: Hash, default: {}

  # Set to return http forbidden to host not on the allow origin list bellow
  settings_items :restrict_to_access_control_origins, default: false
  # Set this according to http://www.w3.org/TR/cors/. Headers are set at every response
  # For multiple domains acts as suggested in http://stackoverflow.com/questions/1653308/access-control-allow-origin-multiple-origin-domains
  settings_items :access_control_allow_origin, type: Array, default: []
  settings_items :access_control_allow_methods, type: String

  settings_items :signup_welcome_screen_body, type: String

  def has_custom_welcome_screen?
    settings[:signup_welcome_screen_body].present?
  end

  settings_items :members_whitelist_enabled, type: :boolean, default: false
  settings_items :members_whitelist, type: Array, default: []

  settings_items :permanent_notifications, type: :boolean, default: false

  def in_whitelist?(person)
    !members_whitelist_enabled || members_whitelist.include?(person.id)
  end

  def members_whitelist=(members)
    settings[:members_whitelist] = members.split(",").map(&:to_i)
  end

  def news_amount_by_folder=(amount)
    settings[:news_amount_by_folder] = amount.to_i
  end

  # Enables a feature identified by its name
  def enable(feature, must_save = true)
    self.settings["#{feature}_enabled".to_sym] = true
    self.save! if must_save
  end

  def enable_plugin(plugin)
    self.enabled_plugins += [plugin.to_s]
    self.enabled_plugins.uniq!
    self.save!
  end

  def enable_all_plugins
    Noosfero::Plugin.available_plugin_names.each do |plugin|
      plugin_name = plugin.to_s + "Plugin"
      unless self.enabled_plugins.include?(plugin_name)
        self.enabled_plugins += [plugin_name]
      end
    end
    self.save!
  end

  # Disables a feature identified by its name
  def disable(feature, must_save = true)
    self.settings["#{feature}_enabled".to_sym] = false
    self.save! if must_save
  end

  def disable_plugin(plugin)
    self.enabled_plugins.delete(plugin.to_s)
    self.save!
  end

  # Tells if a feature, identified by its name, is enabled
  def enabled?(feature)
    self.settings["#{feature}_enabled".to_sym] == true
  end

  def disabled?(feature)
    !self.enabled?(feature)
  end

  def plugin_enabled?(plugin)
    enabled_plugins.include?(plugin.to_s)
  end

  # enables the features identified by <tt>features</tt>, which is expected to
  # be an Enumarable object containing the identifiers of the desired features.
  # Passing <tt>nil</tt> is the same as passing an empty Array.
  def enabled_features=(features)
    features ||= []
    self.class.available_features.keys.each do |feature|
      if features.include? feature
        self.enable(feature)
      else
        self.disable(feature)
      end
    end
  end

  def enabled_features
    features = self.class.available_features
    features.delete_if { |k, v| !self.enabled?(k) }
  end

  DEFAULT_FEATURES = %w(
    disable_gender_icon
    disable_select_city_for_contact
    enterprise_registration
    media_panel
    organizations_are_moderated_by_default
    show_balloon_with_profile_links_when_clicked
    show_zoom_button_on_article_images
    use_portal_community
    enable_appearance
  )

  before_create :enable_default_features
  def enable_default_features
    DEFAULT_FEATURES.each do |feature|
      enable(feature, false)
    end
  end

  store_accessor :metadata
  include MetadataScopes

  include Entitlement::SliderHelper
  include Entitlement::EnvironmentJudge

  CAPTCHA_REQUIREMENTS = {
    suggest_article: { label: _("Suggest a new article"), options: Entitlement::Levels.range_options(0, 1) },
    forgot_password: { label: _("Recover forgotten password"), options: Entitlement::Levels.range_options(0, 1) },
    signup: { label: _("Sign up"), options: Entitlement::Levels.range_options(0, 1) },
  }

  def captcha_requirements
    CAPTCHA_REQUIREMENTS.merge(Profile::CAPTCHA_REQUIREMENTS)
  end

  def default_captcha_requirement
    Entitlement::Levels.levels[:users]
  end

  def get_captcha_level(action)
    (metadata["captcha"] && metadata["captcha"][action.to_s]) || default_captcha_requirement
  end

  def require_captcha?(action, user, profile = nil)
    if profile.present?
      profile.demands?(user, "#{action}_captcha")
    else
      demands?(user, "#{action}_captcha")
    end
  end

  # returns <tt>true</tt> if this Environment has terms of use to be
  # accepted by users before registration.
  def has_terms_of_use?
    !self.terms_of_use.blank?
  end

  # returns <tt>true</tt> if this Environment has terms of enterprise use to be
  # accepted by users before registration or activation of enterprises.
  def has_terms_of_enterprise_use?
    !self.terms_of_enterprise_use.blank?
  end

  # Sets the organization_approval_method. Only accepts the following values:
  #
  # * <tt>:admin</tt>: organization registration must be approved by the
  #   environment administrator.
  # * <tt>:region</tt>: organization registering must be approved by some other
  #   organization asssigned as validator to the Region the new organization
  #   belongs to.
  # * <tt>:none</tt>: organization registration is approved by default.
  #
  # Trying to set organization_approval_method to any other value will raise an
  # ArgumentError.
  #
  # The value passed as argument is converted to a Symbol before being actually
  # set to this setting.
  def organization_approval_method=(value)
    actual_value = value.to_sym

    accepted_values = %w[
      admin
      region
      none
    ].map(&:to_sym)
    raise ArgumentError unless accepted_values.include?(actual_value)

    self.settings[:organization_approval_method] = actual_value
  end

  def all_custom_person_fields
    fields = self.settings[:custom_person_fields].nil? ? {} : self.settings[:custom_person_fields]
    self.person_custom_fields.map do |cf|
      fields[cf.name] = { "active" => cf.active.to_s, "required" => cf.required.to_s, "signup" => cf.signup.to_s }
    end

    fields
  end

  def custom_person_fields
    self.settings[:custom_person_fields].nil? ? {} : self.settings[:custom_person_fields]
  end

  def custom_person_fields=(values)
    if values["schooling"] && values["schooling"]["active"] == "true"
      schooling_status = values["schooling"]
    end

    self.settings[:custom_person_fields] = values.delete_if { |key, value| !Person.fields.include?(key) }
    self.settings[:custom_person_fields].each_pair do |key, value|
      if value["required"] == "true"
        self.settings[:custom_person_fields][key]["active"] = "true"
        self.settings[:custom_person_fields][key]["signup"] = "true"
      end
      if value["signup"] == "true"
        self.settings[:custom_person_fields][key]["active"] = "true"
      end
    end

    if schooling_status
      self.settings[:custom_person_fields]["schooling_status"] = schooling_status
    end
  end

  def custom_person_field(field, status)
    if (custom_person_fields[field] && custom_person_fields[field][status] == "true")
      return true
    end

    false
  end

  def active_person_fields
    (custom_person_fields.delete_if { |key, value| !custom_person_field(key, "active") }).keys || []
  end

  def required_person_fields
    build_fields "active_person_fields", "custom_person_fields", "required"
  end

  def signup_person_fields
    build_fields "active_person_fields", "custom_person_fields", "signup"
  end

  def invitation_mail_template(profile)
    if profile.person?
      message_for_friend_invitation
    else
      message_for_member_invitation
    end
  end

  def all_custom_enterprise_fields
    fields = self.settings[:custom_enterprise_fields].nil? ? {} : self.settings[:custom_enterprise_fields]
    self.enterprise_custom_fields.map do |cf|
      fields[cf.name] = { "active" => cf.active.to_s, "required" => cf.required.to_s, "signup" => cf.signup.to_s }
    end

    fields
  end

  def custom_enterprise_fields
    self.settings[:custom_enterprise_fields].nil? ? {} : self.settings[:custom_enterprise_fields]
  end

  def custom_enterprise_fields=(values)
    self.settings[:custom_enterprise_fields] = values.delete_if { |key, value| !Enterprise.fields.include?(key) }
    self.settings[:custom_enterprise_fields].each_pair do |key, value|
      if value["required"] == "true"
        self.settings[:custom_enterprise_fields][key]["active"] = "true"
        self.settings[:custom_enterprise_fields][key]["signup"] = "true"
      end
      if value["signup"] == "true"
        self.settings[:custom_enterprise_fields][key]["active"] = "true"
      end
    end
  end

  def custom_enterprise_field(field, status)
    if (custom_enterprise_fields[field] && custom_enterprise_fields[field][status] == "true")
      return true
    end

    false
  end

  def build_fields(active_fields, custom_fields, local)
    fields = []
    send(active_fields).each do |field|
      fields << field if send(custom_fields)[field][local] == "true"
    end
    fields
  end

  def active_enterprise_fields
    (custom_enterprise_fields.delete_if { |key, value| !custom_enterprise_field(key, "active") }).keys || []
  end

  def required_enterprise_fields
    build_fields "active_enterprise_fields", "custom_enterprise_fields", "required"
  end

  def signup_enterprise_fields
    build_fields "active_enterprise_fields", "custom_enterprise_fields", "signup"
  end

  def all_custom_community_fields
    fields = self.settings[:custom_community_fields].nil? ? {} : self.settings[:custom_community_fields]
    self.community_custom_fields.map do |cf|
      fields[cf.name] = { "active" => cf.active.to_s, "required" => cf.required.to_s, "signup" => cf.signup.to_s }
    end

    fields
  end

  def custom_community_fields
    self.settings[:custom_community_fields].nil? ? {} : self.settings[:custom_community_fields]
  end

  def custom_community_fields=(values)
    self.settings[:custom_community_fields] = values.delete_if { |key, value| !Community.fields.include?(key) }
    self.settings[:custom_community_fields].each_pair do |key, value|
      if value["required"] == "true"
        self.settings[:custom_community_fields][key]["active"] = "true"
        self.settings[:custom_community_fields][key]["signup"] = "true"
      end
      if value["signup"] == "true"
        self.settings[:custom_community_fields][key]["active"] = "true"
      end
    end
  end

  def custom_community_field(field, status)
    if (custom_community_fields[field] && custom_community_fields[field][status] == "true")
      return true
    end

    false
  end

  def active_community_fields
    (custom_community_fields.delete_if { |key, value| !custom_community_field(key, "active") }).keys
  end

  def required_community_fields
    build_fields "active_community_fields", "custom_community_fields", "required"
  end

  def signup_community_fields
    build_fields "active_community_fields", "custom_community_fields", "signup"
  end

  serialize :signup_welcome_text, Hash
  def signup_welcome_text
    self[:signup_welcome_text] ||= {}
  end

  def signup_welcome_text_subject
    self.signup_welcome_text[:subject]
  end

  def signup_welcome_text_subject=(subject)
    self.signup_welcome_text[:subject] = subject
  end

  def signup_welcome_text_body
    self.signup_welcome_text[:body]
  end

  def signup_welcome_text_body=(body)
    self.signup_welcome_text[:body] = body
  end

  def has_signup_welcome_text?
    signup_welcome_text && !signup_welcome_text_body.blank?
  end

  # #################################################
  # Validations
  # #################################################

  # <tt>name</tt> is mandatory
  validates_presence_of :name

  # only one environment can be the default one
  validates_uniqueness_of :is_default, if: (lambda do |environment| environment.is_default? end), message: N_("Only one Virtual Community can be the default one")

  validates_format_of :contact_email, :noreply_email, with: Noosfero::Constants::EMAIL_FORMAT, allow_blank: true

  xss_terminate only: [:message_for_disabled_enterprise], with: :white_list, on: :validation

  validates_presence_of :theme
  validates_numericality_of :reports_lower_bound, allow_nil: false, only_integer: true, greater_than_or_equal_to: 0

  include WhiteListFilter
  filter_iframes :message_for_disabled_enterprise
  def iframe_whitelist
    trusted_sites_for_iframe
  end

  # #################################################
  # Business logic in general
  # #################################################

  # the default Environment.
  def self.default
    self.where("is_default = ?", true).first
  end

  # returns an array with the top level categories for this environment.
  def top_level_categories
    Category.top_level_for(self).where("type = ? or type = ?", "Category", "Region")
  end

  # returns an array with the top level regions for this environment.
  def top_level_regions
    Region.top_level_for(self)
  end

  # Returns the hostname of the first domain associated to this environment.
  #
  # If #force_www is true, adds 'www.' at the beginning of the hostname. If the
  # environment has not associated domains, returns 'localhost'.
  def default_hostname(email_hostname = false)
    domain = "localhost"
    domains = self.domains.order(:id)
    unless domains.empty?
      domain = (domains.detect { |d| d.is_default } || domains.first).name
      domain = email_hostname ? domain : (force_www ? ("www." + domain) : domain)
    end
    domain
  end

  def admin_url
    { controller: "admin_panel", action: "index" }
  end

  attr_accessor :request_scheme
  def top_url(scheme = nil)
    scheme ||= request_scheme ? request_scheme : "http"

    url = scheme + "://"
    url << (Noosfero.url_options.key?(:host) ? Noosfero.url_options[:host] : default_hostname)
    url << ":" << Noosfero.url_options[:port].to_s if Noosfero.url_options.key?(:port)
    url << Noosfero.root("")
    url.html_safe
  end

  def to_s
    self.name || "?"
  end

  has_many :articles, through: :profiles

  has_many :events, through: :profiles, source: :articles, class_name: "Event"

  has_many :article_tags, through: :articles, source: :tags
  has_many :profile_tags, through: :profiles, source: :tags

  include ScopeTool
  scope :tags, ->environment { ScopeTool.union(environment.article_tags, environment.profile_tags) }

  def tags
    self.class.tags(self)
  end

  def environment_tags
    results = articles.tag_counts.inject({}) do |memo, tag|
      memo[tag.name] = tag.count
      memo
    end

    profiles.tag_counts.inject(results) do |memo, tag|
      memo[tag.name].present? ? memo[tag.name] += tag.count : memo[tag.name] = tag.count
      memo
    end
  end

  def themes
    if settings[:themes]
      Theme.system_themes.select { |theme| settings[:themes].include?(theme.id) }
    else
      []
    end
  end

  def theme_ids
    settings[:themes] || []
  end

  def themes=(values)
    settings[:themes] = values
  end

  def add_themes(values)
    if settings[:themes].nil?
      self.themes = values
    else
      settings[:themes] += values
    end
  end

  def update_theme(theme)
    self.theme = theme
    self.save!
  end

  def update_layout_template(template)
    self.layout_template = template
    self.save!
  end

  before_create do |env|
    env.settings[:themes] ||= %w[
      aluminium
      butter
      chameleon
      chocolate
      noosfero
      orange
      plum
      scarletred
      skyblue
    ]
  end

  def is_default_template?(template)
    is_default = template == community_default_template
    is_default = is_default || template == person_default_template
    is_default = is_default || template == enterprise_default_template
    is_default
  end

  def community_templates
    self.communities.templates
  end

  def community_default_template
    template = Community.find_by id: settings[:community_template_id]
    template if template && template.is_template?
  end

  def community_default_template=(value)
    settings[:community_template_id] = value.kind_of?(Community) ? value.id : value
  end

  def person_templates
    self.people.templates
  end

  def person_default_template
    template = Person.find_by id: settings[:person_template_id]
    template if template && template.is_template?
  end

  def person_default_template=(value)
    settings[:person_template_id] = value.kind_of?(Person) ? value.id : value
  end

  def enterprise_templates
    self.enterprises.templates
  end

  def enterprise_default_template
    template = Enterprise.find_by id: settings[:enterprise_template_id]
    template if template && template.is_template?
  end

  def enterprise_default_template=(value)
    settings[:enterprise_template_id] = value.kind_of?(Enterprise) ? value.id : value
  end

  def inactive_enterprise_template
    template = Enterprise.find_by id: settings[:inactive_enterprise_template_id]
    template if template && template.is_template
  end

  def inactive_enterprise_template=(value)
    settings[:inactive_enterprise_template_id] = value.id
  end

  def replace_enterprise_template_when_enable
    settings[:replace_enterprise_template_when_enable] || false
  end

  def replace_enterprise_template_when_enable=(value)
    settings[:replace_enterprise_template_when_enable] = value
  end

  def portal_community
    Community[settings[:portal_community_identifier]]
  end

  def portal_community=(value)
    settings[:portal_community_identifier] = value.nil? ? nil : value.identifier
  end

  def unset_portal_community!
    self.portal_community = nil
    self.portal_folders = nil
    self.news_amount_by_folder = nil
    self.disable("use_portal_community")
    self.save
  end

  def is_portal_community?(profile)
    portal_community == profile
  end

  def portal_folders
    (settings[:portal_folders] || []).map { |fid| portal_community.articles.where(id: fid).first }.compact
  end

  def portal_folders=(folders)
    settings[:portal_folders] = folders ? folders.map(&:id) : nil
  end

  def portal_news_cache_key(language = "en")
    "home-page-news/#{cache_key}-#{language}"
  end

  def portal_enabled
    portal_community && enabled?("use_portal_community")
  end

  def notification_emails
    [contact_email].select(&:present?) + admins.map(&:email)
  end

  after_create :create_templates

  def create_templates
    prefix = self.name.to_slug + "_"

    enterprise_template = Enterprise.new(
      name: "Enterprise template",
      identifier: prefix + "enterprise_template"
    )

    inactive_enterprise_template = Enterprise.new(
      name: "Inactive Enterprise template",
      identifier: prefix + "inactive_enterprise_template"
    )

    community_template = Community.new(
      name: "Community template",
      identifier: prefix + "community_template"
    )

    [
      enterprise_template,
      inactive_enterprise_template,
      community_template
    ].each do |profile|
      profile.is_template = true
      profile.visible = false
      profile.environment = self
      profile.save!
    end

    pass = Digest::MD5.hexdigest rand.to_s
    user = User.new(login: (prefix + "person_template"), email: (prefix + "template@template.noo"), password: pass, password_confirmation: pass)
    user.environment = self
    user.save!

    person_template = user.person
    person_template.name = "Person template"
    person_template.is_template = true
    person_template.visible = false
    person_template.save!

    self.enterprise_default_template = enterprise_template
    self.inactive_enterprise_template = inactive_enterprise_template
    self.community_default_template = community_template
    self.person_default_template = person_template
    self.save!
  end

  after_create :create_default_licenses
  def create_default_licenses
    [
      { name: "CC (by)", url: "http://creativecommons.org/licenses/by/3.0/legalcode" },
      { name: "CC (by-nd)", url: "http://creativecommons.org/licenses/by-nd/3.0/legalcode" },
      { name: "CC (by-sa)", url: "http://creativecommons.org/licenses/by-sa/3.0/legalcode" },
      { name: "CC (by-nc)", url: "http://creativecommons.org/licenses/by-nc/3.0/legalcode" },
      { name: "CC (by-nc-nd)", url: "http://creativecommons.org/licenses/by-nc-nd/3.0/legalcode" },
      { name: "CC (by-nc-sa)", url: "http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode" },
      { name: "Free Art", url: "http://artlibre.org/licence/lal/en" },
      { name: "GNU FDL", url: "http://www.gnu.org/licenses/fdl-1.3.txt" },
    ].each do |data|
      license = License.new(data)
      license.environment = self
      license.save!
    end
  end

  settings_items :home_cache_in_minutes, type: :integer, default: 5
  settings_items :general_cache_in_minutes, type: :integer, default: 15
  settings_items :profile_cache_in_minutes, type: :integer, default: 15

  def image_galleries
    portal_community ? portal_community.image_galleries : []
  end

  serialize :languages

  before_validation do |environment|
    environment.default_language = nil if environment.default_language.blank?
  end

  validate :default_language_available
  validate :languages_available
  validate :upload_quota_sizes

  def locales
    if languages.present?
      languages.inject({}) { |r, l| r.merge(l => Noosfero.locales[l]) }
    else
      Noosfero.locales
    end
  end

  def default_locale
    default_language || Noosfero.default_locale
  end

  def available_locales
    locales_list = locales.keys
    # move English to the beginning
    if locales_list.include?("en")
      locales_list = ["en"] + (locales_list - ["en"]).sort
    end
    locales_list
  end

  def has_license?
    self.licenses.any?
  end

  def to_liquid
    HashWithIndifferentAccess.new name: name
  end

  def permissions_for(person)
    person.role_assignments.where(resource: self).includes(:role).map { |ra| ra.role.permissions }.flatten.uniq
  end

  def available_blocks(person)
    core_blocks = [ArticleBlock, LoginBlock, RecentDocumentsBlock, EnterprisesBlock,
                   CommunitiesBlock, LinkListBlock, FeedReaderBlock, SlideshowBlock,
                   HighlightsBlock, CategoriesBlock, RawHTMLBlock, TagsCloudBlock]
    core_blocks + plugins.dispatch(:extra_blocks, type: self.class)
  end

  include Noosfero::Plugin::HotSpot
  def environment
    self
  end

  def reserved_identifiers
    plugins.dispatch(:reserved_identifiers).inject([]) do |result, identifier|
      result << identifier.to_s
    end
  end

  def is_identifier_available?(identifier, profile_id = nil)
    profiles = environment.profiles.where(identifier: identifier)
    profiles = profiles.where(["id != ?", profile_id]) if profile_id.present?
    !reserved_identifiers.include?(identifier) && !profiles.exists?
  end

  def quota_for(klass)
    if metadata["quotas"].present?
      quota = metadata["quotas"][klass.to_s]
      quota.blank? ? nil : quota.to_f
    else
      klass.default_quota
    end
  end

  def allow_edit_design?(person = nil)
    person.kind_of?(Profile) && person.has_permission?("edit_environment_design", self)
  end

  def method_missing(method, *args, &block)
    if method.to_s =~ /^(.+)_captcha_requirement$/ && captcha_requirements.keys.include?($1.to_sym)
      self.send(:captcha_requirement, $1)
    else
      super
    end
  end

  private

    def default_language_available
      if default_language.present? && !available_locales.include?(default_language)
        errors.add(:default_language, _("is not available."))
      end
    end

    def languages_available
      if languages.present?
        languages.each do |language|
          if !Noosfero.available_locales.include?(language)
            errors.add(:languages, _("have unsupported languages."))
            break
          end
        end
      end
    end

    def upload_quota_sizes
      quotas = metadata["quotas"] || {}
      quotas.each do |klass, quota|
        float_quota = Float(quota) rescue nil
        if quota.present? && float_quota.nil?
          errors.add(:quota, _("Invalid value"))
        end
      end
    end
end