@@ -0,0 +1,11 | |||||
|
1 | <h2><%=l(:label_confirmation)%></h2> | |||
|
2 | <div class="warning"> | |||
|
3 | <p><%= simple_format l(:text_account_destroy_confirmation)%></p> | |||
|
4 | <p> | |||
|
5 | <% form_tag({}) do %> | |||
|
6 | <label><%= check_box_tag 'confirm', 1 %> <%= l(:general_text_Yes) %></label> | |||
|
7 | <%= submit_tag l(:button_delete_my_account) %> | | |||
|
8 | <%= link_to l(:button_cancel), :action => 'account' %> | |||
|
9 | <% end %> | |||
|
10 | </p> | |||
|
11 | </div> |
@@ -131,14 +131,6 class AccountController < ApplicationController | |||||
131 |
|
131 | |||
132 | private |
|
132 | private | |
133 |
|
133 | |||
134 | def logout_user |
|
|||
135 | if User.current.logged? |
|
|||
136 | cookies.delete :autologin |
|
|||
137 | Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin']) |
|
|||
138 | self.logged_user = nil |
|
|||
139 | end |
|
|||
140 | end |
|
|||
141 |
|
||||
142 | def authenticate_user |
|
134 | def authenticate_user | |
143 | if Setting.openid? && using_open_id? |
|
135 | if Setting.openid? && using_open_id? | |
144 | open_id_authenticate(params[:openid_url]) |
|
136 | open_id_authenticate(params[:openid_url]) |
@@ -126,6 +126,15 class ApplicationController < ActionController::Base | |||||
126 | end |
|
126 | end | |
127 | end |
|
127 | end | |
128 |
|
128 | |||
|
129 | # Logs out current user | |||
|
130 | def logout_user | |||
|
131 | if User.current.logged? | |||
|
132 | cookies.delete :autologin | |||
|
133 | Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin']) | |||
|
134 | self.logged_user = nil | |||
|
135 | end | |||
|
136 | end | |||
|
137 | ||||
129 | # check if login is globally required to access the application |
|
138 | # check if login is globally required to access the application | |
130 | def check_if_login_required |
|
139 | def check_if_login_required | |
131 | # no check needed if user is already logged in |
|
140 | # no check needed if user is already logged in |
@@ -65,6 +65,24 class MyController < ApplicationController | |||||
65 | end |
|
65 | end | |
66 | end |
|
66 | end | |
67 |
|
67 | |||
|
68 | # Destroys user's account | |||
|
69 | def destroy | |||
|
70 | @user = User.current | |||
|
71 | unless @user.own_account_deletable? | |||
|
72 | redirect_to :action => 'account' | |||
|
73 | return | |||
|
74 | end | |||
|
75 | ||||
|
76 | if request.post? && params[:confirm] | |||
|
77 | @user.destroy | |||
|
78 | if @user.destroyed? | |||
|
79 | logout_user | |||
|
80 | flash[:notice] = l(:notice_account_deleted) | |||
|
81 | end | |||
|
82 | redirect_to home_path | |||
|
83 | end | |||
|
84 | end | |||
|
85 | ||||
68 | # Manage user's password |
|
86 | # Manage user's password | |
69 | def password |
|
87 | def password | |
70 | @user = User.current |
|
88 | @user = User.current |
@@ -482,6 +482,12 class User < Principal | |||||
482 | allowed_to?(action, nil, options.reverse_merge(:global => true), &block) |
|
482 | allowed_to?(action, nil, options.reverse_merge(:global => true), &block) | |
483 | end |
|
483 | end | |
484 |
|
484 | |||
|
485 | # Returns true if the user is allowed to delete his own account | |||
|
486 | def own_account_deletable? | |||
|
487 | Setting.unsubscribe? && | |||
|
488 | (!admin? || User.active.first(:conditions => ["admin = ? AND id <> ?", true, id]).present?) | |||
|
489 | end | |||
|
490 | ||||
485 | safe_attributes 'login', |
|
491 | safe_attributes 'login', | |
486 | 'firstname', |
|
492 | 'firstname', | |
487 | 'lastname', |
|
493 | 'lastname', |
@@ -3,6 +3,9 | |||||
3 | <p><%=l(:field_login)%>: <strong><%= link_to_user(@user, :format => :username) %></strong><br /> |
|
3 | <p><%=l(:field_login)%>: <strong><%= link_to_user(@user, :format => :username) %></strong><br /> | |
4 | <%=l(:field_created_on)%>: <%= format_time(@user.created_on) %></p> |
|
4 | <%=l(:field_created_on)%>: <%= format_time(@user.created_on) %></p> | |
5 |
|
5 | |||
|
6 | <% if @user.own_account_deletable? %> | |||
|
7 | <p><%= link_to(l(:button_delete_my_account), {:action => 'destroy'}, :class => 'icon icon-del') %></p> | |||
|
8 | <% end %> | |||
6 |
|
9 | |||
7 | <h4><%= l(:label_feeds_access_key) %></h4> |
|
10 | <h4><%= l(:label_feeds_access_key) %></h4> | |
8 |
|
11 |
@@ -10,6 +10,8 | |||||
10 | [l(:label_registration_manual_activation), "2"], |
|
10 | [l(:label_registration_manual_activation), "2"], | |
11 | [l(:label_registration_automatic_activation), "3"]] %></p> |
|
11 | [l(:label_registration_automatic_activation), "3"]] %></p> | |
12 |
|
12 | |||
|
13 | <p><%= setting_check_box :unsubscribe %></p> | |||
|
14 | ||||
13 | <p><%= setting_text_field :password_min_length, :size => 6 %></p> |
|
15 | <p><%= setting_text_field :password_min_length, :size => 6 %></p> | |
14 |
|
16 | |||
15 | <p><%= setting_check_box :lost_password, :label => :label_password_lost %></p> |
|
17 | <p><%= setting_check_box :lost_password, :label => :label_password_lost %></p> |
@@ -173,6 +173,7 en: | |||||
173 | notice_gantt_chart_truncated: "The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})" |
|
173 | notice_gantt_chart_truncated: "The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})" | |
174 | notice_issue_successful_create: "Issue %{id} created." |
|
174 | notice_issue_successful_create: "Issue %{id} created." | |
175 | notice_issue_update_conflict: "The issue has been updated by an other user while you were editing it." |
|
175 | notice_issue_update_conflict: "The issue has been updated by an other user while you were editing it." | |
|
176 | notice_account_deleted: "Your account has been permanently deleted." | |||
176 |
|
177 | |||
177 | error_can_t_load_default_data: "Default configuration could not be loaded: %{value}" |
|
178 | error_can_t_load_default_data: "Default configuration could not be loaded: %{value}" | |
178 | error_scm_not_found: "The entry or revision was not found in the repository." |
|
179 | error_scm_not_found: "The entry or revision was not found in the repository." | |
@@ -383,6 +384,7 en: | |||||
383 | setting_issue_group_assignment: Allow issue assignment to groups |
|
384 | setting_issue_group_assignment: Allow issue assignment to groups | |
384 | setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues |
|
385 | setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues | |
385 | setting_commit_cross_project_ref: Allow issues of all the other projects to be referenced and fixed |
|
386 | setting_commit_cross_project_ref: Allow issues of all the other projects to be referenced and fixed | |
|
387 | setting_unsubscribe: Allow users to unsubscribe | |||
386 |
|
388 | |||
387 | permission_add_project: Create project |
|
389 | permission_add_project: Create project | |
388 | permission_add_subprojects: Create subprojects |
|
390 | permission_add_subprojects: Create subprojects | |
@@ -894,6 +896,7 en: | |||||
894 | button_show: Show |
|
896 | button_show: Show | |
895 | button_edit_section: Edit this section |
|
897 | button_edit_section: Edit this section | |
896 | button_export: Export |
|
898 | button_export: Export | |
|
899 | button_delete_my_account: Delete my account | |||
897 |
|
900 | |||
898 | status_active: active |
|
901 | status_active: active | |
899 | status_registered: registered |
|
902 | status_registered: registered | |
@@ -978,6 +981,7 en: | |||||
978 | text_issue_conflict_resolution_overwrite: "Apply my changes anyway (previous notes will be kept but some changes may be overwritten)" |
|
981 | text_issue_conflict_resolution_overwrite: "Apply my changes anyway (previous notes will be kept but some changes may be overwritten)" | |
979 | text_issue_conflict_resolution_add_notes: "Add my notes and discard my other changes" |
|
982 | text_issue_conflict_resolution_add_notes: "Add my notes and discard my other changes" | |
980 | text_issue_conflict_resolution_cancel: "Discard all my changes and redisplay %{link}" |
|
983 | text_issue_conflict_resolution_cancel: "Discard all my changes and redisplay %{link}" | |
|
984 | text_account_destroy_confirmation: "Are you sure you want to proceed?\nYour account will be permanently deleted, with no way to reactivate it." | |||
981 |
|
985 | |||
982 | default_role_manager: Manager |
|
986 | default_role_manager: Manager | |
983 | default_role_developer: Developer |
|
987 | default_role_developer: Developer |
@@ -188,6 +188,7 fr: | |||||
188 | notice_gantt_chart_truncated: "Le diagramme a Γ©tΓ© tronquΓ© car il excΓ¨de le nombre maximal d'Γ©lΓ©ments pouvant Γͺtre affichΓ©s (%{max})" |
|
188 | notice_gantt_chart_truncated: "Le diagramme a Γ©tΓ© tronquΓ© car il excΓ¨de le nombre maximal d'Γ©lΓ©ments pouvant Γͺtre affichΓ©s (%{max})" | |
189 | notice_issue_successful_create: "La demande %{id} a été créée." |
|
189 | notice_issue_successful_create: "La demande %{id} a été créée." | |
190 | notice_issue_update_conflict: "La demande a Γ©tΓ© mise Γ jour par un autre utilisateur pendant que vous la modifiez." |
|
190 | notice_issue_update_conflict: "La demande a Γ©tΓ© mise Γ jour par un autre utilisateur pendant que vous la modifiez." | |
|
191 | notice_account_deleted: "Votre compte a Γ©tΓ© dΓ©finitivement supprimΓ©." | |||
191 |
|
192 | |||
192 | error_can_t_load_default_data: "Une erreur s'est produite lors du chargement du paramΓ©trage : %{value}" |
|
193 | error_can_t_load_default_data: "Une erreur s'est produite lors du chargement du paramΓ©trage : %{value}" | |
193 | error_scm_not_found: "L'entrΓ©e et/ou la rΓ©vision demandΓ©e n'existe pas dans le dΓ©pΓ΄t." |
|
194 | error_scm_not_found: "L'entrΓ©e et/ou la rΓ©vision demandΓ©e n'existe pas dans le dΓ©pΓ΄t." | |
@@ -379,6 +380,7 fr: | |||||
379 | setting_issue_group_assignment: Permettre l'assignement des demandes aux groupes |
|
380 | setting_issue_group_assignment: Permettre l'assignement des demandes aux groupes | |
380 | setting_default_issue_start_date_to_creation_date: Donner Γ la date de dΓ©but d'une nouvelle demande la valeur de la date du jour |
|
381 | setting_default_issue_start_date_to_creation_date: Donner Γ la date de dΓ©but d'une nouvelle demande la valeur de la date du jour | |
381 | setting_commit_cross_project_ref: Permettre le rΓ©fΓ©rencement et la rΓ©solution des demandes de tous les autres projets |
|
382 | setting_commit_cross_project_ref: Permettre le rΓ©fΓ©rencement et la rΓ©solution des demandes de tous les autres projets | |
|
383 | setting_unsubscribe: Permettre aux utilisateurs de se dΓ©sinscrire | |||
382 |
|
384 | |||
383 | permission_add_project: CrΓ©er un projet |
|
385 | permission_add_project: CrΓ©er un projet | |
384 | permission_add_subprojects: CrΓ©er des sous-projets |
|
386 | permission_add_subprojects: CrΓ©er des sous-projets | |
@@ -868,6 +870,7 fr: | |||||
868 | button_show: Afficher |
|
870 | button_show: Afficher | |
869 | button_edit_section: Modifier cette section |
|
871 | button_edit_section: Modifier cette section | |
870 | button_export: Exporter |
|
872 | button_export: Exporter | |
|
873 | button_delete_my_account: Supprimer mon compte | |||
871 |
|
874 | |||
872 | status_active: actif |
|
875 | status_active: actif | |
873 | status_registered: enregistrΓ© |
|
876 | status_registered: enregistrΓ© | |
@@ -934,6 +937,7 fr: | |||||
934 | text_issue_conflict_resolution_overwrite: "Appliquer quand mΓͺme ma mise Γ jour (les notes prΓ©cΓ©dentes seront conservΓ©es mais des changements pourront Γͺtre Γ©crasΓ©s)" |
|
937 | text_issue_conflict_resolution_overwrite: "Appliquer quand mΓͺme ma mise Γ jour (les notes prΓ©cΓ©dentes seront conservΓ©es mais des changements pourront Γͺtre Γ©crasΓ©s)" | |
935 | text_issue_conflict_resolution_add_notes: "Ajouter mes notes et ignorer mes autres changements" |
|
938 | text_issue_conflict_resolution_add_notes: "Ajouter mes notes et ignorer mes autres changements" | |
936 | text_issue_conflict_resolution_cancel: "Annuler ma mise Γ jour et rΓ©afficher %{link}" |
|
939 | text_issue_conflict_resolution_cancel: "Annuler ma mise Γ jour et rΓ©afficher %{link}" | |
|
940 | text_account_destroy_confirmation: "Γtes-vous sΓ»r de vouloir continuer ?\nVotre compte sera dΓ©finitivement supprimΓ©, sans aucune possibilitΓ© de le rΓ©activer." | |||
937 |
|
941 | |||
938 | default_role_manager: "Manager " |
|
942 | default_role_manager: "Manager " | |
939 | default_role_developer: "DΓ©veloppeur " |
|
943 | default_role_developer: "DΓ©veloppeur " |
@@ -78,6 +78,8 ActionController::Routing::Routes.draw do |map| | |||||
78 |
|
78 | |||
79 | map.connect 'my/account', :controller => 'my', :action => 'account', |
|
79 | map.connect 'my/account', :controller => 'my', :action => 'account', | |
80 | :conditions => {:method => [:get, :post]} |
|
80 | :conditions => {:method => [:get, :post]} | |
|
81 | map.connect 'my/account/destroy', :controller => 'my', :action => 'destroy', | |||
|
82 | :conditions => {:method => [:get, :post]} | |||
81 | map.connect 'my/page', :controller => 'my', :action => 'page', |
|
83 | map.connect 'my/page', :controller => 'my', :action => 'page', | |
82 | :conditions => {:method => :get} |
|
84 | :conditions => {:method => :get} | |
83 | # Redirects to my/page |
|
85 | # Redirects to my/page |
@@ -31,6 +31,8 self_registration: | |||||
31 | default: '2' |
|
31 | default: '2' | |
32 | lost_password: |
|
32 | lost_password: | |
33 | default: 1 |
|
33 | default: 1 | |
|
34 | unsubscribe: | |||
|
35 | default: 1 | |||
34 | password_min_length: |
|
36 | password_min_length: | |
35 | format: int |
|
37 | format: int | |
36 | default: 4 |
|
38 | default: 4 |
@@ -84,6 +84,45 class MyControllerTest < ActionController::TestCase | |||||
84 | assert user.groups.empty? |
|
84 | assert user.groups.empty? | |
85 | end |
|
85 | end | |
86 |
|
86 | |||
|
87 | def test_my_account_should_show_destroy_link | |||
|
88 | get :account | |||
|
89 | assert_select 'a[href=/my/account/destroy]' | |||
|
90 | end | |||
|
91 | ||||
|
92 | def test_get_destroy_should_display_the_destroy_confirmation | |||
|
93 | get :destroy | |||
|
94 | assert_response :success | |||
|
95 | assert_template 'destroy' | |||
|
96 | assert_select 'form[action=/my/account/destroy]' do | |||
|
97 | assert_select 'input[name=confirm]' | |||
|
98 | end | |||
|
99 | end | |||
|
100 | ||||
|
101 | def test_post_destroy_without_confirmation_should_not_destroy_account | |||
|
102 | assert_no_difference 'User.count' do | |||
|
103 | post :destroy | |||
|
104 | end | |||
|
105 | assert_response :success | |||
|
106 | assert_template 'destroy' | |||
|
107 | end | |||
|
108 | ||||
|
109 | def test_post_destroy_without_confirmation_should_destroy_account | |||
|
110 | assert_difference 'User.count', -1 do | |||
|
111 | post :destroy, :confirm => '1' | |||
|
112 | end | |||
|
113 | assert_redirected_to '/' | |||
|
114 | assert_match /deleted/i, flash[:notice] | |||
|
115 | end | |||
|
116 | ||||
|
117 | def test_post_destroy_with_unsubscribe_not_allowed_should_not_destroy_account | |||
|
118 | User.any_instance.stubs(:own_account_deletable?).returns(false) | |||
|
119 | ||||
|
120 | assert_no_difference 'User.count' do | |||
|
121 | post :destroy, :confirm => '1' | |||
|
122 | end | |||
|
123 | assert_redirected_to '/my/account' | |||
|
124 | end | |||
|
125 | ||||
87 | def test_change_password |
|
126 | def test_change_password | |
88 | get :password |
|
127 | get :password | |
89 | assert_response :success |
|
128 | assert_response :success |
@@ -25,6 +25,12 class RoutingMyTest < ActionController::IntegrationTest | |||||
25 | { :controller => 'my', :action => 'account' } |
|
25 | { :controller => 'my', :action => 'account' } | |
26 | ) |
|
26 | ) | |
27 | end |
|
27 | end | |
|
28 | ["get", "post"].each do |method| | |||
|
29 | assert_routing( | |||
|
30 | { :method => method, :path => "/my/account/destroy" }, | |||
|
31 | { :controller => 'my', :action => 'destroy' } | |||
|
32 | ) | |||
|
33 | end | |||
28 | assert_routing( |
|
34 | assert_routing( | |
29 | { :method => 'get', :path => "/my/page" }, |
|
35 | { :method => 'get', :path => "/my/page" }, | |
30 | { :controller => 'my', :action => 'page' } |
|
36 | { :controller => 'my', :action => 'page' } |
@@ -770,7 +770,34 class UserTest < ActiveSupport::TestCase | |||||
770 | user.auth_source = denied_auth_source |
|
770 | user.auth_source = denied_auth_source | |
771 | assert !user.change_password_allowed?, "User allowed to change password, though auth source does not" |
|
771 | assert !user.change_password_allowed?, "User allowed to change password, though auth source does not" | |
772 | end |
|
772 | end | |
|
773 | end | |||
|
774 | ||||
|
775 | def test_own_account_deletable_should_be_true_with_unsubscrive_enabled | |||
|
776 | with_settings :unsubscribe => '1' do | |||
|
777 | assert_equal true, User.find(2).own_account_deletable? | |||
|
778 | end | |||
|
779 | end | |||
|
780 | ||||
|
781 | def test_own_account_deletable_should_be_false_with_unsubscrive_disabled | |||
|
782 | with_settings :unsubscribe => '0' do | |||
|
783 | assert_equal false, User.find(2).own_account_deletable? | |||
|
784 | end | |||
|
785 | end | |||
773 |
|
786 | |||
|
787 | def test_own_account_deletable_should_be_false_for_a_single_admin | |||
|
788 | User.delete_all(["admin = ? AND id <> ?", true, 1]) | |||
|
789 | ||||
|
790 | with_settings :unsubscribe => '1' do | |||
|
791 | assert_equal false, User.find(1).own_account_deletable? | |||
|
792 | end | |||
|
793 | end | |||
|
794 | ||||
|
795 | def test_own_account_deletable_should_be_true_for_an_admin_if_other_admin_exists | |||
|
796 | User.generate_with_protected(:admin => true) | |||
|
797 | ||||
|
798 | with_settings :unsubscribe => '1' do | |||
|
799 | assert_equal true, User.find(1).own_account_deletable? | |||
|
800 | end | |||
774 | end |
|
801 | end | |
775 |
|
802 | |||
776 | context "#allowed_to?" do |
|
803 | context "#allowed_to?" do |
General Comments 0
You need to be logged in to leave comments.
Login now