@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 PhabricatorPeopleSearchEngine
4 extends PhabricatorApplicationSearchEngine {
5
6 public function getResultTypeDescription() {
7 return pht('Users');
8 }
9
10 public function getApplicationClassName() {
11 return PhabricatorPeopleApplication::class;
12 }
13
14 public function newQuery() {
15 return id(new PhabricatorPeopleQuery())
16 ->needPrimaryEmail(true)
17 ->needProfileImage(true);
18 }
19
20 protected function buildCustomSearchFields() {
21 $fields = array(
22 id(new PhabricatorSearchStringListField())
23 ->setLabel(pht('Usernames'))
24 ->setKey('usernames')
25 ->setAliases(array('username'))
26 ->setDescription(pht('Find users by exact username.')),
27 id(new PhabricatorSearchTextField())
28 ->setLabel(pht('Name Contains'))
29 ->setKey('nameLike')
30 ->setDescription(
31 pht(
32 'Find users whose usernames or real names contain a substring.')),
33 id(new PhabricatorSearchThreeStateField())
34 ->setLabel(pht('Administrators'))
35 ->setKey('isAdmin')
36 ->setOptions(
37 pht('(Show All)'),
38 pht('Show Only Administrators'),
39 pht('Hide Administrators'))
40 ->setDescription(
41 pht(
42 'Pass true to find only administrators, or false to omit '.
43 'administrators.')),
44 id(new PhabricatorSearchThreeStateField())
45 ->setLabel(pht('Disabled'))
46 ->setKey('isDisabled')
47 ->setOptions(
48 pht('(Show All)'),
49 pht('Show Only Disabled Accounts'),
50 pht('Hide Disabled Accounts'))
51 ->setDescription(
52 pht(
53 'Pass true to find only disabled accounts, or false to omit '.
54 'disabled accounts.')),
55 id(new PhabricatorSearchThreeStateField())
56 ->setLabel(pht('Bots'))
57 ->setKey('isBot')
58 ->setAliases(array('isSystemAgent'))
59 ->setOptions(
60 pht('(Show All)'),
61 pht('Show Only Bots'),
62 pht('Hide Bots'))
63 ->setDescription(
64 pht(
65 'Pass true to find only bots, or false to omit bots.')),
66 id(new PhabricatorSearchThreeStateField())
67 ->setLabel(pht('Mailing Lists'))
68 ->setKey('isMailingList')
69 ->setOptions(
70 pht('(Show All)'),
71 pht('Show Only Mailing Lists'),
72 pht('Hide Mailing Lists'))
73 ->setDescription(
74 pht(
75 'Pass true to find only mailing lists, or false to omit '.
76 'mailing lists.')),
77 id(new PhabricatorSearchThreeStateField())
78 ->setLabel(pht('Needs Approval'))
79 ->setKey('needsApproval')
80 ->setOptions(
81 pht('(Show All)'),
82 pht('Show Only Unapproved Users'),
83 pht('Hide Unapproved Users'))
84 ->setDescription(
85 pht(
86 'Pass true to find only users awaiting administrative approval, '.
87 'or false to omit these users.')),
88 );
89
90 $viewer = $this->requireViewer();
91 if ($viewer->getIsAdmin()) {
92 $fields[] = id(new PhabricatorSearchThreeStateField())
93 ->setLabel(pht('Has MFA'))
94 ->setKey('mfa')
95 ->setOptions(
96 pht('(Show All)'),
97 pht('Show Only Users With MFA'),
98 pht('Hide Users With MFA'))
99 ->setDescription(
100 pht(
101 'Pass true to find only users who are enrolled in MFA, or false '.
102 'to omit these users.'));
103 }
104
105 $fields[] = id(new PhabricatorSearchDateField())
106 ->setKey('createdStart')
107 ->setLabel(pht('Joined After'))
108 ->setDescription(
109 pht('Find user accounts created after a given time.'));
110
111 $fields[] = id(new PhabricatorSearchDateField())
112 ->setKey('createdEnd')
113 ->setLabel(pht('Joined Before'))
114 ->setDescription(
115 pht('Find user accounts created before a given time.'));
116
117 return $fields;
118 }
119
120 protected function getDefaultFieldOrder() {
121 return array(
122 '...',
123 'createdStart',
124 'createdEnd',
125 );
126 }
127
128 protected function buildQueryFromParameters(array $map) {
129 $query = $this->newQuery();
130
131 $viewer = $this->requireViewer();
132
133 // If the viewer can't browse the user directory, restrict the query to
134 // just the user's own profile. This is a little bit silly, but serves to
135 // restrict users from creating a dashboard panel which essentially just
136 // contains a user directory anyway.
137 $can_browse = PhabricatorPolicyFilter::hasCapability(
138 $viewer,
139 $this->getApplication(),
140 PeopleBrowseUserDirectoryCapability::CAPABILITY);
141 if (!$can_browse) {
142 $query->withPHIDs(array($viewer->getPHID()));
143 }
144
145 if ($map['usernames']) {
146 $query->withUsernames($map['usernames']);
147 }
148
149 if ($map['nameLike']) {
150 $query->withNameLike($map['nameLike']);
151 }
152
153 if ($map['isAdmin'] !== null) {
154 $query->withIsAdmin($map['isAdmin']);
155 }
156
157 if ($map['isDisabled'] !== null) {
158 $query->withIsDisabled($map['isDisabled']);
159 }
160
161 if ($map['isMailingList'] !== null) {
162 $query->withIsMailingList($map['isMailingList']);
163 }
164
165 if ($map['isBot'] !== null) {
166 $query->withIsSystemAgent($map['isBot']);
167 }
168
169 if ($map['needsApproval'] !== null) {
170 $query->withIsApproved(!$map['needsApproval']);
171 }
172
173 if (idx($map, 'mfa') !== null) {
174 $viewer = $this->requireViewer();
175 if (!$viewer->getIsAdmin()) {
176 throw new PhabricatorSearchConstraintException(
177 pht(
178 'The "Has MFA" query constraint may only be used by '.
179 'administrators, to prevent attackers from using it to target '.
180 'weak accounts.'));
181 }
182
183 $query->withIsEnrolledInMultiFactor($map['mfa']);
184 }
185
186 if ($map['createdStart']) {
187 $query->withDateCreatedAfter($map['createdStart']);
188 }
189
190 if ($map['createdEnd']) {
191 $query->withDateCreatedBefore($map['createdEnd']);
192 }
193
194 return $query;
195 }
196
197 protected function getURI($path) {
198 return '/people/'.$path;
199 }
200
201 protected function getBuiltinQueryNames() {
202 $names = array(
203 'active' => pht('Active'),
204 'admin' => pht('Administrators'),
205 'all' => pht('All'),
206 );
207
208 $viewer = $this->requireViewer();
209 if ($viewer->getIsAdmin()) {
210 $names['approval'] = pht('Approval Queue');
211 }
212
213 return $names;
214 }
215
216 public function buildSavedQueryFromBuiltin($query_key) {
217 $query = $this->newSavedQuery();
218 $query->setQueryKey($query_key);
219
220 switch ($query_key) {
221 case 'all':
222 return $query;
223 case 'active':
224 return $query
225 ->setParameter('isDisabled', false);
226 case 'admin':
227 return $query
228 ->setParameter('isAdmin', true);
229 case 'approval':
230 return $query
231 ->setParameter('needsApproval', true)
232 ->setParameter('isDisabled', false);
233 }
234
235 return parent::buildSavedQueryFromBuiltin($query_key);
236 }
237
238 /**
239 * @param array<PhabricatorUser> $users
240 * @param PhabricatorSavedQuery $query
241 * @param array<PhabricatorObjectHandle> $handles
242 */
243 protected function renderResultList(
244 array $users,
245 PhabricatorSavedQuery $query,
246 array $handles) {
247
248 assert_instances_of($users, PhabricatorUser::class);
249
250 $request = $this->getRequest();
251 $viewer = $this->requireViewer();
252
253 $list = new PHUIObjectItemListView();
254
255 $is_approval = ($query->getQueryKey() == 'approval');
256
257 foreach ($users as $user) {
258 $primary_email = $user->loadPrimaryEmail();
259 if ($primary_email && $primary_email->getIsVerified()) {
260 $email = pht('Verified');
261 } else {
262 $email = pht('Unverified');
263 }
264
265 $item = new PHUIObjectItemView();
266 $item->setHeader($user->getFullName())
267 ->setHref('/p/'.$user->getUsername().'/')
268 ->addAttribute(phabricator_datetime($user->getDateCreated(), $viewer))
269 ->addAttribute($email)
270 ->setImageURI($user->getProfileImageURI());
271
272 if ($is_approval && $primary_email) {
273 $item->addAttribute($primary_email->getAddress());
274 }
275
276 if ($user->getIsDisabled()) {
277 $item->addIcon('fa-ban', pht('Disabled'));
278 $item->setDisabled(true);
279 }
280
281 if (!$is_approval) {
282 if (!$user->getIsApproved()) {
283 $item->addIcon('fa-clock-o', pht('Needs Approval'));
284 }
285 }
286
287 if ($user->getIsAdmin()) {
288 $item->addIcon('fa-star', pht('Admin'));
289 }
290
291 if ($user->getIsSystemAgent()) {
292 $item->addIcon('fa-desktop', pht('Bot'));
293 }
294
295 if ($user->getIsMailingList()) {
296 $item->addIcon('fa-envelope-o', pht('Mailing List'));
297 }
298
299 if ($viewer->getIsAdmin()) {
300 if ($user->getIsEnrolledInMultiFactor()) {
301 $item->addIcon('fa-lock', pht('Has MFA'));
302 }
303 }
304
305 if ($viewer->getIsAdmin()) {
306 $user_id = $user->getID();
307 if ($is_approval) {
308 $item->addAction(
309 id(new PHUIListItemView())
310 ->setIcon('fa-ban')
311 ->setName(pht('Disable'))
312 ->setWorkflow(true)
313 ->setHref($this->getApplicationURI('disapprove/'.$user_id.'/')));
314 $item->addAction(
315 id(new PHUIListItemView())
316 ->setIcon('fa-thumbs-o-up')
317 ->setName(pht('Approve'))
318 ->setWorkflow(true)
319 ->setHref($this->getApplicationURI('approve/'.$user_id.'/')));
320 }
321 }
322
323 $list->addItem($item);
324 }
325
326 $result = new PhabricatorApplicationSearchResultView();
327 $result->setObjectList($list);
328 $result->setNoDataString(pht('No accounts found.'));
329
330 return $result;
331 }
332
333 protected function newExportFields() {
334 return array(
335 id(new PhabricatorStringExportField())
336 ->setKey('username')
337 ->setLabel(pht('Username')),
338 id(new PhabricatorStringExportField())
339 ->setKey('realName')
340 ->setLabel(pht('Real Name')),
341 );
342 }
343
344 protected function newExportData(array $users) {
345 $viewer = $this->requireViewer();
346
347 $export = array();
348 foreach ($users as $user) {
349 $export[] = array(
350 'username' => $user->getUsername(),
351 'realName' => $user->getRealName(),
352 );
353 }
354
355 return $export;
356 }
357
358}