@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
at recaptime-dev/main 389 lines 12 kB view raw
1<?php 2 3final class DifferentialRevisionSearchEngine 4 extends PhabricatorApplicationSearchEngine { 5 6 public function getResultTypeDescription() { 7 return pht('Differential Revisions'); 8 } 9 10 public function getApplicationClassName() { 11 return PhabricatorDifferentialApplication::class; 12 } 13 14 protected function newResultBuckets() { 15 return DifferentialRevisionResultBucket::getAllResultBuckets(); 16 } 17 18 public function newQuery() { 19 return id(new DifferentialRevisionQuery()) 20 ->needFlags(true) 21 ->needDrafts(true) 22 ->needReviewers(true); 23 } 24 25 protected function buildQueryFromParameters(array $map) { 26 $query = $this->newQuery(); 27 28 if ($map['responsiblePHIDs']) { 29 $query->withResponsibleUsers($map['responsiblePHIDs']); 30 } 31 32 if ($map['authorPHIDs']) { 33 $query->withAuthors($map['authorPHIDs']); 34 } 35 36 if ($map['reviewerPHIDs']) { 37 $query->withReviewers($map['reviewerPHIDs']); 38 } 39 40 if ($map['repositoryPHIDs']) { 41 $query->withRepositoryPHIDs($map['repositoryPHIDs']); 42 } 43 44 if ($map['statuses']) { 45 $query->withStatuses($map['statuses']); 46 } 47 48 if ($map['createdStart'] || $map['createdEnd']) { 49 $query->withCreatedEpochBetween( 50 $map['createdStart'], 51 $map['createdEnd']); 52 } 53 54 if ($map['modifiedStart'] || $map['modifiedEnd']) { 55 $query->withUpdatedEpochBetween( 56 $map['modifiedStart'], 57 $map['modifiedEnd']); 58 } 59 60 if ($map['affectedPaths']) { 61 $query->withPaths($map['affectedPaths']); 62 } 63 64 return $query; 65 } 66 67 protected function buildCustomSearchFields() { 68 return array( 69 id(new PhabricatorSearchDatasourceField()) 70 ->setLabel(pht('Responsible Users')) 71 ->setKey('responsiblePHIDs') 72 ->setAliases(array('responsiblePHID', 'responsibles', 'responsible')) 73 ->setDatasource(new DifferentialResponsibleDatasource()) 74 ->setDescription( 75 pht('Find revisions that a given user is responsible for.')), 76 id(new PhabricatorUsersSearchField()) 77 ->setLabel(pht('Authors')) 78 ->setKey('authorPHIDs') 79 ->setAliases(array('author', 'authors', 'authorPHID')) 80 ->setDescription( 81 pht('Find revisions with specific authors.')), 82 id(new PhabricatorSearchDatasourceField()) 83 ->setLabel(pht('Reviewers')) 84 ->setKey('reviewerPHIDs') 85 ->setAliases(array('reviewer', 'reviewers', 'reviewerPHID')) 86 ->setDatasource(new DifferentialReviewerFunctionDatasource()) 87 ->setDescription( 88 pht('Find revisions with specific reviewers.')), 89 id(new PhabricatorSearchDatasourceField()) 90 ->setLabel(pht('Repositories')) 91 ->setKey('repositoryPHIDs') 92 ->setAliases(array('repository', 'repositories', 'repositoryPHID')) 93 ->setDatasource(new DiffusionRepositoryFunctionDatasource()) 94 ->setDescription( 95 pht('Find revisions from specific repositories.')), 96 id(new PhabricatorSearchDatasourceField()) 97 ->setLabel(pht('Statuses')) 98 ->setKey('statuses') 99 ->setAliases(array('status')) 100 ->setDatasource(new DifferentialRevisionStatusFunctionDatasource()) 101 ->setDescription( 102 pht('Find revisions with particular statuses.')), 103 id(new PhabricatorSearchDateField()) 104 ->setLabel(pht('Created After')) 105 ->setKey('createdStart') 106 ->setDescription( 107 pht('Find revisions created at or after a particular time.')), 108 id(new PhabricatorSearchDateField()) 109 ->setLabel(pht('Created Before')) 110 ->setKey('createdEnd') 111 ->setDescription( 112 pht('Find revisions created at or before a particular time.')), 113 id(new PhabricatorSearchDateField()) 114 ->setLabel(pht('Modified After')) 115 ->setKey('modifiedStart') 116 ->setDescription( 117 pht('Find revisions modified at or after a particular time.')), 118 id(new PhabricatorSearchDateField()) 119 ->setLabel(pht('Modified Before')) 120 ->setKey('modifiedEnd') 121 ->setDescription( 122 pht('Find revisions modified at or before a particular time.')), 123 id(new PhabricatorSearchStringListField()) 124 ->setKey('affectedPaths') 125 ->setLabel(pht('Affected Paths')) 126 ->setDescription( 127 pht('Search for revisions affecting particular paths.')) 128 ->setIsHidden(true), 129 ); 130 } 131 132 protected function getURI($path) { 133 return '/differential/'.$path; 134 } 135 136 protected function getBuiltinQueryNames() { 137 $names = array(); 138 139 if ($this->requireViewer()->isLoggedIn()) { 140 $names['active'] = pht('Active Revisions'); 141 $names['authored'] = pht('Authored'); 142 } 143 144 $names['all'] = pht('All Revisions'); 145 146 return $names; 147 } 148 149 public function buildSavedQueryFromBuiltin($query_key) { 150 $query = $this->newSavedQuery(); 151 $query->setQueryKey($query_key); 152 153 $viewer = $this->requireViewer(); 154 155 switch ($query_key) { 156 case 'active': 157 $bucket_key = DifferentialRevisionRequiredActionResultBucket::BUCKETKEY; 158 159 return $query 160 ->setParameter('responsiblePHIDs', array($viewer->getPHID())) 161 ->setParameter('statuses', array('open()')) 162 ->setParameter('bucket', $bucket_key); 163 case 'authored': 164 return $query 165 ->setParameter('authorPHIDs', array($viewer->getPHID())); 166 case 'all': 167 return $query; 168 } 169 170 return parent::buildSavedQueryFromBuiltin($query_key); 171 } 172 173 /** 174 * @param array<DifferentialRevision> $revisions 175 * @param PhabricatorSavedQuery $query 176 * @param array<PhabricatorObjectHandle> $handles 177 */ 178 protected function renderResultList( 179 array $revisions, 180 PhabricatorSavedQuery $query, 181 array $handles) { 182 assert_instances_of($revisions, DifferentialRevision::class); 183 184 $viewer = $this->requireViewer(); 185 $template = id(new DifferentialRevisionListView()) 186 ->setViewer($viewer) 187 ->setNoBox($this->isPanelContext()); 188 189 $bucket = $this->getResultBucket($query); 190 191 $unlanded = $this->loadUnlandedDependencies($revisions); 192 193 $custom_field_lists = $this->loadCustomFields( 194 $revisions, 195 PhabricatorCustomField::ROLE_LIST); 196 197 $views = array(); 198 if ($bucket) { 199 $bucket->setViewer($viewer); 200 201 try { 202 $groups = $bucket->newResultGroups($query, $revisions); 203 204 foreach ($groups as $group) { 205 // Don't show groups in Dashboard Panels 206 if ($group->getObjects() || !$this->isPanelContext()) { 207 $views[] = id(clone $template) 208 ->setHeader($group->getName()) 209 ->setNoDataString($group->getNoDataString()) 210 ->setRevisions($group->getObjects()); 211 } 212 } 213 } catch (Exception $ex) { 214 $this->addError($ex->getMessage()); 215 } 216 } else { 217 $views[] = id(clone $template) 218 ->setRevisions($revisions); 219 } 220 221 if (!$views) { 222 $views[] = id(new DifferentialRevisionListView()) 223 ->setViewer($viewer) 224 ->setNoDataString(pht('No revisions found.')); 225 } 226 227 foreach ($views as $view) { 228 $view->setUnlandedDependencies($unlanded); 229 $view->setCustomFieldLists($custom_field_lists); 230 } 231 232 if (count($views) == 1) { 233 // Reduce this to a PHUIObjectItemListView so we can get the free 234 // support from ApplicationSearch. 235 $list = head($views)->render(); 236 } else { 237 $list = $views; 238 } 239 240 $result = new PhabricatorApplicationSearchResultView(); 241 $result->setContent($list); 242 243 return $result; 244 } 245 246 protected function getNewUserBody() { 247 $create_button = id(new PHUIButtonView()) 248 ->setTag('a') 249 ->setText(pht('Create a Diff')) 250 ->setHref('/differential/diff/create/') 251 ->setColor(PHUIButtonView::GREEN); 252 253 $icon = $this->getApplication()->getIcon(); 254 $app_name = $this->getApplication()->getName(); 255 $view = id(new PHUIBigInfoView()) 256 ->setIcon($icon) 257 ->setTitle(pht('Welcome to %s', $app_name)) 258 ->setDescription( 259 pht('Pre-commit code review. Revisions that are waiting on your input '. 260 'will appear here.')) 261 ->addAction($create_button); 262 263 return $view; 264 } 265 266 private function loadUnlandedDependencies(array $revisions) { 267 $phids = array(); 268 foreach ($revisions as $revision) { 269 if (!$revision->isAccepted()) { 270 continue; 271 } 272 273 $phids[] = $revision->getPHID(); 274 } 275 276 if (!$phids) { 277 return array(); 278 } 279 280 $query = id(new PhabricatorEdgeQuery()) 281 ->withSourcePHIDs($phids) 282 ->withEdgeTypes( 283 array( 284 DifferentialRevisionDependsOnRevisionEdgeType::EDGECONST, 285 )); 286 287 $query->execute(); 288 289 $revision_phids = $query->getDestinationPHIDs(); 290 if (!$revision_phids) { 291 return array(); 292 } 293 294 $viewer = $this->requireViewer(); 295 296 $blocking_revisions = id(new DifferentialRevisionQuery()) 297 ->setViewer($viewer) 298 ->withPHIDs($revision_phids) 299 ->withIsOpen(true) 300 ->execute(); 301 $blocking_revisions = mpull($blocking_revisions, null, 'getPHID'); 302 303 $result = array(); 304 foreach ($revisions as $revision) { 305 $revision_phid = $revision->getPHID(); 306 $blocking_phids = $query->getDestinationPHIDs(array($revision_phid)); 307 $blocking = array_select_keys($blocking_revisions, $blocking_phids); 308 if ($blocking) { 309 $result[$revision_phid] = $blocking; 310 } 311 } 312 313 return $result; 314 } 315 316 protected function newExportFields() { 317 $fields = array( 318 id(new PhabricatorStringExportField()) 319 ->setKey('monogram') 320 ->setLabel(pht('Monogram')), 321 id(new PhabricatorPHIDExportField()) 322 ->setKey('authorPHID') 323 ->setLabel(pht('Author PHID')), 324 id(new PhabricatorStringExportField()) 325 ->setKey('author') 326 ->setLabel(pht('Author')), 327 id(new PhabricatorStringExportField()) 328 ->setKey('status') 329 ->setLabel(pht('Status')), 330 id(new PhabricatorStringExportField()) 331 ->setKey('statusName') 332 ->setLabel(pht('Status Name')), 333 id(new PhabricatorURIExportField()) 334 ->setKey('uri') 335 ->setLabel(pht('URI')), 336 id(new PhabricatorStringExportField()) 337 ->setKey('title') 338 ->setLabel(pht('Title')), 339 id(new PhabricatorStringExportField()) 340 ->setKey('summary') 341 ->setLabel(pht('Summary')), 342 id(new PhabricatorStringExportField()) 343 ->setKey('testPlan') 344 ->setLabel(pht('Test Plan')), 345 ); 346 347 return $fields; 348 } 349 350 protected function newExportData(array $revisions) { 351 $viewer = $this->requireViewer(); 352 353 $phids = array(); 354 foreach ($revisions as $revision) { 355 $phids[] = $revision->getAuthorPHID(); 356 } 357 $handles = $viewer->loadHandles($phids); 358 359 $export = array(); 360 foreach ($revisions as $revision) { 361 362 $author_phid = $revision->getAuthorPHID(); 363 if ($author_phid) { 364 $author_name = $handles[$author_phid]->getName(); 365 } else { 366 $author_name = null; 367 } 368 369 $status = $revision->getStatusObject(); 370 $status_name = $status->getDisplayName(); 371 $status_value = $status->getKey(); 372 373 $export[] = array( 374 'monogram' => $revision->getMonogram(), 375 'authorPHID' => $author_phid, 376 'author' => $author_name, 377 'status' => $status_value, 378 'statusName' => $status_name, 379 'uri' => PhabricatorEnv::getProductionURI($revision->getURI()), 380 'title' => (string)$revision->getTitle(), 381 'summary' => (string)$revision->getSummary(), 382 'testPlan' => (string)$revision->getTestPlan(), 383 ); 384 } 385 386 return $export; 387 } 388 389}