##// END OF EJS Templates
Adds previous/next links to issue (#2850)....
Jean-Philippe Lang -
r8368:b1504ceb434c
parent child
Show More
@@ -0,0 +1,53
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 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 module ActiveRecord
19 class Base
20 def self.find_ids(options={})
21 find_ids_with_associations(options)
22 end
23 end
24
25 module Associations
26 module ClassMethods
27 def find_ids_with_associations(options = {})
28 catch :invalid_query do
29 join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins])
30 return connection.select_values(construct_ids_finder_sql_with_included_associations(options, join_dependency))
31 end
32 []
33 end
34
35 def construct_ids_finder_sql_with_included_associations(options, join_dependency)
36 scope = scope(:find)
37 sql = "SELECT #{table_name}.id FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} "
38 sql << join_dependency.join_associations.collect{|join| join.association_join }.join
39
40 add_joins!(sql, options[:joins], scope)
41 add_conditions!(sql, options[:conditions], scope)
42 add_limited_ids_condition!(sql, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit])
43
44 add_group!(sql, options[:group], options[:having], scope)
45 add_order!(sql, options[:order], scope)
46 add_limit!(sql, options, scope) if using_limitable_reflections?(join_dependency.reflections)
47 add_lock!(sql, options, scope)
48
49 return sanitize_sql(sql)
50 end
51 end
52 end
53 end
@@ -120,7 +120,10 class IssuesController < ApplicationController
120 @priorities = IssuePriority.active
120 @priorities = IssuePriority.active
121 @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
121 @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
122 respond_to do |format|
122 respond_to do |format|
123 format.html { render :template => 'issues/show' }
123 format.html {
124 retrieve_previous_and_next_issue_ids
125 render :template => 'issues/show'
126 }
124 format.api
127 format.api
125 format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' }
128 format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' }
126 format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
129 format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
@@ -277,6 +280,20 private
277 render_404
280 render_404
278 end
281 end
279
282
283 def retrieve_previous_and_next_issue_ids
284 retrieve_query_from_session
285 if @query
286 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
287 sort_update(@query.sortable_columns, 'issues_index_sort')
288 limit = 500
289 issue_ids = @query.issue_ids(:order => sort_clause, :limit => (limit + 1))
290 if (idx = issue_ids.index(@issue.id.to_s)) && idx < limit
291 @prev_issue_id = issue_ids[idx - 1].to_i if idx > 0
292 @next_issue_id = issue_ids[idx + 1].to_i if idx < (issue_ids.size - 1)
293 end
294 end
295 end
296
280 # Used by #edit and #update to set some common instance variables
297 # Used by #edit and #update to set some common instance variables
281 # from the params
298 # from the params
282 # TODO: Refactor, not everything in here is needed by #edit
299 # TODO: Refactor, not everything in here is needed by #edit
@@ -92,6 +92,18 module QueriesHelper
92 end
92 end
93 end
93 end
94
94
95 def retrieve_query_from_session
96 if session[:query]
97 @query = Query.new(:name => "_", :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
98 if session[:query].has_key?(:project_id)
99 @query.project_id = session[:query][:project_id]
100 else
101 @query.project = @project
102 end
103 @query
104 end
105 end
106
95 def build_query_from_params
107 def build_query_from_params
96 if params[:fields] || params[:f]
108 if params[:fields] || params[:f]
97 @query.filters = {}
109 @query.filters = {}
@@ -160,7 +160,8 module SortHelper
160 # sort_clause.
160 # sort_clause.
161 # - criteria can be either an array or a hash of allowed keys
161 # - criteria can be either an array or a hash of allowed keys
162 #
162 #
163 def sort_update(criteria)
163 def sort_update(criteria, sort_name=nil)
164 sort_name ||= self.sort_name
164 @sort_criteria = SortCriteria.new
165 @sort_criteria = SortCriteria.new
165 @sort_criteria.available_criteria = criteria
166 @sort_criteria.available_criteria = criteria
166 @sort_criteria.from_param(params[:sort] || session[sort_name])
167 @sort_criteria.from_param(params[:sort] || session[sort_name])
@@ -3,7 +3,14
3 <h2><%= issue_heading(@issue) %></h2>
3 <h2><%= issue_heading(@issue) %></h2>
4
4
5 <div class="<%= @issue.css_classes %> details">
5 <div class="<%= @issue.css_classes %> details">
6 <%= avatar(@issue.author, :size => "50") %>
6 <% if @prev_issue_id || @next_issue_id %>
7 <div class="next-prev-links contextual">
8 <%= link_to_if @prev_issue_id, '&#171; Previous', issue_path(@prev_issue_id), :title => "##{@prev_issue_id}" %> |
9 <%= link_to_if @next_issue_id, 'Next &#187;', issue_path(@next_issue_id), :title => "##{@next_issue_id}" %>
10 </div>
11 <% end %>
12
13 <%= avatar(@issue.author, :size => "50") %>
7
14
8 <div class="subject">
15 <div class="subject">
9 <%= render_issue_subject_with_tree(@issue) %>
16 <%= render_issue_subject_with_tree(@issue) %>
@@ -292,6 +292,7 div.issue div.subject p {margin: 0; margin-bottom: 0.1em; font-size: 90%; color:
292 div.issue div.subject>div>p { margin-top: 0.5em; }
292 div.issue div.subject>div>p { margin-top: 0.5em; }
293 div.issue div.subject h3 {margin: 0; margin-bottom: 0.1em;}
293 div.issue div.subject h3 {margin: 0; margin-bottom: 0.1em;}
294 div.issue span.private { position:relative; bottom: 2px; text-transform: uppercase; background: #d22; color: #fff; font-weight:bold; padding: 0px 2px 0px 2px; font-size: 60%; margin-right: 2px; border-radius: 2px; -moz-border-radius: 2px;}
294 div.issue span.private { position:relative; bottom: 2px; text-transform: uppercase; background: #d22; color: #fff; font-weight:bold; padding: 0px 2px 0px 2px; font-size: 60%; margin-right: 2px; border-radius: 2px; -moz-border-radius: 2px;}
295 div.issue .next-prev-links {color:#999;}
295
296
296 #issue_tree table.issues, #relations table.issues { border: 0; }
297 #issue_tree table.issues, #relations table.issues { border: 0; }
297 #issue_tree td.checkbox, #relations td.checkbox {display:none;}
298 #issue_tree td.checkbox, #relations td.checkbox {display:none;}
@@ -920,6 +920,77 class IssuesControllerTest < ActionController::TestCase
920 :descendant => {:tag => 'a', :attributes => {:href => '/issues/1'}}
920 :descendant => {:tag => 'a', :attributes => {:href => '/issues/1'}}
921 end
921 end
922
922
923 def test_show_should_not_display_prev_next_links_without_query_in_session
924 get :show, :id => 1
925 assert_response :success
926 assert_nil assigns(:prev_issue_id)
927 assert_nil assigns(:next_issue_id)
928 end
929
930 def test_show_should_display_prev_next_links_with_query_in_session
931 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
932 @request.session['issues_index_sort'] = 'id'
933
934 with_settings :display_subprojects_issues => '0' do
935 get :show, :id => 3
936 end
937
938 assert_response :success
939 # Previous and next issues for all projects
940 assert_equal 2, assigns(:prev_issue_id)
941 assert_equal 5, assigns(:next_issue_id)
942
943 assert_tag 'a', :attributes => {:href => '/issues/2'}, :content => /Previous/
944 assert_tag 'a', :attributes => {:href => '/issues/5'}, :content => /Next/
945 end
946
947 def test_show_should_display_prev_next_links_with_project_query_in_session
948 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
949 @request.session['issues_index_sort'] = 'id'
950
951 with_settings :display_subprojects_issues => '0' do
952 get :show, :id => 3
953 end
954
955 assert_response :success
956 # Previous and next issues inside project
957 assert_equal 2, assigns(:prev_issue_id)
958 assert_equal 7, assigns(:next_issue_id)
959
960 assert_tag 'a', :attributes => {:href => '/issues/2'}, :content => /Previous/
961 assert_tag 'a', :attributes => {:href => '/issues/7'}, :content => /Next/
962 end
963
964 def test_show_should_not_display_prev_link_for_first_issue
965 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
966 @request.session['issues_index_sort'] = 'id'
967
968 with_settings :display_subprojects_issues => '0' do
969 get :show, :id => 1
970 end
971
972 assert_response :success
973 assert_nil assigns(:prev_issue_id)
974 assert_equal 2, assigns(:next_issue_id)
975
976 assert_no_tag 'a', :content => /Previous/
977 assert_tag 'a', :attributes => {:href => '/issues/2'}, :content => /Next/
978 end
979
980 def test_show_should_not_display_prev_next_links_for_issue_not_in_query_results
981 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'c'}}, :project_id => 1}
982 @request.session['issues_index_sort'] = 'id'
983
984 get :show, :id => 1
985
986 assert_response :success
987 assert_nil assigns(:prev_issue_id)
988 assert_nil assigns(:next_issue_id)
989
990 assert_no_tag 'a', :content => /Previous/
991 assert_no_tag 'a', :content => /Next/
992 end
993
923 def test_show_atom
994 def test_show_atom
924 get :show, :id => 2, :format => 'atom'
995 get :show, :id => 2, :format => 'atom'
925 assert_response :success
996 assert_response :success
@@ -612,6 +612,13 class QueryTest < ActiveSupport::TestCase
612 assert_equal %w(Fixnum), count_by_group.values.collect {|k| k.class.name}.uniq
612 assert_equal %w(Fixnum), count_by_group.values.collect {|k| k.class.name}.uniq
613 end
613 end
614
614
615 def test_issue_ids
616 q = Query.new(:name => '_')
617 order = "issues.subject, issues.id"
618 issues = q.issues(:order => order)
619 assert_equal issues.map(&:id).map(&:to_s), q.issue_ids(:order => order)
620 end
621
615 def test_label_for
622 def test_label_for
616 q = Query.new
623 q = Query.new
617 assert_equal 'Assignee', q.label_for('assigned_to_id')
624 assert_equal 'Assignee', q.label_for('assigned_to_id')
General Comments 0
You need to be logged in to leave comments. Login now