]> source.dussan.org Git - redmine.git/commitdiff
Added the ability to archive projects:
authorJean-Philippe Lang <jp_lang@yahoo.fr>
Sun, 27 May 2007 17:42:04 +0000 (17:42 +0000)
committerJean-Philippe Lang <jp_lang@yahoo.fr>
Sun, 27 May 2007 17:42:04 +0000 (17:42 +0000)
* Only administrators can archive/unarchive projects.
* Once archived, the project is visible on the admin project listing only. It doesn't show up anywhere else in the app. Subprojects are also archived.
* Archive/unarchive preserve everything on the project (issues, members, ...).
* A subproject can not be unarchived if its parent project is archived.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@549 e93f8b46-1217-0410-a6f0-8f06a7374b81

35 files changed:
app/controllers/admin_controller.rb
app/controllers/application.rb
app/controllers/projects_controller.rb
app/controllers/reports_controller.rb
app/controllers/users_controller.rb
app/helpers/admin_helper.rb
app/models/project.rb
app/models/query.rb
app/models/user.rb
app/sweepers/project_sweeper.rb [new file with mode: 0644]
app/views/admin/projects.rhtml
app/views/my/blocks/_issuesassignedtome.rhtml
app/views/my/blocks/_issuesreportedbyme.rhtml
app/views/my/blocks/_issueswatched.rhtml
app/views/projects/calendar.rhtml
app/views/projects/destroy.rhtml
app/views/projects/gantt.rhtml
app/views/projects/show.rhtml
db/migrate/051_add_project_status.rb [new file with mode: 0644]
lang/bg.yml
lang/de.yml
lang/en.yml
lang/es.yml
lang/fr.yml
lang/it.yml
lang/ja.yml
lang/nl.yml
lang/pt-br.yml
lang/pt.yml
lang/zh.yml
public/images/unlock.png [new file with mode: 0644]
public/stylesheets/application.css
test/functional/projects_controller_test.rb
test/integration/projects_test.rb [new file with mode: 0644]
test/unit/project_test.rb

index 1c10722de6135d3d425e9fbe232599232708027d..19efb28cc54e0b36cc65065195c23d7d00f348c1 100644 (file)
@@ -27,12 +27,18 @@ class AdminController < ApplicationController
        
   def projects
     sort_init 'name', 'asc'
-    sort_update                
-    @project_count = Project.count             
+    sort_update
+    
+    @status = params[:status] ? params[:status].to_i : 0
+    conditions = nil
+    conditions = ["status=?", @status] unless @status == 0
+    
+    @project_count = Project.count(:conditions => conditions)
     @project_pages = Paginator.new self, @project_count,
-                                                               15,
+                                                               25,
                                                                params['page']                                                          
     @projects = Project.find :all, :order => sort_clause,
+                        :conditions => conditions,
                                                :limit  =>  @project_pages.items_per_page,
                                                :offset =>  @project_pages.current.offset
 
index 54e4768b69c83568345844ec997d7b4ec47c0aeb..58193ba8f039651d17160ce19ef14f0444fa43cb 100644 (file)
@@ -86,6 +86,11 @@ class ApplicationController < ActionController::Base
 
   # authorizes the user for the requested action.
   def authorize(ctrl = params[:controller], action = params[:action])
+    unless @project.active?
+      @project = nil
+      render_404
+      return false
+    end
     # check if action is allowed on public projects
     if @project.is_public? and Permission.allowed_to_public "%s/%s" % [ ctrl, action ]
       return true
@@ -105,6 +110,11 @@ 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
+    unless @project.active?
+      @project = nil
+      render_404
+      return false
+    end
     return true if @project.is_public?
     return false unless logged_in_user
     return true if logged_in_user.admin? || logged_in_user_membership
index debb0a00a9697b623cbba330697ff16f1eb0bac3..1de3788ac70f3b18ad194ba788ad69c63ce36924 100644 (file)
@@ -19,9 +19,11 @@ require 'csv'
 
 class ProjectsController < ApplicationController
   layout 'base'
