Noosfero/noosfero

View on GitHub
plugins/custom_forms/lib/custom_forms_plugin/form.rb

Summary

Maintainability
A
50 mins
Test Coverage
class CustomFormsPlugin::Form < ApplicationRecord
  belongs_to :profile, optional: true

  has_many :fields, -> { order "custom_forms_plugin_fields.position" },
           class_name: "CustomFormsPlugin::Field", dependent: :destroy
  accepts_nested_attributes_for :fields, allow_destroy: true

  has_many :submissions,
           class_name: "CustomFormsPlugin::Submission", dependent: :destroy

  validates_presence_of :profile, :name, :identifier
  validates_uniqueness_of :slug, scope: :profile_id
  validates_uniqueness_of :identifier, scope: :profile_id
  validate :period_range,
           if: Proc.new { |f| f.beginning.present? && f.ending.present? }
  validate :valid_poll_alternatives

  # We are using a belongs_to relation, to avoid change the UploadedFile schema.
  # With the belongs_to instead of the has_one, we keep the change only on the
  # CustomFormsPlugin::Form schema.
  belongs_to :article, class_name: "UploadedFile", dependent: :destroy, optional: true

  attr_accessible :name, :profile, :for_admission, :access, :beginning, :kind,
                  :ending, :description, :fields_attributes, :profile_id,
                  :on_membership, :identifier, :access_result_options, :image

  attr_accessor :remove_image

  delegate :environment, to: :profile

  KINDS = %w(survey poll)
  # Dynamic Translations
  _("Survey")
  _("Surveys")
  _("survey")
  _("surveys")
  _("Poll")
  _("Polls")
  _("poll")
  _("polls")

  validates :kind, inclusion: { in: KINDS, message: _("%{value} is not a valid kind.") }

  SEARCHABLE_FIELDS = {
    name: { label: _("Name"), weight: 10 },
    slug: { label: _("Slug"), weight: 5 },
    identifier: { label: _("identifier"), weight: 5 },
    description: { label: _("Description"), weight: 3 },
  }

  before_validation do |form|
    form.slug = form.name.to_slug if form.name.present?
    form.identifier = form.slug unless form.identifier.present?
    form.access = nil if form.access.blank?
  end

  after_destroy do |form|
    tasks = CustomFormsPlugin::MembershipSurvey.opened.select { |t| t.form_id == form.id }
    tasks.each { |task| task.cancel }
  end

  scope :from_profile, ->profile { where profile_id: profile.id }
  scope :on_memberships, -> { where on_membership: true, for_admission: false }
  scope :for_admissions, -> { where for_admission: true }
  scope :by_kind, ->kind { where kind: kind.to_s }

  scope :closed, -> { where("ending <= ?", DateTime.now) }
  scope :not_open_yet, -> { where("beginning > ?", DateTime.now) }
  scope :not_closed, -> {
                       where("(beginning < ? OR beginning IS NULL) AND "\
                          "(ending > ? OR ending IS NULL)",
                             DateTime.now, DateTime.now)
                     }

  scope :with_public_results, -> { where access_result_options: "public" }
  scope :with_private_results, -> { where access_result_options: "private" }
  scope :with_public_results_after_ends, -> { where access_result_options: "public_after_ends" }
  scope :by_status, ->status {
    case status
    when "opened"
      where("(beginning IS NULL OR beginning <= ?) AND (ending IS NULL OR ending > ?)", Time.now, Time.now)
    when "closed"
      where("ending IS NOT NULL AND ending < ?", Time.now)
    when "to-come"
      where("beginning IS NOT NULL AND beginning > ?", Time.now)
    end
  }

  # TODO Fix this
  scope :accessible_to, ->user, profile {
    where("access <= ?", profile.entitlement(user))
  }

  def display_to?(user)
    access <= profile.entitlement(user)
  end

  def expired?
    (beginning.present? && Time.now < beginning) || (ending.present? && Time.now > ending)
  end

  def will_open?
    beginning.present? && Time.now < beginning
  end

  def access_levels
    Entitlement::Levels.range_options(0, 2)
  end

  def image
    self.article
  end

  def image=(uploaded_file)
    self.article = uploaded_file
  end

  def default_img_url
    "/plugins/custom_forms/images/default-#{kind.underscore}.png"
  end

  def image_url
    image.present? ? image.full_path : default_img_url
  end

  def status
    if beginning.try(:future?)
      :not_open
    elsif ending.try(:future?) && (ending < 3.days.from_now)
      :closing_soon
    elsif ending.nil? || ending.try(:future?)
      :open
    else
      :closed
    end
  end

  def submission_by(person)
    if person.present?
      subm = submissions.find_by(form_id: self.id, profile_id: person.id)
    end
    subm || CustomFormsPlugin::Submission.new(form: self, profile: person)
  end

  def results
    CustomFormsPlugin::Graph.new(self).query_results
  end

  alias_attribute :result_access, :access_result_options

  def show_results_for(person)
    (result_access.blank?) ||
      (result_access == "public") ||
      (
        result_access == "public_after_ends" &&
        ((ending.present? && (ending < DateTime.now)) || can_view?(person))
      ) ||
      ((result_access == "private") && can_view?(person))
  end

  private

    def can_view?(person)
      (person == profile ||
      person.in?(profile.admins) ||
      person.in?(profile.environment.admins))
    end

    def period_range
      errors.add(:base, _("The time range selected is invalid.")) if ending < beginning
    end

    def valid_poll_alternatives
      if kind == "poll" && fields.first.present? && fields.first.alternatives.size < 2
        errors.add(:poll_alternatives, _("can't be less than 2"))
        false
      end
      true
    end
end