]> source.dussan.org Git - redmine.git/commitdiff
Allow multiple instances of custom queries on My page (#1565).
authorJean-Philippe Lang <jp_lang@yahoo.fr>
Thu, 16 Mar 2017 18:02:43 +0000 (18:02 +0000)
committerJean-Philippe Lang <jp_lang@yahoo.fr>
Thu, 16 Mar 2017 18:02:43 +0000 (18:02 +0000)
git-svn-id: http://svn.redmine.org/redmine/trunk@16413 e93f8b46-1217-0410-a6f0-8f06a7374b81

app/helpers/my_helper.rb
app/models/user_preference.rb
app/views/my/blocks/_issue_query_selection.html.erb [new file with mode: 0644]
app/views/my/blocks/_issues.erb
lib/redmine/my_page.rb
test/functional/my_controller_test.rb

index 2fa1272265382eb60d9a0b1baa7daf31c7c775b2..e40ccc27d18f36e85ea68cecc353c5605a6d176a 100644 (file)
@@ -47,26 +47,29 @@ module MyHelper
 
   # Renders a single block content
   def render_block_content(block, user)
-    unless block_definition = Redmine::MyPage.blocks[block]
+    unless block_definition = Redmine::MyPage.find_block(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 => partial, :locals => {:user => user, :settings => settings, :block => block})
-    rescue ActionView::MissingTemplate
-      Rails.logger.warn("Partial \"#{partial}\" missing for block \"#{block}\" found in #{user.login} (id=#{user.id}) preferences")
-      return nil
+    if partial = block_definition[:partial]
+      begin
+        render(:partial => partial, :locals => {:user => user, :settings => settings, :block => block})
+      rescue ActionView::MissingTemplate
+        Rails.logger.warn("Partial \"#{partial}\" missing for block \"#{block}\" found in #{user.login} (id=#{user.id}) preferences")
+        return nil
+      end
+    else
+      send "render_#{block_definition[:name]}_block", block, settings
     end
   end
 
   def block_select_tag(user)
-    disabled = user.pref.my_page_layout.values.flatten
+    blocks_in_use = user.pref.my_page_layout.values.flatten
     options = content_tag('option')
-    Redmine::MyPage.block_options.each do |label, block|
-      options << content_tag('option', label, :value => block, :disabled => disabled.include?(block))
+    Redmine::MyPage.block_options(blocks_in_use).each do |label, block|
+      options << content_tag('option', label, :value => block, :disabled => block.blank?)
     end
     select_tag('block', options, :id => "block-select", :onchange => "$('#block-form').submit();")
   end
@@ -88,45 +91,47 @@ module MyHelper
     send "#{block}_items", settings
   end
 
-  def issuesassignedtome_items(settings)
+  def render_issuesassignedtome_block(block, 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
+    render :partial => 'my/blocks/issues', :locals => {:query => query, :issues => issues, :block => block}
   end
 
-  def issuesreportedbyme_items(settings)
+  def render_issuesreportedbyme_block(block, 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
+    render :partial => 'my/blocks/issues', :locals => {:query => query, :issues => issues, :block => block}
   end
 
-  def issueswatched_items(settings)
+  def render_issueswatched_block(block, 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
+    render :partial => 'my/blocks/issues', :locals => {:query => query, :issues => issues, :block => block}
   end
 
-  def issuequery_items(settings)
+  def render_issuequery_block(block, settings)
     query = IssueQuery.visible.find_by_id(settings[:query_id])
-    return unless query
-
-    query.column_names = settings[:columns] if settings[:columns].present?
-    query.sort_criteria = settings[:sort] if settings[:sort].present?
-    issues = query.issues(:limit => 10)
 
-    return issues, query
+    if query
+      query.column_names = settings[:columns] if settings[:columns].present?
+      query.sort_criteria = settings[:sort] if settings[:sort].present?
+      issues = query.issues(:limit => 10)
+      render :partial => 'my/blocks/issues', :locals => {:query => query, :issues => issues, :block => block, :settings => settings}
+    else
+      render :partial => 'my/blocks/issue_query_selection', :locals => {:block => block, :settings => settings}
+    end
   end
 
   def news_items
index e0b17631c79b8072642957603668356bbe150c59..836ad39786d9aa086e6436f3ccefb5282484e57f 100644 (file)
@@ -109,6 +109,7 @@ class UserPreference < ActiveRecord::Base
     self[:my_page_settings] = arg
   end
 
+  # Removes block from the user page layout
   def remove_block(block)
     block = block.to_s.underscore
     %w(top left right).each do |f|
@@ -117,9 +118,12 @@ class UserPreference < ActiveRecord::Base
     my_page_layout
   end
 
+  # Adds block to the user page layout
+  # Returns nil if block is not valid or if it's already
+  # present in the user page layout
   def add_block(block)
     block = block.to_s.underscore
-    return unless Redmine::MyPage.blocks.key?(block)
+    return unless Redmine::MyPage.valid_block?(block, my_page_layout.values.flatten)
 
     remove_block(block)
     # add it on top
diff --git a/app/views/my/blocks/_issue_query_selection.html.erb b/app/views/my/blocks/_issue_query_selection.html.erb
new file mode 100644 (file)
index 0000000..64a3f32
--- /dev/null
@@ -0,0 +1,21 @@
+<% visible_queries = IssueQuery.visible.sorted %>
+
+<h3>
+  <%= l(:label_issue_plural) %>
+</h3>
+
+<div id="<%= block %>-settings">
+  <%= form_tag(my_page_path, :remote => true) do %>
+    <div class="box">
+      <p>
+        <label>
+          <%= l(:label_query) %>
+          <%= select_tag "settings[#{block}][query_id]", content_tag("option") + options_from_collection_for_select(visible_queries, :id, :name, settings[:query_id]) %>
+        </label>
+      </p>
+    </div>
+    <p>
+      <%= submit_tag l(:button_save) %>
+    </p>
+  <% end %>
+</div>
index 47fb28136a931e4a712abb09893a0b6d7b044fd1..845ef5b857d59dcfaa967c39854a6e1087d0dc5e 100644 (file)
@@ -1,6 +1,3 @@
-<% issues, query = issues_items(block, settings) %>
-
-<% if query %>
 <div class="contextual">
   <%= link_to_function l(:label_options), "$('##{block}-settings').toggle();", :class => 'icon-only icon-settings', :title => l(:label_options) %>
 </div>
                             _project_issues_path(query.project, query.as_params.merge(:format => 'atom', :key => User.current.rss_key)),
                             {:title => query.name}) %>
 <% end %>
-
-<% else %>
-<% visible_queries = IssueQuery.visible.sorted %>
-
-<h3>
-  <%= l(:label_issue_plural) %>
-</h3>
-
-<div id="<%= block %>-settings">
-  <%= form_tag(my_page_path, :remote => true) do %>
-    <div class="box">
-      <p>
-        <label>
-          <%= l(:label_query) %>
-          <%= select_tag 'settings[issuequery][query_id]', content_tag("option") + options_from_collection_for_select(visible_queries, :id, :name, settings[:query_id]) %>
-        </label>
-      </p>
-    </div>
-    <p>
-      <%= submit_tag l(:button_save) %>
-    </p>
-  <% end %>
-</div>
-
-<% end %>
index dc9c6928f52c89b3beb1d12e22502901f33f2b77..34195bbcd1dedc1d7789669758a9f0a056e93bac 100644 (file)
@@ -20,10 +20,10 @@ module Redmine
     include Redmine::I18n
 
     CORE_BLOCKS = {
-        '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'},
-        'issuequery' => {:label => :label_issue_plural, :partial => 'my/blocks/issues'},
+        'issuesassignedtome' => {:label => :label_assigned_to_me_issues},
+        'issuesreportedbyme' => {:label => :label_reported_issues},
+        'issueswatched' => {:label => :label_watched_issues},
+        'issuequery' => {:label => :label_issue_plural, :max_occurs => 3},
         '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'},
@@ -35,15 +35,36 @@ module Redmine
       CORE_BLOCKS.merge(additional_blocks).freeze
     end
 
-    def self.block_options
+    def self.block_options(blocks_in_use=[])
       options = []
       blocks.each do |block, block_options|
+        indexes = blocks_in_use.map {|n|
+          if n =~ /\A#{block}(__(\d+))?\z/
+            $2.to_i
+          end
+        }.compact
+
+        occurs = indexes.size
+        block_id = indexes.any? ? "#{block}__#{indexes.max + 1}" : block
+        disabled = (occurs >= (Redmine::MyPage.blocks[block][:max_occurs] || 1))
+        block_id = nil if disabled
+
         label = block_options[:label]
-        options << [l("my.blocks.#{label}", :default => [label, label.to_s.humanize]), block.dasherize]
+        options << [l("my.blocks.#{label}", :default => [label, label.to_s.humanize]), block_id]
       end
       options
     end
 
+    def self.valid_block?(block, blocks_in_use=[])
+      block.present? && block_options(blocks_in_use).map(&:last).include?(block)
+    end
+
+    def self.find_block(block)
+      block.to_s =~  /\A(.*?)(__\d+)?\z/
+      name = $1
+      blocks.has_key?(name) ? blocks[name].merge(:name => name) : nil
+    end
+
     # Returns the additional blocks that are defined by plugin partials
     def self.additional_blocks
       @@additional_blocks ||= Dir.glob("#{Redmine::Plugin.directory}/*/app/views/my/blocks/_*.{rhtml,erb}").inject({}) do |h,file|
index 09cb1a834dae7995453363a8b5720a34edc667a9..5e3e9e940a13e7cb49d9d7e489944f17913ff6f6 100644 (file)
@@ -165,6 +165,35 @@ class MyControllerTest < Redmine::ControllerTest
     end
   end
 
+  def test_page_with_multiple_issuequery_blocks
+    user = User.find(2)
+    query1 = IssueQuery.create!(:name => 'All issues', :user => user, :column_names => [:tracker, :subject, :status, :assigned_to])
+    query2 = IssueQuery.create!(:name => 'Other issues', :user => user, :column_names => [:tracker, :subject, :priority])
+    user.pref.my_page_layout = {'top' => ['issuequery__1', 'issuequery']}
+    user.pref.my_page_settings = {
+        'issuequery' => {:query_id => query1.id, :columns => [:subject, :due_date]},
+        'issuequery__1' => {:query_id => query2.id}
+      }
+    user.pref.save!
+
+    get :page
+    assert_response :success
+
+    assert_select '#block-issuequery' do
+      assert_select 'h3', :text => /All issues/
+      assert_select 'table.issues th', :text => 'Due date'
+    end
+
+    assert_select '#block-issuequery__1' do
+      assert_select 'h3', :text => /Other issues/
+      assert_select 'table.issues th', :text => 'Priority'
+    end
+
+    assert_select '#block-select' do
+      assert_select 'option[value=?]:not([disabled])', 'issuequery__2', :text => 'Issues'
+    end
+  end
+
   def test_page_with_all_blocks
     blocks = Redmine::MyPage.blocks.keys
     preferences = User.find(2).pref
@@ -348,15 +377,15 @@ class MyControllerTest < Redmine::ControllerTest
   end
 
   def test_add_block
-    post :add_block, :block => 'issuesreportedbyme'
+    post :add_block, :block => 'issueswatched'
     assert_redirected_to '/my/page'
-    assert User.find(2).pref[:my_page_layout]['top'].include?('issuesreportedbyme')
+    assert User.find(2).pref[:my_page_layout]['top'].include?('issueswatched')
   end
 
   def test_add_block_xhr
-    xhr :post, :add_block, :block => 'issuesreportedbyme'
+    xhr :post, :add_block, :block => 'issueswatched'
     assert_response :success
-    assert_include 'issuesreportedbyme', User.find(2).pref[:my_page_layout]['top']
+    assert_include 'issueswatched', User.find(2).pref[:my_page_layout]['top']
   end
 
   def test_add_invalid_block_should_error