-  before_filter :find_project, :authorize, :except => [ :index, :list, :add ]
-  before_filter :require_admin, :only => [ :add, :destroy ]
+  before_filter :find_project, :except => [ :index, :list, :add ]
+  before_filter :authorize, :except => [ :index, :list, :add, :archive, :unarchive, :destroy ]
+  before_filter :require_admin, :only => [ :add, :archive, :unarchive, :destroy ]
   
+  cache_sweeper :project_sweeper, :only => [ :add, :edit, :archive, :unarchive, :destroy ]
   cache_sweeper :issue_sweeper, :only => [ :add_issue ]
 
   helper :sort
@@ -86,7 +88,7 @@ class ProjectsController < ApplicationController
   def show
     @custom_values = @project.custom_values.find(:all, :include => :custom_field)
     @members_by_role = @project.members.find(:all, :include => [:user, :role], :order => 'position').group_by {|m| m.role}
-    @subprojects = @project.children if @project.children.size > 0
+    @subprojects = @project.active_children
     @news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
     @trackers = Tracker.find(:all, :order => 'position')
     @open_issues_by_tracker = Issue.count(:group => :tracker, :joins => "INNER JOIN #{IssueStatus.table_name} ON #{IssueStatus.table_name}.id = #{Issue.table_name}.status_id", :conditions => ["project_id=? and #{IssueStatus.table_name}.is_closed=?", @project.id, false])
@@ -138,12 +140,25 @@ class ProjectsController < ApplicationController
     end
   end
 
+  def archive
+    @project.archive if request.post? && @project.active?
+    redirect_to :controller => 'admin', :action => 'projects'
+  end
+  
+  def unarchive
+    @project.unarchive if request.post? && !@project.active?
+    redirect_to :controller => 'admin', :action => 'projects'
+  end
+  
   # Delete @project
   def destroy
+    @project_to_destroy = @project
     if request.post? and params[:confirm]
-      @project.destroy
+      @project_to_destroy.destroy
       redirect_to :controller => 'admin', :action => 'projects'
     end
+    # hide project in layout
+    @project = nil
   end
        
   # Add a new issue category to @project
index 7c0b34773154a442883a6e83257ad1928f16b341..59f13d7a53f8dcb360a31f4f670bb34dc92b5b2f 100644 (file)
@@ -55,7 +55,7 @@ class ReportsController < ApplicationController
       render :template => "reports/issue_report_details"  
     when "subproject"
       @field = "project_id"
-      @rows = @project.children
+      @rows = @project.active_children
       @data = issues_by_subproject
       @report_title = l(:field_subproject)
       render :template => "reports/issue_report_details"  
@@ -66,7 +66,7 @@ class ReportsController < ApplicationController
       @priorities = Enumeration::get_values('IPRI')
       @categories = @project.issue_categories
       @authors = @project.members.collect { |m| m.user }
-      @subprojects = @project.children
+      @subprojects = @project.active_children
       issues_by_tracker
       issues_by_version
       issues_by_priority
@@ -207,8 +207,8 @@ private
                                                   #{Issue.table_name} i, #{IssueStatus.table_name} s
                                                 where 
                                                   i.status_id=s.id 
