diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb index 1c10722de..19efb28cc 100644 --- a/app/controllers/admin_controller.rb +++ b/app/controllers/admin_controller.rb @@ -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 diff --git a/app/controllers/application.rb b/app/controllers/application.rb index 54e4768b6..58193ba8f 100644 --- a/app/controllers/application.rb +++ b/app/controllers/application.rb @@ -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 diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index debb0a00a..1de3788ac 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -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 diff --git a/app/controllers/reports_controller.rb b/app/controllers/reports_controller.rb index 7c0b34773..59f13d7a5 100644 --- a/app/controllers/reports_controller.rb +++ b/app/controllers/reports_controller.rb @@ -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 diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 93b3a4d25..908001b1a 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -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 diff --git a/app/helpers/admin_helper.rb b/app/helpers/admin_helper.rb index 4f82a3790..1b41d8374 100644 --- a/app/helpers/admin_helper.rb +++ b/app/helpers/admin_helper.rb @@ -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 diff --git a/app/models/project.rb b/app/models/project.rb index 6dd6b2644..bf83c1389 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -16,6 +16,10 @@ # 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,14 +76,35 @@ 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 # (explictly associated custom fields and custom fields enabled for all projects) def custom_fields_for_issues(tracker) diff --git a/app/models/query.rb b/app/models/query.rb index 47468ba12..145ff851a 100644 --- a/app/models/query.rb +++ b/app/models/query.rb @@ -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 diff --git a/app/models/user.rb b/app/models/user.rb index d025651c4..917745b22 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -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 index 000000000..f64f6f564 --- /dev/null +++ b/app/sweepers/project_sweeper.rb @@ -0,0 +1,40 @@ +# 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. + +class ProjectSweeper < ActionController::Caching::Sweeper + observe Project + + def before_save(project) + if project.new_record? + expire_cache_for(project.parent) if project.parent + else + project_before_update = Project.find(project.id) + return if project_before_update.parent_id == project.parent_id && project_before_update.status == project.status + expire_cache_for(project.parent) if project.parent + expire_cache_for(project_before_update.parent) if project_before_update.parent + end + end + + def after_destroy(project) + expire_cache_for(project.parent) if project.parent + end + +private + def expire_cache_for(project) + expire_fragment(Regexp.new("projects/(calendar|gantt)/#{project.id}\\.")) + end +end diff --git a/app/views/admin/projects.rhtml b/app/views/admin/projects.rhtml index 9c0595eb7..a850d0918 100644 --- a/app/views/admin/projects.rhtml +++ b/app/views/admin/projects.rhtml @@ -4,6 +4,15 @@

<%=l(:label_project_plural)%>

+<% form_tag() do %> +
<%= l(:label_filter_plural) %> + +<%= select_tag 'status', project_status_options_for_select(@status), :class => "small", :onchange => "this.form.submit(); return false;" %> +<%= submit_tag l(:button_apply), :class => "small" %> +
+<% end %> +  + <%= sort_header_tag('name', :caption => l(:label_project)) %> @@ -12,22 +21,29 @@ <%= sort_header_tag('created_on', :caption => l(:field_created_on)) %> + <% for project in @projects %> "> - + <% end %>
<%=l(:label_subproject_plural)%>
<%= link_to project.name, :controller => 'projects', :action => 'settings', :id => project %> + <%= project.active? ? link_to(project.name, :controller => 'projects', :action => 'settings', :id => project) : h(project.name) %> <%=h project.description %> <%= image_tag 'true.png' if project.is_public? %> <%= project.children.size %> <%= format_date(project.created_on) %> - - <%= button_to l(:button_delete), { :controller => 'projects', :action => 'destroy', :id => project }, :class => "button-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?) %> + + + <%= link_to(l(:button_delete), { :controller => 'projects', :action => 'destroy', :id => project }, :class => 'icon icon-del') %>
-

