@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 PhabricatorProjectMoveController
4 extends PhabricatorProjectController {
5
6 public function handleRequest(AphrontRequest $request) {
7 $viewer = $request->getViewer();
8 $id = $request->getURIData('id');
9
10 $request->validateCSRF();
11
12 $column_phid = $request->getStr('columnPHID');
13 $object_phid = $request->getStr('objectPHID');
14
15 $after_phids = $request->getStrList('afterPHIDs');
16 $before_phids = $request->getStrList('beforePHIDs');
17
18 $order = $request->getStr('order');
19 if (!strlen($order)) {
20 $order = PhabricatorProjectColumnNaturalOrder::ORDERKEY;
21 }
22
23 $ordering = PhabricatorProjectColumnOrder::getOrderByKey($order);
24 $ordering = id(clone $ordering)
25 ->setViewer($viewer);
26
27 // When the Workboard view is "Group By <something>" the header provides
28 // that context in JSON form
29 $edit_header = null;
30 $raw_header = $request->getStr('header');
31 if (phutil_nonempty_string($raw_header)) {
32 $edit_header = phutil_json_decode($raw_header);
33 } else {
34 $edit_header = array();
35 }
36
37 $project = id(new PhabricatorProjectQuery())
38 ->setViewer($viewer)
39 ->requireCapabilities(
40 array(
41 PhabricatorPolicyCapability::CAN_VIEW,
42 ))
43 ->withIDs(array($id))
44 ->executeOne();
45 if (!$project) {
46 return new Aphront404Response();
47 }
48
49 $cancel_uri = $this->getApplicationURI(
50 new PhutilURI(
51 urisprintf('board/%d/', $project->getID()),
52 array(
53 'order' => $order,
54 )));
55
56 $board_phid = $project->getPHID();
57
58 $object = id(new ManiphestTaskQuery())
59 ->setViewer($viewer)
60 ->withPHIDs(array($object_phid))
61 ->needProjectPHIDs(true)
62 ->requireCapabilities(
63 array(
64 PhabricatorPolicyCapability::CAN_VIEW,
65 PhabricatorPolicyCapability::CAN_EDIT,
66 ))
67 ->executeOne();
68
69 if (!$object) {
70 return new Aphront404Response();
71 }
72
73 $columns = id(new PhabricatorProjectColumnQuery())
74 ->setViewer($viewer)
75 ->withProjectPHIDs(array($project->getPHID()))
76 ->needTriggers(true)
77 ->execute();
78
79 $columns = mpull($columns, null, 'getPHID');
80 $column = idx($columns, $column_phid);
81 if (!$column) {
82 // User is trying to drop this object into a nonexistent column, just kick
83 // them out.
84 return new Aphront404Response();
85 }
86
87 $engine = id(new PhabricatorBoardLayoutEngine())
88 ->setViewer($viewer)
89 ->setBoardPHIDs(array($board_phid))
90 ->setObjectPHIDs(array($object_phid))
91 ->executeLayout();
92
93 $order_params = array(
94 'afterPHIDs' => $after_phids,
95 'beforePHIDs' => $before_phids,
96 );
97
98 $xactions = array();
99 $xactions[] = id(new ManiphestTransaction())
100 ->setTransactionType(PhabricatorTransactions::TYPE_COLUMNS)
101 ->setNewValue(
102 array(
103 array(
104 'columnPHID' => $column->getPHID(),
105 ) + $order_params,
106 ));
107
108 $header_xactions = $ordering->getColumnTransactions(
109 $object,
110 $edit_header);
111 foreach ($header_xactions as $header_xaction) {
112 $xactions[] = $header_xaction;
113 }
114
115 $sounds = array();
116 if ($column->canHaveTrigger()) {
117 $trigger = $column->getTrigger();
118 if ($trigger) {
119 $trigger_xactions = $trigger->newDropTransactions(
120 $viewer,
121 $column,
122 $object);
123 foreach ($trigger_xactions as $trigger_xaction) {
124 $xactions[] = $trigger_xaction;
125 }
126
127 foreach ($trigger->getSoundEffects() as $effect) {
128 $sounds[] = $effect;
129 }
130 }
131 }
132
133 $editor = id(new ManiphestTransactionEditor())
134 ->setActor($viewer)
135 ->setContinueOnMissingFields(true)
136 ->setContinueOnNoEffect(true)
137 ->setContentSourceFromRequest($request)
138 ->setCancelURI($cancel_uri);
139
140 $editor->applyTransactions($object, $xactions);
141
142 return $this->newCardResponse(
143 $board_phid,
144 $object_phid,
145 $ordering,
146 $sounds);
147 }
148
149}