annotate app/models/custom_field.rb @ 8:0c83d98252d9 yuya

* Add custom repo prefix and proper auth realm, remove auth cache (seems like an unwise feature), pass DB handle around, various other bits of tidying
author Chris Cannam
date Thu, 12 Aug 2010 15:31:37 +0100
parents 513646585e45
children 8661b858af72
rev   line source
Chris@0 1 # redMine - project management software
Chris@0 2 # Copyright (C) 2006 Jean-Philippe Lang
Chris@0 3 #
Chris@0 4 # This program is free software; you can redistribute it and/or
Chris@0 5 # modify it under the terms of the GNU General Public License
Chris@0 6 # as published by the Free Software Foundation; either version 2
Chris@0 7 # of the License, or (at your option) any later version.
Chris@0 8 #
Chris@0 9 # This program is distributed in the hope that it will be useful,
Chris@0 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
Chris@0 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Chris@0 12 # GNU General Public License for more details.
Chris@0 13 #
Chris@0 14 # You should have received a copy of the GNU General Public License
Chris@0 15 # along with this program; if not, write to the Free Software
Chris@0 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Chris@0 17
Chris@0 18 class CustomField < ActiveRecord::Base
Chris@0 19 has_many :custom_values, :dependent => :delete_all
Chris@0 20 acts_as_list :scope => 'type = \'#{self.class}\''
Chris@0 21 serialize :possible_values
Chris@0 22
Chris@0 23 validates_presence_of :name, :field_format
Chris@0 24 validates_uniqueness_of :name, :scope => :type
Chris@0 25 validates_length_of :name, :maximum => 30
Chris@0 26 validates_format_of :name, :with => /^[\w\s\.\'\-]*$/i
Chris@0 27 validates_inclusion_of :field_format, :in => Redmine::CustomFieldFormat.available_formats
Chris@0 28
Chris@0 29 def initialize(attributes = nil)
Chris@0 30 super
Chris@0 31 self.possible_values ||= []
Chris@0 32 end
Chris@0 33
Chris@0 34 def before_validation
Chris@0 35 # make sure these fields are not searchable
Chris@0 36 self.searchable = false if %w(int float date bool).include?(field_format)
Chris@0 37 true
Chris@0 38 end
Chris@0 39
Chris@0 40 def validate
Chris@0 41 if self.field_format == "list"
Chris@0 42 errors.add(:possible_values, :blank) if self.possible_values.nil? || self.possible_values.empty?
Chris@0 43 errors.add(:possible_values, :invalid) unless self.possible_values.is_a? Array
Chris@0 44 end
Chris@0 45
Chris@0 46 # validate default value
Chris@0 47 v = CustomValue.new(:custom_field => self.clone, :value => default_value, :customized => nil)
Chris@0 48 v.custom_field.is_required = false
Chris@0 49 errors.add(:default_value, :invalid) unless v.valid?
Chris@0 50 end
Chris@0 51
Chris@0 52 # Makes possible_values accept a multiline string
Chris@0 53 def possible_values=(arg)
Chris@0 54 if arg.is_a?(Array)
Chris@0 55 write_attribute(:possible_values, arg.compact.collect(&:strip).select {|v| !v.blank?})
Chris@0 56 else
Chris@0 57 self.possible_values = arg.to_s.split(/[\n\r]+/)
Chris@0 58 end
Chris@0 59 end
Chris@0 60
Chris@0 61 def cast_value(value)
Chris@0 62 casted = nil
Chris@0 63 unless value.blank?
Chris@0 64 case field_format
Chris@0 65 when 'string', 'text', 'list'
Chris@0 66 casted = value
Chris@0 67 when 'date'
Chris@0 68 casted = begin; value.to_date; rescue; nil end
Chris@0 69 when 'bool'
Chris@0 70 casted = (value == '1' ? true : false)
Chris@0 71 when 'int'
Chris@0 72 casted = value.to_i
Chris@0 73 when 'float'
Chris@0 74 casted = value.to_f
Chris@0 75 end
Chris@0 76 end
Chris@0 77 casted
Chris@0 78 end
Chris@0 79
Chris@0 80 # Returns a ORDER BY clause that can used to sort customized
Chris@0 81 # objects by their value of the custom field.
Chris@0 82 # Returns false, if the custom field can not be used for sorting.
Chris@0 83 def order_statement
Chris@0 84 case field_format
Chris@0 85 when 'string', 'text', 'list', 'date', 'bool'
Chris@0 86 # COALESCE is here to make sure that blank and NULL values are sorted equally
Chris@0 87 "COALESCE((SELECT cv_sort.value FROM #{CustomValue.table_name} cv_sort" +
Chris@0 88 " WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" +
Chris@0 89 " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
Chris@0 90 " AND cv_sort.custom_field_id=#{id} LIMIT 1), '')"
Chris@0 91 when 'int', 'float'
Chris@0 92 # Make the database cast values into numeric
Chris@0 93 # Postgresql will raise an error if a value can not be casted!
Chris@0 94 # CustomValue validations should ensure that it doesn't occur
Chris@0 95 "(SELECT CAST(cv_sort.value AS decimal(60,3)) FROM #{CustomValue.table_name} cv_sort" +
Chris@0 96 " WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" +
Chris@0 97 " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" +
Chris@0 98 " AND cv_sort.custom_field_id=#{id} AND cv_sort.value <> '' AND cv_sort.value IS NOT NULL LIMIT 1)"
Chris@0 99 else
Chris@0 100 nil
Chris@0 101 end
Chris@0 102 end
Chris@0 103
Chris@0 104 def <=>(field)
Chris@0 105 position <=> field.position
Chris@0 106 end
Chris@0 107
Chris@0 108 def self.customized_class
Chris@0 109 self.name =~ /^(.+)CustomField$/
Chris@0 110 begin; $1.constantize; rescue nil; end
Chris@0 111 end
Chris@0 112
Chris@0 113 # to move in project_custom_field
Chris@0 114 def self.for_all
Chris@0 115 find(:all, :conditions => ["is_for_all=?", true], :order => 'position')
Chris@0 116 end
Chris@0 117
Chris@0 118 def type_name
Chris@0 119 nil
Chris@0 120 end
Chris@0 121 end