Feature #1565 » master_1565.diff
| app/controllers/my_controller.rb | ||
|---|---|---|
| 22 | 22 | |
| 23 | 23 |
helper :issues |
| 24 | 24 |
helper :users |
| 25 |
helper :queries |
|
| 25 | 26 |
helper :custom_fields |
| 26 | 27 | |
| 27 | 28 |
BLOCKS = { 'issuesassignedtome' => :label_assigned_to_me_issues,
|
| ... | ... | |
| 45 | 46 |
# Show user's page |
| 46 | 47 |
def page |
| 47 | 48 |
@user = User.current |
| 49 |
@all_blocks = Redmine::Views::MyPage::Block.all_blocks |
|
| 48 | 50 |
@blocks = @user.pref[:my_page_layout] || DEFAULT_LAYOUT |
| 49 | 51 |
end |
| 50 | 52 | |
| ... | ... | |
| 139 | 141 |
# User's page layout configuration |
| 140 | 142 |
def page_layout |
| 141 | 143 |
@user = User.current |
| 144 |
@all_blocks = Redmine::Views::MyPage::Block.all_blocks |
|
| 142 | 145 |
@blocks = @user.pref[:my_page_layout] || DEFAULT_LAYOUT.dup |
| 146 |
blocks_in_use = @blocks.values.flatten |
|
| 147 | ||
| 148 |
# users may lose access to issue queries, it doesnt hurt to clean up their |
|
| 149 |
# layout from time to time: |
|
| 150 |
if (inaccessible_blocks = blocks_in_use - @all_blocks.keys).any? |
|
| 151 |
%w(top left right).each do |f| |
|
| 152 |
@blocks[f] -= inaccessible_blocks if @blocks[f] |
|
| 153 |
end |
|
| 154 |
@user.pref[:my_page_layout] = @blocks |
|
| 155 |
@user.pref.save |
|
| 156 |
end |
|
| 157 | ||
| 143 | 158 |
@block_options = [] |
| 144 | 159 |
BLOCKS.each do |k, v| |
| 145 |
unless @blocks.values.flatten.include?(k)
|
|
| 160 |
unless blocks_in_use.include?(k)
|
|
| 146 | 161 |
@block_options << [l("my.blocks.#{v}", :default => [v, v.to_s.humanize]), k.dasherize]
|
| 147 | 162 |
end |
| 148 | 163 |
end |
| 164 | ||
| 165 |
@issue_query_block_options = [] |
|
| 166 |
Redmine::Views::MyPage::Block.issue_query_blocks.each do |id, name| |
|
| 167 |
unless blocks_in_use.include?(id) |
|
| 168 |
@issue_query_block_options << [name, id] |
|
| 169 |
end |
|
| 170 |
end |
|
| 149 | 171 |
end |
| 150 | 172 | |
| 151 | 173 |
# Add a block to user's page |
| ... | ... | |
| 153 | 175 |
# params[:block] : id of the block to add |
| 154 | 176 |
def add_block |
| 155 | 177 |
block = params[:block].to_s.underscore |
| 156 |
if block.present? && BLOCKS.key?(block)
|
|
| 178 |
if block.present? && Redmine::Views::MyPage::Block.all_blocks.key?(block)
|
|
| 157 | 179 |
@user = User.current |
| 158 | 180 |
layout = @user.pref[:my_page_layout] || {}
|
| 159 | 181 |
# remove if already present in a group |
| app/helpers/my_helper.rb | ||
|---|---|---|
| 31 | 31 |
Document.visible.order("#{Document.table_name}.created_on DESC").limit(10).to_a
|
| 32 | 32 |
end |
| 33 | 33 | |
| 34 |
def mypage_available_block_options |
|
| 35 |
options = options_for_select(@block_options) |
|
| 36 |
options += grouped_options_for_select([[l(:label_query_plural), @issue_query_block_options]]) if @issue_query_block_options.any? |
|
| 37 |
return options |
|
| 38 |
end |
|
| 39 | ||
| 40 |
def render_mypage_box(block, user = @user) |
|
| 41 |
locals = { user: user }
|
|
| 42 |
partial = case block.to_s |
|
| 43 |
when /^issue_query_(\w+)$/ |
|
| 44 |
locals[:query] = IssueQuery.visible.find $1 |
|
| 45 |
"my/blocks/issue_query" |
|
| 46 |
else |
|
| 47 |
"my/blocks/#{block}"
|
|
| 48 |
end |
|
| 49 |
render partial: partial, locals: locals |
|
| 50 |
end |
|
| 51 | ||
| 34 | 52 |
def issuesassignedtome_items |
| 35 | 53 |
Issue.visible.open. |
| 36 | 54 |
where(:assigned_to_id => ([User.current.id] + User.current.group_ids)). |
| app/views/my/_block.html.erb | ||
|---|---|---|
| 5 | 5 |
</div> |
| 6 | 6 | |
| 7 | 7 |
<div class="handle"> |
| 8 |
<%= render :partial => "my/blocks/#{block_name}", :locals => { :user => user } %>
|
|
| 8 |
<%= render_mypage_box block_name, user %>
|
|
| 9 | 9 |
</div> |
| 10 | 10 |
</div> |
| app/views/my/blocks/_issue_query.html.erb | ||
|---|---|---|
| 1 |
<h3> |
|
| 2 |
<%= link_to query.name, query.project ? project_issues_path(query.project, query_id: query.id) : issues_path(query_id: query.id) %> |
|
| 3 |
(<%= link_to_project(query.project) + ', ' if query.project %><%= query.issue_count %>) |
|
| 4 |
</h3> |
|
| 5 | ||
| 6 |
<% @issue_count_by_group = query.issue_count_by_group %> |
|
| 7 | ||
| 8 |
<% if query.issue_count > 0 %> |
|
| 9 |
<%= form_tag({}) do %>
|
|
| 10 |
<table class="list issues"> |
|
| 11 |
<thead><tr> |
|
| 12 |
<% query.inline_columns.each do |column| %> |
|
| 13 |
<th><%= column.caption %></th> |
|
| 14 |
<% end %> |
|
| 15 |
</tr></thead> |
|
| 16 |
<% previous_group = false %> |
|
| 17 |
<tbody> |
|
| 18 |
<% issue_list(query.issues(limit: 10)) do |issue, level| -%> |
|
| 19 | ||
| 20 |
<% if query.grouped? && (group = query.group_by_column.value(issue)) != previous_group %> |
|
| 21 |
<% reset_cycle %> |
|
| 22 |
<tr class="group open"> |
|
| 23 |
<td colspan="<%= query.inline_columns.size + 2 %>"> |
|
| 24 |
<span class="expander" onclick="toggleRowGroup(this);"> </span> |
|
| 25 |
<%= group.blank? ? l(:label_none) : column_content(query.group_by_column, issue) %> <span class="count"><%= @issue_count_by_group[group] %></span> |
|
| 26 |
<%= link_to_function("#{l(:button_collapse_all)}/#{l(:button_expand_all)}",
|
|
| 27 |
"toggleAllRowGroups(this)", :class => 'toggle-all') %> |
|
| 28 |
</td> |
|
| 29 |
</tr> |
|
| 30 |
<% previous_group = group %> |
|
| 31 |
<% end %> |
|
| 32 | ||
| 33 |
<tr id="issue-<%= issue.id %>" class="hascontextmenu <%= cycle('odd', 'even') %> <%= issue.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>">
|
|
| 34 |
<td class="id"> |
|
| 35 |
<%= check_box_tag("ids[]", issue.id, false, :style => 'display:none;', :id => nil) %>
|
|
| 36 |
<%= link_to(issue.id, issue_path(issue)) %> |
|
| 37 |
</td> |
|
| 38 |
<%= raw query.inline_columns.reject{|c| c.name == :id}.map {|column| "<td class=\"#{column.css_classes}\">#{column_content(column, issue)}</td>"}.join %>
|
|
| 39 |
</tr> |
|
| 40 | ||
| 41 |
<% end %> |
|
| 42 |
</tbody> |
|
| 43 |
</table> |
|
| 44 |
<% end %> |
|
| 45 | ||
| 46 |
<% else %> |
|
| 47 |
<p class="nodata"><%= l(:label_no_data) %></p> |
|
| 48 |
<% end %> |
|
| 49 | ||
| 50 |
<% content_for :header_tags do %> |
|
| 51 |
<%= auto_discovery_link_tag(:atom, |
|
| 52 |
{controller: 'issues', action: 'index', query_id: query.id, project_id: query.project.try(:identifier),
|
|
| 53 |
format: 'atom', key: User.current.rss_key}, |
|
| 54 |
{:title => query.name}) %>
|
|
| 55 |
<% end %> |
|
| 56 | ||
| app/views/my/page.html.erb | ||
|---|---|---|
| 6 | 6 | |
| 7 | 7 |
<div id="list-top"> |
| 8 | 8 |
<% @blocks['top'].each do |b| |
| 9 |
next unless MyController::BLOCKS.keys.include? b %>
|
|
| 9 |
next unless @all_blocks.keys.include? b %>
|
|
| 10 | 10 |
<div class="mypage-box"> |
| 11 |
<%= render :partial => "my/blocks/#{b}", :locals => { :user => @user } %>
|
|
| 11 |
<%= render_mypage_box b %>
|
|
| 12 | 12 |
</div> |
| 13 | 13 |
<% end if @blocks['top'] %> |
| 14 | 14 |
</div> |
| 15 | 15 | |
| 16 | 16 |
<div id="list-left" class="splitcontentleft"> |
| 17 | 17 |
<% @blocks['left'].each do |b| |
| 18 |
next unless MyController::BLOCKS.keys.include? b %>
|
|
| 18 |
next unless @all_blocks.keys.include? b %>
|
|
| 19 | 19 |
<div class="mypage-box"> |
| 20 |
<%= render :partial => "my/blocks/#{b}", :locals => { :user => @user } %>
|
|
| 20 |
<%= render_mypage_box b %>
|
|
| 21 | 21 |
</div> |
| 22 | 22 |
<% end if @blocks['left'] %> |
| 23 | 23 |
</div> |
| 24 | 24 | |
| 25 | 25 |
<div id="list-right" class="splitcontentright"> |
| 26 | 26 |
<% @blocks['right'].each do |b| |
| 27 |
next unless MyController::BLOCKS.keys.include? b %>
|
|
| 27 |
next unless @all_blocks.keys.include? b %>
|
|
| 28 | 28 |
<div class="mypage-box"> |
| 29 |
<%= render :partial => "my/blocks/#{b}", :locals => { :user => @user } %>
|
|
| 29 |
<%= render_mypage_box b %>
|
|
| 30 | 30 |
</div> |
| 31 | 31 |
<% end if @blocks['right'] %> |
| 32 | 32 |
</div> |
| app/views/my/page_layout.html.erb | ||
|---|---|---|
| 3 | 3 |
<%= form_tag({:action => "add_block"}, :id => "block-form") do %>
|
| 4 | 4 |
<%= label_tag('block-select', l(:label_my_page_block)) %>:
|
| 5 | 5 |
<%= select_tag 'block', |
| 6 |
content_tag('option') + options_for_select(@block_options),
|
|
| 6 |
content_tag('option') + mypage_available_block_options,
|
|
| 7 | 7 |
:id => "block-select" %> |
| 8 | 8 |
<%= link_to l(:button_add), '#', :onclick => '$("#block-form").submit()', :class => 'icon icon-add' %>
|
| 9 | 9 |
<% end %> |
| ... | ... | |
| 15 | 15 | |
| 16 | 16 |
<div id="list-top" class="block-receiver"> |
| 17 | 17 |
<% @blocks['top'].each do |b| |
| 18 |
next unless MyController::BLOCKS.keys.include? b %>
|
|
| 18 |
next unless @all_blocks.keys.include? b %>
|
|
| 19 | 19 |
<%= render :partial => 'block', :locals => {:user => @user, :block_name => b} %>
|
| 20 | 20 |
<% end if @blocks['top'] %> |
| 21 | 21 |
</div> |
| 22 | 22 | |
| 23 | 23 |
<div id="list-left" class="splitcontentleft block-receiver"> |
| 24 | 24 |
<% @blocks['left'].each do |b| |
| 25 |
next unless MyController::BLOCKS.keys.include? b %>
|
|
| 25 |
next unless @all_blocks.keys.include? b %>
|
|
| 26 | 26 |
<%= render :partial => 'block', :locals => {:user => @user, :block_name => b} %>
|
| 27 | 27 |
<% end if @blocks['left'] %> |
| 28 | 28 |
</div> |
| 29 | 29 | |
| 30 | 30 |
<div id="list-right" class="splitcontentright block-receiver"> |
| 31 | 31 |
<% @blocks['right'].each do |b| |
| 32 |
next unless MyController::BLOCKS.keys.include? b %>
|
|
| 32 |
next unless @all_blocks.keys.include? b %>
|
|
| 33 | 33 |
<%= render :partial => 'block', :locals => {:user => @user, :block_name => b} %>
|
| 34 | 34 |
<% end if @blocks['right'] %> |
| 35 | 35 |
</div> |
| lib/redmine/views/my_page/block.rb | ||
|---|---|---|
| 19 | 19 |
module Views |
| 20 | 20 |
module MyPage |
| 21 | 21 |
module Block |
| 22 |
def self.all_blocks |
|
| 23 |
MyController::BLOCKS.merge issue_query_blocks |
|
| 24 |
end |
|
| 25 | ||
| 26 |
def self.issue_query_blocks |
|
| 27 |
Hash[IssueQuery.visible.map{|q| ["issue_query_#{q.id}", q.name]}]
|
|
| 28 |
end |
|
| 29 | ||
| 22 | 30 |
def self.additional_blocks |
| 23 | 31 |
@@additional_blocks ||= Dir.glob("#{Redmine::Plugin.directory}/*/app/views/my/blocks/_*.{rhtml,erb}").inject({}) do |h,file|
|
| 24 | 32 |
name = File.basename(file).split('.').first.gsub(/^_/, '')
|
| test/functional/my_controller_test.rb | ||
|---|---|---|
| 19 | 19 | |
| 20 | 20 |
class MyControllerTest < ActionController::TestCase |
| 21 | 21 |
fixtures :users, :email_addresses, :user_preferences, :roles, :projects, :members, :member_roles, |
| 22 |
:queries, :enabled_modules, |
|
| 22 | 23 |
:issues, :issue_statuses, :trackers, :enumerations, :custom_fields, :auth_sources |
| 23 | 24 | |
| 24 | 25 |
def setup |
| ... | ... | |
| 37 | 38 |
assert_template 'page' |
| 38 | 39 |
end |
| 39 | 40 | |
| 41 |
def test_page_with_custom_query |
|
| 42 |
preferences = User.find(2).pref |
|
| 43 |
issue = Issue.find 1 |
|
| 44 |
issue.update_attribute :tracker_id, 3 |
|
| 45 |
q = IssueQuery.find 4 |
|
| 46 | ||
| 47 |
preferences[:my_page_layout] = {'top' => ["issue_query_#{q.id}"]}
|
|
| 48 |
preferences.save! |
|
| 49 | ||
| 50 |
get :page |
|
| 51 |
assert_response :success |
|
| 52 |
assert_select 'h3', /#{q.name}/
|
|
| 53 |
assert_select 'td.id a', /#{issue.id}/
|
|
| 54 |
assert_select 'td.subject a', /print recipes/ |
|
| 55 |
end |
|
| 56 | ||
| 57 |
def test_page_layout_should_remove_inaccessible_query |
|
| 58 |
user = User.find 2 |
|
| 59 |
q = IssueQuery.find 3 |
|
| 60 |
assert !IssueQuery.visible(user).include?(q) |
|
| 61 |
preferences = user.pref |
|
| 62 |
preferences[:my_page_layout] = {'top' => ["issue_query_#{q.id}"]}
|
|
| 63 |
preferences.save! |
|
| 64 | ||
| 65 |
get :page_layout |
|
| 66 |
assert_response :success |
|
| 67 |
user.reload |
|
| 68 |
preferences = user.pref |
|
| 69 |
assert_equal [], preferences[:my_page_layout]['top'] |
|
| 70 |
end |
|
| 71 | ||
| 40 | 72 |
def test_page_with_timelog_block |
| 41 | 73 |
preferences = User.find(2).pref |
| 42 | 74 |
preferences[:my_page_layout] = {'top' => ['timelog']}
|