Mercurial > hg > soundsoftware-site
comparison app/models/.svn/text-base/project.rb.svn-base @ 441:cbce1fd3b1b7 redmine-1.2
Update to Redmine 1.2-stable branch (Redmine SVN rev 6000)
author | Chris Cannam |
---|---|
date | Mon, 06 Jun 2011 14:24:13 +0100 |
parents | 8661b858af72 |
children | 0c939c159af4 |
comparison
equal
deleted
inserted
replaced
245:051f544170fe | 441:cbce1fd3b1b7 |
---|---|
1 # redMine - project management software | 1 # Redmine - project management software |
2 # Copyright (C) 2006 Jean-Philippe Lang | 2 # Copyright (C) 2006-2011 Jean-Philippe Lang |
3 # | 3 # |
4 # This program is free software; you can redistribute it and/or | 4 # This program is free software; you can redistribute it and/or |
5 # modify it under the terms of the GNU General Public License | 5 # modify it under the terms of the GNU General Public License |
6 # as published by the Free Software Foundation; either version 2 | 6 # as published by the Free Software Foundation; either version 2 |
7 # of the License, or (at your option) any later version. | 7 # of the License, or (at your option) any later version. |
41 has_many :issue_changes, :through => :issues, :source => :journals | 41 has_many :issue_changes, :through => :issues, :source => :journals |
42 has_many :versions, :dependent => :destroy, :order => "#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC" | 42 has_many :versions, :dependent => :destroy, :order => "#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC" |
43 has_many :time_entries, :dependent => :delete_all | 43 has_many :time_entries, :dependent => :delete_all |
44 has_many :queries, :dependent => :delete_all | 44 has_many :queries, :dependent => :delete_all |
45 has_many :documents, :dependent => :destroy | 45 has_many :documents, :dependent => :destroy |
46 has_many :news, :dependent => :delete_all, :include => :author | 46 has_many :news, :dependent => :destroy, :include => :author |
47 has_many :issue_categories, :dependent => :delete_all, :order => "#{IssueCategory.table_name}.name" | 47 has_many :issue_categories, :dependent => :delete_all, :order => "#{IssueCategory.table_name}.name" |
48 has_many :boards, :dependent => :destroy, :order => "position ASC" | 48 has_many :boards, :dependent => :destroy, :order => "position ASC" |
49 has_one :repository, :dependent => :destroy | 49 has_one :repository, :dependent => :destroy |
50 has_many :changesets, :through => :repository | 50 has_many :changesets, :through => :repository |
51 has_one :wiki, :dependent => :destroy | 51 has_one :wiki, :dependent => :destroy |
54 :class_name => 'IssueCustomField', | 54 :class_name => 'IssueCustomField', |
55 :order => "#{CustomField.table_name}.position", | 55 :order => "#{CustomField.table_name}.position", |
56 :join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}", | 56 :join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}", |
57 :association_foreign_key => 'custom_field_id' | 57 :association_foreign_key => 'custom_field_id' |
58 | 58 |
59 acts_as_nested_set :order => 'name' | 59 acts_as_nested_set :order => 'name', :dependent => :destroy |
60 acts_as_attachable :view_permission => :view_files, | 60 acts_as_attachable :view_permission => :view_files, |
61 :delete_permission => :manage_files | 61 :delete_permission => :manage_files |
62 | 62 |
63 acts_as_customizable | 63 acts_as_customizable |
64 acts_as_searchable :columns => ['name', 'identifier', 'description'], :project_key => 'id', :permission => nil | 64 acts_as_searchable :columns => ['name', 'identifier', 'description'], :project_key => 'id', :permission => nil |
77 # donwcase letters, digits, dashes but not digits only | 77 # donwcase letters, digits, dashes but not digits only |
78 validates_format_of :identifier, :with => /^(?!\d+$)[a-z0-9\-]*$/, :if => Proc.new { |p| p.identifier_changed? } | 78 validates_format_of :identifier, :with => /^(?!\d+$)[a-z0-9\-]*$/, :if => Proc.new { |p| p.identifier_changed? } |
79 # reserved words | 79 # reserved words |
80 validates_exclusion_of :identifier, :in => %w( new ) | 80 validates_exclusion_of :identifier, :in => %w( new ) |
81 | 81 |
82 before_destroy :delete_all_members, :destroy_children | 82 before_destroy :delete_all_members |
83 | 83 |
84 named_scope :has_module, lambda { |mod| { :conditions => ["#{Project.table_name}.id IN (SELECT em.project_id FROM #{EnabledModule.table_name} em WHERE em.name=?)", mod.to_s] } } | 84 named_scope :has_module, lambda { |mod| { :conditions => ["#{Project.table_name}.id IN (SELECT em.project_id FROM #{EnabledModule.table_name} em WHERE em.name=?)", mod.to_s] } } |
85 named_scope :active, { :conditions => "#{Project.table_name}.status = #{STATUS_ACTIVE}"} | 85 named_scope :active, { :conditions => "#{Project.table_name}.status = #{STATUS_ACTIVE}"} |
86 named_scope :all_public, { :conditions => { :is_public => true } } | 86 named_scope :all_public, { :conditions => { :is_public => true } } |
87 named_scope :visible, lambda { { :conditions => Project.visible_by(User.current) } } | 87 named_scope :visible, lambda {|*args| {:conditions => Project.visible_condition(args.shift || User.current, *args) }} |
88 | 88 |
89 def initialize(attributes = nil) | 89 def initialize(attributes = nil) |
90 super | 90 super |
91 | 91 |
92 initialized = (attributes || {}).stringify_keys | 92 initialized = (attributes || {}).stringify_keys |
113 end | 113 end |
114 | 114 |
115 # returns latest created projects | 115 # returns latest created projects |
116 # non public projects will be returned only if user is a member of those | 116 # non public projects will be returned only if user is a member of those |
117 def self.latest(user=nil, count=5) | 117 def self.latest(user=nil, count=5) |
118 find(:all, :limit => count, :conditions => visible_by(user), :order => "created_on DESC") | 118 visible(user).find(:all, :limit => count, :order => "created_on DESC") |
119 end | 119 end |
120 | 120 |
121 # Returns a SQL :conditions string used to find all active projects for the specified user. | 121 def self.visible_by(user=nil) |
122 ActiveSupport::Deprecation.warn "Project.visible_by is deprecated and will be removed in Redmine 1.3.0. Use Project.visible_condition instead." | |
123 visible_condition(user || User.current) | |
124 end | |
125 | |
126 # Returns a SQL conditions string used to find all projects visible by the specified user. | |
122 # | 127 # |
123 # Examples: | 128 # Examples: |
124 # Projects.visible_by(admin) => "projects.status = 1" | 129 # Project.visible_condition(admin) => "projects.status = 1" |
125 # Projects.visible_by(normal_user) => "projects.status = 1 AND projects.is_public = 1" | 130 # Project.visible_condition(normal_user) => "((projects.status = 1) AND (projects.is_public = 1 OR projects.id IN (1,3,4)))" |
126 def self.visible_by(user=nil) | 131 # Project.visible_condition(anonymous) => "((projects.status = 1) AND (projects.is_public = 1))" |
127 user ||= User.current | 132 def self.visible_condition(user, options={}) |
128 if user && user.admin? | 133 allowed_to_condition(user, :view_project, options) |
129 return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}" | 134 end |
130 elsif user && user.memberships.any? | 135 |
131 return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE} AND (#{Project.table_name}.is_public = #{connection.quoted_true} or #{Project.table_name}.id IN (#{user.memberships.collect{|m| m.project_id}.join(',')}))" | 136 # Returns a SQL conditions string used to find all projects for which +user+ has the given +permission+ |
132 else | 137 # |
133 return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE} AND #{Project.table_name}.is_public = #{connection.quoted_true}" | 138 # Valid options: |
134 end | 139 # * :project => limit the condition to project |
135 end | 140 # * :with_subprojects => limit the condition to project and its subprojects |
136 | 141 # * :member => limit the condition to the user projects |
137 def self.allowed_to_condition(user, permission, options={}) | 142 def self.allowed_to_condition(user, permission, options={}) |
138 statements = [] | |
139 base_statement = "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}" | 143 base_statement = "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}" |
140 if perm = Redmine::AccessControl.permission(permission) | 144 if perm = Redmine::AccessControl.permission(permission) |
141 unless perm.project_module.nil? | 145 unless perm.project_module.nil? |
142 # If the permission belongs to a project module, make sure the module is enabled | 146 # If the permission belongs to a project module, make sure the module is enabled |
143 base_statement << " AND #{Project.table_name}.id IN (SELECT em.project_id FROM #{EnabledModule.table_name} em WHERE em.name='#{perm.project_module}')" | 147 base_statement << " AND #{Project.table_name}.id IN (SELECT em.project_id FROM #{EnabledModule.table_name} em WHERE em.name='#{perm.project_module}')" |
146 if options[:project] | 150 if options[:project] |
147 project_statement = "#{Project.table_name}.id = #{options[:project].id}" | 151 project_statement = "#{Project.table_name}.id = #{options[:project].id}" |
148 project_statement << " OR (#{Project.table_name}.lft > #{options[:project].lft} AND #{Project.table_name}.rgt < #{options[:project].rgt})" if options[:with_subprojects] | 152 project_statement << " OR (#{Project.table_name}.lft > #{options[:project].lft} AND #{Project.table_name}.rgt < #{options[:project].rgt})" if options[:with_subprojects] |
149 base_statement = "(#{project_statement}) AND (#{base_statement})" | 153 base_statement = "(#{project_statement}) AND (#{base_statement})" |
150 end | 154 end |
155 | |
151 if user.admin? | 156 if user.admin? |
152 # no restriction | 157 base_statement |
153 else | 158 else |
154 statements << "1=0" | 159 statement_by_role = {} |
160 unless options[:member] | |
161 role = user.logged? ? Role.non_member : Role.anonymous | |
162 if role.allowed_to?(permission) | |
163 statement_by_role[role] = "#{Project.table_name}.is_public = #{connection.quoted_true}" | |
164 end | |
165 end | |
155 if user.logged? | 166 if user.logged? |
156 if Role.non_member.allowed_to?(permission) && !options[:member] | 167 user.projects_by_role.each do |role, projects| |
157 statements << "#{Project.table_name}.is_public = #{connection.quoted_true}" | 168 if role.allowed_to?(permission) |
158 end | 169 statement_by_role[role] = "#{Project.table_name}.id IN (#{projects.collect(&:id).join(',')})" |
159 allowed_project_ids = user.memberships.select {|m| m.roles.detect {|role| role.allowed_to?(permission)}}.collect {|m| m.project_id} | 170 end |
160 statements << "#{Project.table_name}.id IN (#{allowed_project_ids.join(',')})" if allowed_project_ids.any? | 171 end |
172 end | |
173 if statement_by_role.empty? | |
174 "1=0" | |
161 else | 175 else |
162 if Role.anonymous.allowed_to?(permission) && !options[:member] | 176 if block_given? |
163 # anonymous user allowed on public project | 177 statement_by_role.each do |role, statement| |
164 statements << "#{Project.table_name}.is_public = #{connection.quoted_true}" | 178 if s = yield(role, user) |
165 end | 179 statement_by_role[role] = "(#{statement} AND (#{s}))" |
166 end | 180 end |
167 end | 181 end |
168 statements.empty? ? base_statement : "((#{base_statement}) AND (#{statements.join(' OR ')}))" | 182 end |
183 "((#{base_statement}) AND (#{statement_by_role.values.join(' OR ')}))" | |
184 end | |
185 end | |
169 end | 186 end |
170 | 187 |
171 # Returns the Systemwide and project specific activities | 188 # Returns the Systemwide and project specific activities |
172 def activities(include_inactive=false) | 189 def activities(include_inactive=false) |
173 if include_inactive | 190 if include_inactive |
345 end | 362 end |
346 | 363 |
347 # Returns an array of the trackers used by the project and its active sub projects | 364 # Returns an array of the trackers used by the project and its active sub projects |
348 def rolled_up_trackers | 365 def rolled_up_trackers |
349 @rolled_up_trackers ||= | 366 @rolled_up_trackers ||= |
350 Tracker.find(:all, :include => :projects, | 367 Tracker.find(:all, :joins => :projects, |
351 :select => "DISTINCT #{Tracker.table_name}.*", | 368 :select => "DISTINCT #{Tracker.table_name}.*", |
352 :conditions => ["#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status = #{STATUS_ACTIVE}", lft, rgt], | 369 :conditions => ["#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status = #{STATUS_ACTIVE}", lft, rgt], |
353 :order => "#{Tracker.table_name}.position") | 370 :order => "#{Tracker.table_name}.position") |
354 end | 371 end |
355 | 372 |
371 :conditions => ["#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status = #{STATUS_ACTIVE}", lft, rgt]) | 388 :conditions => ["#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status = #{STATUS_ACTIVE}", lft, rgt]) |
372 end | 389 end |
373 | 390 |
374 # Returns a scope of the Versions used by the project | 391 # Returns a scope of the Versions used by the project |
375 def shared_versions | 392 def shared_versions |
376 @shared_versions ||= | 393 @shared_versions ||= begin |
394 r = root? ? self : root | |
377 Version.scoped(:include => :project, | 395 Version.scoped(:include => :project, |
378 :conditions => "#{Project.table_name}.id = #{id}" + | 396 :conditions => "#{Project.table_name}.id = #{id}" + |
379 " OR (#{Project.table_name}.status = #{Project::STATUS_ACTIVE} AND (" + | 397 " OR (#{Project.table_name}.status = #{Project::STATUS_ACTIVE} AND (" + |
380 " #{Version.table_name}.sharing = 'system'" + | 398 " #{Version.table_name}.sharing = 'system'" + |
381 " OR (#{Project.table_name}.lft >= #{root.lft} AND #{Project.table_name}.rgt <= #{root.rgt} AND #{Version.table_name}.sharing = 'tree')" + | 399 " OR (#{Project.table_name}.lft >= #{r.lft} AND #{Project.table_name}.rgt <= #{r.rgt} AND #{Version.table_name}.sharing = 'tree')" + |
382 " OR (#{Project.table_name}.lft < #{lft} AND #{Project.table_name}.rgt > #{rgt} AND #{Version.table_name}.sharing IN ('hierarchy', 'descendants'))" + | 400 " OR (#{Project.table_name}.lft < #{lft} AND #{Project.table_name}.rgt > #{rgt} AND #{Version.table_name}.sharing IN ('hierarchy', 'descendants'))" + |
383 " OR (#{Project.table_name}.lft > #{lft} AND #{Project.table_name}.rgt < #{rgt} AND #{Version.table_name}.sharing = 'hierarchy')" + | 401 " OR (#{Project.table_name}.lft > #{lft} AND #{Project.table_name}.rgt < #{rgt} AND #{Version.table_name}.sharing = 'hierarchy')" + |
384 "))") | 402 "))") |
403 end | |
385 end | 404 end |
386 | 405 |
387 # Returns a hash of project users grouped by role | 406 # Returns a hash of project users grouped by role |
388 def users_by_role | 407 def users_by_role |
389 members.find(:all, :include => [:user, :roles]).inject({}) do |h, m| | 408 members.find(:all, :include => [:user, :roles]).inject({}) do |h, m| |
420 | 439 |
421 # Returns an array of all custom fields enabled for project issues | 440 # Returns an array of all custom fields enabled for project issues |
422 # (explictly associated custom fields and custom fields enabled for all projects) | 441 # (explictly associated custom fields and custom fields enabled for all projects) |
423 def all_issue_custom_fields | 442 def all_issue_custom_fields |
424 @all_issue_custom_fields ||= (IssueCustomField.for_all + issue_custom_fields).uniq.sort | 443 @all_issue_custom_fields ||= (IssueCustomField.for_all + issue_custom_fields).uniq.sort |
444 end | |
445 | |
446 # Returns an array of all custom fields enabled for project time entries | |
447 # (explictly associated custom fields and custom fields enabled for all projects) | |
448 def all_time_entry_custom_fields | |
449 @all_time_entry_custom_fields ||= (TimeEntryCustomField.for_all + time_entry_custom_fields).uniq.sort | |
425 end | 450 end |
426 | 451 |
427 def project | 452 def project |
428 self | 453 self |
429 end | 454 end |
507 end | 532 end |
508 | 533 |
509 def enabled_module_names=(module_names) | 534 def enabled_module_names=(module_names) |
510 if module_names && module_names.is_a?(Array) | 535 if module_names && module_names.is_a?(Array) |
511 module_names = module_names.collect(&:to_s).reject(&:blank?) | 536 module_names = module_names.collect(&:to_s).reject(&:blank?) |
512 # remove disabled modules | 537 self.enabled_modules = module_names.collect {|name| enabled_modules.detect {|mod| mod.name == name} || EnabledModule.new(:name => name)} |
513 enabled_modules.each {|mod| mod.destroy unless module_names.include?(mod.name)} | |
514 # add new modules | |
515 module_names.reject {|name| module_enabled?(name)}.each {|name| enabled_modules << EnabledModule.new(:name => name)} | |
516 else | 538 else |
517 enabled_modules.clear | 539 enabled_modules.clear |
518 end | 540 end |
519 end | 541 end |
520 | 542 |
619 end | 641 end |
620 end | 642 end |
621 | 643 |
622 private | 644 private |
623 | 645 |
624 # Destroys children before destroying self | |
625 def destroy_children | |
626 children.each do |child| | |
627 child.destroy | |
628 end | |
629 end | |
630 | |
631 # Copies wiki from +project+ | 646 # Copies wiki from +project+ |
632 def copy_wiki(project) | 647 def copy_wiki(project) |
633 # Check that the source project has a wiki first | 648 # Check that the source project has a wiki first |
634 unless project.wiki.nil? | 649 unless project.wiki.nil? |
635 self.wiki ||= Wiki.new | 650 self.wiki ||= Wiki.new |
672 self.issue_categories << new_issue_category | 687 self.issue_categories << new_issue_category |
673 end | 688 end |
674 end | 689 end |
675 | 690 |
676 # Copies issues from +project+ | 691 # Copies issues from +project+ |
692 # Note: issues assigned to a closed version won't be copied due to validation rules | |
677 def copy_issues(project) | 693 def copy_issues(project) |
678 # Stores the source issue id as a key and the copied issues as the | 694 # Stores the source issue id as a key and the copied issues as the |
679 # value. Used to map the two togeather for issue relations. | 695 # value. Used to map the two togeather for issue relations. |
680 issues_map = {} | 696 issues_map = {} |
681 | 697 |