diff options
-rw-r--r-- | app/controllers/my_controller.rb | 1 | ||||
-rw-r--r-- | app/helpers/my_helper.rb | 55 | ||||
-rw-r--r-- | app/helpers/queries_helper.rb | 18 | ||||
-rw-r--r-- | app/models/query.rb | 16 | ||||
-rw-r--r-- | app/models/user_preference.rb | 1 | ||||
-rw-r--r-- | app/views/issues/_list.html.erb | 5 | ||||
-rw-r--r-- | app/views/my/blocks/_issues.erb | 42 | ||||
-rw-r--r-- | app/views/my/blocks/_issuesassignedtome.html.erb | 15 | ||||
-rw-r--r-- | app/views/my/blocks/_issuesreportedbyme.html.erb | 15 | ||||
-rw-r--r-- | app/views/my/blocks/_issueswatched.html.erb | 9 | ||||
-rw-r--r-- | app/views/my/page.html.erb | 1 | ||||
-rw-r--r-- | app/views/queries/_columns.html.erb | 30 | ||||
-rw-r--r-- | lib/redmine/my_page.rb | 21 | ||||
-rw-r--r-- | public/stylesheets/application.css | 1 | ||||
-rw-r--r-- | test/functional/issues_controller_test.rb | 2 | ||||
-rw-r--r-- | test/functional/my_controller_test.rb | 41 | ||||
-rw-r--r-- | test/functional/settings_controller_test.rb | 4 |
17 files changed, 185 insertions, 92 deletions
diff --git a/app/controllers/my_controller.rb b/app/controllers/my_controller.rb index 66bd965ba..f0b56e44f 100644 --- a/app/controllers/my_controller.rb +++ b/app/controllers/my_controller.rb @@ -27,6 +27,7 @@ class MyController < ApplicationController helper :issues helper :users helper :custom_fields + helper :queries def index page diff --git a/app/helpers/my_helper.rb b/app/helpers/my_helper.rb index 3975d80b1..2a20a7bf2 100644 --- a/app/helpers/my_helper.rb +++ b/app/helpers/my_helper.rb @@ -44,16 +44,17 @@ module MyHelper # Renders a single block content def render_block_content(block, user) - unless Redmine::MyPage.blocks.key?(block) + unless block_definition = Redmine::MyPage.blocks[block] Rails.logger.warn("Unknown block \"#{block}\" found in #{user.login} (id=#{user.id}) preferences") return end settings = user.pref.my_page_settings(block) + partial = block_definition[:partial] begin - render(:partial => "my/blocks/#{block}", :locals => {:user => user, :settings => settings}) + render(:partial => partial, :locals => {:user => user, :settings => settings, :block => block}) rescue ActionView::MissingTemplate - Rails.logger.warn("Template missing for block \"#{block}\" found in #{user.login} (id=#{user.id}) preferences") + Rails.logger.warn("Partial \"#{partial}\" missing for block \"#{block}\" found in #{user.login} (id=#{user.id}) preferences") return nil end end @@ -80,30 +81,38 @@ module MyHelper Document.visible.order("#{Document.table_name}.created_on DESC").limit(10).to_a end - def issuesassignedtome_items - Issue.visible.open. - assigned_to(User.current). - limit(10). - includes(:status, :project, :tracker, :priority). - references(:status, :project, :tracker, :priority). - order("#{IssuePriority.table_name}.position DESC, #{Issue.table_name}.updated_on DESC") + def issues_items(block, settings) + send "#{block}_items", settings end - def issuesreportedbyme_items - Issue.visible.open. - where(:author_id => User.current.id). - limit(10). - includes(:status, :project, :tracker, :priority). - references(:status, :project, :tracker). - order("#{Issue.table_name}.updated_on DESC") + def issuesassignedtome_items(settings) + query = IssueQuery.new(:name => l(:label_assigned_to_me_issues), :user => User.current) + query.add_filter 'assigned_to_id', '=', ['me'] + query.column_names = settings[:columns].presence || ['project', 'tracker', 'status', 'subject'] + query.sort_criteria = settings[:sort].presence || [['priority', 'desc'], ['updated_on', 'desc']] + issues = query.issues(:limit => 10) + + return issues, query end - def issueswatched_items - Issue.visible.open. - on_active_project.watched_by(User.current.id). - preload(:status, :project, :tracker, :priority). - recently_updated. - limit(10) + def issuesreportedbyme_items(settings) + query = IssueQuery.new(:name => l(:label_reported_issues), :user => User.current) + query.add_filter 'author_id', '=', ['me'] + query.column_names = settings[:columns].presence || ['project', 'tracker', 'status', 'subject'] + query.sort_criteria = settings[:sort].presence || [['updated_on', 'desc']] + issues = query.issues(:limit => 10) + + return issues, query + end + + def issueswatched_items(settings) + query = IssueQuery.new(:name => l(:label_watched_issues), :user => User.current) + query.add_filter 'watcher_id', '=', ['me'] + query.column_names = settings[:columns].presence || ['project', 'tracker', 'status', 'subject'] + query.sort_criteria = settings[:sort].presence || [['updated_on', 'desc']] + issues = query.issues(:limit => 10) + + return issues, query end def news_items diff --git a/app/helpers/queries_helper.rb b/app/helpers/queries_helper.rb index 1d0db2877..b4a0a0b75 100644 --- a/app/helpers/queries_helper.rb +++ b/app/helpers/queries_helper.rb @@ -161,7 +161,7 @@ module QueriesHelper content_tag('span', label + " " + value, :class => "total-for-#{column.name.to_s.dasherize}") end - def column_header(query, column) + def column_header(query, column, options={}) if column.sortable? css, order = nil, column.default_order if column.name.to_s == query.sort_criteria.first_key @@ -173,11 +173,21 @@ module QueriesHelper order = 'asc' end end - sort_param = { :sort => query.sort_criteria.add(column.name, order).to_param } - content = link_to(column.caption, - {:params => request.query_parameters.merge(sort_param)}, + param_key = options[:sort_param] || :sort + sort_param = { param_key => query.sort_criteria.add(column.name, order).to_param } + while sort_param.keys.first.to_s =~ /^(.+)\[(.+)\]$/ + sort_param = {$1 => {$2 => sort_param.values.first}} + end + link_options = { :title => l(:label_sort_by, "\"#{column.caption}\""), :class => css + } + if options[:sort_link_options] + link_options.merge! options[:sort_link_options] + end + content = link_to(column.caption, + {:params => request.query_parameters.deep_merge(sort_param)}, + link_options ) else content = column.caption diff --git a/app/models/query.rb b/app/models/query.rb index 192502024..23eca0278 100644 --- a/app/models/query.rb +++ b/app/models/query.rb @@ -386,6 +386,22 @@ class Query < ActiveRecord::Base new(attributes).build_from_params(params) end + def as_params + params = {} + filters.each do |field, options| + params[:f] ||= [] + params[:f] << field + params[:op] ||= {} + params[:op][field] = options[:operator] + params[:v] ||= {} + params[:v][field] = options[:values] + end + params[:c] = column_names + params[:sort] = sort_criteria.to_param + params[:set_filter] = 1 + params + end + def validate_query_filters filters.each_key do |field| if values_for(field) diff --git a/app/models/user_preference.rb b/app/models/user_preference.rb index 41c0f0b4b..0ebafa839 100644 --- a/app/models/user_preference.rb +++ b/app/models/user_preference.rb @@ -128,6 +128,7 @@ class UserPreference < ActiveRecord::Base end def update_block_settings(block, settings) + block = block.to_s block_settings = my_page_settings(block).merge(settings.symbolize_keys) my_page_settings[block] = block_settings end diff --git a/app/views/issues/_list.html.erb b/app/views/issues/_list.html.erb index f9172143b..5488fb295 100644 --- a/app/views/issues/_list.html.erb +++ b/app/views/issues/_list.html.erb @@ -1,3 +1,6 @@ +<% query_options = nil unless defined?(query_options) %> +<% query_options ||= {} %> + <%= form_tag({}, :data => {:cm_url => issues_context_menu_path}) do -%> <%= hidden_field_tag 'back_url', url_for(:params => request.query_parameters), :id => nil %> <div class="autoscroll"> @@ -9,7 +12,7 @@ :title => "#{l(:button_check_all)}/#{l(:button_uncheck_all)}" %> </th> <% query.inline_columns.each do |column| %> - <%= column_header(query, column) %> + <%= column_header(query, column, query_options) %> <% end %> </tr> </thead> diff --git a/app/views/my/blocks/_issues.erb b/app/views/my/blocks/_issues.erb new file mode 100644 index 000000000..bbd10af33 --- /dev/null +++ b/app/views/my/blocks/_issues.erb @@ -0,0 +1,42 @@ +<% issues, query = issues_items(block, settings) %> + +<div class="contextual"> + <%= link_to_function l(:label_options), "$('##{block}-settings').toggle();", :class => 'icon-only icon-settings' %> +</div> + +<h3> + <%= link_to query.name, issues_path(query.as_params) %> + (<%= query.issue_count %>) +</h3> + +<div id="<%= block %>-settings" style="display:none;"> + <%= form_tag(my_page_path, :remote => true) do %> + <div class="box"> + <%= render_query_columns_selection(query, :name => "settings[#{block}][columns]") %> + </div> + <p> + <%= submit_tag l(:button_save) %> + <%= link_to_function l(:button_cancel), "$('##{block}-settings').toggle();" %> + </p> + <% end %> +</div> + +<% if issues.any? %> + <%= render :partial => 'issues/list', + :locals => { + :issues => issues, + :query => query, + :query_options => { + :sort_param => "settings[#{block}][sort]", + :sort_link_options => {:method => :post, :remote => true} + } + } %> +<% else %> + <p class="nodata"><%= l(:label_no_data) %></p> +<% end %> + +<% content_for :header_tags do %> +<%= auto_discovery_link_tag(:atom, + issues_path(query.as_params.merge(:format => 'atom', :key => User.current.rss_key)), + {:title => query.name}) %> +<% end %> diff --git a/app/views/my/blocks/_issuesassignedtome.html.erb b/app/views/my/blocks/_issuesassignedtome.html.erb deleted file mode 100644 index 94948b7aa..000000000 --- a/app/views/my/blocks/_issuesassignedtome.html.erb +++ /dev/null @@ -1,15 +0,0 @@ -<% assigned_issues = issuesassignedtome_items %> -<h3> - <%= link_to l(:label_assigned_to_me_issues), - issues_path(:set_filter => 1, :assigned_to_id => 'me', :sort => 'priority:desc,updated_on:desc') %> - (<%= assigned_issues.limit(nil).count %>) -</h3> - -<%= render :partial => 'issues/list_simple', :locals => { :issues => assigned_issues.to_a } %> - -<% content_for :header_tags do %> -<%= auto_discovery_link_tag(:atom, - {:controller => 'issues', :action => 'index', :set_filter => 1, - :assigned_to_id => 'me', :format => 'atom', :key => User.current.rss_key}, - {:title => l(:label_assigned_to_me_issues)}) %> -<% end %> diff --git a/app/views/my/blocks/_issuesreportedbyme.html.erb b/app/views/my/blocks/_issuesreportedbyme.html.erb deleted file mode 100644 index 91557c035..000000000 --- a/app/views/my/blocks/_issuesreportedbyme.html.erb +++ /dev/null @@ -1,15 +0,0 @@ -<% reported_issues = issuesreportedbyme_items %> -<h3> - <%= link_to l(:label_reported_issues), - issues_path(:set_filter => 1, :status_id => 'o', :author_id => 'me', :sort => 'updated_on:desc') %> - (<%= reported_issues.limit(nil).count %>) -</h3> - -<%= render :partial => 'issues/list_simple', :locals => { :issues => reported_issues.to_a } %> - -<% content_for :header_tags do %> -<%= auto_discovery_link_tag(:atom, - {:controller => 'issues', :action => 'index', :set_filter => 1, - :author_id => 'me', :format => 'atom', :key => User.current.rss_key}, - {:title => l(:label_reported_issues)}) %> -<% end %> diff --git a/app/views/my/blocks/_issueswatched.html.erb b/app/views/my/blocks/_issueswatched.html.erb deleted file mode 100644 index 512a52238..000000000 --- a/app/views/my/blocks/_issueswatched.html.erb +++ /dev/null @@ -1,9 +0,0 @@ -<% watched_issues = issueswatched_items %> -<h3> - <%= link_to l(:label_watched_issues), - issues_path(:set_filter => 1, :watcher_id => 'me', :sort => 'updated_on:desc') %> - (<%= watched_issues.limit(nil).count %>) -</h3> - - -<%= render :partial => 'issues/list_simple', :locals => { :issues => watched_issues.to_a } %> diff --git a/app/views/my/page.html.erb b/app/views/my/page.html.erb index 1210c471a..7f67d0a90 100644 --- a/app/views/my/page.html.erb +++ b/app/views/my/page.html.erb @@ -25,6 +25,7 @@ <%= javascript_tag do %> $(document).ready(function(){ + $('#block-select').val(''); $('#list-top, #list-left, #list-right').sortable({ connectWith: '.block-receiver', tolerance: 'pointer', diff --git a/app/views/queries/_columns.html.erb b/app/views/queries/_columns.html.erb index d102341c1..26a460780 100644 --- a/app/views/queries/_columns.html.erb +++ b/app/views/queries/_columns.html.erb @@ -1,32 +1,38 @@ +<% tag_id = tag_name.gsub(/[\[\]]+/, '_').sub(/_+$/, '') %> +<% available_tag_id = "available_#{tag_id}" %> +<% selected_tag_id = "selected_#{tag_id}" %> + <table class="query-columns"> <tr> <td style="padding-left:0"> - <%= label_tag "available_columns", l(:description_available_columns) %> + <%= label_tag available_tag_id, l(:description_available_columns) %> <br /> <%= select_tag 'available_columns', options_for_select(query_available_inline_columns_options(query)), + :id => available_tag_id, :multiple => true, :size => 10, :style => "width:150px", - :ondblclick => "moveOptions(this.form.available_columns, this.form.selected_columns);" %> + :ondblclick => "moveOptions(this.form.#{available_tag_id}, this.form.#{selected_tag_id});" %> </td> <td class="buttons"> <input type="button" value="→" - onclick="moveOptions(this.form.available_columns, this.form.selected_columns);" /><br /> + onclick="moveOptions(this.form.<%= available_tag_id %>, this.form.<%= selected_tag_id %>);" /><br /> <input type="button" value="←" - onclick="moveOptions(this.form.selected_columns, this.form.available_columns);" /> + onclick="moveOptions(this.form.<%= selected_tag_id %>, this.form.<%= available_tag_id %>);" /> </td> <td> - <%= label_tag "selected_columns", l(:description_selected_columns) %> + <%= label_tag selected_tag_id, l(:description_selected_columns) %> <br /> <%= select_tag tag_name, options_for_select(query_selected_inline_columns_options(query)), - :id => 'selected_columns', :multiple => true, :size => 10, :style => "width:150px", - :ondblclick => "moveOptions(this.form.selected_columns, this.form.available_columns);" %> + :id => selected_tag_id, + :multiple => true, :size => 10, :style => "width:150px", + :ondblclick => "moveOptions(this.form.#{selected_tag_id}, this.form.#{available_tag_id});" %> </td> <td class="buttons"> - <input type="button" value="⇈" onclick="moveOptionTop(this.form.selected_columns);" /><br /> - <input type="button" value="↑" onclick="moveOptionUp(this.form.selected_columns);" /><br /> - <input type="button" value="↓" onclick="moveOptionDown(this.form.selected_columns);" /><br /> - <input type="button" value="⇊" onclick="moveOptionBottom(this.form.selected_columns);" /> + <input type="button" value="⇈" onclick="moveOptionTop(this.form.<%= selected_tag_id %>);" /><br /> + <input type="button" value="↑" onclick="moveOptionUp(this.form.<%= selected_tag_id %>);" /><br /> + <input type="button" value="↓" onclick="moveOptionDown(this.form.<%= selected_tag_id %>);" /><br /> + <input type="button" value="⇊" onclick="moveOptionBottom(this.form.<%= selected_tag_id %>);" /> </td> </tr> </table> @@ -34,7 +40,7 @@ <%= javascript_tag do %> $(document).ready(function(){ $('.query-columns').closest('form').submit(function(){ - $('#selected_columns option').prop('selected', true); + $('#<%= selected_tag_id %> option').prop('selected', true); }); }); <% end %> diff --git a/lib/redmine/my_page.rb b/lib/redmine/my_page.rb index 0c11459e4..06f750f4e 100644 --- a/lib/redmine/my_page.rb +++ b/lib/redmine/my_page.rb @@ -20,13 +20,13 @@ module Redmine include Redmine::I18n CORE_BLOCKS = { - 'issuesassignedtome' => :label_assigned_to_me_issues, - 'issuesreportedbyme' => :label_reported_issues, - 'issueswatched' => :label_watched_issues, - 'news' => :label_news_latest, - 'calendar' => :label_calendar, - 'documents' => :label_document_plural, - 'timelog' => :label_spent_time + 'issuesassignedtome' => {:label => :label_assigned_to_me_issues, :partial => 'my/blocks/issues'}, + 'issuesreportedbyme' => {:label => :label_reported_issues, :partial => 'my/blocks/issues'}, + 'issueswatched' => {:label => :label_watched_issues, :partial => 'my/blocks/issues'}, + 'news' => {:label => :label_news_latest, :partial => 'my/blocks/news'}, + 'calendar' => {:label => :label_calendar, :partial => 'my/blocks/calendar'}, + 'documents' => {:label => :label_document_plural, :partial => 'my/blocks/documents'}, + 'timelog' => {:label => :label_spent_time, :partial => 'my/blocks/timelog'} } # Returns the available blocks @@ -36,8 +36,9 @@ module Redmine def self.block_options options = [] - blocks.each do |k, v| - options << [l("my.blocks.#{v}", :default => [v, v.to_s.humanize]), k.dasherize] + blocks.each do |block, block_options| + label = block_options[:label] + options << [l("my.blocks.#{label}", :default => [label, label.to_s.humanize]), block.dasherize] end options end @@ -46,7 +47,7 @@ module Redmine def self.additional_blocks @@additional_blocks ||= Dir.glob("#{Redmine::Plugin.directory}/*/app/views/my/blocks/_*.{rhtml,erb}").inject({}) do |h,file| name = File.basename(file).split('.').first.gsub(/^_/, '') - h[name] = name.to_sym + h[name] = {:label => name.to_sym, :partial => "my/blocks/#{name}"} h end end diff --git a/public/stylesheets/application.css b/public/stylesheets/application.css index b60f6a51b..1b0206d7c 100644 --- a/public/stylesheets/application.css +++ b/public/stylesheets/application.css @@ -1132,6 +1132,7 @@ div.wiki img {vertical-align:middle; max-width:100%;} .handle {cursor: move;} +#my-page .list th.checkbox, #my-page .list td.checkbox {display:none;} /***** Gantt chart *****/ .gantt_hdr { position:absolute; diff --git a/test/functional/issues_controller_test.rb b/test/functional/issues_controller_test.rb index 914d7c386..10ed24c1c 100644 --- a/test/functional/issues_controller_test.rb +++ b/test/functional/issues_controller_test.rb @@ -797,7 +797,7 @@ class IssuesControllerTest < Redmine::ControllerTest assert_equal columns, session[:issue_query][:column_names].map(&:to_s) # ensure only these columns are kept in the selected columns list - assert_select 'select#selected_columns option' do + assert_select 'select[name=?] option', 'c[]' do assert_select 'option', 3 assert_select 'option[value=tracker]' assert_select 'option[value=project]', 0 diff --git a/test/functional/my_controller_test.rb b/test/functional/my_controller_test.rb index 76237dfb5..f79cca5d9 100644 --- a/test/functional/my_controller_test.rb +++ b/test/functional/my_controller_test.rb @@ -51,6 +51,47 @@ class MyControllerTest < Redmine::ControllerTest end end + def test_page_with_assigned_issues_block_and_no_custom_settings + preferences = User.find(2).pref + preferences.my_page_layout = {'top' => ['issuesassignedtome']} + preferences.my_page_settings = nil + preferences.save! + + get :page + assert_select '#block-issuesassignedtome' do + assert_select 'table.issues' do + assert_select 'th a[data-remote=true][data-method=post]', :text => 'Tracker' + end + assert_select '#issuesassignedtome-settings' do + assert_select 'select[name=?]', 'settings[issuesassignedtome][columns][]' + end + end + end + + def test_page_with_assigned_issues_block_and_custom_columns + preferences = User.find(2).pref + preferences.my_page_layout = {'top' => ['issuesassignedtome']} + preferences.my_page_settings = {'issuesassignedtome' => {:columns => ['tracker', 'subject', 'due_date']}} + preferences.save! + + get :page + assert_select '#block-issuesassignedtome' do + assert_select 'table.issues td.due_date' + end + end + + def test_page_with_assigned_issues_block_and_custom_sort + preferences = User.find(2).pref + preferences.my_page_layout = {'top' => ['issuesassignedtome']} + preferences.my_page_settings = {'issuesassignedtome' => {:sort => 'due_date'}} + preferences.save! + + get :page + assert_select '#block-issuesassignedtome' do + assert_select 'table.issues.sort-by-due-date' + end + end + def test_page_with_all_blocks blocks = Redmine::MyPage.blocks.keys preferences = User.find(2).pref diff --git a/test/functional/settings_controller_test.rb b/test/functional/settings_controller_test.rb index d7dacc0ee..098c4b55b 100644 --- a/test/functional/settings_controller_test.rb +++ b/test/functional/settings_controller_test.rb @@ -51,7 +51,7 @@ class SettingsControllerTest < Redmine::ControllerTest assert_response :success end - assert_select 'select[id=selected_columns][name=?]', 'settings[issue_list_default_columns][]' do + assert_select 'select[name=?]', 'settings[issue_list_default_columns][]' do assert_select 'option', 4 assert_select 'option[value=tracker]', :text => 'Tracker' assert_select 'option[value=subject]', :text => 'Subject' @@ -59,7 +59,7 @@ class SettingsControllerTest < Redmine::ControllerTest assert_select 'option[value=updated_on]', :text => 'Updated' end - assert_select 'select[id=available_columns]' do + assert_select 'select[name=?]', 'available_columns[]' do assert_select 'option[value=tracker]', 0 assert_select 'option[value=priority]', :text => 'Priority' end |