@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class PhabricatorSystemRemoveDestroyWorkflow
4 extends PhabricatorSystemRemoveWorkflow {
5
6 protected function didConstruct() {
7 $this
8 ->setName('destroy')
9 ->setSynopsis(pht('Permanently destroy objects.'))
10 ->setExamples('**destroy** [__options__] __object__ ...')
11 ->setArguments(
12 array(
13 array(
14 'name' => 'force',
15 'help' => pht('Destroy objects without prompting.'),
16 ),
17 array(
18 'name' => 'objects',
19 'wildcard' => true,
20 ),
21 ));
22 }
23
24 public function execute(PhutilArgumentParser $args) {
25 $console = PhutilConsole::getConsole();
26
27 $object_names = $args->getArg('objects');
28 if (!$object_names) {
29 throw new PhutilArgumentUsageException(
30 pht('Specify one or more objects to destroy.'));
31 }
32
33 $object_query = id(new PhabricatorObjectQuery())
34 ->setViewer($this->getViewer())
35 ->withNames($object_names);
36
37 $object_query->execute();
38
39 $named_objects = $object_query->getNamedResults();
40 foreach ($object_names as $object_name) {
41 if (empty($named_objects[$object_name])) {
42 throw new PhutilArgumentUsageException(
43 pht('No such object "%s" exists!', $object_name));
44 }
45 }
46
47 foreach ($named_objects as $object_name => $object) {
48 if (!($object instanceof PhabricatorDestructibleInterface)) {
49 throw new PhutilArgumentUsageException(
50 pht(
51 'Object "%s" can not be destroyed (it does not implement %s).',
52 $object_name,
53 'PhabricatorDestructibleInterface'));
54 }
55 }
56
57 $banner = <<<EOBANNER
58 uuuuuuu
59 uu###########uu
60 uu#################uu
61 u#####################u
62 u#######################u
63 u#########################u
64 u#########################u
65 u######" "###" "######u
66 "####" u#u ####"
67 ###u u#u u###
68 ###u u###u u###
69 "####uu### ###uu####"
70 "#######" "#######"
71 u#######u#######u
72 u#"#"#"#"#"#"#u
73 uuu ##u# # # # #u## uuu
74 u#### #####u#u#u### u####
75 #####uu "#########" uu######
76 u###########uu """"" uuuu##########
77 ####"""##########uuu uu#########"""###"
78 """ ""###########uu ""#"""
79 uuuu ""##########uuu
80 u###uuu#########uu ""###########uuu###
81 ##########"""" ""###########"
82 "#####" ""####""
83 ###" ####"
84
85EOBANNER;
86
87
88 $console->writeOut("\n\n<fg:red>%s</fg>\n\n", $banner);
89
90 $console->writeOut(
91 "<bg:red>** %s **</bg> %s\n\n%s\n\n".
92 "<bg:red>** %s **</bg> %s\n\n%s\n\n",
93 pht('IMPORTANT'),
94 pht('DATA WILL BE PERMANENTLY DESTROYED'),
95 phutil_console_wrap(
96 pht(
97 'Objects will be permanently destroyed. There is no way to '.
98 'undo this operation or ever retrieve this data unless you '.
99 'maintain external backups.')),
100 pht('IMPORTANT'),
101 pht('DELETING OBJECTS OFTEN BREAKS THINGS'),
102 phutil_console_wrap(
103 pht(
104 'Destroying objects may cause related objects to stop working, '.
105 'and may leave scattered references to objects which no longer '.
106 'exist. In most cases, it is much better to disable or archive '.
107 'objects instead of destroying them. This risk is greatest when '.
108 'deleting complex or highly connected objects like repositories, '.
109 'projects and users.'.
110 "\n\n".
111 'These tattered edges are an expected consequence of destroying '.
112 'objects, and the upstream will not help you fix them. We '.
113 'strongly recommend disabling or archiving objects instead.')));
114
115 $phids = mpull($named_objects, 'getPHID');
116 $handles = PhabricatorUser::getOmnipotentUser()->loadHandles($phids);
117
118 $console->writeOut(
119 pht(
120 'These %s object(s) will be destroyed forever:',
121 phutil_count($named_objects))."\n\n");
122
123 foreach ($named_objects as $object_name => $object) {
124 $phid = $object->getPHID();
125
126 $console->writeOut(
127 " - %s (%s) %s\n",
128 $object_name,
129 get_class($object),
130 $handles[$phid]->getFullName());
131 }
132
133 $force = $args->getArg('force');
134 if (!$force) {
135 $ok = $console->confirm(
136 pht(
137 'Are you absolutely certain you want to destroy these %s object(s)?',
138 phutil_count($named_objects)));
139 if (!$ok) {
140 throw new PhutilArgumentUsageException(
141 pht('Aborted, your objects are safe.'));
142 }
143 }
144
145 $console->writeOut("%s\n", pht('Destroying objects...'));
146
147 $notes = array();
148 foreach ($named_objects as $object_name => $object) {
149 $console->writeOut(
150 pht(
151 "Destroying %s **%s**...\n",
152 get_class($object),
153 $object_name));
154
155 $engine = id(new PhabricatorDestructionEngine())
156 ->setCollectNotes(true);
157
158 $engine->destroyObject($object);
159
160 foreach ($engine->getNotes() as $note) {
161 $notes[] = $note;
162 }
163 }
164
165 $console->writeOut(
166 "%s\n",
167 pht(
168 'Permanently destroyed %s object(s).',
169 phutil_count($named_objects)));
170
171 if ($notes) {
172 id(new PhutilConsoleList())
173 ->addItems($notes)
174 ->draw();
175 }
176
177 return 0;
178 }
179
180}