@@ -0,0 +1,40 | |||||
|
1 | # redMine - project management software | |||
|
2 | # Copyright (C) 2006-2007 Jean-Philippe Lang | |||
|
3 | # | |||
|
4 | # This program is free software; you can redistribute it and/or | |||
|
5 | # modify it under the terms of the GNU General Public License | |||
|
6 | # as published by the Free Software Foundation; either version 2 | |||
|
7 | # of the License, or (at your option) any later version. | |||
|
8 | # | |||
|
9 | # This program is distributed in the hope that it will be useful, | |||
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
12 | # GNU General Public License for more details. | |||
|
13 | # | |||
|
14 | # You should have received a copy of the GNU General Public License | |||
|
15 | # along with this program; if not, write to the Free Software | |||
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
|
17 | ||||
|
18 | class ProjectSweeper < ActionController::Caching::Sweeper | |||
|
19 | observe Project | |||
|
20 | ||||
|
21 | def before_save(project) | |||
|
22 | if project.new_record? | |||
|
23 | expire_cache_for(project.parent) if project.parent | |||
|
24 | else | |||
|
25 | project_before_update = Project.find(project.id) | |||
|
26 | return if project_before_update.parent_id == project.parent_id && project_before_update.status == project.status | |||
|
27 | expire_cache_for(project.parent) if project.parent | |||
|
28 | expire_cache_for(project_before_update.parent) if project_before_update.parent | |||
|
29 | end | |||
|
30 | end | |||
|
31 | ||||
|
32 | def after_destroy(project) | |||
|
33 | expire_cache_for(project.parent) if project.parent | |||
|
34 | end | |||
|
35 | ||||
|
36 | private | |||
|
37 | def expire_cache_for(project) | |||
|
38 | expire_fragment(Regexp.new("projects/(calendar|gantt)/#{project.id}\\.")) | |||
|
39 | end | |||
|
40 | end |
@@ -0,0 +1,9 | |||||
|
1 | class AddProjectStatus < ActiveRecord::Migration | |||
|
2 | def self.up | |||
|
3 | add_column :projects, :status, :integer, :default => 1, :null => false | |||
|
4 | end | |||
|
5 | ||||
|
6 | def self.down | |||
|
7 | remove_column :projects, :status | |||
|
8 | end | |||
|
9 | end |
1 | NO CONTENT: new file 100644, binary diff hidden |
|
NO CONTENT: new file 100644, binary diff hidden |
@@ -0,0 +1,44 | |||||
|
1 | # redMine - project management software | |||
|
2 | # Copyright (C) 2006-2007 Jean-Philippe Lang | |||
|
3 | # | |||
|
4 | # This program is free software; you can redistribute it and/or | |||
|
5 | # modify it under the terms of the GNU General Public License | |||
|
6 | # as published by the Free Software Foundation; either version 2 | |||
|
7 | # of the License, or (at your option) any later version. | |||
|
8 | # | |||
|
9 | # This program is distributed in the hope that it will be useful, | |||
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
12 | # GNU General Public License for more details. | |||
|
13 | # | |||
|
14 | # You should have received a copy of the GNU General Public License | |||
|
15 | # along with this program; if not, write to the Free Software | |||
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
|
17 | ||||
|
18 | require "#{File.dirname(__FILE__)}/../test_helper" | |||
|
19 | ||||
|
20 | class ProjectsTest < ActionController::IntegrationTest | |||
|
21 | fixtures :projects, :users, :members | |||
|
22 | ||||
|
23 | def test_archive_project | |||
|
24 | subproject = Project.find(1).children.first | |||
|
25 | log_user("admin", "admin") | |||
|
26 | get "admin/projects" | |||
|
27 | assert_response :success | |||
|
28 | assert_template "admin/projects" | |||
|
29 | post "projects/archive", :id => 1 | |||
|
30 | assert_redirected_to "admin/projects" | |||
|
31 | assert !Project.find(1).active? | |||
|
32 | ||||
|
33 | get "projects/show", :id => 1 | |||
|
34 | assert_response :missing | |||
|
35 | get "projects/show", :id => subproject.id | |||
|
36 | assert_response :missing | |||
|
37 | ||||
|
38 | post "projects/unarchive", :id => 1 | |||
|
39 | assert_redirected_to "admin/projects" | |||
|
40 | assert Project.find(1).active? | |||
|
41 | get "projects/show", :id => 1 | |||
|
42 | assert_response :success | |||
|
43 | end | |||
|
44 | end |
@@ -27,12 +27,18 class AdminController < ApplicationController | |||||
27 |
|
27 | |||
28 | def projects |
|
28 | def projects | |
29 | sort_init 'name', 'asc' |
|
29 | sort_init 'name', 'asc' | |
30 |
sort_update |
|
30 | sort_update | |
31 | @project_count = Project.count |
|
31 | ||
|
32 | @status = params[:status] ? params[:status].to_i : 0 | |||
|
33 | conditions = nil | |||
|
34 | conditions = ["status=?", @status] unless @status == 0 | |||
|
35 | ||||
|
36 | @project_count = Project.count(:conditions => conditions) | |||
32 | @project_pages = Paginator.new self, @project_count, |
|
37 | @project_pages = Paginator.new self, @project_count, | |
33 |
|
|
38 | 25, | |
34 | params['page'] |
|
39 | params['page'] | |
35 | @projects = Project.find :all, :order => sort_clause, |
|
40 | @projects = Project.find :all, :order => sort_clause, | |
|
41 | :conditions => conditions, | |||
36 | :limit => @project_pages.items_per_page, |
|
42 | :limit => @project_pages.items_per_page, | |
37 | :offset => @project_pages.current.offset |
|
43 | :offset => @project_pages.current.offset | |
38 |
|
44 |
@@ -86,6 +86,11 class ApplicationController < ActionController::Base | |||||
86 |
|
86 | |||
87 | # authorizes the user for the requested action. |
|
87 | # authorizes the user for the requested action. | |
88 | def authorize(ctrl = params[:controller], action = params[:action]) |
|
88 | def authorize(ctrl = params[:controller], action = params[:action]) | |
|
89 | unless @project.active? | |||
|
90 | @project = nil | |||
|
91 | render_404 | |||
|
92 | return false | |||
|
93 | end | |||
89 | # check if action is allowed on public projects |
|
94 | # check if action is allowed on public projects | |
90 | if @project.is_public? and Permission.allowed_to_public "%s/%s" % [ ctrl, action ] |
|
95 | if @project.is_public? and Permission.allowed_to_public "%s/%s" % [ ctrl, action ] | |
91 | return true |
|
96 | return true | |
@@ -105,6 +110,11 class ApplicationController < ActionController::Base | |||||
105 | # make sure that the user is a member of the project (or admin) if project is private |
|
110 | # make sure that the user is a member of the project (or admin) if project is private | |
106 | # used as a before_filter for actions that do not require any particular permission on the project |
|
111 | # used as a before_filter for actions that do not require any particular permission on the project | |
107 | def check_project_privacy |
|
112 | def check_project_privacy | |
|
113 | unless @project.active? | |||
|
114 | @project = nil | |||
|
115 | render_404 | |||
|
116 | return false | |||
|
117 | end | |||
108 | return true if @project.is_public? |
|
118 | return true if @project.is_public? | |
109 | return false unless logged_in_user |
|
119 | return false unless logged_in_user | |
110 | return true if logged_in_user.admin? || logged_in_user_membership |
|
120 | return true if logged_in_user.admin? || logged_in_user_membership |
@@ -19,9 +19,11 require 'csv' | |||||
19 |
|
19 | |||
20 | class ProjectsController < ApplicationController |
|
20 | class ProjectsController < ApplicationController | |
21 | layout 'base' |
|
21 | layout 'base' | |
22 |
before_filter :find_project |
|
22 | before_filter :find_project, :except => [ :index, :list, :add ] | |
23 | before_filter :require_admin, :only => [ :add, :destroy ] |
|
23 | before_filter :authorize, :except => [ :index, :list, :add, :archive, :unarchive, :destroy ] | |
|
24 | before_filter :require_admin, :only => [ :add, :archive, :unarchive, :destroy ] | |||
24 |
|
25 | |||
|
26 | cache_sweeper :project_sweeper, :only => [ :add, :edit, :archive, :unarchive, :destroy ] | |||
25 | cache_sweeper :issue_sweeper, :only => [ :add_issue ] |
|
27 | cache_sweeper :issue_sweeper, :only => [ :add_issue ] | |
26 |
|
28 | |||
27 | helper :sort |
|
29 | helper :sort | |
@@ -86,7 +88,7 class ProjectsController < ApplicationController | |||||
86 | def show |
|
88 | def show | |
87 | @custom_values = @project.custom_values.find(:all, :include => :custom_field) |
|
89 | @custom_values = @project.custom_values.find(:all, :include => :custom_field) | |
88 | @members_by_role = @project.members.find(:all, :include => [:user, :role], :order => 'position').group_by {|m| m.role} |
|
90 | @members_by_role = @project.members.find(:all, :include => [:user, :role], :order => 'position').group_by {|m| m.role} | |
89 |
@subprojects = @project. |
|
91 | @subprojects = @project.active_children | |
90 | @news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC") |
|
92 | @news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC") | |
91 | @trackers = Tracker.find(:all, :order => 'position') |
|
93 | @trackers = Tracker.find(:all, :order => 'position') | |
92 | @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]) |
|
94 | @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 | |||||
138 | end |
|
140 | end | |
139 | end |
|
141 | end | |
140 |
|
142 | |||
|
143 | def archive | |||
|
144 | @project.archive if request.post? && @project.active? | |||
|
145 | redirect_to :controller => 'admin', :action => 'projects' | |||
|
146 | end | |||
|
147 | ||||
|
148 | def unarchive | |||
|
149 | @project.unarchive if request.post? && !@project.active? | |||
|
150 | redirect_to :controller => 'admin', :action => 'projects' | |||
|
151 | end | |||
|
152 | ||||
141 | # Delete @project |
|
153 | # Delete @project | |
142 | def destroy |
|
154 | def destroy | |
|
155 | @project_to_destroy = @project | |||
143 | if request.post? and params[:confirm] |
|
156 | if request.post? and params[:confirm] | |
144 | @project.destroy |
|
157 | @project_to_destroy.destroy | |
145 | redirect_to :controller => 'admin', :action => 'projects' |
|
158 | redirect_to :controller => 'admin', :action => 'projects' | |
146 | end |
|
159 | end | |
|
160 | # hide project in layout | |||
|
161 | @project = nil | |||
147 | end |
|
162 | end | |
148 |
|
163 | |||
149 | # Add a new issue category to @project |
|
164 | # Add a new issue category to @project |
@@ -55,7 +55,7 class ReportsController < ApplicationController | |||||
55 | render :template => "reports/issue_report_details" |
|
55 | render :template => "reports/issue_report_details" | |
56 | when "subproject" |
|
56 | when "subproject" | |
57 | @field = "project_id" |
|
57 | @field = "project_id" | |
58 | @rows = @project.children |
|
58 | @rows = @project.active_children | |
59 | @data = issues_by_subproject |
|
59 | @data = issues_by_subproject | |
60 | @report_title = l(:field_subproject) |
|
60 | @report_title = l(:field_subproject) | |
61 | render :template => "reports/issue_report_details" |
|
61 | render :template => "reports/issue_report_details" | |
@@ -66,7 +66,7 class ReportsController < ApplicationController | |||||
66 | @priorities = Enumeration::get_values('IPRI') |
|
66 | @priorities = Enumeration::get_values('IPRI') | |
67 | @categories = @project.issue_categories |
|
67 | @categories = @project.issue_categories | |
68 | @authors = @project.members.collect { |m| m.user } |
|
68 | @authors = @project.members.collect { |m| m.user } | |
69 | @subprojects = @project.children |
|
69 | @subprojects = @project.active_children | |
70 | issues_by_tracker |
|
70 | issues_by_tracker | |
71 | issues_by_version |
|
71 | issues_by_version | |
72 | issues_by_priority |
|
72 | issues_by_priority | |
@@ -207,8 +207,8 private | |||||
207 | #{Issue.table_name} i, #{IssueStatus.table_name} s |
|
207 | #{Issue.table_name} i, #{IssueStatus.table_name} s | |
208 | where |
|
208 | where | |
209 | i.status_id=s.id |
|
209 | i.status_id=s.id | |
210 | and i.project_id IN (#{@project.children.collect{|p| p.id}.join(',')}) |
|
210 | and i.project_id IN (#{@project.active_children.collect{|p| p.id}.join(',')}) | |
211 | group by s.id, s.is_closed, i.project_id") if @project.children.any? |
|
211 | group by s.id, s.is_closed, i.project_id") if @project.active_children.any? | |
212 | @issues_by_subproject ||= [] |
|
212 | @issues_by_subproject ||= [] | |
213 | end |
|
213 | end | |
214 | end |
|
214 | end |
@@ -88,7 +88,7 class UsersController < ApplicationController | |||||
88 | end |
|
88 | end | |
89 | @auth_sources = AuthSource.find(:all) |
|
89 | @auth_sources = AuthSource.find(:all) | |
90 | @roles = Role.find(:all, :order => 'position') |
|
90 | @roles = Role.find(:all, :order => 'position') | |
91 | @projects = Project.find(:all) - @user.projects |
|
91 | @projects = Project.find(:all, :order => 'name', :conditions => "status=#{Project::STATUS_ACTIVE}") - @user.projects | |
92 | @membership ||= Member.new |
|
92 | @membership ||= Member.new | |
93 | end |
|
93 | end | |
94 |
|
94 |
@@ -16,4 +16,8 | |||||
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | module AdminHelper |
|
18 | module AdminHelper | |
|
19 | def project_status_options_for_select(selected) | |||
|
20 | options_for_select([[l(:label_all), "*"], | |||
|
21 | [l(:status_active), 1]], selected) | |||
|
22 | end | |||
19 | end |
|
23 | end |
@@ -16,6 +16,10 | |||||
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | class Project < ActiveRecord::Base |
|
18 | class Project < ActiveRecord::Base | |
|
19 | # Project statuses | |||
|
20 | STATUS_ACTIVE = 1 | |||
|
21 | STATUS_ARCHIVED = 9 | |||
|
22 | ||||
19 | has_many :versions, :dependent => :destroy, :order => "#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC" |
|
23 | has_many :versions, :dependent => :destroy, :order => "#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC" | |
20 | has_many :members, :dependent => :delete_all, :include => :user, :conditions => "#{User.table_name}.status=#{User::STATUS_ACTIVE}" |
|
24 | has_many :members, :dependent => :delete_all, :include => :user, :conditions => "#{User.table_name}.status=#{User::STATUS_ACTIVE}" | |
21 | has_many :users, :through => :members |
|
25 | has_many :users, :through => :members | |
@@ -32,6 +36,8 class Project < ActiveRecord::Base | |||||
32 | 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' |
|
36 | 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' | |
33 | acts_as_tree :order => "name", :counter_cache => true |
|
37 | acts_as_tree :order => "name", :counter_cache => true | |
34 |
|
38 | |||
|
39 | attr_protected :status | |||
|
40 | ||||
35 | validates_presence_of :name, :description, :identifier |
|
41 | validates_presence_of :name, :description, :identifier | |
36 | validates_uniqueness_of :name, :identifier |
|
42 | validates_uniqueness_of :name, :identifier | |
37 | validates_associated :custom_values, :on => :update |
|
43 | validates_associated :custom_values, :on => :update | |
@@ -52,12 +58,11 class Project < ActiveRecord::Base | |||||
52 |
|
58 | |||
53 | def issues_with_subprojects(include_subprojects=false) |
|
59 | def issues_with_subprojects(include_subprojects=false) | |
54 | conditions = nil |
|
60 | conditions = nil | |
55 |
if include_subprojects && children. |
|
61 | if include_subprojects && !active_children.empty? | |
56 | ids = [id] + children.collect {|c| c.id} |
|
62 | ids = [id] + active_children.collect {|c| c.id} | |
57 | conditions = ["#{Issue.table_name}.project_id IN (#{ids.join(',')})"] |
|
63 | conditions = ["#{Issue.table_name}.project_id IN (#{ids.join(',')})"] | |
58 | else |
|
|||
59 | conditions = ["#{Issue.table_name}.project_id = ?", id] |
|
|||
60 | end |
|
64 | end | |
|
65 | conditions ||= ["#{Issue.table_name}.project_id = ?", id] | |||
61 | Issue.with_scope :find => { :conditions => conditions } do |
|
66 | Issue.with_scope :find => { :conditions => conditions } do | |
62 | yield |
|
67 | yield | |
63 | end |
|
68 | end | |
@@ -71,12 +76,33 class Project < ActiveRecord::Base | |||||
71 |
|
76 | |||
72 | def self.visible_by(user=nil) |
|
77 | def self.visible_by(user=nil) | |
73 | if user && user.admin? |
|
78 | if user && user.admin? | |
74 | return nil |
|
79 | return ["#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"] | |
75 | elsif user && !user.memberships.empty? |
|
80 | elsif user && !user.memberships.empty? | |
76 | return ["#{Project.table_name}.is_public = ? or #{Project.table_name}.id IN (#{user.memberships.collect{|m| m.project_id}.join(',')})", true] |
|
81 | 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] | |
77 | else |
|
82 | else | |
78 | return ["#{Project.table_name}.is_public = ?", true] |
|
83 | return ["#{Project.table_name}.status=#{Project::STATUS_ACTIVE} AND #{Project.table_name}.is_public = ?", true] | |
|
84 | end | |||
|
85 | end | |||
|
86 | ||||
|
87 | def active? | |||
|
88 | self.status == STATUS_ACTIVE | |||
|
89 | end | |||
|
90 | ||||
|
91 | def archive | |||
|
92 | # Archive subprojects if any | |||
|
93 | children.each do |subproject| | |||
|
94 | subproject.archive | |||
79 | end |
|
95 | end | |
|
96 | update_attribute :status, STATUS_ARCHIVED | |||
|
97 | end | |||
|
98 | ||||
|
99 | def unarchive | |||
|
100 | return false if parent && !parent.active? | |||
|
101 | update_attribute :status, STATUS_ACTIVE | |||
|
102 | end | |||
|
103 | ||||
|
104 | def active_children | |||
|
105 | children.select {|child| child.active?} | |||
80 | end |
|
106 | end | |
81 |
|
107 | |||
82 | # Returns an array of all custom fields enabled for project issues |
|
108 | # Returns an array of all custom fields enabled for project issues |
@@ -95,8 +95,8 class Query < ActiveRecord::Base | |||||
95 | @available_filters["author_id"] = { :type => :list, :order => 5, :values => user_values } |
|
95 | @available_filters["author_id"] = { :type => :list, :order => 5, :values => user_values } | |
96 | @available_filters["category_id"] = { :type => :list_optional, :order => 6, :values => @project.issue_categories.collect{|s| [s.name, s.id.to_s] } } |
|
96 | @available_filters["category_id"] = { :type => :list_optional, :order => 6, :values => @project.issue_categories.collect{|s| [s.name, s.id.to_s] } } | |
97 | @available_filters["fixed_version_id"] = { :type => :list_optional, :order => 7, :values => @project.versions.sort.collect{|s| [s.name, s.id.to_s] } } |
|
97 | @available_filters["fixed_version_id"] = { :type => :list_optional, :order => 7, :values => @project.versions.sort.collect{|s| [s.name, s.id.to_s] } } | |
98 | unless @project.children.empty? |
|
98 | unless @project.active_children.empty? | |
99 | @available_filters["subproject_id"] = { :type => :list_one_or_more, :order => 13, :values => @project.children.collect{|s| [s.name, s.id.to_s] } } |
|
99 | @available_filters["subproject_id"] = { :type => :list_one_or_more, :order => 13, :values => @project.active_children.collect{|s| [s.name, s.id.to_s] } } | |
100 | end |
|
100 | end | |
101 | @project.all_custom_fields.select(&:is_filter?).each do |field| |
|
101 | @project.all_custom_fields.select(&:is_filter?).each do |field| | |
102 | case field.field_format |
|
102 | case field.field_format | |
@@ -164,7 +164,7 class Query < ActiveRecord::Base | |||||
164 | if operator_for("subproject_id") == "=" |
|
164 | if operator_for("subproject_id") == "=" | |
165 | subproject_ids = values_for("subproject_id").each(&:to_i) |
|
165 | subproject_ids = values_for("subproject_id").each(&:to_i) | |
166 | else |
|
166 | else | |
167 | subproject_ids = project.children.collect{|p| p.id} |
|
167 | subproject_ids = project.active_children.collect{|p| p.id} | |
168 | end |
|
168 | end | |
169 | sql << " AND #{Issue.table_name}.project_id IN (%d,%s)" % [project.id, subproject_ids.join(",")] if project |
|
169 | sql << " AND #{Issue.table_name}.project_id IN (%d,%s)" % [project.id, subproject_ids.join(",")] if project | |
170 | else |
|
170 | else |
@@ -23,7 +23,7 class User < ActiveRecord::Base | |||||
23 | STATUS_REGISTERED = 2 |
|
23 | STATUS_REGISTERED = 2 | |
24 | STATUS_LOCKED = 3 |
|
24 | STATUS_LOCKED = 3 | |
25 |
|
25 | |||
26 | has_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :order => "#{Project.table_name}.name", :dependent => :delete_all |
|
26 | 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 | |
27 | has_many :projects, :through => :memberships |
|
27 | has_many :projects, :through => :memberships | |
28 | has_many :custom_values, :dependent => :delete_all, :as => :customized |
|
28 | has_many :custom_values, :dependent => :delete_all, :as => :customized | |
29 | has_one :preference, :dependent => :destroy, :class_name => 'UserPreference' |
|
29 | has_one :preference, :dependent => :destroy, :class_name => 'UserPreference' |
@@ -4,6 +4,15 | |||||
4 |
|
4 | |||
5 | <h2><%=l(:label_project_plural)%></h2> |
|
5 | <h2><%=l(:label_project_plural)%></h2> | |
6 |
|
6 | |||
|
7 | <% form_tag() do %> | |||
|
8 | <fieldset><legend><%= l(:label_filter_plural) %></legend> | |||
|
9 | <label><%= l(:field_status) %> :</label> | |||
|
10 | <%= select_tag 'status', project_status_options_for_select(@status), :class => "small", :onchange => "this.form.submit(); return false;" %> | |||
|
11 | <%= submit_tag l(:button_apply), :class => "small" %> | |||
|
12 | </fieldset> | |||
|
13 | <% end %> | |||
|
14 | | |||
|
15 | ||||
7 | <table class="list"> |
|
16 | <table class="list"> | |
8 | <thead><tr> |
|
17 | <thead><tr> | |
9 | <%= sort_header_tag('name', :caption => l(:label_project)) %> |
|
18 | <%= sort_header_tag('name', :caption => l(:label_project)) %> | |
@@ -12,22 +21,29 | |||||
12 | <th><%=l(:label_subproject_plural)%></th> |
|
21 | <th><%=l(:label_subproject_plural)%></th> | |
13 | <%= sort_header_tag('created_on', :caption => l(:field_created_on)) %> |
|
22 | <%= sort_header_tag('created_on', :caption => l(:field_created_on)) %> | |
14 | <th></th> |
|
23 | <th></th> | |
|
24 | <th></th> | |||
15 | </tr></thead> |
|
25 | </tr></thead> | |
16 | <tbody> |
|
26 | <tbody> | |
17 | <% for project in @projects %> |
|
27 | <% for project in @projects %> | |
18 | <tr class="<%= cycle("odd", "even") %>"> |
|
28 | <tr class="<%= cycle("odd", "even") %>"> | |
19 |
<td><%= link_to |
|
29 | <td><%= project.active? ? link_to(project.name, :controller => 'projects', :action => 'settings', :id => project) : h(project.name) %> | |
20 | <td><%=h project.description %> |
|
30 | <td><%=h project.description %> | |
21 | <td align="center"><%= image_tag 'true.png' if project.is_public? %> |
|
31 | <td align="center"><%= image_tag 'true.png' if project.is_public? %> | |
22 | <td align="center"><%= project.children.size %> |
|
32 | <td align="center"><%= project.children.size %> | |
23 | <td align="center"><%= format_date(project.created_on) %> |
|
33 | <td align="center"><%= format_date(project.created_on) %> | |
24 | <td align="center"> |
|
34 | <td align="center" style="width:10%"> | |
25 | <%= button_to l(:button_delete), { :controller => 'projects', :action => 'destroy', :id => project }, :class => "button-small" %> |
|
35 | <small> | |
|
36 | <%= link_to(l(:button_archive), { :controller => 'projects', :action => 'archive', :id => project }, :method => :post, :class => 'icon icon-lock') if project.active? %> | |||
|
37 | <%= 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?) %> | |||
|
38 | </small> | |||
|
39 | </td> | |||
|
40 | <td align="center" style="width:10%"> | |||
|
41 | <small><%= link_to(l(:button_delete), { :controller => 'projects', :action => 'destroy', :id => project }, :class => 'icon icon-del') %></small> | |||
26 | </td> |
|
42 | </td> | |
27 | </tr> |
|
43 | </tr> | |
28 | <% end %> |
|
44 | <% end %> | |
29 | </tbody> |
|
45 | </tbody> | |
30 | </table> |
|
46 | </table> | |
31 |
|
47 | |||
32 | <p><%= pagination_links_full @project_pages %> |
|
48 | <p><%= pagination_links_full @project_pages, :status => @status %> | |
33 | [ <%= @project_pages.current.first_item %> - <%= @project_pages.current.last_item %> / <%= @project_count %> ]</p> No newline at end of file |
|
49 | [ <%= @project_pages.current.first_item %> - <%= @project_pages.current.last_item %> / <%= @project_count %> ]</p> |
@@ -1,6 +1,6 | |||||
1 | <h3><%=l(:label_assigned_to_me_issues)%></h3> |
|
1 | <h3><%=l(:label_assigned_to_me_issues)%></h3> | |
2 | <% assigned_issues = Issue.find(:all, |
|
2 | <% assigned_issues = Issue.find(:all, | |
3 | :conditions => ["assigned_to_id=? AND #{IssueStatus.table_name}.is_closed=?", user.id, false], |
|
3 | :conditions => ["assigned_to_id=? AND #{IssueStatus.table_name}.is_closed=? AND #{Project.table_name}.status=#{Project::STATUS_ACTIVE}", user.id, false], | |
4 | :limit => 10, |
|
4 | :limit => 10, | |
5 | :include => [ :status, :project, :tracker ], |
|
5 | :include => [ :status, :project, :tracker ], | |
6 | :order => "#{Issue.table_name}.updated_on DESC") %> |
|
6 | :order => "#{Issue.table_name}.updated_on DESC") %> |
@@ -1,6 +1,6 | |||||
1 | <h3><%=l(:label_reported_issues)%></h3> |
|
1 | <h3><%=l(:label_reported_issues)%></h3> | |
2 | <% reported_issues = Issue.find(:all, |
|
2 | <% reported_issues = Issue.find(:all, | |
3 | :conditions => ["author_id=?", user.id], |
|
3 | :conditions => ["author_id=? AND #{Project.table_name}.status=#{Project::STATUS_ACTIVE}", user.id], | |
4 | :limit => 10, |
|
4 | :limit => 10, | |
5 | :include => [ :status, :project, :tracker ], |
|
5 | :include => [ :status, :project, :tracker ], | |
6 | :order => "#{Issue.table_name}.updated_on DESC") %> |
|
6 | :order => "#{Issue.table_name}.updated_on DESC") %> |
@@ -2,7 +2,7 | |||||
2 | <% watched_issues = Issue.find(:all, |
|
2 | <% watched_issues = Issue.find(:all, | |
3 | :include => [:status, :project, :tracker, :watchers], |
|
3 | :include => [:status, :project, :tracker, :watchers], | |
4 | :limit => 10, |
|
4 | :limit => 10, | |
5 | :conditions => ["#{Watcher.table_name}.user_id = ?", user.id], |
|
5 | :conditions => ["#{Watcher.table_name}.user_id = ? AND #{Project.table_name}.status=#{Project::STATUS_ACTIVE}", user.id], | |
6 | :order => "#{Issue.table_name}.updated_on DESC") %> |
|
6 | :order => "#{Issue.table_name}.updated_on DESC") %> | |
7 | <%= render :partial => 'issues/list_simple', :locals => { :issues => watched_issues } %> |
|
7 | <%= render :partial => 'issues/list_simple', :locals => { :issues => watched_issues } %> | |
8 | <% if watched_issues.length > 0 %> |
|
8 | <% if watched_issues.length > 0 %> |
@@ -23,7 +23,7 | |||||
23 | <%= check_box_tag "tracker_ids[]", tracker.id, (@selected_tracker_ids.include? tracker.id.to_s) %> |
|
23 | <%= check_box_tag "tracker_ids[]", tracker.id, (@selected_tracker_ids.include? tracker.id.to_s) %> | |
24 | <%= tracker.name %><br /> |
|
24 | <%= tracker.name %><br /> | |
25 | <% end %> |
|
25 | <% end %> | |
26 | <% if @project.children.any? %> |
|
26 | <% if @project.active_children.any? %> | |
27 | <p><strong><%=l(:label_subproject_plural)%></strong></p> |
|
27 | <p><strong><%=l(:label_subproject_plural)%></strong></p> | |
28 | <%= check_box_tag "with_subprojects", 1, params[:with_subprojects] %> <%= l(:general_text_Yes) %> |
|
28 | <%= check_box_tag "with_subprojects", 1, params[:with_subprojects] %> <%= l(:general_text_Yes) %> | |
29 | <% end %> |
|
29 | <% end %> |
@@ -1,11 +1,11 | |||||
1 | <h2><%=l(:label_confirmation)%></h2> |
|
1 | <h2><%=l(:label_confirmation)%></h2> | |
2 | <div class="box"> |
|
2 | <div class="box"> | |
3 | <center> |
|
3 | <center> | |
4 | <p><strong><%= @project.name %></strong><br /> |
|
4 | <p><strong><%= @project_to_destroy.name %></strong><br /> | |
5 | <%=l(:text_project_destroy_confirmation)%></p> |
|
5 | <%=l(:text_project_destroy_confirmation)%></p> | |
6 |
|
6 | |||
7 | <p> |
|
7 | <p> | |
8 | <% form_tag({:controller => 'projects', :action => 'destroy', :id => @project}) do %> |
|
8 | <% form_tag({:controller => 'projects', :action => 'destroy', :id => @project_to_destroy}) do %> | |
9 | <%= hidden_field_tag "confirm", 1 %> |
|
9 | <%= hidden_field_tag "confirm", 1 %> | |
10 | <%= submit_tag l(:button_delete) %> |
|
10 | <%= submit_tag l(:button_delete) %> | |
11 | <% end %> |
|
11 | <% end %> |
@@ -49,7 +49,7 t_height = g_height + headers_height | |||||
49 | <%= check_box_tag "tracker_ids[]", tracker.id, (@selected_tracker_ids.include? tracker.id.to_s) %> |
|
49 | <%= check_box_tag "tracker_ids[]", tracker.id, (@selected_tracker_ids.include? tracker.id.to_s) %> | |
50 | <%= tracker.name %><br /> |
|
50 | <%= tracker.name %><br /> | |
51 | <% end %> |
|
51 | <% end %> | |
52 | <% if @project.children.any? %> |
|
52 | <% if @project.active_children.any? %> | |
53 | <p><strong><%=l(:label_subproject_plural)%></strong></p> |
|
53 | <p><strong><%=l(:label_subproject_plural)%></strong></p> | |
54 | <%= check_box_tag "with_subprojects", 1, params[:with_subprojects] %> <%= l(:general_text_Yes) %> |
|
54 | <%= check_box_tag "with_subprojects", 1, params[:with_subprojects] %> <%= l(:general_text_Yes) %> | |
55 | <% end %> |
|
55 | <% end %> |
@@ -45,7 +45,7 | |||||
45 | <% end %> |
|
45 | <% end %> | |
46 | </div> |
|
46 | </div> | |
47 |
|
47 | |||
48 | <% if @subprojects %> |
|
48 | <% if @subprojects.any? %> | |
49 | <div class="box"> |
|
49 | <div class="box"> | |
50 | <h3 class="icon22 icon22-projects"><%=l(:label_subproject_plural)%></h3> |
|
50 | <h3 class="icon22 icon22-projects"><%=l(:label_subproject_plural)%></h3> | |
51 | <%= @subprojects.collect{|p| link_to(p.name, :action => 'show', :id => p)}.join(", ") %> |
|
51 | <%= @subprojects.collect{|p| link_to(p.name, :action => 'show', :id => p)}.join(", ") %> |
@@ -425,6 +425,8 button_rollback: Върни се към тази ревизия | |||||
425 | button_watch: Наблюдавай |
|
425 | button_watch: Наблюдавай | |
426 | button_unwatch: Спри наблюдението |
|
426 | button_unwatch: Спри наблюдението | |
427 | button_reply: Reply |
|
427 | button_reply: Reply | |
|
428 | button_archive: Archive | |||
|
429 | button_unarchive: Unarchive | |||
428 |
|
430 | |||
429 | status_active: активен |
|
431 | status_active: активен | |
430 | status_registered: регистриран |
|
432 | status_registered: регистриран |
@@ -425,6 +425,8 button_rollback: Rollback to this version | |||||
425 | button_watch: Watch |
|
425 | button_watch: Watch | |
426 | button_unwatch: Unwatch |
|
426 | button_unwatch: Unwatch | |
427 | button_reply: Reply |
|
427 | button_reply: Reply | |
|
428 | button_archive: Archive | |||
|
429 | button_unarchive: Unarchive | |||
428 |
|
430 | |||
429 | status_active: aktiv |
|
431 | status_active: aktiv | |
430 | status_registered: angemeldet |
|
432 | status_registered: angemeldet |
@@ -425,6 +425,8 button_rollback: Rollback to this version | |||||
425 | button_watch: Watch |
|
425 | button_watch: Watch | |
426 | button_unwatch: Unwatch |
|
426 | button_unwatch: Unwatch | |
427 | button_reply: Reply |
|
427 | button_reply: Reply | |
|
428 | button_archive: Archive | |||
|
429 | button_unarchive: Unarchive | |||
428 |
|
430 | |||
429 | status_active: active |
|
431 | status_active: active | |
430 | status_registered: registered |
|
432 | status_registered: registered |
@@ -425,6 +425,8 button_rollback: Rollback to this version | |||||
425 | button_watch: Watch |
|
425 | button_watch: Watch | |
426 | button_unwatch: Unwatch |
|
426 | button_unwatch: Unwatch | |
427 | button_reply: Reply |
|
427 | button_reply: Reply | |
|
428 | button_archive: Archive | |||
|
429 | button_unarchive: Unarchive | |||
428 |
|
430 | |||
429 | status_active: active |
|
431 | status_active: active | |
430 | status_registered: registered |
|
432 | status_registered: registered |
@@ -425,6 +425,8 button_rollback: Revenir à cette version | |||||
425 | button_watch: Surveiller |
|
425 | button_watch: Surveiller | |
426 | button_unwatch: Ne plus surveiller |
|
426 | button_unwatch: Ne plus surveiller | |
427 | button_reply: Répondre |
|
427 | button_reply: Répondre | |
|
428 | button_archive: Archiver | |||
|
429 | button_unarchive: Désarchiver | |||
428 |
|
430 | |||
429 | status_active: actif |
|
431 | status_active: actif | |
430 | status_registered: enregistré |
|
432 | status_registered: enregistré |
@@ -425,6 +425,8 button_rollback: Ripristina questa versione | |||||
425 | button_watch: Watch |
|
425 | button_watch: Watch | |
426 | button_unwatch: Unwatch |
|
426 | button_unwatch: Unwatch | |
427 | button_reply: Reply |
|
427 | button_reply: Reply | |
|
428 | button_archive: Archive | |||
|
429 | button_unarchive: Unarchive | |||
428 |
|
430 | |||
429 | status_active: attivo |
|
431 | status_active: attivo | |
430 | status_registered: registrato |
|
432 | status_registered: registrato |
@@ -426,6 +426,8 button_rollback: このバージョンにロールバック | |||||
426 | button_watch: Watch |
|
426 | button_watch: Watch | |
427 | button_unwatch: Unwatch |
|
427 | button_unwatch: Unwatch | |
428 | button_reply: Reply |
|
428 | button_reply: Reply | |
|
429 | button_archive: Archive | |||
|
430 | button_unarchive: Unarchive | |||
429 |
|
431 | |||
430 | status_active: 有効 |
|
432 | status_active: 有効 | |
431 | status_registered: 登録 |
|
433 | status_registered: 登録 |
@@ -425,6 +425,8 button_rollback: Rollback naar deze versie | |||||
425 | button_watch: Monitor |
|
425 | button_watch: Monitor | |
426 | button_unwatch: Niet meer monitoren |
|
426 | button_unwatch: Niet meer monitoren | |
427 | button_reply: Antwoord |
|
427 | button_reply: Antwoord | |
|
428 | button_archive: Archive | |||
|
429 | button_unarchive: Unarchive | |||
428 |
|
430 | |||
429 | status_active: Actief |
|
431 | status_active: Actief | |
430 | status_registered: geregistreerd |
|
432 | status_registered: geregistreerd |
@@ -425,6 +425,8 button_rollback: Voltar para esta versao | |||||
425 | button_watch: Watch |
|
425 | button_watch: Watch | |
426 | button_unwatch: Unwatch |
|
426 | button_unwatch: Unwatch | |
427 | button_reply: Reply |
|
427 | button_reply: Reply | |
|
428 | button_archive: Archive | |||
|
429 | button_unarchive: Unarchive | |||
428 |
|
430 | |||
429 | status_active: ativo |
|
431 | status_active: ativo | |
430 | status_registered: registrado |
|
432 | status_registered: registrado |
@@ -425,6 +425,8 button_rollback: Voltar para esta versão | |||||
425 | button_watch: Observar |
|
425 | button_watch: Observar | |
426 | button_unwatch: Não observar |
|
426 | button_unwatch: Não observar | |
427 | button_reply: Reply |
|
427 | button_reply: Reply | |
|
428 | button_archive: Archive | |||
|
429 | button_unarchive: Unarchive | |||
428 |
|
430 | |||
429 | status_active: ativo |
|
431 | status_active: ativo | |
430 | status_registered: registrado |
|
432 | status_registered: registrado |
@@ -428,6 +428,8 button_rollback: Rollback to this version | |||||
428 | button_watch: Watch |
|
428 | button_watch: Watch | |
429 | button_unwatch: Unwatch |
|
429 | button_unwatch: Unwatch | |
430 | button_reply: Reply |
|
430 | button_reply: Reply | |
|
431 | button_archive: Archive | |||
|
432 | button_unarchive: Unarchive | |||
431 |
|
433 | |||
432 | status_active: 激活 |
|
434 | status_active: 激活 | |
433 | status_registered: 已注册 |
|
435 | status_registered: 已注册 |
@@ -163,6 +163,8 vertical-align: middle; | |||||
163 | .icon-fav { background-image: url(../images/fav.png); } |
|
163 | .icon-fav { background-image: url(../images/fav.png); } | |
164 | .icon-fav-off { background-image: url(../images/fav_off.png); } |
|
164 | .icon-fav-off { background-image: url(../images/fav_off.png); } | |
165 | .icon-reload { background-image: url(../images/reload.png); } |
|
165 | .icon-reload { background-image: url(../images/reload.png); } | |
|
166 | .icon-lock { background-image: url(../images/locked.png); } | |||
|
167 | .icon-unlock { background-image: url(../images/unlock.png); } | |||
166 |
|
168 | |||
167 | .icon22-projects { background-image: url(../images/22x22/projects.png); } |
|
169 | .icon22-projects { background-image: url(../images/22x22/projects.png); } | |
168 | .icon22-users { background-image: url(../images/22x22/users.png); } |
|
170 | .icon22-users { background-image: url(../images/22x22/users.png); } |
@@ -1,5 +1,5 | |||||
1 | # redMine - project management software |
|
1 | # redMine - project management software | |
2 | # Copyright (C) 2006 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2007 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
@@ -125,4 +125,19 class ProjectsControllerTest < Test::Unit::TestCase | |||||
125 | assert_template 'activity' |
|
125 | assert_template 'activity' | |
126 | assert_not_nil assigns(:events_by_day) |
|
126 | assert_not_nil assigns(:events_by_day) | |
127 | end |
|
127 | end | |
|
128 | ||||
|
129 | def test_archive | |||
|
130 | @request.session[:user_id] = 1 # admin | |||
|
131 | post :archive, :id => 1 | |||
|
132 | assert_redirected_to 'admin/projects' | |||
|
133 | assert !Project.find(1).active? | |||
|
134 | end | |||
|
135 | ||||
|
136 | def test_unarchive | |||
|
137 | @request.session[:user_id] = 1 # admin | |||
|
138 | Project.find(1).archive | |||
|
139 | post :unarchive, :id => 1 | |||
|
140 | assert_redirected_to 'admin/projects' | |||
|
141 | assert Project.find(1).active? | |||
|
142 | end | |||
128 | end |
|
143 | end |
@@ -1,5 +1,5 | |||||
1 | # redMine - project management software |
|
1 | # redMine - project management software | |
2 | # Copyright (C) 2006 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2007 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
@@ -51,6 +51,34 class ProjectTest < Test::Unit::TestCase | |||||
51 | assert_equal true, public_projects[0].is_public? |
|
51 | assert_equal true, public_projects[0].is_public? | |
52 | end |
|
52 | end | |
53 |
|
53 | |||
|
54 | def test_archive | |||
|
55 | user = @ecookbook.members.first.user | |||
|
56 | @ecookbook.archive | |||
|
57 | @ecookbook.reload | |||
|
58 | ||||
|
59 | assert !@ecookbook.active? | |||
|
60 | assert !user.projects.include?(@ecookbook) | |||
|
61 | # Subproject are also archived | |||
|
62 | assert !@ecookbook.children.empty? | |||
|
63 | assert @ecookbook.active_children.empty? | |||
|
64 | end | |||
|
65 | ||||
|
66 | def test_unarchive | |||
|
67 | user = @ecookbook.members.first.user | |||
|
68 | @ecookbook.archive | |||
|
69 | # A subproject of an archived project can not be unarchived | |||
|
70 | assert !@ecookbook_sub1.unarchive | |||
|
71 | ||||
|
72 | # Unarchive project | |||
|
73 | assert @ecookbook.unarchive | |||
|
74 | @ecookbook.reload | |||
|
75 | assert @ecookbook.active? | |||
|
76 | assert user.projects.include?(@ecookbook) | |||
|
77 | # Subproject can now be unarchived | |||
|
78 | @ecookbook_sub1.reload | |||
|
79 | assert @ecookbook_sub1.unarchive | |||
|
80 | end | |||
|
81 | ||||
54 | def test_destroy |
|
82 | def test_destroy | |
55 | @ecookbook.destroy |
|
83 | @ecookbook.destroy | |
56 | assert_raise(ActiveRecord::RecordNotFound) { Project.find(@ecookbook.id) } |
|
84 | assert_raise(ActiveRecord::RecordNotFound) { Project.find(@ecookbook.id) } |
General Comments 0
You need to be logged in to leave comments.
Login now