Merge branch 'confirm_destroy' of git://github.com/vlegoff/evennia into vlegoff-confirm_destroy

This commit is contained in:
Griatch 2017-09-09 18:38:07 +02:00
commit 0dedda35e8
2 changed files with 61 additions and 14 deletions

View file

@ -614,35 +614,37 @@ class CmdDestroy(COMMAND_DEFAULT_CLASS):
switches: switches:
override - The @destroy command will usually avoid accidentally override - The @destroy command will usually avoid accidentally
destroying account objects. This switch overrides this safety. destroying account objects. This switch overrides this safety.
force - destroy without confirmation.
examples: examples:
@destroy house, roof, door, 44-78 @destroy house, roof, door, 44-78
@destroy 5-10, flower, 45 @destroy 5-10, flower, 45
@destroy/force north
Destroys one or many objects. If dbrefs are used, a range to delete can be Destroys one or many objects. If dbrefs are used, a range to delete can be
given, e.g. 4-10. Also the end points will be deleted. given, e.g. 4-10. Also the end points will be deleted. This command
displays a confirmation before destroying, to make sure of your choice.
You can specify the /force switch to bypass this confirmation.
""" """
key = "@destroy" key = "@destroy"
aliases = ["@delete", "@del"] aliases = ["@delete", "@del"]
locks = "cmd:perm(destroy) or perm(Builder)" locks = "cmd:perm(destroy) or perm(Builder)"
help_category = "Building" help_category = "Building"
confirm = True # set to False to always bypass confirmation
def func(self): def func(self):
"Implements the command." "Implements the command."
caller = self.caller caller = self.caller
delete = True
if not self.args or not self.lhslist: if not self.args or not self.lhslist:
caller.msg("Usage: @destroy[/switches] [obj, obj2, obj3, [dbref-dbref],...]") caller.msg("Usage: @destroy[/switches] [obj, obj2, obj3, [dbref-dbref],...]")
return "" delete = False
def delobj(objname, byref=False): def delobj(obj):
# helper function for deleting a single object # helper function for deleting a single object
string = "" string = ""
obj = caller.search(objname)
if not obj:
self.caller.msg(" (Objects to destroy must either be local or specified with a unique #dbref.)")
return ""
objname = obj.name objname = obj.name
if not (obj.access(caller, "control") or obj.access(caller, 'delete')): if not (obj.access(caller, "control") or obj.access(caller, 'delete')):
return "\nYou don't have permission to delete %s." % objname return "\nYou don't have permission to delete %s." % objname
@ -668,21 +670,55 @@ class CmdDestroy(COMMAND_DEFAULT_CLASS):
string += " Objects inside %s were moved to their homes." % objname string += " Objects inside %s were moved to their homes." % objname
return string return string
result = [] objs = []
for objname in self.lhslist: for objname in self.lhslist:
if not delete:
continue
if '-' in objname: if '-' in objname:
# might be a range of dbrefs # might be a range of dbrefs
dmin, dmax = [utils.dbref(part, reqhash=False) dmin, dmax = [utils.dbref(part, reqhash=False)
for part in objname.split('-', 1)] for part in objname.split('-', 1)]
if dmin and dmax: if dmin and dmax:
for dbref in range(int(dmin), int(dmax + 1)): for dbref in range(int(dmin), int(dmax + 1)):
result.append(delobj("#" + str(dbref), True)) obj = caller.search("#" + str(dbref))
if obj:
objs.append(obj)
continue
else: else:
result.append(delobj(objname)) obj = caller.search(objname)
else: else:
result.append(delobj(objname, True)) obj = caller.search(objname)
if result:
caller.msg("".join(result).strip()) if obj is None:
self.caller.msg(" (Objects to destroy must either be local or specified with a unique #dbref.)")
elif obj not in objs:
objs.append(obj)
if objs and ("force" not in self.switches and type(self).confirm):
confirm = "Are you sure you want to destroy "
if len(objs) == 1:
confirm += objs[0].get_display_name(caller)
elif len(objs) < 5:
confirm += ", ".join([obj.get_display_name(caller) for obj in objs])
else:
confirm += ", ".join(["#{}".format(obj.id) for obj in objs])
confirm += " (yes/no)?"
answer = yield(confirm)
while answer.strip().lower() not in ("y", "yes", "n", "no"):
answer = yield(confirm)
if answer.strip().lower() in ("n", "no"):
caller.msg("Cancelled: no object was destroyed.")
delete = False
if delete:
results = []
for obj in objs:
results.append(delobj(obj))
if results:
caller.msg("".join(results).strip())
class CmdDig(ObjManipCommand): class CmdDig(ObjManipCommand):

View file

@ -13,6 +13,7 @@ main test suite started with
""" """
import re import re
import types
from django.conf import settings from django.conf import settings
from mock import Mock from mock import Mock
@ -73,8 +74,12 @@ class CommandTest(EvenniaTest):
receiver.msg = Mock() receiver.msg = Mock()
cmdobj.at_pre_cmd() cmdobj.at_pre_cmd()
cmdobj.parse() cmdobj.parse()
cmdobj.func() ret = cmdobj.func()
if isinstance(ret, types.GeneratorType):
ret.next()
cmdobj.at_post_cmd() cmdobj.at_post_cmd()
except StopIteration:
pass
except InterruptCommand: except InterruptCommand:
pass pass
finally: finally:
@ -250,7 +255,10 @@ class TestBuilding(CommandTest):
self.call(building.CmdDesc(), "Obj2=TestDesc", "The description was set on Obj2(#5).") self.call(building.CmdDesc(), "Obj2=TestDesc", "The description was set on Obj2(#5).")
def test_wipe(self): def test_wipe(self):
confirm = building.CmdDestroy.confirm
building.CmdDestroy.confirm = False
self.call(building.CmdDestroy(), "Obj", "Obj was destroyed.") self.call(building.CmdDestroy(), "Obj", "Obj was destroyed.")
building.CmdDestroy.confirm = confirm
def test_dig(self): def test_dig(self):
self.call(building.CmdDig(), "TestRoom1=testroom;tr,back;b", "Created room TestRoom1") self.call(building.CmdDig(), "TestRoom1=testroom;tr,back;b", "Created room TestRoom1")
@ -330,7 +338,10 @@ class TestBatchProcess(CommandTest):
# cannot test batchcode here, it must run inside the server process # cannot test batchcode here, it must run inside the server process
self.call(batchprocess.CmdBatchCommands(), "example_batch_cmds", "Running Batchcommand processor Automatic mode for example_batch_cmds") self.call(batchprocess.CmdBatchCommands(), "example_batch_cmds", "Running Batchcommand processor Automatic mode for example_batch_cmds")
# we make sure to delete the button again here to stop the running reactor # we make sure to delete the button again here to stop the running reactor
confirm = building.CmdDestroy.confirm
building.CmdDestroy.confirm = False
self.call(building.CmdDestroy(), "button", "button was destroyed.") self.call(building.CmdDestroy(), "button", "button was destroyed.")
building.CmdDestroy.confirm = confirm
class CmdInterrupt(Command): class CmdInterrupt(Command):