@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
3$table = new ManiphestTask();
4$conn = $table->establishConnection('w');
5$viewer = PhabricatorUser::getOmnipotentUser();
6
7foreach (new LiskMigrationIterator($table) as $task) {
8 if ($task->getClosedEpoch()) {
9 // Task already has a closed date.
10 continue;
11 }
12
13 $status = $task->getStatus();
14 if (!ManiphestTaskStatus::isClosedStatus($status)) {
15 // Task isn't closed.
16 continue;
17 }
18
19 // Look through the transactions from newest to oldest until we find one
20 // where the task was closed. A merge also counts as a close, even though
21 // it doesn't currently produce a separate transaction.
22
23 $type_status = ManiphestTaskStatusTransaction::TRANSACTIONTYPE;
24 $type_merge = ManiphestTaskMergedIntoTransaction::TRANSACTIONTYPE;
25
26 $xactions = id(new ManiphestTransactionQuery())
27 ->setViewer($viewer)
28 ->withObjectPHIDs(array($task->getPHID()))
29 ->needHandles(false)
30 ->withTransactionTypes(
31 array(
32 $type_merge,
33 $type_status,
34 ))
35 ->execute();
36 foreach ($xactions as $xaction) {
37 $old = $xaction->getOldValue();
38 $new = $xaction->getNewValue();
39
40 $type = $xaction->getTransactionType();
41
42 // If this is a status change, but is not a close, don't use it.
43 // (We always use merges, even though it's possible to merge a task which
44 // was previously closed: we can't tell when this happens very easily.)
45 if ($type === $type_status) {
46 if (!ManiphestTaskStatus::isClosedStatus($new)) {
47 continue;
48 }
49
50 if ($old && ManiphestTaskStatus::isClosedStatus($old)) {
51 continue;
52 }
53 }
54
55 queryfx(
56 $conn,
57 'UPDATE %T SET closedEpoch = %d, closerPHID = %ns
58 WHERE id = %d',
59 $table->getTableName(),
60 $xaction->getDateCreated(),
61 $xaction->getAuthorPHID(),
62 $task->getID());
63
64 break;
65 }
66}