Class: Project
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- Project
- Defined in:
- app/models/project.rb,
vendor/plugins/classic_pagination/test/fixtures/project.rb
Overview
redMine - project management software Copyright (C) 2006 Jean-Philippe Lang
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Constant Summary
- STATUS_ACTIVE =
Project statuses
1
- STATUS_ARCHIVED =
9
Class Method Summary
- + (Object) allowed_to_condition(user, permission, options = {})
-
+ (Object) copy_from(project)
Copies project and returns the new instance.
- + (Object) find(*args)
-
+ (Object) latest(user = nil, count = 5)
returns latest created projects non public projects will be returned only if user is a member of those.
-
+ (Object) next_identifier
Returns an auto-generated project identifier based on the last identifier used.
-
+ (Object) visible_by(user = nil)
Returns a SQL :conditions string used to find all active projects for the specified user.
Instance Method Summary
- - (Object) <=>(project)
- - (Boolean) active?
-
- (Object) activities(include_inactive = false)
Returns the Systemwide and project specific activities.
-
- (Object) all_issue_custom_fields
Returns an array of all custom fields enabled for project issues (explictly associated custom fields and custom fields enabled for all projects).
-
- (Object) allowed_parents
Returns an array of projects the project can be moved to by the current user.
-
- (Boolean) allows_to?(action)
Return true if this project is allowed to do the specified action.
-
- (Object) archive
Archives the project and its descendants.
-
- (Object) assignable_users
Users issues can be assigned to.
-
- (Object) close_completed_versions
Closes open and locked project versions that are completed.
-
- (Object) copy(project, options = {})
Copies and saves the Project instance based on the project.
-
- (Object) create_time_entry_activity_if_needed(activity)
Create a new TimeEntryActivity if it overrides a system TimeEntryActivity.
-
- (Object) delete_all_members
Deletes all project’s members.
- - (Object) enabled_module_names(module_names)
- - (Object) identifier(identifier)
- - (Boolean) identifier_frozen?
- - (Boolean) module_enabled?(module_name)
-
- (Object) notified_users
Returns the users that should be notified on project events.
- - (Object) project
-
- (Object) project_condition(with_subprojects)
Returns a :conditions SQL string that can be used to find the issues associated with this project.
-
- (Object) recipients
Returns the mail adresses of users that should be always notified on project events.
-
- (Object) rolled_up_trackers
Returns an array of the trackers used by the project and its active sub projects.
-
- (Object) set_allowed_parent!(p)
Sets the parent of the project with authorization check.
-
- (Object) set_parent!(p)
Sets the parent of the project Argument can be either a Project, a String, a Fixnum or nil.
-
- (Object) shared_versions
Returns a scope of the Versions used by the project.
-
- (Object) short_description(length = 255)
Returns a short description of the projects (first lines).
- - (Object) to_param
- - (Object) to_s
-
- (Object) unarchive
Unarchives the project All its ancestors must be active.
-
- (Object) update_or_create_time_entry_activity(id, activity_hash)
Will create a new Project specific Activity or update an existing one.
-
- (Object) users_by_role
Returns a hash of project users grouped by role.
Methods inherited from ActiveRecord::Base
Class Method Details
+ (Object) allowed_to_condition(user, permission, options = {})
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'app/models/project.rb', line 114 def self.allowed_to_condition(user, , ={}) statements = [] base_statement = "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}" if perm = Redmine::AccessControl.() unless perm.project_module.nil? # If the permission belongs to a project module, make sure the module is enabled base_statement << " AND #{Project.table_name}.id IN (SELECT em.project_id FROM #{EnabledModule.table_name} em WHERE em.name='#{perm.project_module}')" end end if [:project] project_statement = "#{Project.table_name}.id = #{options[:project].id}" project_statement << " OR (#{Project.table_name}.lft > #{options[:project].lft} AND #{Project.table_name}.rgt < #{options[:project].rgt})" if [:with_subprojects] base_statement = "(#{project_statement}) AND (#{base_statement})" end if user.admin? # no restriction else statements << "1=0" if user.logged? if Role.non_member.allowed_to?() && ![:member] statements << "#{Project.table_name}.is_public = #{connection.quoted_true}" end allowed_project_ids = user.memberships.select {|m| m.roles.detect {|role| role.allowed_to?()}}.collect {|m| m.project_id} statements << "#{Project.table_name}.id IN (#{allowed_project_ids.join(',')})" if allowed_project_ids.any? else if Role.anonymous.allowed_to?() && ![:member] # anonymous user allowed on public project statements << "#{Project.table_name}.is_public = #{connection.quoted_true}" end end end statements.empty? ? base_statement : "((#{base_statement}) AND (#{statements.join(' OR ')}))" end |
+ (Object) copy_from(project)
Copies project and returns the new instance. This will not save the copy
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 |
# File 'app/models/project.rb', line 480 def self.copy_from(project) begin project = project.is_a?(Project) ? project : Project.find(project) if project # clear unique attributes attributes = project.attributes.dup.except('id', 'name', 'identifier', 'status', 'parent_id', 'lft', 'rgt') copy = Project.new(attributes) copy.enabled_modules = project.enabled_modules copy.trackers = project.trackers copy.custom_values = project.custom_values.collect {|v| v.clone} copy.issue_custom_fields = project.issue_custom_fields return copy else return nil end rescue ActiveRecord::RecordNotFound return nil end end |
+ (Object) find(*args)
204 205 206 207 208 209 210 211 212 |
# File 'app/models/project.rb', line 204 def self.find(*args) if args.first && args.first.is_a?(String) && !args.first.match(/^\d*$/) project = find_by_identifier(*args) raise ActiveRecord::RecordNotFound, "Couldn't find Project with identifier=#{args.first}" if project.nil? project else super end end |
+ (Object) latest(user = nil, count = 5)
returns latest created projects non public projects will be returned only if user is a member of those
94 95 96 |
# File 'app/models/project.rb', line 94 def self.latest(user=nil, count=5) find(:all, :limit => count, :conditions => visible_by(user), :order => "created_on DESC") end |
+ (Object) next_identifier
Returns an auto-generated project identifier based on the last identifier used
439 440 441 442 |
# File 'app/models/project.rb', line 439 def self.next_identifier p = Project.find(:first, :order => 'created_on DESC') p.nil? ? nil : p.identifier.to_s.succ end |
+ (Object) visible_by(user = nil)
Returns a SQL :conditions string used to find all active projects for the specified user.
Examples:
Projects.visible_by(admin) => "projects.status = 1" Projects.visible_by(normal_user) => "projects.status = 1 AND projects.is_public = 1"
103 104 105 106 107 108 109 110 111 112 |
# File 'app/models/project.rb', line 103 def self.visible_by(user=nil) user ||= User.current if user && user.admin? return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}" elsif user && user.memberships.any? 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(',')}))" else return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE} AND #{Project.table_name}.is_public = #{connection.quoted_true}" end end |
Instance Method Details
- (Object) <=>(project)
396 397 398 |
# File 'app/models/project.rb', line 396 def <=>(project) name.downcase <=> project.name.downcase end |
- (Boolean) active?
219 220 221 |
# File 'app/models/project.rb', line 219 def active? self.status == STATUS_ACTIVE end |
- (Object) activities(include_inactive = false)
Returns the Systemwide and project specific activities
149 150 151 152 153 154 155 |
# File 'app/models/project.rb', line 149 def activities(include_inactive=false) if include_inactive return all_activities else return active_activities end end |
- (Object) all_issue_custom_fields
Returns an array of all custom fields enabled for project issues (explictly associated custom fields and custom fields enabled for all projects)
388 389 390 |
# File 'app/models/project.rb', line 388 def all_issue_custom_fields @all_issue_custom_fields ||= (IssueCustomField.for_all + issue_custom_fields).uniq.sort end |
- (Object) allowed_parents
Returns an array of projects the project can be moved to by the current user
248 249 250 251 252 253 254 255 256 257 258 259 |
# File 'app/models/project.rb', line 248 def allowed_parents return @allowed_parents if @allowed_parents @allowed_parents = Project.find(:all, :conditions => Project.allowed_to_condition(User.current, :add_subprojects)) @allowed_parents = @allowed_parents - self_and_descendants if User.current.allowed_to?(:add_project, nil, :global => true) || (!new_record? && parent.nil?) @allowed_parents << nil end unless parent.nil? || @allowed_parents.empty? || @allowed_parents.include?(parent) @allowed_parents << parent end @allowed_parents end |
- (Boolean) allows_to?(action)
Return true if this project is allowed to do the specified action. action can be:
- a parameter-like Hash (eg. :controller => ‘projects’, :action => ‘edit’)
- a permission Symbol (eg. :edit_project)
413 414 415 416 417 418 419 |
# File 'app/models/project.rb', line 413 def allows_to?(action) if action.is_a? Hash allowed_actions.include? "#{action[:controller]}/#{action[:action]}" else .include? action end end |
- (Object) archive
Archives the project and its descendants
224 225 226 227 228 229 230 231 232 233 234 235 236 237 |
# File 'app/models/project.rb', line 224 def archive # Check that there is no issue of a non descendant project that is assigned # to one of the project or descendant versions v_ids = self_and_descendants.collect {|p| p.version_ids}.flatten if v_ids.any? && Issue.find(:first, :include => :project, :conditions => ["(#{Project.table_name}.lft < ? OR #{Project.table_name}.rgt > ?)" + " AND #{Issue.table_name}.fixed_version_id IN (?)", lft, rgt, v_ids]) return false end Project.transaction do archive! end true end |
- (Object) assignable_users
Users issues can be assigned to
372 373 374 |
# File 'app/models/project.rb', line 372 def assignable_users members.select {|m| m.roles.detect {|role| role.assignable?}}.collect {|m| m.user}.sort end |
- (Object) close_completed_versions
Closes open and locked project versions that are completed
330 331 332 333 334 335 336 337 338 |
# File 'app/models/project.rb', line 330 def close_completed_versions Version.transaction do versions.find(:all, :conditions => {:status => %w(open locked)}).each do |version| if version.completed? version.update_attribute(:status, 'closed') end end end end |
- (Object) copy(project, options = {})
Copies and saves the Project instance based on the project. Duplicates the source project’s:
- Wiki
- Versions
- Categories
- Issues
- Members
- Queries
Accepts an options argument to specify what to copy
Examples:
project.copy(1) # => copies everything project.copy(1, :only => 'members') # => copies members only project.copy(1, :only => ['members', 'versions']) # => copies members and versions
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 |
# File 'app/models/project.rb', line 459 def copy(project, ={}) project = project.is_a?(Project) ? project : Project.find(project) to_be_copied = %w(wiki versions issue_categories issues members queries boards) to_be_copied = to_be_copied & [:only].to_a unless [:only].nil? Project.transaction do if save reload to_be_copied.each do |name| send "copy_#{name}", project end Redmine::Hook.call_hook(:model_project_copy_before_save, :source_project => project, :destination_project => self) save end end end |
- (Object) create_time_entry_activity_if_needed(activity)
Create a new TimeEntryActivity if it overrides a system TimeEntryActivity
This will raise a ActiveRecord::Rollback if the TimeEntryActivity does not successfully save.
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'app/models/project.rb', line 174 def create_time_entry_activity_if_needed(activity) if activity['parent_id'] parent_activity = TimeEntryActivity.find(activity['parent_id']) activity['name'] = parent_activity.name activity['position'] = parent_activity.position if Enumeration.overridding_change?(activity, parent_activity) project_activity = self.time_entry_activities.create(activity) if project_activity.new_record? raise ActiveRecord::Rollback, "Overridding TimeEntryActivity was not successfully saved" else self.time_entries.update_all("activity_id = #{project_activity.id}", ["activity_id = ?", parent_activity.id]) end end end end |
- (Object) delete_all_members
Deletes all project’s members
365 366 367 368 369 |
# File 'app/models/project.rb', line 365 def delete_all_members me, mr = Member.table_name, MemberRole.table_name connection.delete("DELETE FROM #{mr} WHERE #{mr}.member_id IN (SELECT #{me}.id FROM #{me} WHERE #{me}.project_id = #{id})") Member.delete_all(['project_id = ?', id]) end |
- (Object) enabled_module_names=(module_names)
426 427 428 429 430 431 432 433 434 435 436 |
# File 'app/models/project.rb', line 426 def enabled_module_names=(module_names) if module_names && module_names.is_a?(Array) module_names = module_names.collect(&:to_s) # remove disabled modules enabled_modules.each {|mod| mod.destroy unless module_names.include?(mod.name)} # add new modules module_names.reject {|name| module_enabled?(name)}.each {|name| enabled_modules << EnabledModule.new(:name => name)} else enabled_modules.clear end end |
- (Object) identifier=(identifier)
84 85 86 |
# File 'app/models/project.rb', line 84 def identifier=(identifier) super unless identifier_frozen? end |
- (Boolean) identifier_frozen?
88 89 90 |
# File 'app/models/project.rb', line 88 def identifier_frozen? errors[:identifier].nil? && !(new_record? || identifier.blank?) end |
- (Boolean) module_enabled?(module_name)
421 422 423 424 |
# File 'app/models/project.rb', line 421 def module_enabled?(module_name) module_name = module_name.to_s enabled_modules.detect {|m| m.name == module_name} end |
- (Object) notified_users
Returns the users that should be notified on project events
382 383 384 |
# File 'app/models/project.rb', line 382 def notified_users members.select {|m| m.mail_notification? || m.user.mail_notification?}.collect {|m| m.user} end |
- (Object) project
392 393 394 |
# File 'app/models/project.rb', line 392 def project self end |
- (Object) project_condition(with_subprojects)
Returns a :conditions SQL string that can be used to find the issues associated with this project.
Examples:
project.project_condition(true) => "(projects.id = 1 OR (projects.lft > 1 AND projects.rgt < 10))" project.project_condition(false) => "projects.id = 1"
198 199 200 201 202 |
# File 'app/models/project.rb', line 198 def project_condition(with_subprojects) cond = "#{Project.table_name}.id = #{id}" cond = "(#{cond} OR (#{Project.table_name}.lft > #{lft} AND #{Project.table_name}.rgt < #{rgt}))" if with_subprojects cond end |
- (Object) recipients
Returns the mail adresses of users that should be always notified on project events
377 378 379 |
# File 'app/models/project.rb', line 377 def recipients members.select {|m| m.mail_notification? || m.user.mail_notification?}.collect {|m| m.user.mail} end |
- (Object) rolled_up_trackers
Returns an array of the trackers used by the project and its active sub projects
321 322 323 324 325 326 327 |
# File 'app/models/project.rb', line 321 def rolled_up_trackers @rolled_up_trackers ||= Tracker.find(:all, :include => :projects, :select => "DISTINCT #{Tracker.table_name}.*", :conditions => ["#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status = #{STATUS_ACTIVE}", lft, rgt], :order => "#{Tracker.table_name}.position") end |
- (Object) set_allowed_parent!(p)
Sets the parent of the project with authorization check
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 |
# File 'app/models/project.rb', line 262 def set_allowed_parent!(p) unless p.nil? || p.is_a?(Project) if p.to_s.blank? p = nil else p = Project.find_by_id(p) return false unless p end end if p.nil? if !new_record? && allowed_parents.empty? return false end elsif !allowed_parents.include?(p) return false end set_parent!(p) end |
- (Object) set_parent!(p)
Sets the parent of the project Argument can be either a Project, a String, a Fixnum or nil
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 |
# File 'app/models/project.rb', line 283 def set_parent!(p) unless p.nil? || p.is_a?(Project) if p.to_s.blank? p = nil else p = Project.find_by_id(p) return false unless p end end if p == parent && !p.nil? # Nothing to do true elsif p.nil? || (p.active? && move_possible?(p)) # Insert the project so that target's children or root projects stay alphabetically sorted sibs = (p.nil? ? self.class.roots : p.children) to_be_inserted_before = sibs.detect {|c| c.name.to_s.downcase > name.to_s.downcase } if to_be_inserted_before move_to_left_of(to_be_inserted_before) elsif p.nil? if sibs.empty? # move_to_root adds the project in first (ie. left) position move_to_root else move_to_right_of(sibs.last) unless self == sibs.last end else # move_to_child_of adds the project in last (ie.right) position move_to_child_of(p) end Issue.update_versions_from_hierarchy_change(self) true else # Can not move to the given target false end end |
Returns a scope of the Versions used by the project
341 342 343 344 345 346 347 348 349 350 351 |
# File 'app/models/project.rb', line 341 def @shared_versions ||= Version.scoped(:include => :project, :conditions => "#{Project.table_name}.id = #{id}" + " OR (#{Project.table_name}.status = #{Project::STATUS_ACTIVE} AND (" + " #{Version.table_name}.sharing = 'system'" + " OR (#{Project.table_name}.lft >= #{root.lft} AND #{Project.table_name}.rgt <= #{root.rgt} AND #{Version.table_name}.sharing = 'tree')" + " OR (#{Project.table_name}.lft < #{lft} AND #{Project.table_name}.rgt > #{rgt} AND #{Version.table_name}.sharing IN ('hierarchy', 'descendants'))" + " OR (#{Project.table_name}.lft > #{lft} AND #{Project.table_name}.rgt < #{rgt} AND #{Version.table_name}.sharing = 'hierarchy')" + "))") end |
- (Object) short_description(length = 255)
Returns a short description of the projects (first lines)
405 406 407 |
# File 'app/models/project.rb', line 405 def short_description(length = 255) description.gsub(/^(.{#{length}}[^\n\r]*).*$/m, '\1...').strip if description end |
- (Object) to_param
214 215 216 217 |
# File 'app/models/project.rb', line 214 def to_param # id is used for projects with a numeric identifier (compatibility) @to_param ||= (identifier.to_s =~ %r{^\d*$} ? id : identifier) end |
- (Object) to_s
400 401 402 |
# File 'app/models/project.rb', line 400 def to_s name end |
- (Object) unarchive
Unarchives the project All its ancestors must be active
241 242 243 244 |
# File 'app/models/project.rb', line 241 def unarchive return false if ancestors.detect {|a| !a.active?} update_attribute :status, STATUS_ACTIVE end |
- (Object) update_or_create_time_entry_activity(id, activity_hash)
Will create a new Project specific Activity or update an existing one
This will raise a ActiveRecord::Rollback if the TimeEntryActivity does not successfully save.
161 162 163 164 165 166 167 168 |
# File 'app/models/project.rb', line 161 def update_or_create_time_entry_activity(id, activity_hash) if activity_hash.respond_to?(:has_key?) && activity_hash.has_key?('parent_id') self.create_time_entry_activity_if_needed(activity_hash) else activity = project.time_entry_activities.find_by_id(id.to_i) activity.update_attributes(activity_hash) if activity end end |
- (Object) users_by_role
Returns a hash of project users grouped by role
354 355 356 357 358 359 360 361 362 |
# File 'app/models/project.rb', line 354 def users_by_role members.find(:all, :include => [:user, :roles]).inject({}) do |h, m| m.roles.each do |r| h[r] ||= [] h[r] << m.user end h end end |