forked from fixin.me/fixin.me
Compare commits
1 Commits
fix/quanti
...
pr70-sole-
| Author | SHA1 | Date | |
|---|---|---|---|
| f626a814a8 |
@@ -1,6 +1,10 @@
|
||||
class User::ProfilesController < Devise::RegistrationsController
|
||||
def destroy
|
||||
# TODO: Disallow/disable deletion for last admin account; update :edit view
|
||||
if current_user.sole_admin?
|
||||
redirect_back fallback_location: edit_user_registration_path,
|
||||
alert: t(".sole_admin")
|
||||
return
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
|
||||
@@ -63,16 +63,10 @@ class Quantity < ApplicationRecord
|
||||
scope :ordered, ->(root: nil, include_root: true) {
|
||||
numbered = Arel::Table.new('numbered')
|
||||
|
||||
path_expr = if connection.adapter_name =~ /mysql/i
|
||||
numbered.cast(numbered[:child_number], 'BINARY')
|
||||
else
|
||||
numbered[:child_number]
|
||||
end
|
||||
|
||||
self.model.with(numbered: numbered(:parent_id, :name)).with_recursive(arel_table.name => [
|
||||
numbered.project(
|
||||
numbered[Arel.star],
|
||||
path_expr.as('path')
|
||||
numbered.cast(numbered[:child_number], 'BINARY').as('path')
|
||||
).where(numbered[root && include_root ? :id : :parent_id].eq(root)),
|
||||
numbered.project(
|
||||
numbered[Arel.star],
|
||||
@@ -86,25 +80,20 @@ class Quantity < ApplicationRecord
|
||||
# be merged with :ordered
|
||||
# https://gist.github.com/ProGM/c6df08da14708dcc28b5ca325df37ceb#extending-arel
|
||||
scope :numbered, ->(parent_column, order_column) {
|
||||
row_num = Arel::Nodes::NamedFunction.new('ROW_NUMBER', [])
|
||||
.over(Arel::Nodes::Window.new.partition(parent_column).order(order_column))
|
||||
|
||||
child_number = if connection.adapter_name =~ /mysql/i
|
||||
select(
|
||||
arel_table[Arel.star],
|
||||
Arel::Nodes::NamedFunction.new(
|
||||
'LPAD',
|
||||
[
|
||||
row_num,
|
||||
Arel::Nodes::NamedFunction.new('ROW_NUMBER', [])
|
||||
.over(Arel::Nodes::Window.new.partition(parent_column).order(order_column)),
|
||||
Arel::SelectManager.new.project(
|
||||
Arel::Nodes::NamedFunction.new('LENGTH', [Arel.star.count])
|
||||
).from(arel_table),
|
||||
Arel::Nodes.build_quoted('0')
|
||||
]
|
||||
],
|
||||
).as('child_number')
|
||||
)
|
||||
else
|
||||
Arel::Nodes::NamedFunction.new('format', [Arel::Nodes.build_quoted('%09d'), row_num])
|
||||
end
|
||||
|
||||
select(arel_table[Arel.star], child_number.as('child_number'))
|
||||
}
|
||||
|
||||
def to_s
|
||||
|
||||
@@ -29,4 +29,11 @@ class User < ApplicationRecord
|
||||
def at_least(status)
|
||||
User.statuses[self.status] >= User.statuses[status]
|
||||
end
|
||||
|
||||
# Returns true when this user is the only admin account in the system.
|
||||
# Used to block actions that would leave the application without an admin
|
||||
# (account deletion, status demotion).
|
||||
def sole_admin?
|
||||
admin? && !User.admin.where.not(id: id).exists?
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
<% end %>
|
||||
|
||||
<div class="rightside-area buttongrid">
|
||||
<%#= TODO: Disallow/disable deletion for last admin account, image_button_to_if %>
|
||||
<%= image_button_to t('.delete'), 'account-remove-outline', user_registration_path,
|
||||
form_class: 'tools-area', method: :delete, data: {turbo: false},
|
||||
<%= image_button_to_if !current_user.sole_admin?, t('.delete'), 'account-remove-outline',
|
||||
user_registration_path, form_class: 'tools-area', method: :delete, data: {turbo: false},
|
||||
onclick: {confirm: t('.confirm_delete')} %>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -162,6 +162,9 @@ en:
|
||||
New password:
|
||||
<br><em>leave blank to keep unchanged</em>
|
||||
%{password_length_hint_html}
|
||||
registrations:
|
||||
destroy:
|
||||
sole_admin: You cannot delete the only admin account.
|
||||
actions: Actions
|
||||
add: Add
|
||||
apply: Apply
|
||||
|
||||
18
test/controllers/registrations_controller_test.rb
Normal file
18
test/controllers/registrations_controller_test.rb
Normal file
@@ -0,0 +1,18 @@
|
||||
require "test_helper"
|
||||
|
||||
class RegistrationsControllerTest < ActionDispatch::IntegrationTest
|
||||
test "sole admin cannot delete account" do
|
||||
sign_in users(:admin)
|
||||
delete user_registration_path
|
||||
assert_redirected_to edit_user_registration_path
|
||||
assert_equal t("registrations.destroy.sole_admin"), flash[:alert]
|
||||
assert User.exists?(users(:admin).id)
|
||||
end
|
||||
|
||||
test "non-admin can delete account" do
|
||||
sign_in users(:alice)
|
||||
assert_difference ->{ User.count }, -1 do
|
||||
delete user_registration_path
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -182,8 +182,8 @@ class UsersTest < ApplicationSystemTestCase
|
||||
assert_title 'Access is forbidden to this page (403)'
|
||||
end
|
||||
|
||||
test 'delete profile' do
|
||||
user = sign_in
|
||||
test "delete profile" do
|
||||
user = sign_in user: users.reject(&:admin?).select(&:confirmed?).sample
|
||||
# TODO: remove condition after root_url changed to different path than
|
||||
# profile in routes.rb
|
||||
unless has_current_path?(edit_user_registration_path)
|
||||
@@ -196,7 +196,15 @@ class UsersTest < ApplicationSystemTestCase
|
||||
assert_text t("devise.registrations.destroyed")
|
||||
end
|
||||
|
||||
test 'index forbidden for non admin' do
|
||||
test "sole admin cannot delete profile" do
|
||||
sign_in user: users(:admin)
|
||||
unless has_current_path?(edit_user_registration_path)
|
||||
first(:link_or_button, users(:admin).email).click
|
||||
end
|
||||
assert find(:button, t("users.registrations.edit.delete"))[:disabled]
|
||||
end
|
||||
|
||||
test "index forbidden for non admin" do
|
||||
sign_in user: users.reject(&:admin?).select(&:confirmed?).sample
|
||||
visit users_path
|
||||
assert_title "Access is forbidden to this page (403)"
|
||||
|
||||
Reference in New Issue
Block a user