@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 PhabricatorProjectColumnOwnerOrder
4 extends PhabricatorProjectColumnOrder {
5
6 const ORDERKEY = 'owner';
7
8 public function getDisplayName() {
9 return pht('Group by Owner');
10 }
11
12 protected function newMenuIconIcon() {
13 return 'fa-users';
14 }
15
16 public function getHasHeaders() {
17 return true;
18 }
19
20 public function getCanReorder() {
21 return true;
22 }
23
24 public function getMenuOrder() {
25 return 2000;
26 }
27
28 protected function newHeaderKeyForObject($object) {
29 return $this->newHeaderKeyForOwnerPHID($object->getOwnerPHID());
30 }
31
32 private function newHeaderKeyForOwnerPHID($owner_phid) {
33 if ($owner_phid === null) {
34 $owner_phid = '<null>';
35 }
36
37 return sprintf('owner(%s)', $owner_phid);
38 }
39
40 protected function newSortVectorsForObjects(array $objects) {
41 $owner_phids = mpull($objects, null, 'getOwnerPHID');
42 $owner_phids = array_keys($owner_phids);
43 $owner_phids = array_filter($owner_phids);
44
45 if ($owner_phids) {
46 $owner_users = id(new PhabricatorPeopleQuery())
47 ->setViewer($this->getViewer())
48 ->withPHIDs($owner_phids)
49 ->execute();
50 $owner_users = mpull($owner_users, null, 'getPHID');
51 } else {
52 $owner_users = array();
53 }
54
55 $vectors = array();
56 foreach ($objects as $vector_key => $object) {
57 $owner_phid = $object->getOwnerPHID();
58 if (!$owner_phid) {
59 $vector = $this->newSortVectorForUnowned();
60 } else {
61 $owner = idx($owner_users, $owner_phid);
62 if ($owner) {
63 $vector = $this->newSortVectorForOwner($owner);
64 } else {
65 $vector = $this->newSortVectorForOwnerPHID($owner_phid);
66 }
67 }
68
69 $vectors[$vector_key] = $vector;
70 }
71
72 return $vectors;
73 }
74
75 private function newSortVectorForUnowned() {
76 // Always put unasssigned tasks at the top.
77 return array(
78 0,
79 );
80 }
81
82 private function newSortVectorForOwner(PhabricatorUser $user) {
83 // Put assigned tasks with a valid owner after "Unassigned", but above
84 // assigned tasks with an invalid owner. Sort these tasks by the owner's
85 // username.
86 return array(
87 1,
88 $user->getUsername(),
89 );
90 }
91
92 private function newSortVectorForOwnerPHID($owner_phid) {
93 // If we have tasks with a nonempty owner but can't load the associated
94 // "User" object, move them to the bottom. We can only sort these by the
95 // PHID.
96 return array(
97 2,
98 $owner_phid,
99 );
100 }
101
102 protected function newHeadersForObjects(array $objects) {
103 $owner_phids = mpull($objects, null, 'getOwnerPHID');
104 $owner_phids = array_keys($owner_phids);
105 $owner_phids = array_filter($owner_phids);
106
107 if ($owner_phids) {
108 $owner_users = id(new PhabricatorPeopleQuery())
109 ->setViewer($this->getViewer())
110 ->withPHIDs($owner_phids)
111 ->needProfileImage(true)
112 ->execute();
113 $owner_users = mpull($owner_users, null, 'getPHID');
114 } else {
115 $owner_users = array();
116 }
117
118 array_unshift($owner_phids, null);
119
120 $headers = array();
121 foreach ($owner_phids as $owner_phid) {
122 $header_key = $this->newHeaderKeyForOwnerPHID($owner_phid);
123
124 $owner_image = null;
125 $effect_content = null;
126 if ($owner_phid === null) {
127 $owner = null;
128 $sort_vector = $this->newSortVectorForUnowned();
129 $owner_name = pht('Not Assigned');
130
131 $effect_content = pht('Remove task assignee.');
132 } else {
133 $owner = idx($owner_users, $owner_phid);
134 if ($owner) {
135 $sort_vector = $this->newSortVectorForOwner($owner);
136 $owner_name = $owner->getUsername();
137 $owner_image = $owner->getProfileImageURI();
138
139 $effect_content = pht(
140 'Assign task to %s.',
141 phutil_tag('strong', array(), $owner_name));
142 } else {
143 $sort_vector = $this->newSortVectorForOwnerPHID($owner_phid);
144 $owner_name = pht('Unknown User ("%s")', $owner_phid);
145 }
146 }
147
148 $owner_icon = 'fa-user';
149 $owner_color = 'bluegrey';
150
151 $icon_view = new PHUIIconView();
152
153 if ($owner_image) {
154 $icon_view->setImage($owner_image);
155 } else {
156 $icon_view->setIcon($owner_icon, $owner_color);
157 }
158
159 $header = $this->newHeader()
160 ->setHeaderKey($header_key)
161 ->setSortVector($sort_vector)
162 ->setName($owner_name)
163 ->setIcon($icon_view)
164 ->setEditProperties(
165 array(
166 'value' => $owner_phid,
167 ));
168
169 if ($effect_content !== null) {
170 $header->addDropEffect(
171 $this->newEffect()
172 ->setIcon($owner_icon)
173 ->setColor($owner_color)
174 ->addCondition('owner', '!=', $owner_phid)
175 ->setContent($effect_content));
176 }
177
178 $headers[] = $header;
179 }
180
181 return $headers;
182 }
183
184 protected function newColumnTransactions($object, array $header) {
185 $new_owner = idx($header, 'value');
186
187 if ($object->getOwnerPHID() === $new_owner) {
188 return null;
189 }
190
191 $xactions = array();
192 $xactions[] = $this->newTransaction($object)
193 ->setTransactionType(ManiphestTaskOwnerTransaction::TRANSACTIONTYPE)
194 ->setNewValue($new_owner);
195
196 return $xactions;
197 }
198
199}