##// END OF EJS Templates
Fixes block reordering on my page (#2971)....
Jean-Philippe Lang -
r3080:9d120c872c1b
parent child
Show More
@@ -1,167 +1,166
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class MyController < ApplicationController
19 19 before_filter :require_login
20 20
21 21 helper :issues
22 22 helper :custom_fields
23 23
24 24 BLOCKS = { 'issuesassignedtome' => :label_assigned_to_me_issues,
25 25 'issuesreportedbyme' => :label_reported_issues,
26 26 'issueswatched' => :label_watched_issues,
27 27 'news' => :label_news_latest,
28 28 'calendar' => :label_calendar,
29 29 'documents' => :label_document_plural,
30 30 'timelog' => :label_spent_time
31 31 }.merge(Redmine::Views::MyPage::Block.additional_blocks).freeze
32 32
33 33 DEFAULT_LAYOUT = { 'left' => ['issuesassignedtome'],
34 34 'right' => ['issuesreportedbyme']
35 35 }.freeze
36 36
37 37 verify :xhr => true,
38 :session => :page_layout,
39 38 :only => [:add_block, :remove_block, :order_blocks]
40 39
41 40 def index
42 41 page
43 42 render :action => 'page'
44 43 end
45 44
46 45 # Show user's page
47 46 def page
48 47 @user = User.current
49 48 @blocks = @user.pref[:my_page_layout] || DEFAULT_LAYOUT
50 49 end
51 50
52 51 # Edit user's account
53 52 def account
54 53 @user = User.current
55 54 @pref = @user.pref
56 55 if request.post?
57 56 @user.attributes = params[:user]
58 57 @user.mail_notification = (params[:notification_option] == 'all')
59 58 @user.pref.attributes = params[:pref]
60 59 @user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
61 60 if @user.save
62 61 @user.pref.save
63 62 @user.notified_project_ids = (params[:notification_option] == 'selected' ? params[:notified_project_ids] : [])
64 63 set_language_if_valid @user.language
65 64 flash[:notice] = l(:notice_account_updated)
66 65 redirect_to :action => 'account'
67 66 return
68 67 end
69 68 end
70 69 @notification_options = [[l(:label_user_mail_option_all), 'all'],
71 70 [l(:label_user_mail_option_none), 'none']]
72 71 # Only users that belong to more than 1 project can select projects for which they are notified
73 72 # Note that @user.membership.size would fail since AR ignores :include association option when doing a count
74 73 @notification_options.insert 1, [l(:label_user_mail_option_selected), 'selected'] if @user.memberships.length > 1
75 74 @notification_option = @user.mail_notification? ? 'all' : (@user.notified_projects_ids.empty? ? 'none' : 'selected')
76 75 end
77 76
78 77 # Manage user's password
79 78 def password
80 79 @user = User.current
81 80 if @user.auth_source_id
82 81 flash[:error] = l(:notice_can_t_change_password)
83 82 redirect_to :action => 'account'
84 83 return
85 84 end
86 85 if request.post?
87 86 if @user.check_password?(params[:password])
88 87 @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
89 88 if @user.save
90 89 flash[:notice] = l(:notice_account_password_updated)
91 90 redirect_to :action => 'account'
92 91 end
93 92 else
94 93 flash[:error] = l(:notice_account_wrong_password)
95 94 end
96 95 end
97 96 end
98 97
99 98 # Create a new feeds key
100 99 def reset_rss_key
101 100 if request.post? && User.current.rss_token
102 101 User.current.rss_token.destroy
103 102 flash[:notice] = l(:notice_feeds_access_key_reseted)
104 103 end
105 104 redirect_to :action => 'account'
106 105 end
107 106
108 107 # User's page layout configuration
109 108 def page_layout
110 109 @user = User.current
111 110 @blocks = @user.pref[:my_page_layout] || DEFAULT_LAYOUT.dup
112 session[:page_layout] = @blocks
113 %w(top left right).each {|f| session[:page_layout][f] ||= [] }
114 111 @block_options = []
115 112 BLOCKS.each {|k, v| @block_options << [l("my.blocks.#{v}", :default => [v, v.to_s.humanize]), k.dasherize]}
116 113 end
117 114
118 115 # Add a block to user's page
119 116 # The block is added on top of the page
120 117 # params[:block] : id of the block to add
121 118 def add_block
122 119 block = params[:block].to_s.underscore
123 120 (render :nothing => true; return) unless block && (BLOCKS.keys.include? block)
124 121 @user = User.current
122 layout = @user.pref[:my_page_layout] || {}
125 123 # remove if already present in a group
126 %w(top left right).each {|f| (session[:page_layout][f] ||= []).delete block }
124 %w(top left right).each {|f| (layout[f] ||= []).delete block }
127 125 # add it on top
128 session[:page_layout]['top'].unshift block
126 layout['top'].unshift block
127 @user.pref[:my_page_layout] = layout
128 @user.pref.save
129 129 render :partial => "block", :locals => {:user => @user, :block_name => block}
130 130 end
131 131
132 132 # Remove a block to user's page
133 133 # params[:block] : id of the block to remove
134 134 def remove_block
135 135 block = params[:block].to_s.underscore
136 @user = User.current
136 137 # remove block in all groups
137 %w(top left right).each {|f| (session[:page_layout][f] ||= []).delete block }
138 layout = @user.pref[:my_page_layout] || {}
139 %w(top left right).each {|f| (layout[f] ||= []).delete block }
140 @user.pref[:my_page_layout] = layout
141 @user.pref.save
138 142 render :nothing => true
139 143 end
140 144
141 145 # Change blocks order on user's page
142 146 # params[:group] : group to order (top, left or right)
143 147 # params[:list-(top|left|right)] : array of block ids of the group
144 148 def order_blocks
145 149 group = params[:group]
150 @user = User.current
146 151 if group.is_a?(String)
147 152 group_items = (params["list-#{group}"] || []).collect(&:underscore)
148 153 if group_items and group_items.is_a? Array
154 layout = @user.pref[:my_page_layout] || {}
149 155 # remove group blocks if they are presents in other groups
150 156 %w(top left right).each {|f|
151 session[:page_layout][f] = (session[:page_layout][f] || []) - group_items
157 layout[f] = (layout[f] || []) - group_items
152 158 }
153 session[:page_layout][group] = group_items
159 layout[group] = group_items
160 @user.pref[:my_page_layout] = layout
161 @user.pref.save
154 162 end
155 163 end
156 164 render :nothing => true
157 165 end
158
159 # Save user's page layout
160 def page_layout_save
161 @user = User.current
162 @user.pref[:my_page_layout] = session[:page_layout] if session[:page_layout]
163 @user.pref.save
164 session[:page_layout] = nil
165 redirect_to :action => 'page'
166 end
167 166 end
@@ -1,107 +1,106
1 1 <script language="JavaScript">
2 2 //<![CDATA[
3 3 function recreateSortables() {
4 4 Sortable.destroy('list-top');
5 5 Sortable.destroy('list-left');
6 6 Sortable.destroy('list-right');
7 7
8 8 Sortable.create("list-top", {constraint:false, containment:['list-top','list-left','list-right'], dropOnEmpty:true, handle:'handle', onUpdate:function(){new Ajax.Request('/my/order_blocks?group=top', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize("list-top")})}, only:'mypage-box', tag:'div'})
9 9 Sortable.create("list-left", {constraint:false, containment:['list-top','list-left','list-right'], dropOnEmpty:true, handle:'handle', onUpdate:function(){new Ajax.Request('/my/order_blocks?group=left', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize("list-left")})}, only:'mypage-box', tag:'div'})
10 10 Sortable.create("list-right", {constraint:false, containment:['list-top','list-left','list-right'], dropOnEmpty:true, handle:'handle', onUpdate:function(){new Ajax.Request('/my/order_blocks?group=right', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize("list-right")})}, only:'mypage-box', tag:'div'})
11 11 }
12 12
13 13 function updateSelect() {
14 14 s = $('block-select')
15 15 for (var i = 0; i < s.options.length; i++) {
16 16 if ($('block_' + s.options[i].value)) {
17 17 s.options[i].disabled = true;
18 18 } else {
19 19 s.options[i].disabled = false;
20 20 }
21 21 }
22 22 s.options[0].selected = true;
23 23 }
24 24
25 25 function afterAddBlock() {
26 26 recreateSortables();
27 27 updateSelect();
28 28 }
29 29
30 30 function removeBlock(block) {
31 31 Effect.DropOut(block);
32 32 updateSelect();
33 33 }
34 34 //]]>
35 35 </script>
36 36
37 37 <div class="contextual">
38 38 <% form_tag({:action => "add_block"}, :id => "block-form") do %>
39 39 <%= select_tag 'block', "<option></option>" + options_for_select(@block_options), :id => "block-select" %>
40 40 <%= link_to_remote l(:button_add),
41 41 {:url => { :action => "add_block" },
42 42 :with => "Form.serialize('block-form')",
43 43 :update => "list-top",
44 44 :position => :top,
45 45 :complete => "afterAddBlock();"
46 46 }, :class => 'icon icon-add'
47 47 %>
48 48 <% end %>
49 <%= link_to l(:button_save), {:action => 'page_layout_save'}, :class => 'icon icon-save' %>
50 <%= link_to l(:button_cancel), {:action => 'page'}, :class => 'icon icon-cancel' %>
49 <%= link_to l(:button_back), {:action => 'page'}, :class => 'icon icon-cancel' %>
51 50 </div>
52 51
53 52 <h2><%=l(:label_my_page)%></h2>
54 53
55 54 <div id="list-top" class="block-receiver">
56 55 <% @blocks['top'].each do |b|
57 56 next unless MyController::BLOCKS.keys.include? b %>
58 57 <%= render :partial => 'block', :locals => {:user => @user, :block_name => b} %>
59 58 <% end if @blocks['top'] %>
60 59 </div>
61 60
62 61 <div id="list-left" class="splitcontentleft block-receiver">
63 62 <% @blocks['left'].each do |b|
64 63 next unless MyController::BLOCKS.keys.include? b %>
65 64 <%= render :partial => 'block', :locals => {:user => @user, :block_name => b} %>
66 65 <% end if @blocks['left'] %>
67 66 </div>
68 67
69 68 <div id="list-right" class="splitcontentright block-receiver">
70 69 <% @blocks['right'].each do |b|
71 70 next unless MyController::BLOCKS.keys.include? b %>
72 71 <%= render :partial => 'block', :locals => {:user => @user, :block_name => b} %>
73 72 <% end if @blocks['right'] %>
74 73 </div>
75 74
76 75 <%= sortable_element 'list-top',
77 76 :tag => 'div',
78 77 :only => 'mypage-box',
79 78 :handle => "handle",
80 79 :dropOnEmpty => true,
81 80 :containment => ['list-top', 'list-left', 'list-right'],
82 81 :constraint => false,
83 82 :url => { :action => "order_blocks", :group => "top" }
84 83 %>
85 84
86 85
87 86 <%= sortable_element 'list-left',
88 87 :tag => 'div',
89 88 :only => 'mypage-box',
90 89 :handle => "handle",
91 90 :dropOnEmpty => true,
92 91 :containment => ['list-top', 'list-left', 'list-right'],
93 92 :constraint => false,
94 93 :url => { :action => "order_blocks", :group => "left" }
95 94 %>
96 95
97 96 <%= sortable_element 'list-right',
98 97 :tag => 'div',
99 98 :only => 'mypage-box',
100 99 :handle => "handle",
101 100 :dropOnEmpty => true,
102 101 :containment => ['list-top', 'list-left', 'list-right'],
103 102 :constraint => false,
104 103 :url => { :action => "order_blocks", :group => "right" }
105 104 %>
106 105
107 106 <%= javascript_tag "updateSelect()" %>
@@ -1,24 +1,31
1 1 ---
2 2 user_preferences_001:
3 3 others: |
4 4 ---
5 5 :my_page_layout:
6 6 left:
7 - latest_news
7 - latestnews
8 8 - documents
9 9 right:
10 - issues_assigned_to_me
11 - issues_reported_by_me
10 - issuesassignedtome
12 11 top:
13 12 - calendar
14 13
15 14 id: 1
16 15 user_id: 1
17 16 hide_mail: true
18 17 user_preferences_002:
19 others: |+
20 --- {}
21
18 others: |
19 ---
20 :my_page_layout:
21 left:
22 - latestnews
23 - documents
24 right:
25 - issuesassignedtome
26 top:
27 - calendar
28
22 29 id: 2
23 30 user_id: 3
24 31 hide_mail: false No newline at end of file
@@ -1,108 +1,132
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.dirname(__FILE__) + '/../test_helper'
19 19 require 'my_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class MyController; def rescue_action(e) raise e end; end
23 23
24 24 class MyControllerTest < ActionController::TestCase
25 fixtures :users, :issues, :issue_statuses, :trackers, :enumerations, :custom_fields
25 fixtures :users, :user_preferences, :roles, :projects, :issues, :issue_statuses, :trackers, :enumerations, :custom_fields
26 26
27 27 def setup
28 28 @controller = MyController.new
29 29 @request = ActionController::TestRequest.new
30 30 @request.session[:user_id] = 2
31 31 @response = ActionController::TestResponse.new
32 32 end
33 33
34 34 def test_index
35 35 get :index
36 36 assert_response :success
37 37 assert_template 'page'
38 38 end
39 39
40 40 def test_page
41 41 get :page
42 42 assert_response :success
43 43 assert_template 'page'
44 44 end
45 45
46 46 def test_my_account_should_show_editable_custom_fields
47 47 get :account
48 48 assert_response :success
49 49 assert_template 'account'
50 50 assert_equal User.find(2), assigns(:user)
51 51
52 52 assert_tag :input, :attributes => { :name => 'user[custom_field_values][4]'}
53 53 end
54 54
55 55 def test_my_account_should_not_show_non_editable_custom_fields
56 56 UserCustomField.find(4).update_attribute :editable, false
57 57
58 58 get :account
59 59 assert_response :success
60 60 assert_template 'account'
61 61 assert_equal User.find(2), assigns(:user)
62 62
63 63 assert_no_tag :input, :attributes => { :name => 'user[custom_field_values][4]'}
64 64 end
65 65
66 66 def test_update_account
67 67 post :account, :user => {:firstname => "Joe",
68 68 :login => "root",
69 69 :admin => 1,
70 70 :custom_field_values => {"4" => "0100562500"}}
71 71 assert_redirected_to 'my/account'
72 72 user = User.find(2)
73 73 assert_equal user, assigns(:user)
74 74 assert_equal "Joe", user.firstname
75 75 assert_equal "jsmith", user.login
76 76 assert_equal "0100562500", user.custom_value_for(4).value
77 77 assert !user.admin?
78 78 end
79 79
80 80 def test_change_password
81 81 get :password
82 82 assert_response :success
83 83 assert_template 'password'
84 84
85 85 # non matching password confirmation
86 86 post :password, :password => 'jsmith',
87 87 :new_password => 'hello',
88 88 :new_password_confirmation => 'hello2'
89 89 assert_response :success
90 90 assert_template 'password'
91 91 assert_tag :tag => "div", :attributes => { :class => "errorExplanation" }
92 92
93 93 # wrong password
94 94 post :password, :password => 'wrongpassword',
95 95 :new_password => 'hello',
96 96 :new_password_confirmation => 'hello'
97 97 assert_response :success
98 98 assert_template 'password'
99 99 assert_equal 'Wrong password', flash[:error]
100 100
101 101 # good password
102 102 post :password, :password => 'jsmith',
103 103 :new_password => 'hello',
104 104 :new_password_confirmation => 'hello'
105 105 assert_redirected_to 'my/account'
106 106 assert User.try_to_login('jsmith', 'hello')
107 107 end
108
109 def test_page_layout
110 get :page_layout
111 assert_response :success
112 assert_template 'page_layout'
113 end
114
115 def test_add_block
116 xhr :post, :add_block, :block => 'issuesreportedbyme'
117 assert_response :success
118 assert User.find(2).pref[:my_page_layout]['top'].include?('issuesreportedbyme')
119 end
120
121 def test_remove_block
122 xhr :post, :remove_block, :block => 'issuesassignedtome'
123 assert_response :success
124 assert !User.find(2).pref[:my_page_layout].values.flatten.include?('issuesassignedtome')
125 end
126
127 def test_order_blocks
128 xhr :post, :order_blocks, :group => 'left', 'list-left' => ['documents', 'calendar', 'latestnews']
129 assert_response :success
130 assert_equal ['documents', 'calendar', 'latestnews'], User.find(2).pref[:my_page_layout]['left']
131 end
108 132 end
General Comments 0
You need to be logged in to leave comments. Login now