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']} |