summaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorJean-Philippe Lang <jp_lang@yahoo.fr>2012-06-25 17:49:35 +0000
committerJean-Philippe Lang <jp_lang@yahoo.fr>2012-06-25 17:49:35 +0000
commitac56c0c99ccd14c7229145fc22d6e9eb13ee0af0 (patch)
treef54401b77f7195a1795f4a189f9f9d35734c0a2b /app
parent5961a1e70d1efdfb5c4fd28c20dc8cc4d9a51bac (diff)
downloadredmine-ac56c0c99ccd14c7229145fc22d6e9eb13ee0af0.tar.gz
redmine-ac56c0c99ccd14c7229145fc22d6e9eb13ee0af0.zip
Ability to close projects (read-only) (#3640).
A new permission (Close/reopen project) is available to give non-admin users the ability to close their projects. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@9883 e93f8b46-1217-0410-a6f0-8f06a7374b81
Diffstat (limited to 'app')
-rw-r--r--app/controllers/application_controller.rb2
-rw-r--r--app/controllers/projects_controller.rb16
-rw-r--r--app/helpers/admin_helper.rb4
-rw-r--r--app/helpers/application_helper.rb8
-rw-r--r--app/models/principal.rb2
-rw-r--r--app/models/project.rb35
-rw-r--r--app/models/user.rb6
-rw-r--r--app/views/admin/projects.html.erb6
-rw-r--r--app/views/projects/index.html.erb8
-rw-r--r--app/views/projects/show.html.erb13
10 files changed, 77 insertions, 23 deletions
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 17968bf79..645e8389b 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -276,7 +276,7 @@ class ApplicationController < ActionController::Base
# make sure that the user is a member of the project (or admin) if project is private
# used as a before_filter for actions that do not require any particular permission on the project
def check_project_privacy
- if @project && @project.active?
+ if @project && !@project.archived?
if @project.visible?
true
else
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index c89167aa3..492e31795 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -48,7 +48,11 @@ class ProjectsController < ApplicationController
def index
respond_to do |format|
format.html {
- @projects = Project.visible.find(:all, :order => 'lft')
+ scope = Project
+ unless params[:closed]
+ scope = scope.active
+ end
+ @projects = scope.visible.order('lft').all
}
format.api {
@offset, @limit = api_offset_and_limit
@@ -224,6 +228,16 @@ class ProjectsController < ApplicationController
redirect_to(url_for(:controller => 'admin', :action => 'projects', :status => params[:status]))
end
+ def close
+ @project.close
+ redirect_to project_path(@project)
+ end
+
+ def reopen
+ @project.reopen
+ redirect_to project_path(@project)
+ end
+
# Delete @project
def destroy
@project_to_destroy = @project
diff --git a/app/helpers/admin_helper.rb b/app/helpers/admin_helper.rb
index 01f394dd1..bacc7b8ea 100644
--- a/app/helpers/admin_helper.rb
+++ b/app/helpers/admin_helper.rb
@@ -20,6 +20,8 @@
module AdminHelper
def project_status_options_for_select(selected)
options_for_select([[l(:label_all), ''],
- [l(:status_active), '1']], selected.to_s)
+ [l(:project_status_active), '1'],
+ [l(:project_status_closed), '5'],
+ [l(:project_status_archived), '9']], selected.to_s)
end
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 794cc6f54..8c328326f 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -145,11 +145,11 @@ module ApplicationHelper
# link_to_project(project, {}, :class => "project") # => html options with default url (project overview)
#
def link_to_project(project, options={}, html_options = nil)
- if project.active?
+ if project.archived?
+ h(project)
+ else
url = {:controller => 'projects', :action => 'show', :id => project}.merge(options)
link_to(h(project), url, html_options)
- else
- h(project)
end
end
@@ -237,7 +237,7 @@ module ApplicationHelper
# Renders the project quick-jump box
def render_project_jump_box
return unless User.current.logged?
- projects = User.current.memberships.collect(&:project).compact.uniq
+ projects = User.current.memberships.collect(&:project).compact.select(&:active?).uniq
if projects.any?
s = '<select onchange="if (this.value != \'\') { window.location = this.value; }">' +
"<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
diff --git a/app/models/principal.rb b/app/models/principal.rb
index c4968016a..2b89e16a9 100644
--- a/app/models/principal.rb
+++ b/app/models/principal.rb
@@ -19,7 +19,7 @@ class Principal < ActiveRecord::Base
self.table_name = "#{table_name_prefix}users#{table_name_suffix}"
has_many :members, :foreign_key => 'user_id', :dependent => :destroy
- has_many :memberships, :class_name => 'Member', :foreign_key => 'user_id', :include => [ :project, :roles ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name"
+ has_many :memberships, :class_name => 'Member', :foreign_key => 'user_id', :include => [ :project, :roles ], :conditions => "#{Project.table_name}.status<>#{Project::STATUS_ARCHIVED}", :order => "#{Project.table_name}.name"
has_many :projects, :through => :memberships
has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
diff --git a/app/models/project.rb b/app/models/project.rb
index 1b8f30db3..966806ca9 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -20,6 +20,7 @@ class Project < ActiveRecord::Base
# Project statuses
STATUS_ACTIVE = 1
+ STATUS_CLOSED = 5
STATUS_ARCHIVED = 9
# Maximum length for project identifiers
@@ -161,12 +162,11 @@ class Project < ActiveRecord::Base
# * :with_subprojects => limit the condition to project and its subprojects
# * :member => limit the condition to the user projects
def self.allowed_to_condition(user, permission, options={})
- base_statement = "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"
- if perm = Redmine::AccessControl.permission(permission)
- 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
+ perm = Redmine::AccessControl.permission(permission)
+ base_statement = (perm && perm.read? ? "#{Project.table_name}.status <> #{Project::STATUS_ARCHIVED}" : "#{Project.table_name}.status = #{Project::STATUS_ACTIVE}")
+ if perm && perm.project_module
+ # 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
if options[:project]
project_statement = "#{Project.table_name}.id = #{options[:project].id}"
@@ -325,6 +325,14 @@ class Project < ActiveRecord::Base
update_attribute :status, STATUS_ACTIVE
end
+ def close
+ self_and_descendants.status(STATUS_ACTIVE).update_all :status => STATUS_CLOSED
+ end
+
+ def reopen
+ self_and_descendants.status(STATUS_CLOSED).update_all :status => STATUS_ACTIVE
+ end
+
# Returns an array of projects the project can be moved to
# by the current user
def allowed_parents
@@ -404,7 +412,7 @@ class Project < ActiveRecord::Base
@rolled_up_trackers ||=
Tracker.find(:all, :joins => :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],
+ :conditions => ["#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status <> #{STATUS_ARCHIVED}", lft, rgt],
:order => "#{Tracker.table_name}.position")
end
@@ -423,20 +431,20 @@ class Project < ActiveRecord::Base
def rolled_up_versions
@rolled_up_versions ||=
Version.scoped(:include => :project,
- :conditions => ["#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status = #{STATUS_ACTIVE}", lft, rgt])
+ :conditions => ["#{Project.table_name}.lft >= ? AND #{Project.table_name}.rgt <= ? AND #{Project.table_name}.status <> #{STATUS_ARCHIVED}", lft, rgt])
end
# Returns a scope of the Versions used by the project
def shared_versions
if new_record?
Version.scoped(:include => :project,
- :conditions => "#{Project.table_name}.status = #{Project::STATUS_ACTIVE} AND #{Version.table_name}.sharing = 'system'")
+ :conditions => "#{Project.table_name}.status <> #{Project::STATUS_ARCHIVED} AND #{Version.table_name}.sharing = 'system'")
else
@shared_versions ||= begin
r = root? ? self : root
Version.scoped(:include => :project,
:conditions => "#{Project.table_name}.id = #{id}" +
- " OR (#{Project.table_name}.status = #{Project::STATUS_ACTIVE} AND (" +
+ " OR (#{Project.table_name}.status <> #{Project::STATUS_ARCHIVED} AND (" +
" #{Version.table_name}.sharing = 'system'" +
" OR (#{Project.table_name}.lft >= #{r.lft} AND #{Project.table_name}.rgt <= #{r.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'))" +
@@ -515,6 +523,13 @@ class Project < ActiveRecord::Base
s << ' root' if root?
s << ' child' if child?
s << (leaf? ? ' leaf' : ' parent')
+ unless active?
+ if archived?
+ s << ' archived'
+ else
+ s << ' closed'
+ end
+ end
s
end
diff --git a/app/models/user.rb b/app/models/user.rb
index d0d1df834..1b7a8ea4f 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -394,7 +394,7 @@ class User < Principal
def roles_for_project(project)
roles = []
# No role on archived projects
- return roles unless project && project.active?
+ return roles if project.nil? || project.archived?
if logged?
# Find project membership
membership = memberships.detect {|m| m.project_id == project.id}
@@ -456,9 +456,11 @@ class User < Principal
def allowed_to?(action, context, options={}, &block)
if context && context.is_a?(Project)
# No action allowed on archived projects
- return false unless context.active?
+ return false if context.archived?
# No action allowed on disabled modules
return false unless context.allows_to?(action)
+ # No write action allowed on closed projects
+ return false unless context.active? || Redmine::AccessControl.read_action?(action)
# Admin users are authorized for anything else
return true if admin?
diff --git a/app/views/admin/projects.html.erb b/app/views/admin/projects.html.erb
index 6525f8ea5..1d8bcc002 100644
--- a/app/views/admin/projects.html.erb
+++ b/app/views/admin/projects.html.erb
@@ -27,12 +27,12 @@
<tbody>
<% project_tree(@projects) do |project, level| %>
<tr class="<%= cycle("odd", "even") %> <%= project.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>">
- <td class="name"><span><%= link_to_project(project, {:action => 'settings'}, :title => project.short_description) %></span></td>
+ <td class="name"><span><%= link_to_project(project, {:action => (project.active? ? 'settings' : 'show')}, :title => project.short_description) %></span></td>
<td align="center"><%= checked_image project.is_public? %></td>
<td align="center"><%= format_date(project.created_on) %></td>
<td class="buttons">
- <%= link_to(l(:button_archive), { :controller => 'projects', :action => 'archive', :id => project, :status => params[:status] }, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-lock') if project.active? %>
- <%= link_to(l(:button_unarchive), { :controller => 'projects', :action => 'unarchive', :id => project, :status => params[:status] }, :method => :post, :class => 'icon icon-unlock') if !project.active? && (project.parent.nil? || project.parent.active?) %>
+ <%= link_to(l(:button_archive), { :controller => 'projects', :action => 'archive', :id => project, :status => params[:status] }, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-lock') unless project.archived? %>
+ <%= link_to(l(:button_unarchive), { :controller => 'projects', :action => 'unarchive', :id => project, :status => params[:status] }, :method => :post, :class => 'icon icon-unlock') if project.archived? && (project.parent.nil? || !project.parent.archived?) %>
<%= link_to(l(:button_copy), { :controller => 'projects', :action => 'copy', :id => project }, :class => 'icon icon-copy') %>
<%= link_to(l(:button_delete), project_path(project), :method => :delete, :class => 'icon icon-del') %>
</td>
diff --git a/app/views/projects/index.html.erb b/app/views/projects/index.html.erb
index 42b400ff7..3d527917a 100644
--- a/app/views/projects/index.html.erb
+++ b/app/views/projects/index.html.erb
@@ -25,4 +25,12 @@
<%= f.link_to 'Atom', :url => {:key => User.current.rss_key} %>
<% end %>
+<% content_for :sidebar do %>
+ <%= form_tag({}, :method => :get) do %>
+ <h3><%= l(:label_project_plural) %></h3>
+ <label for="closed"><%= check_box_tag 'closed', 1, params[:closed] %> <%= l(:label_show_closed_projects) %></label>
+ <p><%= submit_tag l(:button_apply), :class => 'button-small', :name => nil %></p>
+ <% end %>
+<% end %>
+
<% html_title(l(:label_project_plural)) -%>
diff --git a/app/views/projects/show.html.erb b/app/views/projects/show.html.erb
index 4963ebbe4..7bd37fc7b 100644
--- a/app/views/projects/show.html.erb
+++ b/app/views/projects/show.html.erb
@@ -2,14 +2,27 @@
<% if User.current.allowed_to?(:add_subprojects, @project) %>
<%= link_to l(:label_subproject_new), {:controller => 'projects', :action => 'new', :parent_id => @project}, :class => 'icon icon-add' %>
<% end %>
+ <% if User.current.allowed_to?(:close_project, @project) %>
+ <% if @project.active? %>
+ <%= link_to l(:button_close), close_project_path(@project), :data => {:confirm => l(:text_are_you_sure)}, :method => :post, :class => 'icon icon-lock' %>
+ <% else %>
+ <%= link_to l(:button_reopen), reopen_project_path(@project), :data => {:confirm => l(:text_are_you_sure)}, :method => :post, :class => 'icon icon-unlock' %>
+ <% end %>
+ <% end %>
</div>
<h2><%=l(:label_overview)%></h2>
+<% unless @project.active? %>
+ <p class="warning"><span class="icon icon-lock"><%= l(:text_project_closed) %></span></p>
+<% end %>
+
<div class="splitcontentleft">
+ <% if @project.description.present? %>
<div class="wiki">
<%= textilizable @project.description %>
</div>
+ <% end %>
<ul>
<% unless @project.homepage.blank? %>
<li><%=l(:field_homepage)%>: <%= link_to h(@project.homepage), @project.homepage %></li>