@@ -0,0 +1,9 | |||||
|
1 | <h3 class="title"><%=l(:label_version_new)%></h3> | |||
|
2 | ||||
|
3 | <% labelled_remote_form_for @version, :url => project_versions_path(@project) do |f| %> | |||
|
4 | <%= render :partial => 'versions/form', :locals => { :f => f } %> | |||
|
5 | <p class="buttons"> | |||
|
6 | <%= submit_tag l(:button_create), :name => nil %> | |||
|
7 | <%= submit_tag l(:button_cancel), :name => nil, :onclick => "hideModal(this);", :type => 'button' %> | |||
|
8 | </p> | |||
|
9 | <% end %> |
@@ -1,189 +1,205 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2011 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2011 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 | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
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 |
|
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. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | class VersionsController < ApplicationController |
|
18 | class VersionsController < ApplicationController | |
19 | menu_item :roadmap |
|
19 | menu_item :roadmap | |
20 | model_object Version |
|
20 | model_object Version | |
21 | before_filter :find_model_object, :except => [:index, :new, :create, :close_completed] |
|
21 | before_filter :find_model_object, :except => [:index, :new, :create, :close_completed] | |
22 | before_filter :find_project_from_association, :except => [:index, :new, :create, :close_completed] |
|
22 | before_filter :find_project_from_association, :except => [:index, :new, :create, :close_completed] | |
23 | before_filter :find_project, :only => [:index, :new, :create, :close_completed] |
|
23 | before_filter :find_project, :only => [:index, :new, :create, :close_completed] | |
24 | before_filter :authorize |
|
24 | before_filter :authorize | |
25 |
|
25 | |||
26 | accept_api_auth :index, :create, :update, :destroy |
|
26 | accept_api_auth :index, :create, :update, :destroy | |
27 |
|
27 | |||
28 | helper :custom_fields |
|
28 | helper :custom_fields | |
29 | helper :projects |
|
29 | helper :projects | |
30 |
|
30 | |||
31 | def index |
|
31 | def index | |
32 | respond_to do |format| |
|
32 | respond_to do |format| | |
33 | format.html { |
|
33 | format.html { | |
34 | @trackers = @project.trackers.find(:all, :order => 'position') |
|
34 | @trackers = @project.trackers.find(:all, :order => 'position') | |
35 | retrieve_selected_tracker_ids(@trackers, @trackers.select {|t| t.is_in_roadmap?}) |
|
35 | retrieve_selected_tracker_ids(@trackers, @trackers.select {|t| t.is_in_roadmap?}) | |
36 | @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1') |
|
36 | @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1') | |
37 | project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id] |
|
37 | project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id] | |
38 |
|
38 | |||
39 | @versions = @project.shared_versions || [] |
|
39 | @versions = @project.shared_versions || [] | |
40 | @versions += @project.rolled_up_versions.visible if @with_subprojects |
|
40 | @versions += @project.rolled_up_versions.visible if @with_subprojects | |
41 | @versions = @versions.uniq.sort |
|
41 | @versions = @versions.uniq.sort | |
42 | unless params[:completed] |
|
42 | unless params[:completed] | |
43 | @completed_versions = @versions.select {|version| version.closed? || version.completed? } |
|
43 | @completed_versions = @versions.select {|version| version.closed? || version.completed? } | |
44 | @versions -= @completed_versions |
|
44 | @versions -= @completed_versions | |
45 | end |
|
45 | end | |
46 |
|
46 | |||
47 | @issues_by_version = {} |
|
47 | @issues_by_version = {} | |
48 | unless @selected_tracker_ids.empty? |
|
48 | unless @selected_tracker_ids.empty? | |
49 | @versions.each do |version| |
|
49 | @versions.each do |version| | |
50 | issues = version.fixed_issues.visible.find(:all, |
|
50 | issues = version.fixed_issues.visible.find(:all, | |
51 | :include => [:project, :status, :tracker, :priority], |
|
51 | :include => [:project, :status, :tracker, :priority], | |
52 | :conditions => {:tracker_id => @selected_tracker_ids, :project_id => project_ids}, |
|
52 | :conditions => {:tracker_id => @selected_tracker_ids, :project_id => project_ids}, | |
53 | :order => "#{Project.table_name}.lft, #{Tracker.table_name}.position, #{Issue.table_name}.id") |
|
53 | :order => "#{Project.table_name}.lft, #{Tracker.table_name}.position, #{Issue.table_name}.id") | |
54 | @issues_by_version[version] = issues |
|
54 | @issues_by_version[version] = issues | |
55 | end |
|
55 | end | |
56 | end |
|
56 | end | |
57 | @versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].blank?} |
|
57 | @versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].blank?} | |
58 | } |
|
58 | } | |
59 | format.api { |
|
59 | format.api { | |
60 | @versions = @project.shared_versions.all |
|
60 | @versions = @project.shared_versions.all | |
61 | } |
|
61 | } | |
62 | end |
|
62 | end | |
63 | end |
|
63 | end | |
64 |
|
64 | |||
65 | def show |
|
65 | def show | |
66 | respond_to do |format| |
|
66 | respond_to do |format| | |
67 | format.html { |
|
67 | format.html { | |
68 | @issues = @version.fixed_issues.visible.find(:all, |
|
68 | @issues = @version.fixed_issues.visible.find(:all, | |
69 | :include => [:status, :tracker, :priority], |
|
69 | :include => [:status, :tracker, :priority], | |
70 | :order => "#{Tracker.table_name}.position, #{Issue.table_name}.id") |
|
70 | :order => "#{Tracker.table_name}.position, #{Issue.table_name}.id") | |
71 | } |
|
71 | } | |
72 | format.api |
|
72 | format.api | |
73 | end |
|
73 | end | |
74 | end |
|
74 | end | |
75 |
|
75 | |||
76 | def new |
|
76 | def new | |
77 | @version = @project.versions.build(params[:version]) |
|
77 | @version = @project.versions.build(params[:version]) | |
|
78 | ||||
|
79 | respond_to do |format| | |||
|
80 | format.html | |||
|
81 | format.js do | |||
|
82 | render :update do |page| | |||
|
83 | page.replace_html 'ajax-modal', :partial => 'versions/new_modal' | |||
|
84 | page << "showModal('ajax-modal', '600px');" | |||
|
85 | page << "Form.Element.focus('version_name');" | |||
|
86 | end | |||
|
87 | end | |||
|
88 | end | |||
78 | end |
|
89 | end | |
79 |
|
90 | |||
80 | def create |
|
91 | def create | |
81 | @version = @project.versions.build |
|
92 | @version = @project.versions.build | |
82 | if params[:version] |
|
93 | if params[:version] | |
83 | attributes = params[:version].dup |
|
94 | attributes = params[:version].dup | |
84 | attributes.delete('sharing') unless attributes.nil? || @version.allowed_sharings.include?(attributes['sharing']) |
|
95 | attributes.delete('sharing') unless attributes.nil? || @version.allowed_sharings.include?(attributes['sharing']) | |
85 | @version.attributes = attributes |
|
96 | @version.attributes = attributes | |
86 | end |
|
97 | end | |
87 |
|
98 | |||
88 | if request.post? |
|
99 | if request.post? | |
89 | if @version.save |
|
100 | if @version.save | |
90 | respond_to do |format| |
|
101 | respond_to do |format| | |
91 | format.html do |
|
102 | format.html do | |
92 | flash[:notice] = l(:notice_successful_create) |
|
103 | flash[:notice] = l(:notice_successful_create) | |
93 | redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project |
|
104 | redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project | |
94 | end |
|
105 | end | |
95 | format.js do |
|
106 | format.js do | |
96 | # IE doesn't support the replace_html rjs method for select box options |
|
107 | render(:update) {|page| | |
97 | render(:update) {|page| page.replace "issue_fixed_version_id", |
|
108 | page << 'hideModal();' | |
98 | content_tag('select', '<option></option>' + version_options_for_select(@project.shared_versions.open, @version), :id => 'issue_fixed_version_id', :name => 'issue[fixed_version_id]') |
|
109 | # IE doesn't support the replace_html rjs method for select box options | |
|
110 | page.replace "issue_fixed_version_id", | |||
|
111 | content_tag('select', '<option></option>' + version_options_for_select(@project.shared_versions.open, @version), :id => 'issue_fixed_version_id', :name => 'issue[fixed_version_id]') | |||
99 | } |
|
112 | } | |
100 | end |
|
113 | end | |
101 | format.api do |
|
114 | format.api do | |
102 | render :action => 'show', :status => :created, :location => version_url(@version) |
|
115 | render :action => 'show', :status => :created, :location => version_url(@version) | |
103 | end |
|
116 | end | |
104 | end |
|
117 | end | |
105 | else |
|
118 | else | |
106 | respond_to do |format| |
|
119 | respond_to do |format| | |
107 | format.html { render :action => 'new' } |
|
120 | format.html { render :action => 'new' } | |
108 | format.js do |
|
121 | format.js do | |
109 | render(:update) {|page| page.alert(@version.errors.full_messages.join('\n')) } |
|
122 | render :update do |page| | |
|
123 | page.replace_html 'ajax-modal', :partial => 'versions/new_modal' | |||
|
124 | page << "Form.Element.focus('version_name');" | |||
|
125 | end | |||
110 | end |
|
126 | end | |
111 | format.api { render_validation_errors(@version) } |
|
127 | format.api { render_validation_errors(@version) } | |
112 | end |
|
128 | end | |
113 | end |
|
129 | end | |
114 | end |
|
130 | end | |
115 | end |
|
131 | end | |
116 |
|
132 | |||
117 | def edit |
|
133 | def edit | |
118 | end |
|
134 | end | |
119 |
|
135 | |||
120 | def update |
|
136 | def update | |
121 | if request.put? && params[:version] |
|
137 | if request.put? && params[:version] | |
122 | attributes = params[:version].dup |
|
138 | attributes = params[:version].dup | |
123 | attributes.delete('sharing') unless @version.allowed_sharings.include?(attributes['sharing']) |
|
139 | attributes.delete('sharing') unless @version.allowed_sharings.include?(attributes['sharing']) | |
124 | if @version.update_attributes(attributes) |
|
140 | if @version.update_attributes(attributes) | |
125 | respond_to do |format| |
|
141 | respond_to do |format| | |
126 | format.html { |
|
142 | format.html { | |
127 | flash[:notice] = l(:notice_successful_update) |
|
143 | flash[:notice] = l(:notice_successful_update) | |
128 | redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project |
|
144 | redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project | |
129 | } |
|
145 | } | |
130 | format.api { head :ok } |
|
146 | format.api { head :ok } | |
131 | end |
|
147 | end | |
132 | else |
|
148 | else | |
133 | respond_to do |format| |
|
149 | respond_to do |format| | |
134 | format.html { render :action => 'edit' } |
|
150 | format.html { render :action => 'edit' } | |
135 | format.api { render_validation_errors(@version) } |
|
151 | format.api { render_validation_errors(@version) } | |
136 | end |
|
152 | end | |
137 | end |
|
153 | end | |
138 | end |
|
154 | end | |
139 | end |
|
155 | end | |
140 |
|
156 | |||
141 | def close_completed |
|
157 | def close_completed | |
142 | if request.put? |
|
158 | if request.put? | |
143 | @project.close_completed_versions |
|
159 | @project.close_completed_versions | |
144 | end |
|
160 | end | |
145 | redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project |
|
161 | redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project | |
146 | end |
|
162 | end | |
147 |
|
163 | |||
148 | verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed } |
|
164 | verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed } | |
149 | def destroy |
|
165 | def destroy | |
150 | if @version.fixed_issues.empty? |
|
166 | if @version.fixed_issues.empty? | |
151 | @version.destroy |
|
167 | @version.destroy | |
152 | respond_to do |format| |
|
168 | respond_to do |format| | |
153 | format.html { redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project } |
|
169 | format.html { redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project } | |
154 | format.api { head :ok } |
|
170 | format.api { head :ok } | |
155 | end |
|
171 | end | |
156 | else |
|
172 | else | |
157 | respond_to do |format| |
|
173 | respond_to do |format| | |
158 | format.html { |
|
174 | format.html { | |
159 | flash[:error] = l(:notice_unable_delete_version) |
|
175 | flash[:error] = l(:notice_unable_delete_version) | |
160 | redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project |
|
176 | redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project | |
161 | } |
|
177 | } | |
162 | format.api { head :unprocessable_entity } |
|
178 | format.api { head :unprocessable_entity } | |
163 | end |
|
179 | end | |
164 | end |
|
180 | end | |
165 | end |
|
181 | end | |
166 |
|
182 | |||
167 | def status_by |
|
183 | def status_by | |
168 | respond_to do |format| |
|
184 | respond_to do |format| | |
169 | format.html { render :action => 'show' } |
|
185 | format.html { render :action => 'show' } | |
170 | format.js { render(:update) {|page| page.replace_html 'status_by', render_issue_status_by(@version, params[:status_by])} } |
|
186 | format.js { render(:update) {|page| page.replace_html 'status_by', render_issue_status_by(@version, params[:status_by])} } | |
171 | end |
|
187 | end | |
172 | end |
|
188 | end | |
173 |
|
189 | |||
174 | private |
|
190 | private | |
175 | def find_project |
|
191 | def find_project | |
176 | @project = Project.find(params[:project_id]) |
|
192 | @project = Project.find(params[:project_id]) | |
177 | rescue ActiveRecord::RecordNotFound |
|
193 | rescue ActiveRecord::RecordNotFound | |
178 | render_404 |
|
194 | render_404 | |
179 | end |
|
195 | end | |
180 |
|
196 | |||
181 | def retrieve_selected_tracker_ids(selectable_trackers, default_trackers=nil) |
|
197 | def retrieve_selected_tracker_ids(selectable_trackers, default_trackers=nil) | |
182 | if ids = params[:tracker_ids] |
|
198 | if ids = params[:tracker_ids] | |
183 | @selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s } |
|
199 | @selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s } | |
184 | else |
|
200 | else | |
185 | @selected_tracker_ids = (default_trackers || selectable_trackers).collect {|t| t.id.to_s } |
|
201 | @selected_tracker_ids = (default_trackers || selectable_trackers).collect {|t| t.id.to_s } | |
186 | end |
|
202 | end | |
187 | end |
|
203 | end | |
188 |
|
204 | |||
189 | end |
|
205 | end |
@@ -1,69 +1,67 | |||||
1 | <% labelled_fields_for :issue, @issue do |f| %> |
|
1 | <% labelled_fields_for :issue, @issue do |f| %> | |
2 |
|
2 | |||
3 | <div class="splitcontentleft"> |
|
3 | <div class="splitcontentleft"> | |
4 | <% if @issue.safe_attribute? 'status_id' %> |
|
4 | <% if @issue.safe_attribute? 'status_id' %> | |
5 | <p><%= f.select :status_id, (@allowed_statuses.collect {|p| [p.name, p.id]}), :required => true %></p> |
|
5 | <p><%= f.select :status_id, (@allowed_statuses.collect {|p| [p.name, p.id]}), :required => true %></p> | |
6 | <% else %> |
|
6 | <% else %> | |
7 | <p><label><%= l(:field_status) %></label> <%= h(@issue.status.name) %></p> |
|
7 | <p><label><%= l(:field_status) %></label> <%= h(@issue.status.name) %></p> | |
8 | <% end %> |
|
8 | <% end %> | |
9 |
|
9 | |||
10 | <% if @issue.safe_attribute? 'priority_id' %> |
|
10 | <% if @issue.safe_attribute? 'priority_id' %> | |
11 | <p><%= f.select :priority_id, (@priorities.collect {|p| [p.name, p.id]}), {:required => true}, :disabled => !@issue.leaf? %></p> |
|
11 | <p><%= f.select :priority_id, (@priorities.collect {|p| [p.name, p.id]}), {:required => true}, :disabled => !@issue.leaf? %></p> | |
12 | <% end %> |
|
12 | <% end %> | |
13 |
|
13 | |||
14 | <% if @issue.safe_attribute? 'assigned_to_id' %> |
|
14 | <% if @issue.safe_attribute? 'assigned_to_id' %> | |
15 | <p><%= f.select :assigned_to_id, principals_options_for_select(@issue.assignable_users, @issue.assigned_to), :include_blank => true %></p> |
|
15 | <p><%= f.select :assigned_to_id, principals_options_for_select(@issue.assignable_users, @issue.assigned_to), :include_blank => true %></p> | |
16 | <% end %> |
|
16 | <% end %> | |
17 |
|
17 | |||
18 | <% if @issue.safe_attribute?('category_id') && @issue.project.issue_categories.any? %> |
|
18 | <% if @issue.safe_attribute?('category_id') && @issue.project.issue_categories.any? %> | |
19 | <p><%= f.select :category_id, (@issue.project.issue_categories.collect {|c| [c.name, c.id]}), :include_blank => true %> |
|
19 | <p><%= f.select :category_id, (@issue.project.issue_categories.collect {|c| [c.name, c.id]}), :include_blank => true %> | |
20 | <%= prompt_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'), |
|
20 | <%= prompt_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'), | |
21 | l(:label_issue_category_new), |
|
21 | l(:label_issue_category_new), | |
22 | 'issue_category[name]', |
|
22 | 'issue_category[name]', | |
23 | {:controller => 'issue_categories', :action => 'create', :project_id => @issue.project}, |
|
23 | {:controller => 'issue_categories', :action => 'create', :project_id => @issue.project}, | |
24 | :title => l(:label_issue_category_new), |
|
24 | :title => l(:label_issue_category_new), | |
25 | :tabindex => 199) if User.current.allowed_to?(:manage_categories, @issue.project) %></p> |
|
25 | :tabindex => 199) if User.current.allowed_to?(:manage_categories, @issue.project) %></p> | |
26 | <% end %> |
|
26 | <% end %> | |
27 |
|
27 | |||
28 | <% if @issue.safe_attribute?('fixed_version_id') && @issue.assignable_versions.any? %> |
|
28 | <% if @issue.safe_attribute?('fixed_version_id') && @issue.assignable_versions.any? %> | |
29 | <p><%= f.select :fixed_version_id, version_options_for_select(@issue.assignable_versions, @issue.fixed_version), :include_blank => true %> |
|
29 | <p><%= f.select :fixed_version_id, version_options_for_select(@issue.assignable_versions, @issue.fixed_version), :include_blank => true %> | |
30 |
<%= |
|
30 | <%= link_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'), | |
31 | l(:label_version_new), |
|
31 | {:url => new_project_version_path(@issue.project), :method => 'get'}, | |
32 |
|
|
32 | :title => l(:label_version_new), | |
33 | {:controller => 'versions', :action => 'create', :project_id => @issue.project}, |
|
33 | :tabindex => 200) if User.current.allowed_to?(:manage_versions, @issue.project) %> | |
34 | :title => l(:label_version_new), |
|
|||
35 | :tabindex => 200) if User.current.allowed_to?(:manage_versions, @issue.project) %> |
|
|||
36 | </p> |
|
34 | </p> | |
37 | <% end %> |
|
35 | <% end %> | |
38 | </div> |
|
36 | </div> | |
39 |
|
37 | |||
40 | <div class="splitcontentright"> |
|
38 | <div class="splitcontentright"> | |
41 | <% if @issue.safe_attribute? 'parent_issue_id' %> |
|
39 | <% if @issue.safe_attribute? 'parent_issue_id' %> | |
42 | <p id="parent_issue"><%= f.text_field :parent_issue_id, :size => 10 %></p> |
|
40 | <p id="parent_issue"><%= f.text_field :parent_issue_id, :size => 10 %></p> | |
43 | <div id="parent_issue_candidates" class="autocomplete"></div> |
|
41 | <div id="parent_issue_candidates" class="autocomplete"></div> | |
44 | <%= javascript_tag "observeParentIssueField('#{auto_complete_issues_path(:id => @issue, :project_id => @issue.project) }')" %> |
|
42 | <%= javascript_tag "observeParentIssueField('#{auto_complete_issues_path(:id => @issue, :project_id => @issue.project) }')" %> | |
45 | <% end %> |
|
43 | <% end %> | |
46 |
|
44 | |||
47 | <% if @issue.safe_attribute? 'start_date' %> |
|
45 | <% if @issue.safe_attribute? 'start_date' %> | |
48 | <p><%= f.text_field :start_date, :size => 10, :disabled => !@issue.leaf? %><%= calendar_for('issue_start_date') if @issue.leaf? %></p> |
|
46 | <p><%= f.text_field :start_date, :size => 10, :disabled => !@issue.leaf? %><%= calendar_for('issue_start_date') if @issue.leaf? %></p> | |
49 | <% end %> |
|
47 | <% end %> | |
50 |
|
48 | |||
51 | <% if @issue.safe_attribute? 'due_date' %> |
|
49 | <% if @issue.safe_attribute? 'due_date' %> | |
52 | <p><%= f.text_field :due_date, :size => 10, :disabled => !@issue.leaf? %><%= calendar_for('issue_due_date') if @issue.leaf? %></p> |
|
50 | <p><%= f.text_field :due_date, :size => 10, :disabled => !@issue.leaf? %><%= calendar_for('issue_due_date') if @issue.leaf? %></p> | |
53 | <% end %> |
|
51 | <% end %> | |
54 |
|
52 | |||
55 | <% if @issue.safe_attribute? 'estimated_hours' %> |
|
53 | <% if @issue.safe_attribute? 'estimated_hours' %> | |
56 | <p><%= f.text_field :estimated_hours, :size => 3, :disabled => !@issue.leaf? %> <%= l(:field_hours) %></p> |
|
54 | <p><%= f.text_field :estimated_hours, :size => 3, :disabled => !@issue.leaf? %> <%= l(:field_hours) %></p> | |
57 | <% end %> |
|
55 | <% end %> | |
58 |
|
56 | |||
59 | <% if @issue.safe_attribute?('done_ratio') && @issue.leaf? && Issue.use_field_for_done_ratio? %> |
|
57 | <% if @issue.safe_attribute?('done_ratio') && @issue.leaf? && Issue.use_field_for_done_ratio? %> | |
60 | <p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p> |
|
58 | <p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p> | |
61 | <% end %> |
|
59 | <% end %> | |
62 | </div> |
|
60 | </div> | |
63 |
|
61 | |||
64 | <div style="clear:both;"> </div> |
|
62 | <div style="clear:both;"> </div> | |
65 | <% if @issue.safe_attribute? 'custom_field_values' %> |
|
63 | <% if @issue.safe_attribute? 'custom_field_values' %> | |
66 | <%= render :partial => 'issues/form_custom_fields' %> |
|
64 | <%= render :partial => 'issues/form_custom_fields' %> | |
67 | <% end %> |
|
65 | <% end %> | |
68 |
|
66 | |||
69 | <% end %> |
|
67 | <% end %> |
@@ -1,517 +1,522 | |||||
1 | /* Redmine - project management software |
|
1 | /* Redmine - project management software | |
2 | Copyright (C) 2006-2012 Jean-Philippe Lang */ |
|
2 | Copyright (C) 2006-2012 Jean-Philippe Lang */ | |
3 |
|
3 | |||
4 | function checkAll (id, checked) { |
|
4 | function checkAll (id, checked) { | |
5 | var els = Element.descendants(id); |
|
5 | var els = Element.descendants(id); | |
6 | for (var i = 0; i < els.length; i++) { |
|
6 | for (var i = 0; i < els.length; i++) { | |
7 | if (els[i].disabled==false) { |
|
7 | if (els[i].disabled==false) { | |
8 | els[i].checked = checked; |
|
8 | els[i].checked = checked; | |
9 | } |
|
9 | } | |
10 | } |
|
10 | } | |
11 | } |
|
11 | } | |
12 |
|
12 | |||
13 | function toggleCheckboxesBySelector(selector) { |
|
13 | function toggleCheckboxesBySelector(selector) { | |
14 | boxes = $$(selector); |
|
14 | boxes = $$(selector); | |
15 | var all_checked = true; |
|
15 | var all_checked = true; | |
16 | for (i = 0; i < boxes.length; i++) { if (boxes[i].checked == false) { all_checked = false; } } |
|
16 | for (i = 0; i < boxes.length; i++) { if (boxes[i].checked == false) { all_checked = false; } } | |
17 | for (i = 0; i < boxes.length; i++) { boxes[i].checked = !all_checked; } |
|
17 | for (i = 0; i < boxes.length; i++) { boxes[i].checked = !all_checked; } | |
18 | } |
|
18 | } | |
19 |
|
19 | |||
20 | function setCheckboxesBySelector(checked, selector) { |
|
20 | function setCheckboxesBySelector(checked, selector) { | |
21 | var boxes = $$(selector); |
|
21 | var boxes = $$(selector); | |
22 | boxes.each(function(ele) { |
|
22 | boxes.each(function(ele) { | |
23 | ele.checked = checked; |
|
23 | ele.checked = checked; | |
24 | }); |
|
24 | }); | |
25 | } |
|
25 | } | |
26 |
|
26 | |||
27 | function showAndScrollTo(id, focus) { |
|
27 | function showAndScrollTo(id, focus) { | |
28 | Element.show(id); |
|
28 | Element.show(id); | |
29 | if (focus!=null) { Form.Element.focus(focus); } |
|
29 | if (focus!=null) { Form.Element.focus(focus); } | |
30 | Element.scrollTo(id); |
|
30 | Element.scrollTo(id); | |
31 | } |
|
31 | } | |
32 |
|
32 | |||
33 | function toggleRowGroup(el) { |
|
33 | function toggleRowGroup(el) { | |
34 | var tr = Element.up(el, 'tr'); |
|
34 | var tr = Element.up(el, 'tr'); | |
35 | var n = Element.next(tr); |
|
35 | var n = Element.next(tr); | |
36 | tr.toggleClassName('open'); |
|
36 | tr.toggleClassName('open'); | |
37 | while (n != undefined && !n.hasClassName('group')) { |
|
37 | while (n != undefined && !n.hasClassName('group')) { | |
38 | Element.toggle(n); |
|
38 | Element.toggle(n); | |
39 | n = Element.next(n); |
|
39 | n = Element.next(n); | |
40 | } |
|
40 | } | |
41 | } |
|
41 | } | |
42 |
|
42 | |||
43 | function collapseAllRowGroups(el) { |
|
43 | function collapseAllRowGroups(el) { | |
44 | var tbody = Element.up(el, 'tbody'); |
|
44 | var tbody = Element.up(el, 'tbody'); | |
45 | tbody.childElements('tr').each(function(tr) { |
|
45 | tbody.childElements('tr').each(function(tr) { | |
46 | if (tr.hasClassName('group')) { |
|
46 | if (tr.hasClassName('group')) { | |
47 | tr.removeClassName('open'); |
|
47 | tr.removeClassName('open'); | |
48 | } else { |
|
48 | } else { | |
49 | tr.hide(); |
|
49 | tr.hide(); | |
50 | } |
|
50 | } | |
51 | }) |
|
51 | }) | |
52 | } |
|
52 | } | |
53 |
|
53 | |||
54 | function expandAllRowGroups(el) { |
|
54 | function expandAllRowGroups(el) { | |
55 | var tbody = Element.up(el, 'tbody'); |
|
55 | var tbody = Element.up(el, 'tbody'); | |
56 | tbody.childElements('tr').each(function(tr) { |
|
56 | tbody.childElements('tr').each(function(tr) { | |
57 | if (tr.hasClassName('group')) { |
|
57 | if (tr.hasClassName('group')) { | |
58 | tr.addClassName('open'); |
|
58 | tr.addClassName('open'); | |
59 | } else { |
|
59 | } else { | |
60 | tr.show(); |
|
60 | tr.show(); | |
61 | } |
|
61 | } | |
62 | }) |
|
62 | }) | |
63 | } |
|
63 | } | |
64 |
|
64 | |||
65 | function toggleAllRowGroups(el) { |
|
65 | function toggleAllRowGroups(el) { | |
66 | var tr = Element.up(el, 'tr'); |
|
66 | var tr = Element.up(el, 'tr'); | |
67 | if (tr.hasClassName('open')) { |
|
67 | if (tr.hasClassName('open')) { | |
68 | collapseAllRowGroups(el); |
|
68 | collapseAllRowGroups(el); | |
69 | } else { |
|
69 | } else { | |
70 | expandAllRowGroups(el); |
|
70 | expandAllRowGroups(el); | |
71 | } |
|
71 | } | |
72 | } |
|
72 | } | |
73 |
|
73 | |||
74 | function toggleFieldset(el) { |
|
74 | function toggleFieldset(el) { | |
75 | var fieldset = Element.up(el, 'fieldset'); |
|
75 | var fieldset = Element.up(el, 'fieldset'); | |
76 | fieldset.toggleClassName('collapsed'); |
|
76 | fieldset.toggleClassName('collapsed'); | |
77 | Effect.toggle(fieldset.down('div'), 'slide', {duration:0.2}); |
|
77 | Effect.toggle(fieldset.down('div'), 'slide', {duration:0.2}); | |
78 | } |
|
78 | } | |
79 |
|
79 | |||
80 | function hideFieldset(el) { |
|
80 | function hideFieldset(el) { | |
81 | var fieldset = Element.up(el, 'fieldset'); |
|
81 | var fieldset = Element.up(el, 'fieldset'); | |
82 | fieldset.toggleClassName('collapsed'); |
|
82 | fieldset.toggleClassName('collapsed'); | |
83 | fieldset.down('div').hide(); |
|
83 | fieldset.down('div').hide(); | |
84 | } |
|
84 | } | |
85 |
|
85 | |||
86 | function add_filter() { |
|
86 | function add_filter() { | |
87 | select = $('add_filter_select'); |
|
87 | select = $('add_filter_select'); | |
88 | field = select.value |
|
88 | field = select.value | |
89 | Element.show('tr_' + field); |
|
89 | Element.show('tr_' + field); | |
90 | check_box = $('cb_' + field); |
|
90 | check_box = $('cb_' + field); | |
91 | check_box.checked = true; |
|
91 | check_box.checked = true; | |
92 | toggle_filter(field); |
|
92 | toggle_filter(field); | |
93 | select.selectedIndex = 0; |
|
93 | select.selectedIndex = 0; | |
94 |
|
94 | |||
95 | for (i=0; i<select.options.length; i++) { |
|
95 | for (i=0; i<select.options.length; i++) { | |
96 | if (select.options[i].value == field) { |
|
96 | if (select.options[i].value == field) { | |
97 | select.options[i].disabled = true; |
|
97 | select.options[i].disabled = true; | |
98 | } |
|
98 | } | |
99 | } |
|
99 | } | |
100 | } |
|
100 | } | |
101 |
|
101 | |||
102 | function toggle_filter(field) { |
|
102 | function toggle_filter(field) { | |
103 | check_box = $('cb_' + field); |
|
103 | check_box = $('cb_' + field); | |
104 | if (check_box.checked) { |
|
104 | if (check_box.checked) { | |
105 | Element.show("operators_" + field); |
|
105 | Element.show("operators_" + field); | |
106 | Form.Element.enable("operators_" + field); |
|
106 | Form.Element.enable("operators_" + field); | |
107 | toggle_operator(field); |
|
107 | toggle_operator(field); | |
108 | } else { |
|
108 | } else { | |
109 | Element.hide("operators_" + field); |
|
109 | Element.hide("operators_" + field); | |
110 | Form.Element.disable("operators_" + field); |
|
110 | Form.Element.disable("operators_" + field); | |
111 | enableValues(field, []); |
|
111 | enableValues(field, []); | |
112 | } |
|
112 | } | |
113 | } |
|
113 | } | |
114 |
|
114 | |||
115 | function enableValues(field, indexes) { |
|
115 | function enableValues(field, indexes) { | |
116 | var f = $$(".values_" + field); |
|
116 | var f = $$(".values_" + field); | |
117 | for(var i=0;i<f.length;i++) { |
|
117 | for(var i=0;i<f.length;i++) { | |
118 | if (indexes.include(i)) { |
|
118 | if (indexes.include(i)) { | |
119 | Form.Element.enable(f[i]); |
|
119 | Form.Element.enable(f[i]); | |
120 | f[i].up('span').show(); |
|
120 | f[i].up('span').show(); | |
121 | } else { |
|
121 | } else { | |
122 | f[i].value = ''; |
|
122 | f[i].value = ''; | |
123 | Form.Element.disable(f[i]); |
|
123 | Form.Element.disable(f[i]); | |
124 | f[i].up('span').hide(); |
|
124 | f[i].up('span').hide(); | |
125 | } |
|
125 | } | |
126 | } |
|
126 | } | |
127 | if (indexes.length > 0) { |
|
127 | if (indexes.length > 0) { | |
128 | Element.show("div_values_" + field); |
|
128 | Element.show("div_values_" + field); | |
129 | } else { |
|
129 | } else { | |
130 | Element.hide("div_values_" + field); |
|
130 | Element.hide("div_values_" + field); | |
131 | } |
|
131 | } | |
132 | } |
|
132 | } | |
133 |
|
133 | |||
134 | function toggle_operator(field) { |
|
134 | function toggle_operator(field) { | |
135 | operator = $("operators_" + field); |
|
135 | operator = $("operators_" + field); | |
136 | switch (operator.value) { |
|
136 | switch (operator.value) { | |
137 | case "!*": |
|
137 | case "!*": | |
138 | case "*": |
|
138 | case "*": | |
139 | case "t": |
|
139 | case "t": | |
140 | case "w": |
|
140 | case "w": | |
141 | case "o": |
|
141 | case "o": | |
142 | case "c": |
|
142 | case "c": | |
143 | enableValues(field, []); |
|
143 | enableValues(field, []); | |
144 | break; |
|
144 | break; | |
145 | case "><": |
|
145 | case "><": | |
146 | enableValues(field, [0,1]); |
|
146 | enableValues(field, [0,1]); | |
147 | break; |
|
147 | break; | |
148 | case "<t+": |
|
148 | case "<t+": | |
149 | case ">t+": |
|
149 | case ">t+": | |
150 | case "t+": |
|
150 | case "t+": | |
151 | case ">t-": |
|
151 | case ">t-": | |
152 | case "<t-": |
|
152 | case "<t-": | |
153 | case "t-": |
|
153 | case "t-": | |
154 | enableValues(field, [2]); |
|
154 | enableValues(field, [2]); | |
155 | break; |
|
155 | break; | |
156 | default: |
|
156 | default: | |
157 | enableValues(field, [0]); |
|
157 | enableValues(field, [0]); | |
158 | break; |
|
158 | break; | |
159 | } |
|
159 | } | |
160 | } |
|
160 | } | |
161 |
|
161 | |||
162 | function toggle_multi_select(el) { |
|
162 | function toggle_multi_select(el) { | |
163 | var select = $(el); |
|
163 | var select = $(el); | |
164 | if (select.multiple == true) { |
|
164 | if (select.multiple == true) { | |
165 | select.multiple = false; |
|
165 | select.multiple = false; | |
166 | } else { |
|
166 | } else { | |
167 | select.multiple = true; |
|
167 | select.multiple = true; | |
168 | } |
|
168 | } | |
169 | } |
|
169 | } | |
170 |
|
170 | |||
171 | function submit_query_form(id) { |
|
171 | function submit_query_form(id) { | |
172 | selectAllOptions("selected_columns"); |
|
172 | selectAllOptions("selected_columns"); | |
173 | $(id).submit(); |
|
173 | $(id).submit(); | |
174 | } |
|
174 | } | |
175 |
|
175 | |||
176 | function apply_filters_observer() { |
|
176 | function apply_filters_observer() { | |
177 | $$("#query_form input[type=text]").invoke("observe", "keypress", function(e){ |
|
177 | $$("#query_form input[type=text]").invoke("observe", "keypress", function(e){ | |
178 | if(e.keyCode == Event.KEY_RETURN) { |
|
178 | if(e.keyCode == Event.KEY_RETURN) { | |
179 | submit_query_form("query_form"); |
|
179 | submit_query_form("query_form"); | |
180 | } |
|
180 | } | |
181 | }); |
|
181 | }); | |
182 | } |
|
182 | } | |
183 |
|
183 | |||
184 | var fileFieldCount = 1; |
|
184 | var fileFieldCount = 1; | |
185 |
|
185 | |||
186 | function addFileField() { |
|
186 | function addFileField() { | |
187 | var fields = $('attachments_fields'); |
|
187 | var fields = $('attachments_fields'); | |
188 | if (fields.childElements().length >= 10) return false; |
|
188 | if (fields.childElements().length >= 10) return false; | |
189 | fileFieldCount++; |
|
189 | fileFieldCount++; | |
190 | var s = new Element('span'); |
|
190 | var s = new Element('span'); | |
191 | s.update(fields.down('span').innerHTML); |
|
191 | s.update(fields.down('span').innerHTML); | |
192 | s.down('input.file').name = "attachments[" + fileFieldCount + "][file]"; |
|
192 | s.down('input.file').name = "attachments[" + fileFieldCount + "][file]"; | |
193 | s.down('input.description').name = "attachments[" + fileFieldCount + "][description]"; |
|
193 | s.down('input.description').name = "attachments[" + fileFieldCount + "][description]"; | |
194 | fields.appendChild(s); |
|
194 | fields.appendChild(s); | |
195 | } |
|
195 | } | |
196 |
|
196 | |||
197 | function removeFileField(el) { |
|
197 | function removeFileField(el) { | |
198 | var fields = $('attachments_fields'); |
|
198 | var fields = $('attachments_fields'); | |
199 | var s = Element.up(el, 'span'); |
|
199 | var s = Element.up(el, 'span'); | |
200 | if (fields.childElements().length > 1) { |
|
200 | if (fields.childElements().length > 1) { | |
201 | s.remove(); |
|
201 | s.remove(); | |
202 | } else { |
|
202 | } else { | |
203 | s.update(s.innerHTML); |
|
203 | s.update(s.innerHTML); | |
204 | } |
|
204 | } | |
205 | } |
|
205 | } | |
206 |
|
206 | |||
207 | function checkFileSize(el, maxSize, message) { |
|
207 | function checkFileSize(el, maxSize, message) { | |
208 | var files = el.files; |
|
208 | var files = el.files; | |
209 | if (files) { |
|
209 | if (files) { | |
210 | for (var i=0; i<files.length; i++) { |
|
210 | for (var i=0; i<files.length; i++) { | |
211 | if (files[i].size > maxSize) { |
|
211 | if (files[i].size > maxSize) { | |
212 | alert(message); |
|
212 | alert(message); | |
213 | el.value = ""; |
|
213 | el.value = ""; | |
214 | } |
|
214 | } | |
215 | } |
|
215 | } | |
216 | } |
|
216 | } | |
217 | } |
|
217 | } | |
218 |
|
218 | |||
219 | function showTab(name) { |
|
219 | function showTab(name) { | |
220 | var f = $$('div#content .tab-content'); |
|
220 | var f = $$('div#content .tab-content'); | |
221 | for(var i=0; i<f.length; i++){ |
|
221 | for(var i=0; i<f.length; i++){ | |
222 | Element.hide(f[i]); |
|
222 | Element.hide(f[i]); | |
223 | } |
|
223 | } | |
224 | var f = $$('div.tabs a'); |
|
224 | var f = $$('div.tabs a'); | |
225 | for(var i=0; i<f.length; i++){ |
|
225 | for(var i=0; i<f.length; i++){ | |
226 | Element.removeClassName(f[i], "selected"); |
|
226 | Element.removeClassName(f[i], "selected"); | |
227 | } |
|
227 | } | |
228 | Element.show('tab-content-' + name); |
|
228 | Element.show('tab-content-' + name); | |
229 | Element.addClassName('tab-' + name, "selected"); |
|
229 | Element.addClassName('tab-' + name, "selected"); | |
230 | return false; |
|
230 | return false; | |
231 | } |
|
231 | } | |
232 |
|
232 | |||
233 | function moveTabRight(el) { |
|
233 | function moveTabRight(el) { | |
234 | var lis = Element.up(el, 'div.tabs').down('ul').childElements(); |
|
234 | var lis = Element.up(el, 'div.tabs').down('ul').childElements(); | |
235 | var tabsWidth = 0; |
|
235 | var tabsWidth = 0; | |
236 | var i; |
|
236 | var i; | |
237 | for (i=0; i<lis.length; i++) { |
|
237 | for (i=0; i<lis.length; i++) { | |
238 | if (lis[i].visible()) { |
|
238 | if (lis[i].visible()) { | |
239 | tabsWidth += lis[i].getWidth() + 6; |
|
239 | tabsWidth += lis[i].getWidth() + 6; | |
240 | } |
|
240 | } | |
241 | } |
|
241 | } | |
242 | if (tabsWidth < Element.up(el, 'div.tabs').getWidth() - 60) { |
|
242 | if (tabsWidth < Element.up(el, 'div.tabs').getWidth() - 60) { | |
243 | return; |
|
243 | return; | |
244 | } |
|
244 | } | |
245 | i=0; |
|
245 | i=0; | |
246 | while (i<lis.length && !lis[i].visible()) { |
|
246 | while (i<lis.length && !lis[i].visible()) { | |
247 | i++; |
|
247 | i++; | |
248 | } |
|
248 | } | |
249 | lis[i].hide(); |
|
249 | lis[i].hide(); | |
250 | } |
|
250 | } | |
251 |
|
251 | |||
252 | function moveTabLeft(el) { |
|
252 | function moveTabLeft(el) { | |
253 | var lis = Element.up(el, 'div.tabs').down('ul').childElements(); |
|
253 | var lis = Element.up(el, 'div.tabs').down('ul').childElements(); | |
254 | var i = 0; |
|
254 | var i = 0; | |
255 | while (i<lis.length && !lis[i].visible()) { |
|
255 | while (i<lis.length && !lis[i].visible()) { | |
256 | i++; |
|
256 | i++; | |
257 | } |
|
257 | } | |
258 | if (i>0) { |
|
258 | if (i>0) { | |
259 | lis[i-1].show(); |
|
259 | lis[i-1].show(); | |
260 | } |
|
260 | } | |
261 | } |
|
261 | } | |
262 |
|
262 | |||
263 | function displayTabsButtons() { |
|
263 | function displayTabsButtons() { | |
264 | var lis; |
|
264 | var lis; | |
265 | var tabsWidth = 0; |
|
265 | var tabsWidth = 0; | |
266 | var i; |
|
266 | var i; | |
267 | $$('div.tabs').each(function(el) { |
|
267 | $$('div.tabs').each(function(el) { | |
268 | lis = el.down('ul').childElements(); |
|
268 | lis = el.down('ul').childElements(); | |
269 | for (i=0; i<lis.length; i++) { |
|
269 | for (i=0; i<lis.length; i++) { | |
270 | if (lis[i].visible()) { |
|
270 | if (lis[i].visible()) { | |
271 | tabsWidth += lis[i].getWidth() + 6; |
|
271 | tabsWidth += lis[i].getWidth() + 6; | |
272 | } |
|
272 | } | |
273 | } |
|
273 | } | |
274 | if ((tabsWidth < el.getWidth() - 60) && (lis[0].visible())) { |
|
274 | if ((tabsWidth < el.getWidth() - 60) && (lis[0].visible())) { | |
275 | el.down('div.tabs-buttons').hide(); |
|
275 | el.down('div.tabs-buttons').hide(); | |
276 | } else { |
|
276 | } else { | |
277 | el.down('div.tabs-buttons').show(); |
|
277 | el.down('div.tabs-buttons').show(); | |
278 | } |
|
278 | } | |
279 | }); |
|
279 | }); | |
280 | } |
|
280 | } | |
281 |
|
281 | |||
282 | function setPredecessorFieldsVisibility() { |
|
282 | function setPredecessorFieldsVisibility() { | |
283 | relationType = $('relation_relation_type'); |
|
283 | relationType = $('relation_relation_type'); | |
284 | if (relationType && (relationType.value == "precedes" || relationType.value == "follows")) { |
|
284 | if (relationType && (relationType.value == "precedes" || relationType.value == "follows")) { | |
285 | Element.show('predecessor_fields'); |
|
285 | Element.show('predecessor_fields'); | |
286 | } else { |
|
286 | } else { | |
287 | Element.hide('predecessor_fields'); |
|
287 | Element.hide('predecessor_fields'); | |
288 | } |
|
288 | } | |
289 | } |
|
289 | } | |
290 |
|
290 | |||
291 | function promptToRemote(text, param, url) { |
|
291 | function promptToRemote(text, param, url) { | |
292 | value = prompt(text + ':'); |
|
292 | value = prompt(text + ':'); | |
293 | if (value) { |
|
293 | if (value) { | |
294 | new Ajax.Request(url + '?' + param + '=' + encodeURIComponent(value), {asynchronous:true, evalScripts:true}); |
|
294 | new Ajax.Request(url + '?' + param + '=' + encodeURIComponent(value), {asynchronous:true, evalScripts:true}); | |
295 | return false; |
|
295 | return false; | |
296 | } |
|
296 | } | |
297 | } |
|
297 | } | |
298 |
|
298 | |||
299 | function showModal(id, width) { |
|
299 | function showModal(id, width) { | |
300 | el = $(id); |
|
300 | el = $(id); | |
301 | if (el == undefined || el.visible()) {return;} |
|
301 | if (el == undefined || el.visible()) {return;} | |
302 | var h = $$('body')[0].getHeight(); |
|
302 | var h = $$('body')[0].getHeight(); | |
303 | var d = document.createElement("div"); |
|
303 | var d = document.createElement("div"); | |
304 | d.id = 'modalbg'; |
|
304 | d.id = 'modalbg'; | |
305 | $('main').appendChild(d); |
|
305 | $('main').appendChild(d); | |
306 | $('modalbg').setStyle({ width: '100%', height: h + 'px' }); |
|
306 | $('modalbg').setStyle({ width: '100%', height: h + 'px' }); | |
307 | $('modalbg').show(); |
|
307 | $('modalbg').show(); | |
308 |
|
308 | |||
309 | var pageWidth = document.viewport.getWidth(); |
|
309 | var pageWidth = document.viewport.getWidth(); | |
310 | if (width) { |
|
310 | if (width) { | |
311 | el.setStyle({'width': width}); |
|
311 | el.setStyle({'width': width}); | |
312 | } |
|
312 | } | |
313 | el.setStyle({'left': (((pageWidth - el.getWidth())/2 *100) / pageWidth) + '%'}); |
|
313 | el.setStyle({'left': (((pageWidth - el.getWidth())/2 *100) / pageWidth) + '%'}); | |
314 | el.addClassName('modal'); |
|
314 | el.addClassName('modal'); | |
315 | el.show(); |
|
315 | el.show(); | |
316 |
|
316 | |||
317 | var submit = el.down("input[type=submit]"); |
|
317 | var submit = el.down("input[type=submit]"); | |
318 | if (submit) { |
|
318 | if (submit) { | |
319 | submit.focus(); |
|
319 | submit.focus(); | |
320 | } |
|
320 | } | |
321 | } |
|
321 | } | |
322 |
|
322 | |||
323 | function hideModal(el) { |
|
323 | function hideModal(el) { | |
324 | var modal = Element.up(el, 'div.modal'); |
|
324 | var modal; | |
|
325 | if (el) { | |||
|
326 | modal = Element.up(el, 'div.modal'); | |||
|
327 | } else { | |||
|
328 | modal = $('ajax-modal'); | |||
|
329 | } | |||
325 | if (modal) { |
|
330 | if (modal) { | |
326 | modal.hide(); |
|
331 | modal.hide(); | |
327 | } |
|
332 | } | |
328 | var bg = $('modalbg'); |
|
333 | var bg = $('modalbg'); | |
329 | if (bg) { |
|
334 | if (bg) { | |
330 | bg.remove(); |
|
335 | bg.remove(); | |
331 | } |
|
336 | } | |
332 | } |
|
337 | } | |
333 |
|
338 | |||
334 | function collapseScmEntry(id) { |
|
339 | function collapseScmEntry(id) { | |
335 | var els = document.getElementsByClassName(id, 'browser'); |
|
340 | var els = document.getElementsByClassName(id, 'browser'); | |
336 | for (var i = 0; i < els.length; i++) { |
|
341 | for (var i = 0; i < els.length; i++) { | |
337 | if (els[i].hasClassName('open')) { |
|
342 | if (els[i].hasClassName('open')) { | |
338 | collapseScmEntry(els[i].id); |
|
343 | collapseScmEntry(els[i].id); | |
339 | } |
|
344 | } | |
340 | Element.hide(els[i]); |
|
345 | Element.hide(els[i]); | |
341 | } |
|
346 | } | |
342 | $(id).removeClassName('open'); |
|
347 | $(id).removeClassName('open'); | |
343 | } |
|
348 | } | |
344 |
|
349 | |||
345 | function expandScmEntry(id) { |
|
350 | function expandScmEntry(id) { | |
346 | var els = document.getElementsByClassName(id, 'browser'); |
|
351 | var els = document.getElementsByClassName(id, 'browser'); | |
347 | for (var i = 0; i < els.length; i++) { |
|
352 | for (var i = 0; i < els.length; i++) { | |
348 | Element.show(els[i]); |
|
353 | Element.show(els[i]); | |
349 | if (els[i].hasClassName('loaded') && !els[i].hasClassName('collapsed')) { |
|
354 | if (els[i].hasClassName('loaded') && !els[i].hasClassName('collapsed')) { | |
350 | expandScmEntry(els[i].id); |
|
355 | expandScmEntry(els[i].id); | |
351 | } |
|
356 | } | |
352 | } |
|
357 | } | |
353 | $(id).addClassName('open'); |
|
358 | $(id).addClassName('open'); | |
354 | } |
|
359 | } | |
355 |
|
360 | |||
356 | function scmEntryClick(id) { |
|
361 | function scmEntryClick(id) { | |
357 | el = $(id); |
|
362 | el = $(id); | |
358 | if (el.hasClassName('open')) { |
|
363 | if (el.hasClassName('open')) { | |
359 | collapseScmEntry(id); |
|
364 | collapseScmEntry(id); | |
360 | el.addClassName('collapsed'); |
|
365 | el.addClassName('collapsed'); | |
361 | return false; |
|
366 | return false; | |
362 | } else if (el.hasClassName('loaded')) { |
|
367 | } else if (el.hasClassName('loaded')) { | |
363 | expandScmEntry(id); |
|
368 | expandScmEntry(id); | |
364 | el.removeClassName('collapsed'); |
|
369 | el.removeClassName('collapsed'); | |
365 | return false; |
|
370 | return false; | |
366 | } |
|
371 | } | |
367 | if (el.hasClassName('loading')) { |
|
372 | if (el.hasClassName('loading')) { | |
368 | return false; |
|
373 | return false; | |
369 | } |
|
374 | } | |
370 | el.addClassName('loading'); |
|
375 | el.addClassName('loading'); | |
371 | return true; |
|
376 | return true; | |
372 | } |
|
377 | } | |
373 |
|
378 | |||
374 | function scmEntryLoaded(id) { |
|
379 | function scmEntryLoaded(id) { | |
375 | Element.addClassName(id, 'open'); |
|
380 | Element.addClassName(id, 'open'); | |
376 | Element.addClassName(id, 'loaded'); |
|
381 | Element.addClassName(id, 'loaded'); | |
377 | Element.removeClassName(id, 'loading'); |
|
382 | Element.removeClassName(id, 'loading'); | |
378 | } |
|
383 | } | |
379 |
|
384 | |||
380 | function randomKey(size) { |
|
385 | function randomKey(size) { | |
381 | var chars = new Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'); |
|
386 | var chars = new Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'); | |
382 | var key = ''; |
|
387 | var key = ''; | |
383 | for (i = 0; i < size; i++) { |
|
388 | for (i = 0; i < size; i++) { | |
384 | key += chars[Math.floor(Math.random() * chars.length)]; |
|
389 | key += chars[Math.floor(Math.random() * chars.length)]; | |
385 | } |
|
390 | } | |
386 | return key; |
|
391 | return key; | |
387 | } |
|
392 | } | |
388 |
|
393 | |||
389 | function observeParentIssueField(url) { |
|
394 | function observeParentIssueField(url) { | |
390 | new Ajax.Autocompleter('issue_parent_issue_id', |
|
395 | new Ajax.Autocompleter('issue_parent_issue_id', | |
391 | 'parent_issue_candidates', |
|
396 | 'parent_issue_candidates', | |
392 | url, |
|
397 | url, | |
393 | { minChars: 3, |
|
398 | { minChars: 3, | |
394 | frequency: 0.5, |
|
399 | frequency: 0.5, | |
395 | paramName: 'q', |
|
400 | paramName: 'q', | |
396 | method: 'get', |
|
401 | method: 'get', | |
397 | updateElement: function(value) { |
|
402 | updateElement: function(value) { | |
398 | document.getElementById('issue_parent_issue_id').value = value.id; |
|
403 | document.getElementById('issue_parent_issue_id').value = value.id; | |
399 | }}); |
|
404 | }}); | |
400 | } |
|
405 | } | |
401 |
|
406 | |||
402 | function observeRelatedIssueField(url) { |
|
407 | function observeRelatedIssueField(url) { | |
403 | new Ajax.Autocompleter('relation_issue_to_id', |
|
408 | new Ajax.Autocompleter('relation_issue_to_id', | |
404 | 'related_issue_candidates', |
|
409 | 'related_issue_candidates', | |
405 | url, |
|
410 | url, | |
406 | { minChars: 3, |
|
411 | { minChars: 3, | |
407 | frequency: 0.5, |
|
412 | frequency: 0.5, | |
408 | paramName: 'q', |
|
413 | paramName: 'q', | |
409 | method: 'get', |
|
414 | method: 'get', | |
410 | updateElement: function(value) { |
|
415 | updateElement: function(value) { | |
411 | document.getElementById('relation_issue_to_id').value = value.id; |
|
416 | document.getElementById('relation_issue_to_id').value = value.id; | |
412 | }, |
|
417 | }, | |
413 | parameters: 'scope=all' |
|
418 | parameters: 'scope=all' | |
414 | }); |
|
419 | }); | |
415 | } |
|
420 | } | |
416 |
|
421 | |||
417 | function setVisible(id, visible) { |
|
422 | function setVisible(id, visible) { | |
418 | var el = $(id); |
|
423 | var el = $(id); | |
419 | if (el) {if (visible) {el.show();} else {el.hide();}} |
|
424 | if (el) {if (visible) {el.show();} else {el.hide();}} | |
420 | } |
|
425 | } | |
421 |
|
426 | |||
422 | function observeProjectModules() { |
|
427 | function observeProjectModules() { | |
423 | var f = function() { |
|
428 | var f = function() { | |
424 | /* Hides trackers and issues custom fields on the new project form when issue_tracking module is disabled */ |
|
429 | /* Hides trackers and issues custom fields on the new project form when issue_tracking module is disabled */ | |
425 | var c = ($('project_enabled_module_names_issue_tracking').checked == true); |
|
430 | var c = ($('project_enabled_module_names_issue_tracking').checked == true); | |
426 | setVisible('project_trackers', c); |
|
431 | setVisible('project_trackers', c); | |
427 | setVisible('project_issue_custom_fields', c); |
|
432 | setVisible('project_issue_custom_fields', c); | |
428 | }; |
|
433 | }; | |
429 |
|
434 | |||
430 | Event.observe(window, 'load', f); |
|
435 | Event.observe(window, 'load', f); | |
431 | Event.observe('project_enabled_module_names_issue_tracking', 'change', f); |
|
436 | Event.observe('project_enabled_module_names_issue_tracking', 'change', f); | |
432 | } |
|
437 | } | |
433 |
|
438 | |||
434 | /* |
|
439 | /* | |
435 | * Class used to warn user when leaving a page with unsaved textarea |
|
440 | * Class used to warn user when leaving a page with unsaved textarea | |
436 | * Author: mathias.fischer@berlinonline.de |
|
441 | * Author: mathias.fischer@berlinonline.de | |
437 | */ |
|
442 | */ | |
438 |
|
443 | |||
439 | var WarnLeavingUnsaved = Class.create({ |
|
444 | var WarnLeavingUnsaved = Class.create({ | |
440 | observedForms: false, |
|
445 | observedForms: false, | |
441 | observedElements: false, |
|
446 | observedElements: false, | |
442 | changedForms: false, |
|
447 | changedForms: false, | |
443 | message: null, |
|
448 | message: null, | |
444 |
|
449 | |||
445 | initialize: function(message){ |
|
450 | initialize: function(message){ | |
446 | this.observedForms = $$('form'); |
|
451 | this.observedForms = $$('form'); | |
447 | this.observedElements = $$('textarea'); |
|
452 | this.observedElements = $$('textarea'); | |
448 | this.message = message; |
|
453 | this.message = message; | |
449 |
|
454 | |||
450 | this.observedElements.each(this.observeChange.bind(this)); |
|
455 | this.observedElements.each(this.observeChange.bind(this)); | |
451 | this.observedForms.each(this.submitAction.bind(this)); |
|
456 | this.observedForms.each(this.submitAction.bind(this)); | |
452 |
|
457 | |||
453 | window.onbeforeunload = this.unload.bind(this); |
|
458 | window.onbeforeunload = this.unload.bind(this); | |
454 | }, |
|
459 | }, | |
455 |
|
460 | |||
456 | unload: function(){ |
|
461 | unload: function(){ | |
457 | this.observedElements.each(function(el) {el.blur();}) |
|
462 | this.observedElements.each(function(el) {el.blur();}) | |
458 | if(this.changedForms) |
|
463 | if(this.changedForms) | |
459 | return this.message; |
|
464 | return this.message; | |
460 | }, |
|
465 | }, | |
461 |
|
466 | |||
462 | setChanged: function(){ |
|
467 | setChanged: function(){ | |
463 | this.changedForms = true; |
|
468 | this.changedForms = true; | |
464 | }, |
|
469 | }, | |
465 |
|
470 | |||
466 | setUnchanged: function(){ |
|
471 | setUnchanged: function(){ | |
467 | this.changedForms = false; |
|
472 | this.changedForms = false; | |
468 | }, |
|
473 | }, | |
469 |
|
474 | |||
470 | observeChange: function(element){ |
|
475 | observeChange: function(element){ | |
471 | element.observe('change',this.setChanged.bindAsEventListener(this)); |
|
476 | element.observe('change',this.setChanged.bindAsEventListener(this)); | |
472 | }, |
|
477 | }, | |
473 |
|
478 | |||
474 | submitAction: function(element){ |
|
479 | submitAction: function(element){ | |
475 | element.observe('submit',this.setUnchanged.bindAsEventListener(this)); |
|
480 | element.observe('submit',this.setUnchanged.bindAsEventListener(this)); | |
476 | } |
|
481 | } | |
477 | }); |
|
482 | }); | |
478 |
|
483 | |||
479 | /* |
|
484 | /* | |
480 | * 1 - registers a callback which copies the csrf token into the |
|
485 | * 1 - registers a callback which copies the csrf token into the | |
481 | * X-CSRF-Token header with each ajax request. Necessary to |
|
486 | * X-CSRF-Token header with each ajax request. Necessary to | |
482 | * work with rails applications which have fixed |
|
487 | * work with rails applications which have fixed | |
483 | * CVE-2011-0447 |
|
488 | * CVE-2011-0447 | |
484 | * 2 - shows and hides ajax indicator |
|
489 | * 2 - shows and hides ajax indicator | |
485 | */ |
|
490 | */ | |
486 | Ajax.Responders.register({ |
|
491 | Ajax.Responders.register({ | |
487 | onCreate: function(request){ |
|
492 | onCreate: function(request){ | |
488 | var csrf_meta_tag = $$('meta[name=csrf-token]')[0]; |
|
493 | var csrf_meta_tag = $$('meta[name=csrf-token]')[0]; | |
489 |
|
494 | |||
490 | if (csrf_meta_tag) { |
|
495 | if (csrf_meta_tag) { | |
491 | var header = 'X-CSRF-Token', |
|
496 | var header = 'X-CSRF-Token', | |
492 | token = csrf_meta_tag.readAttribute('content'); |
|
497 | token = csrf_meta_tag.readAttribute('content'); | |
493 |
|
498 | |||
494 | if (!request.options.requestHeaders) { |
|
499 | if (!request.options.requestHeaders) { | |
495 | request.options.requestHeaders = {}; |
|
500 | request.options.requestHeaders = {}; | |
496 | } |
|
501 | } | |
497 | request.options.requestHeaders[header] = token; |
|
502 | request.options.requestHeaders[header] = token; | |
498 | } |
|
503 | } | |
499 |
|
504 | |||
500 | if ($('ajax-indicator') && Ajax.activeRequestCount > 0) { |
|
505 | if ($('ajax-indicator') && Ajax.activeRequestCount > 0) { | |
501 | Element.show('ajax-indicator'); |
|
506 | Element.show('ajax-indicator'); | |
502 | } |
|
507 | } | |
503 | }, |
|
508 | }, | |
504 | onComplete: function(){ |
|
509 | onComplete: function(){ | |
505 | if ($('ajax-indicator') && Ajax.activeRequestCount == 0) { |
|
510 | if ($('ajax-indicator') && Ajax.activeRequestCount == 0) { | |
506 | Element.hide('ajax-indicator'); |
|
511 | Element.hide('ajax-indicator'); | |
507 | } |
|
512 | } | |
508 | } |
|
513 | } | |
509 | }); |
|
514 | }); | |
510 |
|
515 | |||
511 | function hideOnLoad() { |
|
516 | function hideOnLoad() { | |
512 | $$('.hol').each(function(el) { |
|
517 | $$('.hol').each(function(el) { | |
513 | el.hide(); |
|
518 | el.hide(); | |
514 | }); |
|
519 | }); | |
515 | } |
|
520 | } | |
516 |
|
521 | |||
517 | Event.observe(window, 'load', hideOnLoad); |
|
522 | Event.observe(window, 'load', hideOnLoad); |
@@ -1,187 +1,211 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2011 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2011 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 | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
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 |
|
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. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | require File.expand_path('../../test_helper', __FILE__) |
|
18 | require File.expand_path('../../test_helper', __FILE__) | |
19 | require 'versions_controller' |
|
19 | require 'versions_controller' | |
20 |
|
20 | |||
21 | # Re-raise errors caught by the controller. |
|
21 | # Re-raise errors caught by the controller. | |
22 | class VersionsController; def rescue_action(e) raise e end; end |
|
22 | class VersionsController; def rescue_action(e) raise e end; end | |
23 |
|
23 | |||
24 | class VersionsControllerTest < ActionController::TestCase |
|
24 | class VersionsControllerTest < ActionController::TestCase | |
25 | fixtures :projects, :versions, :issues, :users, :roles, :members, :member_roles, :enabled_modules, :issue_statuses |
|
25 | fixtures :projects, :versions, :issues, :users, :roles, :members, :member_roles, :enabled_modules, :issue_statuses | |
26 |
|
26 | |||
27 | def setup |
|
27 | def setup | |
28 | @controller = VersionsController.new |
|
28 | @controller = VersionsController.new | |
29 | @request = ActionController::TestRequest.new |
|
29 | @request = ActionController::TestRequest.new | |
30 | @response = ActionController::TestResponse.new |
|
30 | @response = ActionController::TestResponse.new | |
31 | User.current = nil |
|
31 | User.current = nil | |
32 | end |
|
32 | end | |
33 |
|
33 | |||
34 | def test_index |
|
34 | def test_index | |
35 | get :index, :project_id => 1 |
|
35 | get :index, :project_id => 1 | |
36 | assert_response :success |
|
36 | assert_response :success | |
37 | assert_template 'index' |
|
37 | assert_template 'index' | |
38 | assert_not_nil assigns(:versions) |
|
38 | assert_not_nil assigns(:versions) | |
39 | # Version with no date set appears |
|
39 | # Version with no date set appears | |
40 | assert assigns(:versions).include?(Version.find(3)) |
|
40 | assert assigns(:versions).include?(Version.find(3)) | |
41 | # Completed version doesn't appear |
|
41 | # Completed version doesn't appear | |
42 | assert !assigns(:versions).include?(Version.find(1)) |
|
42 | assert !assigns(:versions).include?(Version.find(1)) | |
43 | # Context menu on issues |
|
43 | # Context menu on issues | |
44 | assert_select "script", :text => Regexp.new(Regexp.escape("new ContextMenu('/issues/context_menu')")) |
|
44 | assert_select "script", :text => Regexp.new(Regexp.escape("new ContextMenu('/issues/context_menu')")) | |
45 | # Links to versions anchors |
|
45 | # Links to versions anchors | |
46 | assert_tag 'a', :attributes => {:href => '#2.0'}, |
|
46 | assert_tag 'a', :attributes => {:href => '#2.0'}, | |
47 | :ancestor => {:tag => 'div', :attributes => {:id => 'sidebar'}} |
|
47 | :ancestor => {:tag => 'div', :attributes => {:id => 'sidebar'}} | |
48 | # Links to completed versions in the sidebar |
|
48 | # Links to completed versions in the sidebar | |
49 | assert_tag 'a', :attributes => {:href => '/versions/1'}, |
|
49 | assert_tag 'a', :attributes => {:href => '/versions/1'}, | |
50 | :ancestor => {:tag => 'div', :attributes => {:id => 'sidebar'}} |
|
50 | :ancestor => {:tag => 'div', :attributes => {:id => 'sidebar'}} | |
51 | end |
|
51 | end | |
52 |
|
52 | |||
53 | def test_index_with_completed_versions |
|
53 | def test_index_with_completed_versions | |
54 | get :index, :project_id => 1, :completed => 1 |
|
54 | get :index, :project_id => 1, :completed => 1 | |
55 | assert_response :success |
|
55 | assert_response :success | |
56 | assert_template 'index' |
|
56 | assert_template 'index' | |
57 | assert_not_nil assigns(:versions) |
|
57 | assert_not_nil assigns(:versions) | |
58 | # Version with no date set appears |
|
58 | # Version with no date set appears | |
59 | assert assigns(:versions).include?(Version.find(3)) |
|
59 | assert assigns(:versions).include?(Version.find(3)) | |
60 | # Completed version appears |
|
60 | # Completed version appears | |
61 | assert assigns(:versions).include?(Version.find(1)) |
|
61 | assert assigns(:versions).include?(Version.find(1)) | |
62 | end |
|
62 | end | |
63 |
|
63 | |||
64 | def test_index_with_tracker_ids |
|
64 | def test_index_with_tracker_ids | |
65 | get :index, :project_id => 1, :tracker_ids => [1, 3] |
|
65 | get :index, :project_id => 1, :tracker_ids => [1, 3] | |
66 | assert_response :success |
|
66 | assert_response :success | |
67 | assert_template 'index' |
|
67 | assert_template 'index' | |
68 | assert_not_nil assigns(:issues_by_version) |
|
68 | assert_not_nil assigns(:issues_by_version) | |
69 | assert_nil assigns(:issues_by_version).values.flatten.detect {|issue| issue.tracker_id == 2} |
|
69 | assert_nil assigns(:issues_by_version).values.flatten.detect {|issue| issue.tracker_id == 2} | |
70 | end |
|
70 | end | |
71 |
|
71 | |||
72 | def test_index_showing_subprojects_versions |
|
72 | def test_index_showing_subprojects_versions | |
73 | @subproject_version = Version.generate!(:project => Project.find(3)) |
|
73 | @subproject_version = Version.generate!(:project => Project.find(3)) | |
74 | get :index, :project_id => 1, :with_subprojects => 1 |
|
74 | get :index, :project_id => 1, :with_subprojects => 1 | |
75 | assert_response :success |
|
75 | assert_response :success | |
76 | assert_template 'index' |
|
76 | assert_template 'index' | |
77 | assert_not_nil assigns(:versions) |
|
77 | assert_not_nil assigns(:versions) | |
78 |
|
78 | |||
79 | assert assigns(:versions).include?(Version.find(4)), "Shared version not found" |
|
79 | assert assigns(:versions).include?(Version.find(4)), "Shared version not found" | |
80 | assert assigns(:versions).include?(@subproject_version), "Subproject version not found" |
|
80 | assert assigns(:versions).include?(@subproject_version), "Subproject version not found" | |
81 | end |
|
81 | end | |
82 |
|
82 | |||
83 | def test_show |
|
83 | def test_show | |
84 | get :show, :id => 2 |
|
84 | get :show, :id => 2 | |
85 | assert_response :success |
|
85 | assert_response :success | |
86 | assert_template 'show' |
|
86 | assert_template 'show' | |
87 | assert_not_nil assigns(:version) |
|
87 | assert_not_nil assigns(:version) | |
88 |
|
88 | |||
89 | assert_tag :tag => 'h2', :content => /1.0/ |
|
89 | assert_tag :tag => 'h2', :content => /1.0/ | |
90 | end |
|
90 | end | |
91 |
|
91 | |||
92 | def test_new |
|
92 | def test_new | |
93 | @request.session[:user_id] = 2 |
|
93 | @request.session[:user_id] = 2 | |
94 | get :new, :project_id => '1' |
|
94 | get :new, :project_id => '1' | |
95 | assert_response :success |
|
95 | assert_response :success | |
96 | assert_template 'new' |
|
96 | assert_template 'new' | |
97 | end |
|
97 | end | |
98 |
|
98 | |||
|
99 | def test_new_from_issue_form | |||
|
100 | @request.session[:user_id] = 2 | |||
|
101 | xhr :get, :new, :project_id => '1' | |||
|
102 | assert_response :success | |||
|
103 | assert_select_rjs :replace_html, "ajax-modal" do | |||
|
104 | assert_select "form[action=/projects/ecookbook/versions]" | |||
|
105 | assert_select "input#version_name" | |||
|
106 | end | |||
|
107 | end | |||
|
108 | ||||
99 | def test_create |
|
109 | def test_create | |
100 | @request.session[:user_id] = 2 # manager |
|
110 | @request.session[:user_id] = 2 # manager | |
101 | assert_difference 'Version.count' do |
|
111 | assert_difference 'Version.count' do | |
102 | post :create, :project_id => '1', :version => {:name => 'test_add_version'} |
|
112 | post :create, :project_id => '1', :version => {:name => 'test_add_version'} | |
103 | end |
|
113 | end | |
104 | assert_redirected_to '/projects/ecookbook/settings/versions' |
|
114 | assert_redirected_to '/projects/ecookbook/settings/versions' | |
105 | version = Version.find_by_name('test_add_version') |
|
115 | version = Version.find_by_name('test_add_version') | |
106 | assert_not_nil version |
|
116 | assert_not_nil version | |
107 | assert_equal 1, version.project_id |
|
117 | assert_equal 1, version.project_id | |
108 | end |
|
118 | end | |
109 |
|
119 | |||
110 | def test_create_from_issue_form |
|
120 | def test_create_from_issue_form | |
111 |
@request.session[:user_id] = 2 |
|
121 | @request.session[:user_id] = 2 | |
112 | assert_difference 'Version.count' do |
|
122 | assert_difference 'Version.count' do | |
113 | xhr :post, :create, :project_id => '1', :version => {:name => 'test_add_version_from_issue_form'} |
|
123 | xhr :post, :create, :project_id => '1', :version => {:name => 'test_add_version_from_issue_form'} | |
114 | end |
|
124 | end | |
115 | assert_response :success |
|
|||
116 | assert_select_rjs :replace, 'issue_fixed_version_id' |
|
|||
117 | version = Version.find_by_name('test_add_version_from_issue_form') |
|
125 | version = Version.find_by_name('test_add_version_from_issue_form') | |
118 | assert_not_nil version |
|
126 | assert_not_nil version | |
119 | assert_equal 1, version.project_id |
|
127 | assert_equal 1, version.project_id | |
|
128 | ||||
|
129 | assert_response :success | |||
|
130 | assert_select_rjs :replace, 'issue_fixed_version_id' do | |||
|
131 | assert_select "option[value=#{version.id}][selected=selected]" | |||
|
132 | end | |||
|
133 | end | |||
|
134 | ||||
|
135 | def test_create_from_issue_form_with_failure | |||
|
136 | @request.session[:user_id] = 2 | |||
|
137 | assert_no_difference 'Version.count' do | |||
|
138 | xhr :post, :create, :project_id => '1', :version => {:name => ''} | |||
|
139 | end | |||
|
140 | assert_response :success | |||
|
141 | assert_select_rjs :replace_html, "ajax-modal" do | |||
|
142 | assert_select "div#errorExplanation" | |||
|
143 | end | |||
120 | end |
|
144 | end | |
121 |
|
145 | |||
122 | def test_get_edit |
|
146 | def test_get_edit | |
123 | @request.session[:user_id] = 2 |
|
147 | @request.session[:user_id] = 2 | |
124 | get :edit, :id => 2 |
|
148 | get :edit, :id => 2 | |
125 | assert_response :success |
|
149 | assert_response :success | |
126 | assert_template 'edit' |
|
150 | assert_template 'edit' | |
127 | end |
|
151 | end | |
128 |
|
152 | |||
129 | def test_close_completed |
|
153 | def test_close_completed | |
130 | Version.update_all("status = 'open'") |
|
154 | Version.update_all("status = 'open'") | |
131 | @request.session[:user_id] = 2 |
|
155 | @request.session[:user_id] = 2 | |
132 | put :close_completed, :project_id => 'ecookbook' |
|
156 | put :close_completed, :project_id => 'ecookbook' | |
133 | assert_redirected_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => 'ecookbook' |
|
157 | assert_redirected_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => 'ecookbook' | |
134 | assert_not_nil Version.find_by_status('closed') |
|
158 | assert_not_nil Version.find_by_status('closed') | |
135 | end |
|
159 | end | |
136 |
|
160 | |||
137 | def test_post_update |
|
161 | def test_post_update | |
138 | @request.session[:user_id] = 2 |
|
162 | @request.session[:user_id] = 2 | |
139 | put :update, :id => 2, |
|
163 | put :update, :id => 2, | |
140 | :version => { :name => 'New version name', |
|
164 | :version => { :name => 'New version name', | |
141 | :effective_date => Date.today.strftime("%Y-%m-%d")} |
|
165 | :effective_date => Date.today.strftime("%Y-%m-%d")} | |
142 | assert_redirected_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => 'ecookbook' |
|
166 | assert_redirected_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => 'ecookbook' | |
143 | version = Version.find(2) |
|
167 | version = Version.find(2) | |
144 | assert_equal 'New version name', version.name |
|
168 | assert_equal 'New version name', version.name | |
145 | assert_equal Date.today, version.effective_date |
|
169 | assert_equal Date.today, version.effective_date | |
146 | end |
|
170 | end | |
147 |
|
171 | |||
148 | def test_post_update_with_validation_failure |
|
172 | def test_post_update_with_validation_failure | |
149 | @request.session[:user_id] = 2 |
|
173 | @request.session[:user_id] = 2 | |
150 | put :update, :id => 2, |
|
174 | put :update, :id => 2, | |
151 | :version => { :name => '', |
|
175 | :version => { :name => '', | |
152 | :effective_date => Date.today.strftime("%Y-%m-%d")} |
|
176 | :effective_date => Date.today.strftime("%Y-%m-%d")} | |
153 | assert_response :success |
|
177 | assert_response :success | |
154 | assert_template 'edit' |
|
178 | assert_template 'edit' | |
155 | end |
|
179 | end | |
156 |
|
180 | |||
157 | def test_destroy |
|
181 | def test_destroy | |
158 | @request.session[:user_id] = 2 |
|
182 | @request.session[:user_id] = 2 | |
159 | assert_difference 'Version.count', -1 do |
|
183 | assert_difference 'Version.count', -1 do | |
160 | delete :destroy, :id => 3 |
|
184 | delete :destroy, :id => 3 | |
161 | end |
|
185 | end | |
162 | assert_redirected_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => 'ecookbook' |
|
186 | assert_redirected_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => 'ecookbook' | |
163 | assert_nil Version.find_by_id(3) |
|
187 | assert_nil Version.find_by_id(3) | |
164 | end |
|
188 | end | |
165 |
|
189 | |||
166 | def test_destroy_version_in_use_should_fail |
|
190 | def test_destroy_version_in_use_should_fail | |
167 | @request.session[:user_id] = 2 |
|
191 | @request.session[:user_id] = 2 | |
168 | assert_no_difference 'Version.count' do |
|
192 | assert_no_difference 'Version.count' do | |
169 | delete :destroy, :id => 2 |
|
193 | delete :destroy, :id => 2 | |
170 | end |
|
194 | end | |
171 | assert_redirected_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => 'ecookbook' |
|
195 | assert_redirected_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => 'ecookbook' | |
172 | assert flash[:error].match(/Unable to delete version/) |
|
196 | assert flash[:error].match(/Unable to delete version/) | |
173 | assert Version.find_by_id(2) |
|
197 | assert Version.find_by_id(2) | |
174 | end |
|
198 | end | |
175 |
|
199 | |||
176 | def test_issue_status_by |
|
200 | def test_issue_status_by | |
177 | xhr :get, :status_by, :id => 2 |
|
201 | xhr :get, :status_by, :id => 2 | |
178 | assert_response :success |
|
202 | assert_response :success | |
179 | assert_template '_issue_counts' |
|
203 | assert_template '_issue_counts' | |
180 | end |
|
204 | end | |
181 |
|
205 | |||
182 | def test_issue_status_by_status |
|
206 | def test_issue_status_by_status | |
183 | xhr :get, :status_by, :id => 2, :status_by => 'status' |
|
207 | xhr :get, :status_by, :id => 2, :status_by => 'status' | |
184 | assert_response :success |
|
208 | assert_response :success | |
185 | assert_template '_issue_counts' |
|
209 | assert_template '_issue_counts' | |
186 | end |
|
210 | end | |
187 | end |
|
211 | end |
General Comments 0
You need to be logged in to leave comments.
Login now