##// END OF EJS Templates
Adds autocomplete to issue field on time logging form....
Jean-Philippe Lang -
r9999:f1650b8ff48a
parent child
Show More
@@ -1,42 +1,44
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 # 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 AutoCompletesController < ApplicationController
18 class AutoCompletesController < ApplicationController
19 before_filter :find_project
19 before_filter :find_project
20
20
21 def issues
21 def issues
22 @issues = []
22 @issues = []
23 q = (params[:q] || params[:term]).to_s.strip
23 q = (params[:q] || params[:term]).to_s.strip
24 if q.present?
24 if q.present?
25 scope = (params[:scope] == "all" ? Issue : @project.issues).visible
25 scope = (params[:scope] == "all" || @project.nil? ? Issue : @project.issues).visible
26 if q.match(/^\d+$/)
26 if q.match(/^\d+$/)
27 @issues << scope.find_by_id(q.to_i)
27 @issues << scope.find_by_id(q.to_i)
28 end
28 end
29 @issues += scope.where("LOWER(#{Issue.table_name}.subject) LIKE ?", "%#{q.downcase}%").order("#{Issue.table_name}.id DESC").limit(10).all
29 @issues += scope.where("LOWER(#{Issue.table_name}.subject) LIKE ?", "%#{q.downcase}%").order("#{Issue.table_name}.id DESC").limit(10).all
30 @issues.compact!
30 @issues.compact!
31 end
31 end
32 render :layout => false
32 render :layout => false
33 end
33 end
34
34
35 private
35 private
36
36
37 def find_project
37 def find_project
38 @project = Project.find(params[:project_id])
38 if params[:project_id].present?
39 @project = Project.find(params[:project_id])
40 end
39 rescue ActiveRecord::RecordNotFound
41 rescue ActiveRecord::RecordNotFound
40 render_404
42 render_404
41 end
43 end
42 end
44 end
@@ -1,21 +1,23
1 <%= error_messages_for 'time_entry' %>
1 <%= error_messages_for 'time_entry' %>
2 <%= back_url_hidden_field_tag %>
2 <%= back_url_hidden_field_tag %>
3
3
4 <div class="box tabular">
4 <div class="box tabular">
5 <% if @time_entry.new_record? %>
5 <% if @time_entry.new_record? %>
6 <% if params[:project_id] %>
6 <% if params[:project_id] %>
7 <%= f.hidden_field :project_id %>
7 <%= f.hidden_field :project_id %>
8 <% else %>
8 <% else %>
9 <p><%= f.select :project_id, project_tree_options_for_select(Project.allowed_to(:log_time).all, :selected => @time_entry.project), :required => true %></p>
9 <p><%= f.select :project_id, project_tree_options_for_select(Project.allowed_to(:log_time).all, :selected => @time_entry.project), :required => true %></p>
10 <% end %>
10 <% end %>
11 <% end %>
11 <% end %>
12 <p><%= f.text_field :issue_id, :size => 6 %> <em><%= h("#{@time_entry.issue.tracker.name} ##{@time_entry.issue.id}: #{@time_entry.issue.subject}") if @time_entry.issue %></em></p>
12 <p><%= f.text_field :issue_id, :size => 6 %> <em><%= h("#{@time_entry.issue.tracker.name} ##{@time_entry.issue.id}: #{@time_entry.issue.subject}") if @time_entry.issue %></em></p>
13 <p><%= f.text_field :spent_on, :size => 10, :required => true %><%= calendar_for('time_entry_spent_on') %></p>
13 <p><%= f.text_field :spent_on, :size => 10, :required => true %><%= calendar_for('time_entry_spent_on') %></p>
14 <p><%= f.text_field :hours, :size => 6, :required => true %></p>
14 <p><%= f.text_field :hours, :size => 6, :required => true %></p>
15 <p><%= f.text_field :comments, :size => 100 %></p>
15 <p><%= f.text_field :comments, :size => 100 %></p>
16 <p><%= f.select :activity_id, activity_collection_for_select_options(@time_entry), :required => true %></p>
16 <p><%= f.select :activity_id, activity_collection_for_select_options(@time_entry), :required => true %></p>
17 <% @time_entry.custom_field_values.each do |value| %>
17 <% @time_entry.custom_field_values.each do |value| %>
18 <p><%= custom_field_tag_with_label :time_entry, value %></p>
18 <p><%= custom_field_tag_with_label :time_entry, value %></p>
19 <% end %>
19 <% end %>
20 <%= call_hook(:view_timelog_edit_form_bottom, { :time_entry => @time_entry, :form => f }) %>
20 <%= call_hook(:view_timelog_edit_form_bottom, { :time_entry => @time_entry, :form => f }) %>
21 </div>
21 </div>
22
23 <%= javascript_tag "observeAutocompleteField('time_entry_issue_id', '#{escape_javascript auto_complete_issues_path(:project_id => @project, :scope => (@project ? nil : 'all'))}')" %>
@@ -1,60 +1,67
1 require File.expand_path('../../test_helper', __FILE__)
1 require File.expand_path('../../test_helper', __FILE__)
2
2
3 class AutoCompletesControllerTest < ActionController::TestCase
3 class AutoCompletesControllerTest < ActionController::TestCase
4 fixtures :projects, :issues, :issue_statuses,
4 fixtures :projects, :issues, :issue_statuses,
5 :enumerations, :users, :issue_categories,
5 :enumerations, :users, :issue_categories,
6 :trackers,
6 :trackers,
7 :projects_trackers,
7 :projects_trackers,
8 :roles,
8 :roles,
9 :member_roles,
9 :member_roles,
10 :members,
10 :members,
11 :enabled_modules,
11 :enabled_modules,
12 :workflows,
12 :workflows,
13 :journals, :journal_details
13 :journals, :journal_details
14
14
15 def test_issues_should_not_be_case_sensitive
15 def test_issues_should_not_be_case_sensitive
16 get :issues, :project_id => 'ecookbook', :q => 'ReCiPe'
16 get :issues, :project_id => 'ecookbook', :q => 'ReCiPe'
17 assert_response :success
17 assert_response :success
18 assert_not_nil assigns(:issues)
18 assert_not_nil assigns(:issues)
19 assert assigns(:issues).detect {|issue| issue.subject.match /recipe/}
19 assert assigns(:issues).detect {|issue| issue.subject.match /recipe/}
20 end
20 end
21
21
22 def test_issues_should_accept_term_param
22 def test_issues_should_accept_term_param
23 get :issues, :project_id => 'ecookbook', :term => 'ReCiPe'
23 get :issues, :project_id => 'ecookbook', :term => 'ReCiPe'
24 assert_response :success
24 assert_response :success
25 assert_not_nil assigns(:issues)
25 assert_not_nil assigns(:issues)
26 assert assigns(:issues).detect {|issue| issue.subject.match /recipe/}
26 assert assigns(:issues).detect {|issue| issue.subject.match /recipe/}
27 end
27 end
28
28
29 def test_issues_should_return_issue_with_given_id
29 def test_issues_should_return_issue_with_given_id
30 get :issues, :project_id => 'subproject1', :q => '13'
30 get :issues, :project_id => 'subproject1', :q => '13'
31 assert_response :success
31 assert_response :success
32 assert_not_nil assigns(:issues)
32 assert_not_nil assigns(:issues)
33 assert assigns(:issues).include?(Issue.find(13))
33 assert assigns(:issues).include?(Issue.find(13))
34 end
34 end
35
35
36 def test_auto_complete_with_scope_all_should_search_other_projects
36 def test_auto_complete_with_scope_all_should_search_other_projects
37 get :issues, :project_id => 'ecookbook', :q => '13', :scope => 'all'
37 get :issues, :project_id => 'ecookbook', :q => '13', :scope => 'all'
38 assert_response :success
38 assert_response :success
39 assert_not_nil assigns(:issues)
39 assert_not_nil assigns(:issues)
40 assert assigns(:issues).include?(Issue.find(13))
40 assert assigns(:issues).include?(Issue.find(13))
41 end
41 end
42
42
43 def test_auto_complete_without_project_should_search_all_projects
44 get :issues, :q => '13'
45 assert_response :success
46 assert_not_nil assigns(:issues)
47 assert assigns(:issues).include?(Issue.find(13))
48 end
49
43 def test_auto_complete_without_scope_all_should_not_search_other_projects
50 def test_auto_complete_without_scope_all_should_not_search_other_projects
44 get :issues, :project_id => 'ecookbook', :q => '13'
51 get :issues, :project_id => 'ecookbook', :q => '13'
45 assert_response :success
52 assert_response :success
46 assert_equal [], assigns(:issues)
53 assert_equal [], assigns(:issues)
47 end
54 end
48
55
49 def test_issues_should_return_json
56 def test_issues_should_return_json
50 get :issues, :project_id => 'subproject1', :q => '13'
57 get :issues, :project_id => 'subproject1', :q => '13'
51 assert_response :success
58 assert_response :success
52 json = ActiveSupport::JSON.decode(response.body)
59 json = ActiveSupport::JSON.decode(response.body)
53 assert_kind_of Array, json
60 assert_kind_of Array, json
54 issue = json.first
61 issue = json.first
55 assert_kind_of Hash, issue
62 assert_kind_of Hash, issue
56 assert_equal 13, issue['id']
63 assert_equal 13, issue['id']
57 assert_equal 13, issue['value']
64 assert_equal 13, issue['value']
58 assert_equal 'Bug #13: Subproject issue two', issue['label']
65 assert_equal 'Bug #13: Subproject issue two', issue['label']
59 end
66 end
60 end
67 end
General Comments 0
You need to be logged in to leave comments. Login now