<%= pagination_links_full @project_pages %> -[ <%= @project_pages.current.first_item %> - <%= @project_pages.current.last_item %> / <%= @project_count %> ]

\ No newline at end of file +

<%= pagination_links_full @project_pages, :status => @status %> +[ <%= @project_pages.current.first_item %> - <%= @project_pages.current.last_item %> / <%= @project_count %> ]

diff --git a/app/views/my/blocks/_issuesassignedtome.rhtml b/app/views/my/blocks/_issuesassignedtome.rhtml index b75382218..0d49279f4 100644 --- a/app/views/my/blocks/_issuesassignedtome.rhtml +++ b/app/views/my/blocks/_issuesassignedtome.rhtml @@ -1,6 +1,6 @@

<%=l(:label_assigned_to_me_issues)%>

<% 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") %> diff --git a/app/views/my/blocks/_issuesreportedbyme.rhtml b/app/views/my/blocks/_issuesreportedbyme.rhtml index 3c6eb4a38..250e8265d 100644 --- a/app/views/my/blocks/_issuesreportedbyme.rhtml +++ b/app/views/my/blocks/_issuesreportedbyme.rhtml @@ -1,6 +1,6 @@

<%=l(:label_reported_issues)%>

<% 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") %> diff --git a/app/views/my/blocks/_issueswatched.rhtml b/app/views/my/blocks/_issueswatched.rhtml index 7324e9a47..e5c2f23ab 100644 --- a/app/views/my/blocks/_issueswatched.rhtml +++ b/app/views/my/blocks/_issueswatched.rhtml @@ -2,7 +2,7 @@ <% watched_issues = Issue.find(:all, :include => [:status, :project, :tracker, :watchers], :limit => 10, - :conditions => ["#{Watcher.table_name}.user_id = ?", user.id], + :conditions => ["#{Watcher.table_name}.user_id = ? AND #{Project.table_name}.status=#{Project::STATUS_ACTIVE}", user.id], :order => "#{Issue.table_name}.updated_on DESC") %> <%= render :partial => 'issues/list_simple', :locals => { :issues => watched_issues } %> <% if watched_issues.length > 0 %> diff --git a/app/views/projects/calendar.rhtml b/app/views/projects/calendar.rhtml index 48fdaf64d..6fb8a1365 100644 --- a/app/views/projects/calendar.rhtml +++ b/app/views/projects/calendar.rhtml @@ -23,7 +23,7 @@ <%= check_box_tag "tracker_ids[]", tracker.id, (@selected_tracker_ids.include? tracker.id.to_s) %> <%= tracker.name %>
<% end %> - <% if @project.children.any? %> + <% if @project.active_children.any? %>

<%=l(:label_subproject_plural)%>

<%= check_box_tag "with_subprojects", 1, params[:with_subprojects] %> <%= l(:general_text_Yes) %> <% end %> diff --git a/app/views/projects/destroy.rhtml b/app/views/projects/destroy.rhtml index cb159216d..8ef23197d 100644 --- a/app/views/projects/destroy.rhtml +++ b/app/views/projects/destroy.rhtml @@ -1,11 +1,11 @@

<%=l(:label_confirmation)%>

-

<%= @project.name %>
+

<%= @project_to_destroy.name %>
<%=l(:text_project_destroy_confirmation)%>

- <% 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 %> diff --git a/app/views/projects/gantt.rhtml b/app/views/projects/gantt.rhtml index ccc9a019a..c46ef9f5f 100644 --- a/app/views/projects/gantt.rhtml +++ b/app/views/projects/gantt.rhtml @@ -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 %>
<% end %> - <% if @project.children.any? %> + <% if @project.active_children.any? %>

<%=l(:label_subproject_plural)%>

