Noosfero/noosfero

View on GitHub
app/models/block.rb

Summary

Maintainability
C
1 day
Test Coverage
class Block < ApplicationRecord
  attr_accessible :title, :subtitle, :display, :limit, :box_id, :posts_per_page,
                  :visualization_format, :language, :display_user, :position,
                  :box, :edit_modes, :move_modes, :mirror, :visualization, :images_builder, :api_content,
                  :css

  include ActionView::Helpers::TagHelper

  # Block-specific stuff
  include BlockHelper

  delegate :environment, to: :box, allow_nil: true

  acts_as_list scope: :box_id

  belongs_to :box, optional: true
  belongs_to :mirror_block, class_name: "Block", optional: true
  has_many :observers, class_name: "Block", foreign_key: "mirror_block_id"
  has_many :images, foreign_key: "owner_id"

  extend ActsAsHavingSettings::ClassMethods
  acts_as_having_settings

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

  store_accessor :metadata
  include MetadataScopes

  scope :enabled, -> { where enabled: true }

  after_save do |block|
    if block.owner.kind_of?(Profile) && block.owner.is_template? && block.mirror?
      block.observers.each do |observer|
        observer.copy_from(block)
        observer.title = block.title
        observer.save
      end
    end
  end

  after_destroy do |block|
    if block.owner.kind_of?(Profile) && block.owner.is_template? && block.mirror?
      block.observers.each do |observer|
        observer.destroy
      end
    end
  end

  def embedable?
    false
  end

  def get_limit
    [0, limit.to_i].max
  end

  def embed_code
    me = self
    proc do
      content_tag("iframe", "",
                  src: url_for(controller: "embed", action: "block", id: me.id, only_path: false),
                  frameborder: 0,
                  width: 1024,
                  height: 768,
                  class: "embed block #{me.class.name.to_css_class}")
    end
  end

  # Determines whether a given block must be visible. Optionally a
  # <tt>context</tt> must be specified. <tt>context</tt> must be a hash, and
  # may contain the following keys:
  #
  # * <tt>:article</tt>: the article being viewed currently
  # * <tt>:language</tt>: in which language the block will be displayed
  # * <tt>:user</tt>: the logged user
  def visible?(context = nil)
    return false if display == "never"

    if context
      return false if language != "all" && language != context[:locale]
      return false unless display_to_user?(context[:user])

      begin
        return self.send("display_#{display}", context)
      rescue NoMethodError => exception
        raise "Display '#{display}' is not a valid value."
      end
    end

    true
  end

  def visible_to_user?(user)
    visible = self.display_to_user?(user)
    if self.owner.kind_of?(Profile)
      visible &= self.owner.display_to?(user)
      visible &= (self.visible? || user && user.has_permission?(:edit_profile_design, self.owner))
    elsif self.owner.kind_of?(Environment)
      visible &= (self.visible? || user && user.has_permission?(:edit_environment_design, self.owner))
    end
    visible
  end

  def display_to_user?(user)
    display_user == "all" || (environment.present? && environment.admins.include?(user)) || (user.nil? && display_user == "not_logged") || (user && display_user == "logged") || (user && !self.owner.kind_of?(Environment) && display_user == "followers" && owner.in_social_circle?(user) && self.owner.kind_of?(Profile))
  end

  def display_always(context)
    true
  end

  def display_home_page_only(context)
    if context[:article]
      return context[:article] == owner.home_page
    else
      return home_page_path?(context[:request_path])
    end
  end

  def display_except_home_page(context)
    if context[:article]
      return context[:article] != owner.home_page
    else
      return !home_page_path?(context[:request_path])
    end
  end

  # The condition for displaying a block. It can assume the following values:
  #
  # * <tt>'always'</tt>: the block is always displayed
  # * <tt>'never'</tt>: the block is hidden (it does not appear for visitors)
  # * <tt>'home_page_only'</tt> the block is displayed only when viewing the
  #   homepage of its owner.
  # * <tt>'except_home_page'</tt> the block is displayed only when viewing
  #   the homepage of its owner.
  settings_items :display, type: :string, default: "always"

  # The condition for displaying a block to users. It can assume the following values:
  #
  # * <tt>'all'</tt>: the block is always displayed
  # * <tt>'logged'</tt>: the block is displayed to logged users only
  # * <tt>'not_logged'</tt>: the block is displayed only to not logged users
  settings_items :display_user, type: :string, default: "all"

  # The block can be configured to be displayed in all languages or in just one language. It can assume any locale of the environment:
  #
  # * <tt>'all'</tt>: the block is always displayed
  settings_items :language, type: :string, default: "all"

  # The block can be configured to define the edition modes options. Only can be edited by environment admins
  # It can assume the following values:
  #
  # * <tt>'all'</tt>: the block owner has all edit options for this block
  # * <tt>'none'</tt>: the block owner can't do anything with the block
  settings_items :edit_modes, type: :string, default: "all"
  settings_items :move_modes, type: :string, default: "all"

  # returns the description of the block, used when the user sees a list of
  # blocks to choose one to include in the design.
  #
  # Must be redefined in subclasses to match the description of each block
  # type.
  def self.description
    "(dummy)"
  end

  def self.short_description
    self.pretty_name
  end

  def self.icon
    "/images/icon_block.png"
  end

  def self.icon_path
    basename = self.name.split("::").last.underscore
    File.join("images", "blocks", basename, "icon.png")
  end

  def self.pretty_name
    self.name.split("::").last.gsub("Block", "")
  end

  def self.default_icon_path
    "/images/icon_block.png"
  end

  def self.preview_path
    base_name = self.name.split("::").last.underscore
    File.join("blocks", base_name, "previews")
  end

  def self.default_preview_path
    "/images/block_preview.png"
  end

  # Is this block editable? (Default to <tt>true</tt>)
  def editable?(user = nil)
    self.edit_modes == "all"
  end

  def movable?
    self.move_modes == "all"
  end

  # must always return false, except on MainBlock class.
  def main?
    false
  end

  def owner
    box ? box.owner : nil
  end

  def default_title
    ""
  end

  def title
    if self[:title].blank?
      self.default_title
    else
      self[:title]
    end
  end

  def view_title
    title
  end

  def cacheable?
    true
  end

  alias :active_record_cache_key :cache_key
  def cache_key(language = "en", user = nil)
    active_record_cache_key + "-" + language
  end

  def timeout
    4.hours
  end

  def has_macro?
    false
  end

  # Override in your subclasses.
  # Define which events and context should cause the block cache to expire
  # Possible events are: :article, :profile, :friendship, :category, :role_assignment
  # Possible contexts are: :profile, :environment
  def self.expire_on
    {
      profile: [],
      environment: []
    }
  end

  DISPLAY_OPTIONS = {
    "always" => _("In all pages"),
    "home_page_only" => _("Only in the homepage"),
    "except_home_page" => _("In all pages, except in the homepage"),
    "never" => _("Don't display"),
  }

  def display_options_available
    DISPLAY_OPTIONS.keys
  end

  def display_options
    DISPLAY_OPTIONS.slice(*display_options_available)
  end

  def display_user_options
    @display_user_options ||= {
      "all" => _("All users"),
      "logged" => _("Logged"),
      "not_logged" => _("Not logged"),
      "followers" => owner.class != Environment && owner.organization? ? _("Members") : _("Friends")
    }
  end

  def edit_block_options
    @edit_options ||= {
      "all" => _("Can be modified"),
      "none" => _("Cannot be modified")
    }
  end

  def move_block_options
    @move_options ||= {
      "all" => _("Can be moved"),
      "none" => _("Cannot be moved")
    }
  end

  def duplicate
    duplicated_block = self.dup
    duplicated_block.display = "never"
    duplicated_block.created_at = nil
    duplicated_block.updated_at = nil
    duplicated_block.save!
    duplicated_block.insert_at(self.position + 1)
    duplicated_block
  end

  def copy_from(block)
    self.settings = block.settings
    self.position = block.position
  end

  def add_observer(block)
    self.observers << block
  end

  def api_content(options = {})
    nil
  end

  def api_content=(values = {})
    settings[:display] = values[:display]
    settings[:display_user] = values[:display_user]
  end

  def display_api_content_by_default?
    false
  end

  def allow_edit?(person)
    return false if person.nil? || (!person.is_admin? && !editable?(person))
    if self.owner.kind_of?(Profile)
      return person.has_permission?(:edit_profile_design, owner)
    elsif self.owner.kind_of?(Environment)
      return person.has_permission?(:edit_environment_design, owner)
    end

    false
  end

  def images_builder=(raw_images)
    raw_images.each do |img|
      if img[:remove_image] == true || img[:remove_image] == "true"
        images.find_by(id: img[:id]).destroy!
      elsif !img[:uploaded_data].blank?
        images.build(img)
      end
    end
  end

  private

    def home_page_path
      home_page_url = Noosfero.root("/")

      if owner.kind_of?(Profile)
        home_page_url += "profile/" if owner.home_page.nil?
        home_page_url += owner.identifier
      end

      return home_page_url
    end

    def home_page_path?(path)
      return path == home_page_path || path == (home_page_path + "/")
    end
end