#-- # Copyright (C) 2008 Dimitrij Denissenko # Please read LICENSE document for more information. #++ class Project < ActiveRecord::Base has_and_belongs_to_many :groups, :uniq => true, :include => [:users] belongs_to :repository has_many :tickets, :dependent => :destroy has_many :ticket_changes, :through => :tickets, :dependent => :destroy has_many :ticket_reports, :order => 'ticket_reports.rank', :dependent => :destroy has_many :milestones, :order => 'milestones.rank', :dependent => :destroy has_many :ticket_property_types, :include => [:ticket_properties], :order => 'ticket_property_types.rank, ticket_property_types.id', :dependent => :destroy has_many :ticket_properties, :through => :ticket_property_types, :include => [:ticket_property_type], :order => 'ticket_property_types.rank, ticket_property_types.id, ticket_properties.rank, ticket_properties.id', :dependent => :destroy validates_presence_of :name validates_uniqueness_of :name fn_regex = '[~\#\-!\w\.\+]' validates_format_of :root_path, :with => %r{^(#{fn_regex}+([ /]#{fn_regex}+)*/)?$} validates_length_of :name, :in => 2..80 validates_format_of :name, :with => %r{^[A-Za-z]}, :message => _('must begin with a latin character') validates_inclusion_of :locale, :in => RetroI18n.maps[:names].stringify_keys.keys, :allow_nil => true serialize :disabled_modules, Array serialize :existing_tickets, Hash serialize :existing_revisions, Array attr_accessor :original_path def self.current=(project) @current_project = project end def self.current @current_project.is_a?(Project) ? @current_project : nil end # Overload ActiveRecord::Base's 'instantiate' method to save the original root path attribute def self.instantiate(record) object = super(record) object.original_path = object[:root_path] object end def disabled_modules value = read_attribute(:disabled_modules) value.is_a?(Array) ? value : [] end def module_disabled?(retro_module_name) self.disabled_modules.include?(retro_module_name) end def existing_tickets value = read_attribute(:existing_tickets) value.is_a?(Hash) ? value : {} end def existing_revisions value = read_attribute(:existing_revisions) value.is_a?(Array) ? value : [] end def update_existing_revisions revision_cache = self.changesets.map(&:revision) self.update_attribute(:existing_revisions, revision_cache) end def changesets Changeset.find_by_project(self, :all) end def find_in_changesets(*args) Changeset.find_by_project(self, *args) end def users return [] if self.closed? @users ||= self.groups.inject(User.admin_users) do |users, group| users += group.users end.uniq end def users_with_permission(permission_name) self.groups.inject(User.admin_users) do |users, group| users += group.users if group.includes_permission?(permission_name) users end.uniq end def find_in_users_by_login(login) self.users.find{|u| u.login == login } end def milestones_at(date = nil) date ||= Time.now self.milestones.find(:all, :conditions => ['finished_on IS NULL OR finished_on > ?', date]) end def relativize_path(path) path.to_s.gsub(/^\/?#{self.root_path}\/?/, '') end def absolutize_path(path = nil) path ||= '' root_path.blank? ? path : "#{root_path}#{path}" end def ticket_property_map @ticket_property_map ||= self.ticket_property_types.find(:all, :include => :ticket_properties) end def clear_association_cache #:nodoc: super @users = nil @ticket_property_map = nil end protected def before_validation # Re-format root path self.root_path ||= '' self.root_path.strip! self.root_path = self.root_path.chomp('/') + '/' self.root_path.gsub!(%r{^/}, '') # Set the short name if self.name.is_a?(String) self.short_name = RetroInflector.web_safe_short_name(self.name) end # Set locale to nil if it is blank self.locale = nil if self.locale.blank? end def validate_on_create if self.class.exists?(['short_name = ?', self.short_name]) self.errors.add(:name, 'overlaps with an existing project') end end end