<%= check_box_tag "with_subprojects", 1, params[:with_subprojects] %> <%= l(:general_text_Yes) %> <% end %> diff --git a/app/views/projects/show.rhtml b/app/views/projects/show.rhtml index 2afeb8253..92668ad71 100644 --- a/app/views/projects/show.rhtml +++ b/app/views/projects/show.rhtml @@ -45,7 +45,7 @@ <% end %>
- <% if @subprojects %> + <% if @subprojects.any? %>

<%=l(:label_subproject_plural)%>

<%= @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 index 000000000..fba36d237 --- /dev/null +++ b/db/migrate/051_add_project_status.rb @@ -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 diff --git a/lang/bg.yml b/lang/bg.yml index 47281f068..6b9626499 100644 --- a/lang/bg.yml +++ b/lang/bg.yml @@ -425,6 +425,8 @@ button_rollback: Върни се към тази ревизия button_watch: Наблюдавай button_unwatch: Спри наблюдението button_reply: Reply +button_archive: Archive +button_unarchive: Unarchive status_active: активен status_registered: регистриран diff --git a/lang/de.yml b/lang/de.yml index 5b23bc7d2..feb3ca833 100644 --- a/lang/de.yml +++ b/lang/de.yml @@ -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 diff --git a/lang/en.yml b/lang/en.yml index c6cb1a770..2fa6ddabb 100644 --- a/lang/en.yml +++ b/lang/en.yml @@ -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 diff --git a/lang/es.yml b/lang/es.yml index c46ec9c57..4b39c345e 100644 --- a/lang/es.yml +++ b/lang/es.yml @@ -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 diff --git a/lang/fr.yml b/lang/fr.yml index 7231c558b..822e39da7 100644 --- a/lang/fr.yml +++ b/lang/fr.yml @@ -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é diff --git a/lang/it.yml b/lang/it.yml index 827dcfd06..20ec63546 100644 --- a/lang/it.yml +++ b/lang/it.yml @@ -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 diff --git a/lang/ja.yml b/lang/ja.yml index d3fce4f73..30c400f29 100644 --- a/lang/ja.yml +++ b/lang/ja.yml @@ -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: 登録 diff --git a/lang/nl.yml b/lang/nl.yml index 5f713f415..29aca8b33 100644 --- a/lang/nl.yml +++ b/lang/nl.yml @@ -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 diff --git a/lang/pt-br.yml b/lang/pt-br.yml index 3171f8945..ea6ad5deb 100644 --- a/lang/pt-br.yml +++ b/lang/pt-br.yml @@ -425,6 +425,8 @@ button_rollback: Voltar para esta versao button_watch: Watch button_unwatch: Unwatch button_reply: Reply +button_archive: Archive +button_unarchive: Unarchive status_active: ativo status_registered: registrado diff --git a/lang/pt.yml b/lang/pt.yml index b91cfa2da..71b5fe077 100644 --- a/lang/pt.yml +++ b/lang/pt.yml @@ -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 diff --git a/lang/zh.yml b/lang/zh.yml index 17aef65bd..bd40141d3 100644 --- a/lang/zh.yml +++ b/lang/zh.yml @@ -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 index 000000000..e0d414978 Binary files /dev/null and b/public/images/unlock.png differ diff --git a/public/stylesheets/application.css b/public/stylesheets/application.css index a10062f53..15a4603e6 100644 --- a/public/stylesheets/application.css +++ b/public/stylesheets/application.css @@ -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); } diff --git a/test/functional/projects_controller_test.rb b/test/functional/projects_controller_test.rb index 69d5ed912..0e1ff121b 100644 --- a/test/functional/projects_controller_test.rb +++ b/test/functional/projects_controller_test.rb @@ -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 index 000000000..69b16cf85 --- /dev/null +++ b/test/integration/projects_test.rb @@ -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 diff --git a/test/unit/project_test.rb b/test/unit/project_test.rb index 931939e6b..14612d306 100644 --- a/test/unit/project_test.rb +++ b/test/unit/project_test.rb @@ -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) }