-                                                  and i.project_id IN (#{@project.children.collect{|p| p.id}.join(',')})
-                                                group by s.id, s.is_closed, i.project_id") if @project.children.any?
+                                                  and i.project_id IN (#{@project.active_children.collect{|p| p.id}.join(',')})
+                                                group by s.id, s.is_closed, i.project_id") if @project.active_children.any?
     @issues_by_subproject ||= []
   end
 end
index 93b3a4d25426ef5d50e4c16bfa25be347fc1898c..908001b1ad6f8766f1dc827681273273aa91f0d7 100644 (file)
@@ -88,7 +88,7 @@ class UsersController < ApplicationController
     end
     @auth_sources = AuthSource.find(:all)
     @roles = Role.find(:all, :order => 'position')
-    @projects = Project.find(:all) - @user.projects
+    @projects = Project.find(:all, :order => 'name', :conditions => "status=#{Project::STATUS_ACTIVE}") - @user.projects
     @membership ||= Member.new
   end
   
index 4f82a3790dfb7ed454a4e8582f12f50582f40269..1b41d8374de719c87fee98223602fe9096ab4da5 100644 (file)
@@ -16,4 +16,8 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 module AdminHelper
+  def project_status_options_for_select(selected)
+    options_for_select([[l(:label_all), "*"], 
+                        [l(:status_active), 1]], selected)
+  end
 end
index 6dd6b2644ee00915c4bdb511f1d903678622eee1..bf83c13891bc9da515c5a104f8a9f25f2c0ef801 100644 (file)
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 class Project < ActiveRecord::Base
+  # Project statuses
+  STATUS_ACTIVE     = 1
+  STATUS_ARCHIVED   = 9
+  
   has_many :versions, :dependent => :destroy, :order => "#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC"
   has_many :members, :dependent => :delete_all, :include => :user, :conditions => "#{User.table_name}.status=#{User::STATUS_ACTIVE}"
   has_many :users, :through => :members
@@ -32,6 +36,8 @@ class Project < ActiveRecord::Base
   has_and_belongs_to_many :custom_fields, :class_name => 'IssueCustomField', :join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}", :association_foreign_key => 'custom_field_id'
   acts_as_tree :order => "name", :counter_cache => true
   
+  attr_protected :status
+  
   validates_presence_of :name, :description, :identifier
   validates_uniqueness_of :name, :identifier
   validates_associated :custom_values, :on => :update
@@ -52,12 +58,11 @@ class Project < ActiveRecord::Base
   
   def issues_with_subprojects(include_subprojects=false)
     conditions = nil
-    if include_subprojects && children.size > 0
-      ids = [id] + children.collect {|c| c.id}
+    if include_subprojects && !active_children.empty?
+      ids = [id] + active_children.collect {|c| c.id}
       conditions = ["#{Issue.table_name}.project_id IN (#{ids.join(',')})"]
-    else
-      conditions = ["#{Issue.table_name}.project_id = ?", id]
     end
+    conditions ||= ["#{Issue.table_name}.project_id = ?", id]
     Issue.with_scope :find => { :conditions => conditions } do 
       yield
     end 
@@ -71,12 +76,33 @@ class Project < ActiveRecord::Base
 
   def self.visible_by(user=nil)
     if user && user.admin?
-      return nil
+      return ["#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"]
     elsif user && !user.memberships.empty?
-      return ["#{Project.table_name}.is_public = ? or #{Project.table_name}.id IN (#{user.memberships.collect{|m| m.project_id}.join(',')})", true]
+      return ["#{Project.table_name}.status=#{Project::STATUS_ACTIVE} AND (#{Project.table_name}.is_public = ? or #{Project.table_name}.id IN (#{user.memberships.collect{|m| m.project_id}.join(',')}))", true]
     else
-      return ["#{Project.table_name}.is_public = ?", true]
+      return ["#{Project.table_name}.status=#{Project::STATUS_ACTIVE} AND #{Project.table_name}.is_public = ?", true]
+    end
+  end
+  
+  def active?
+    self.status == STATUS_ACTIVE
+  end
+  
+  def archive
+    # Archive subprojects if any
+    children.each do |subproject|
+      subproject.archive
     end
+    update_attribute :status, STATUS_ARCHIVED
+  end
+  
+  def unarchive
+    return false if parent && !parent.active?
+    update_attribute :status, STATUS_ACTIVE
+  end
+  
+  def active_children
+    children.select {|child| child.active?}
   end
   
   # Returns an array of all custom fields enabled for project issues
index 47468ba12602c629c160e72ef5b8f8023f077d08..145ff851a5edd9bede45ac6dd3f09d2695417128 100644 (file)
@@ -95,8 +95,8 @@ class Query < ActiveRecord::Base
       @available_filters["author_id"] = { :type => :list, :order => 5, :values => user_values }  
       @available_filters["category_id"] = { :type => :list_optional, :order => 6, :values => @project.issue_categories.collect{|s| [s.name, s.id.to_s] } }
       @available_filters["fixed_version_id"] = { :type => :list_optional, :order => 7, :values => @project.versions.sort.collect{|s| [s.name, s.id.to_s] } }
-      unless @project.children.empty?
-        @available_filters["subproject_id"] = { :type => :list_one_or_more, :order => 13, :values => @project.children.collect{|s| [s.name, s.id.to_s] } }
+      unless @project.active_children.empty?
+        @available_filters["subproject_id"] = { :type => :list_one_or_more, :order => 13, :values => @project.active_children.collect{|s| [s.name, s.id.to_s] } }
       end
       @project.all_custom_fields.select(&:is_filter?).each do |field|
         case field.field_format
@@ -164,7 +164,7 @@ class Query < ActiveRecord::Base
       if operator_for("subproject_id") == "="
         subproject_ids = values_for("subproject_id").each(&:to_i)
       else
-        subproject_ids = project.children.collect{|p| p.id}
+        subproject_ids = project.active_children.collect{|p| p.id}
       end
       sql << " AND #{Issue.table_name}.project_id IN (%d,%s)" % [project.id, subproject_ids.join(",")] if project
     else
index d025651c400045b70135b2f6d462fa6d523244a2..917745b224fedc3e0912a7ad6b4283645e729d27 100644 (file)
@@ -23,7 +23,7 @@ class User < ActiveRecord::Base
   STATUS_REGISTERED = 2
   STATUS_LOCKED     = 3
 
-  has_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :order => "#{Project.table_name}.name", :dependent => :delete_all
+  has_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name", :dependent => :delete_all
   has_many :projects, :through => :memberships
   has_many :custom_values, :dependent => :delete_all, :as => :customized
   has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
diff --git a/app/sweepers/project_sweeper.rb b/app/sweepers/project_sweeper.rb
new file mode 100644 (file)
index 0000000..f64f6f5
--- /dev/null
@@ -0,0 +1,40 @@
+# redMine - project management software\r
+# Copyright (C) 2006-2007  Jean-Philippe Lang\r
+#\r
+# This program is free software; you can redistribute it and/or\r
+# modify it under the terms of the GNU General Public License\r
+# as published by the Free Software Foundation; either version 2\r
+# of the License, or (at your option) any later version.\r
+# \r
+# This program is distributed in the hope that it will be useful,\r
+# but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+# GNU General Public License for more details.\r
+# \r
+# You should have received a copy of the GNU General Public License\r
+# along with this program; if not, write to the Free Software\r
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\r
+\r
+class ProjectSweeper < ActionController::Caching::Sweeper\r
+  observe Project\r
+\r
+  def before_save(project)\r
+    if project.new_record?\r
+      expire_cache_for(project.parent) if project.parent\r
+    else\r
+      project_before_update = Project.find(project.id)\r
+      return if project_before_update.parent_id == project.parent_id && project_before_update.status == project.status\r
+      expire_cache_for(project.parent) if project.parent      \r
+      expire_cache_for(project_before_update.parent) if project_before_update.parent\r
+    end\r
+  end\r
+  \r
+  def after_destroy(project)\r
+    expire_cache_for(project.parent) if project.parent\r
+  end\r
+          \r
+private\r
+  def expire_cache_for(project)\r
+    expire_fragment(Regexp.new("projects/(calendar|gantt)/#{project.id}\\."))\r
+  end\r
+end\r
index 9c0595eb7e757c966034d7313a9380dc226378e5..a850d0918b0a860f0dbcc2c81e851ef741586878 100644 (file)
@@ -4,6 +4,15 @@
 
 <h2><%=l(:label_project_plural)%></h2>
 
+<% form_tag() do %>
+<fieldset><legend><%= l(:label_filter_plural) %></legend>
+<label><%= l(:field_status) %> :</label>
+<%= select_tag 'status', project_status_options_for_select(@status), :class => "small", :onchange => "this.form.submit(); return false;"  %>
+<%= submit_tag l(:button_apply), :class => "small" %>
+</fieldset>
+<% end %>
+&nbsp;
+
 <table class="list">
   <thead><tr>
        <%= sort_header_tag('name', :caption => l(:label_project)) %>
        <th><%=l(:label_subproject_plural)%></th>
        <%= sort_header_tag('created_on', :caption => l(:field_created_on)) %>
     <th></th>
+    <th></th>
   </tr></thead>
   <tbody>
 <% for project in @projects %>
   <tr class="<%= cycle("odd", "even") %>">
-       <td><%= link_to project.name, :controller => 'projects', :action => 'settings', :id => project %>
+       <td><%= project.active? ? link_to(project.name, :controller => 'projects', :action => 'settings', :id => project) : h(project.name) %>
        <td><%=h project.description %>
        <td align="center"><%= image_tag 'true.png' if project.is_public? %>
        <td align="center"><%= project.children.size %>
        <td align="center"><%= format_date(project.created_on) %>
-  <td align="center">
-    <%= button_to l(:button_delete), { :controller => 'projects', :action => 'destroy', :id => project }, :class => "button-small" %>
+  <td align="center" style="width:10%">
+    <small>
+    <%= link_to(l(:button_archive), { :controller => 'projects', :action => 'archive', :id => project }, :method => :post, :class => 'icon icon-lock') if project.active? %>
+    <%= link_to(l(:button_unarchive), { :controller => 'projects', :action => 'unarchive', :id => project }, :method => :post, :class => 'icon icon-unlock') if !project.active? && (project.parent.nil? || project.parent.active?) %>
+    </small>
+  </td>
+  <td align="center" style="width:10%">
+    <small><%= link_to(l(:button_delete), { :controller => 'projects', :action => 'destroy', :id => project }, :class => 'icon icon-del') %></small>
   </td>
   </tr>
 <% end %>
   </tbody>
 </table>
 
-<p><%= pagination_links_full @project_pages %>
-[ <%= @project_pages.current.first_item %> - <%= @project_pages.current.last_item %> / <%= @project_count %> ]</p>
\ No newline at end of file
+<p><%= pagination_links_full @project_pages, :status => @status %>
+[ <%= @project_pages.current.first_item %> - <%= @project_pages.current.last_item %> / <%= @project_count %> ]</p>
index b75382218abae31e7f723ee0e6019910031213cf..0d49279f4b7c4c52b50f81e1595da9c475767426 100644 (file)
@@ -1,6 +1,6 @@
 <h3><%=l(:label_assigned_to_me_issues)%></h3>
 <% assigned_issues = Issue.find(:all, 
-                                :conditions => ["assigned_to_id=? AND #{IssueStatus.table_name}.is_closed=?", user.id, false],
+                                :conditions => ["assigned_to_id=? AND #{IssueStatus.table_name}.is_closed=? AND #{Project.table_name}.status=#{Project::STATUS_ACTIVE}", user.id, false],
                                 :limit => 10, 
                                 :include => [ :status, :project, :tracker ], 
                                 :order => "#{Issue.table_name}.updated_on DESC") %>
index 3c6eb4a38c188760530b728176bbb5dda6914139..250e8265dae0ae0ba5f0e65ed36a69ae1c99e699 100644 (file)
@@ -1,6 +1,6 @@
 <h3><%=l(:label_reported_issues)%></h3>
 <% reported_issues = Issue.find(:all, 
-                                :conditions => ["author_id=?", user.id],
+                                :conditions => ["author_id=? AND #{Project.table_name}.status=#{Project::STATUS_ACTIVE}", user.id],
                                 :limit => 10, 
                                 :include => [ :status, :project, :tracker ], 
                                 :order => "#{Issue.table_name}.updated_on DESC") %>
index 7324e9a4783635e6b1fbece0d8222650838d60d7..e5c2f23aba2cd3d7d2a4bf9cd92c0f2358c9aacd 100644 (file)
@@ -2,7 +2,7 @@
 <% watched_issues = Issue.find(:all, \r
                                :include => [:status, :project, :tracker, :watchers],\r
                                :limit => 10, \r
-                               :conditions => ["#{Watcher.table_name}.user_id = ?", user.id],\r
+                               :conditions => ["#{Watcher.table_name}.user_id = ? AND #{Project.table_name}.status=#{Project::STATUS_ACTIVE}", user.id],\r
                                :order => "#{Issue.table_name}.updated_on DESC") %>\r
 <%= render :partial => 'issues/list_simple', :locals => { :issues => watched_issues } %>\r
 <% if watched_issues.length > 0 %>\r
index 48fdaf64d14bbf4302f6e68107d4a56baffa41be..6fb8a13653e32bc91a47167d969a155312941a4a 100644 (file)
@@ -23,7 +23,7 @@
       <%= check_box_tag "tracker_ids[]", tracker.id, (@selected_tracker_ids.include? tracker.id.to_s) %>
       <%= tracker.name %><br />
     <% end %>
-    <% if @project.children.any? %>
+    <% if @project.active_children.any? %>
     <p><strong><%=l(:label_subproject_plural)%></strong></p>
     <%= check_box_tag "with_subprojects", 1, params[:with_subprojects] %> <%= l(:general_text_Yes) %>
     <% end %>
index cb159216d2b32c409afe9d1923e0baae0d72b0a8..8ef23197d22e2a4bf1f8c083ca7e8ab0d997a542 100644 (file)
@@ -1,11 +1,11 @@
 <h2><%=l(:label_confirmation)%></h2>
 <div class="box">
 <center>
-<p><strong><%= @project.name %></strong><br />
+<p><strong><%= @project_to_destroy.name %></strong><br />
 <%=l(:text_project_destroy_confirmation)%></p>
 
 <p>
-    <% form_tag({:controller => 'projects', :action => 'destroy', :id => @project}) do %>
+    <% form_tag({:controller => 'projects', :action => 'destroy', :id => @project_to_destroy}) do %>
     <%= hidden_field_tag "confirm", 1 %>
     <%= submit_tag l(:button_delete) %>
     <% end %>
index ccc9a019a94656e0671f2eb25dfc716eeb263ea2..c46ef9f5fe64ce1fd399a022c3fe9d0cd52c2e4d 100644 (file)
@@ -49,7 +49,7 @@ t_height = g_height + headers_height
       <%= check_box_tag "tracker_ids[]", tracker.id, (@selected_tracker_ids.include? tracker.id.to_s) %>
       <%= tracker.name %><br />
     <% end %>
-    <% if @project.children.any? %>
+    <% if @project.active_children.any? %>
     <p><strong><%=l(:label_subproject_plural)%></strong></p>
     <%= check_box_tag "with_subprojects", 1, params[:with_subprojects] %> <%= l(:general_text_Yes) %>
     <% end %>
index 2afeb8253c928644e5854485eeaa55e27481d386..92668ad7123d1ed3c50719495b3f4efcc182f9f6 100644 (file)
@@ -45,7 +45,7 @@
                <% end %>               
        </div>
   
-  <% if @subprojects %>
+  <% if @subprojects.any? %>
        <div class="box">
                <h3 class="icon22 icon22-projects"><%=l(:label_subproject_plural)%></h3>        
                <%= @subprojects.collect{|p| link_to(p.name, :action => 'show', :id => p)}.join(", ") %>
diff --git a/db/migrate/051_add_project_status.rb b/db/migrate/051_add_project_status.rb
new file mode 100644 (file)
index 0000000..fba36d2
--- /dev/null
@@ -0,0 +1,9 @@
+class AddProjectStatus < ActiveRecord::Migration
+  def self.up
+    add_column :projects, :status, :integer, :default => 1, :null => false
+  end
+
+  def self.down
+    remove_column :projects, :status
+  end
+end
index 47281f068ce66440b0e85b69d168eff2939e64dd..6b96264994bd0de669ce0f1013e8831bd707677f 100644 (file)
@@ -425,6 +425,8 @@ button_rollback: Върни се към тази ревизия
 button_watch: Наблюдавай
 button_unwatch: Спри наблюдението
 button_reply: Reply
+button_archive: Archive
+button_unarchive: Unarchive
 
 status_active: активен
 status_registered: регистриран
index 5b23bc7d2b4a2441fb7dba1cfaf8272253dc6ea7..feb3ca83360f867aed561d3213fe5b0f0c83f87d 100644 (file)
@@ -425,6 +425,8 @@ button_rollback: Rollback to this version
 button_watch: Watch
 button_unwatch: Unwatch
 button_reply: Reply
+button_archive: Archive
+button_unarchive: Unarchive
 
 status_active: aktiv
 status_registered: angemeldet
index c6cb1a770009c7bc3aa55aa6af02f5ca7fa35673..2fa6ddabbbe67ac706f4bbb666a69c59a819c6a8 100644 (file)
@@ -425,6 +425,8 @@ button_rollback: Rollback to this version
 button_watch: Watch
 button_unwatch: Unwatch
 button_reply: Reply
+button_archive: Archive
+button_unarchive: Unarchive
 
 status_active: active
 status_registered: registered
index c46ec9c570c66eefa42f7de715e916ae73eed80d..4b39c345e8e4c6e5bd5192c43aaa81773add89af 100644 (file)
@@ -425,6 +425,8 @@ button_rollback: Rollback to this version
 button_watch: Watch
 button_unwatch: Unwatch
 button_reply: Reply
+button_archive: Archive
+button_unarchive: Unarchive
 
 status_active: active
 status_registered: registered
index 7231c558bc28014ebcae27da559cee0755fc8861..822e39da7a1b62c6a4491c862613ba843625b2f7 100644 (file)
@@ -425,6 +425,8 @@ button_rollback: Revenir à cette version
 button_watch: Surveiller
 button_unwatch: Ne plus surveiller
 button_reply: Répondre
+button_archive: Archiver
+button_unarchive: Désarchiver
 
 status_active: actif
 status_registered: enregistré
index 827dcfd062e49bf7180d763f41c6feaa15d16f81..20ec63546c77ea9ed13e13df671ead203ef46f6b 100644 (file)
@@ -425,6 +425,8 @@ button_rollback: Ripristina questa versione
 button_watch: Watch
 button_unwatch: Unwatch
 button_reply: Reply
+button_archive: Archive
+button_unarchive: Unarchive
 
 status_active: attivo
 status_registered: registrato
index d3fce4f73603a2670f5ac268719e79b524650252..30c400f29e4590dfea9f401db4ffa3f8d4872252 100644 (file)
@@ -426,6 +426,8 @@ button_rollback: このバージョンにロールバック
 button_watch: Watch
 button_unwatch: Unwatch
 button_reply: Reply
+button_archive: Archive
+button_unarchive: Unarchive
 
 status_active: 有効
 status_registered: 登録
index 5f713f4154c013370d81b2512e4b031b05cc6aa5..29aca8b33ae3556fdbf2f84e347e4de7d2c14d23 100644 (file)
@@ -425,6 +425,8 @@ button_rollback: Rollback naar deze versie
 button_watch: Monitor
 button_unwatch: Niet meer monitoren
 button_reply: Antwoord
+button_archive: Archive
+button_unarchive: Unarchive
 
 status_active: Actief
 status_registered: geregistreerd
index 3171f89459a3bf9068bee97c7b5fe938ad1b83b9..ea6ad5deb95b1d923e021a4ae0f1cee9bba05ea0 100644 (file)
@@ -425,6 +425,8 @@ button_rollback: Voltar para esta versao
 button_watch: Watch\r
 button_unwatch: Unwatch\r
 button_reply: Reply\r
+button_archive: Archive\r
+button_unarchive: Unarchive\r
 \r
 status_active: ativo\r
 status_registered: registrado\r
index b91cfa2da2b42ed274297144d0c9ea8fc77d77d9..71b5fe0776a0c8837a1c42b31930ea1373933d97 100644 (file)
@@ -425,6 +425,8 @@ button_rollback: Voltar para esta versão
 button_watch: Observar
 button_unwatch: Não observar
 button_reply: Reply
+button_archive: Archive
+button_unarchive: Unarchive
 
 status_active: ativo
 status_registered: registrado
index 17aef65bd23cd5ce866acf4bc8c131024e51d79e..bd40141d368b7822fee4d94d3daba01ecf84d9b6 100644 (file)
@@ -428,6 +428,8 @@ button_rollback: Rollback to this version
 button_watch: Watch
 button_unwatch: Unwatch
 button_reply: Reply
+button_archive: Archive
+button_unarchive: Unarchive
 
 status_active: 激活
 status_registered: 已注册
diff --git a/public/images/unlock.png b/public/images/unlock.png
new file mode 100644 (file)
index 0000000..e0d4149
Binary files /dev/null and b/public/images/unlock.png differ
index a10062f53936fbb97b078e544cac5436ece7f271..15a4603e687c90efbea6644a15dd95b631c1fc9f 100644 (file)
@@ -163,6 +163,8 @@ vertical-align: middle;
 .icon-fav  { background-image: url(../images/fav.png); }
 .icon-fav-off  { background-image: url(../images/fav_off.png); }
 .icon-reload  { background-image: url(../images/reload.png); }
+.icon-lock  { background-image: url(../images/locked.png); }
+.icon-unlock  { background-image: url(../images/unlock.png); }
 
 .icon22-projects { background-image: url(../images/22x22/projects.png); }
 .icon22-users { background-image: url(../images/22x22/users.png); }
index 69d5ed9122071e93f98a5a6e2b585d86649270d9..0e1ff121b109c37a83029ff70a6852274e6f1435 100644 (file)
@@ -1,5 +1,5 @@
 # redMine - project management software
-# Copyright (C) 2006  Jean-Philippe Lang
+# Copyright (C) 2006-2007  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
@@ -125,4 +125,19 @@ class ProjectsControllerTest < Test::Unit::TestCase
     assert_template 'activity'
     assert_not_nil assigns(:events_by_day)
   end
+  
+  def test_archive    
+    @request.session[:user_id] = 1 # admin
+    post :archive, :id => 1
+    assert_redirected_to 'admin/projects'
+    assert !Project.find(1).active?
+  end
+  
+  def test_unarchive
+    @request.session[:user_id] = 1 # admin
+    Project.find(1).archive
+    post :unarchive, :id => 1
+    assert_redirected_to 'admin/projects'
+    assert Project.find(1).active?
+  end
 end
diff --git a/test/integration/projects_test.rb b/test/integration/projects_test.rb
new file mode 100644 (file)
index 0000000..69b16cf
--- /dev/null
@@ -0,0 +1,44 @@
+# redMine - project management software
+# Copyright (C) 2006-2007  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.
+
+require "#{File.dirname(__FILE__)}/../test_helper"
+
+class ProjectsTest < ActionController::IntegrationTest
+  fixtures :projects, :users, :members
+  
+  def test_archive_project
+    subproject = Project.find(1).children.first
+    log_user("admin", "admin")
+    get "admin/projects"
+    assert_response :success
+    assert_template "admin/projects"
+    post "projects/archive", :id => 1
+    assert_redirected_to "admin/projects"    
+    assert !Project.find(1).active?
+    
+    get "projects/show", :id => 1
+    assert_response :missing
+    get "projects/show", :id => subproject.id
+    assert_response :missing
+    
+    post "projects/unarchive", :id => 1
+    assert_redirected_to "admin/projects"    
+    assert Project.find(1).active?
+    get "projects/show", :id => 1
+    assert_response :success
+  end  
+end
index 931939e6bd67dcc4db56954000ae61bd6a52b7ad..14612d306bdec2aaef7ceb1c2e09f1f4c0eeb474 100644 (file)
@@ -1,5 +1,5 @@
 # redMine - project management software
-# Copyright (C) 2006  Jean-Philippe Lang
+# Copyright (C) 2006-2007  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
@@ -51,6 +51,34 @@ class ProjectTest < Test::Unit::TestCase
     assert_equal true, public_projects[0].is_public?
   end
   
+  def test_archive
+    user = @ecookbook.members.first.user
+    @ecookbook.archive
+    @ecookbook.reload
+    
+    assert !@ecookbook.active?
+    assert !user.projects.include?(@ecookbook)
+    # Subproject are also archived
+    assert !@ecookbook.children.empty?
+    assert @ecookbook.active_children.empty?
+  end
+  
+  def test_unarchive
+    user = @ecookbook.members.first.user
+    @ecookbook.archive
+    # A subproject of an archived project can not be unarchived
+    assert !@ecookbook_sub1.unarchive
+    
+    # Unarchive project
+    assert @ecookbook.unarchive
+    @ecookbook.reload
+    assert @ecookbook.active?
+    assert user.projects.include?(@ecookbook)
+    # Subproject can now be unarchived
+    @ecookbook_sub1.reload
+    assert @ecookbook_sub1.unarchive
+  end
+  
   def test_destroy
     @ecookbook.destroy
     assert_raise(ActiveRecord::RecordNotFound) { Project.find(@ecookbook.id) }