@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
fork

Configure Feed

Select the types of activity you want to include in your feed.

Modernize Audit search engine

Summary:
Fixes T9279. Modernizes the SearchEngine and Query classes. User-facing changes:

- Added order by commit date, default to order by commit date with newest commits first.
- Added explicit "Needs Audit by".
- Added new `packages(...)` typeahead function.
- Picked up automatic subscribers, projects, and order fields.

This changes behavior a little bit: we previously attempted to exclude, e.g., commits which a package you own needs to audit, but which you have resigned from. This is difficult in general and I think it needs a more comprehensive solution. This shouldn't impact users much, anyway.

Test Plan: {F767628}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T9279

Differential Revision: https://secure.phabricator.com/D14013

+447 -204
+10
src/__phutil_library_map__.php
··· 496 496 'DifferentialUpdateRevisionConduitAPIMethod' => 'applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php', 497 497 'DifferentialViewPolicyField' => 'applications/differential/customfield/DifferentialViewPolicyField.php', 498 498 'DiffusionAuditorDatasource' => 'applications/diffusion/typeahead/DiffusionAuditorDatasource.php', 499 + 'DiffusionAuditorFunctionDatasource' => 'applications/diffusion/typeahead/DiffusionAuditorFunctionDatasource.php', 499 500 'DiffusionAuditorsAddAuditorsHeraldAction' => 'applications/diffusion/herald/DiffusionAuditorsAddAuditorsHeraldAction.php', 500 501 'DiffusionAuditorsAddSelfHeraldAction' => 'applications/diffusion/herald/DiffusionAuditorsAddSelfHeraldAction.php', 501 502 'DiffusionAuditorsHeraldAction' => 'applications/diffusion/herald/DiffusionAuditorsHeraldAction.php', ··· 2436 2437 'PhabricatorOwnersOwner' => 'applications/owners/storage/PhabricatorOwnersOwner.php', 2437 2438 'PhabricatorOwnersPackage' => 'applications/owners/storage/PhabricatorOwnersPackage.php', 2438 2439 'PhabricatorOwnersPackageDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageDatasource.php', 2440 + 'PhabricatorOwnersPackageFunctionDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageFunctionDatasource.php', 2441 + 'PhabricatorOwnersPackageOwnerDatasource' => 'applications/owners/typeahead/PhabricatorOwnersPackageOwnerDatasource.php', 2439 2442 'PhabricatorOwnersPackagePHIDType' => 'applications/owners/phid/PhabricatorOwnersPackagePHIDType.php', 2440 2443 'PhabricatorOwnersPackageQuery' => 'applications/owners/query/PhabricatorOwnersPackageQuery.php', 2441 2444 'PhabricatorOwnersPackageSearchEngine' => 'applications/owners/query/PhabricatorOwnersPackageSearchEngine.php', ··· 2632 2635 'PhabricatorProjectNoProjectsDatasource' => 'applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php', 2633 2636 'PhabricatorProjectObjectHasProjectEdgeType' => 'applications/project/edge/PhabricatorProjectObjectHasProjectEdgeType.php', 2634 2637 'PhabricatorProjectOrUserDatasource' => 'applications/project/typeahead/PhabricatorProjectOrUserDatasource.php', 2638 + 'PhabricatorProjectOrUserFunctionDatasource' => 'applications/project/typeahead/PhabricatorProjectOrUserFunctionDatasource.php', 2635 2639 'PhabricatorProjectProfileController' => 'applications/project/controller/PhabricatorProjectProfileController.php', 2636 2640 'PhabricatorProjectProjectHasMemberEdgeType' => 'applications/project/edge/PhabricatorProjectProjectHasMemberEdgeType.php', 2637 2641 'PhabricatorProjectProjectHasObjectEdgeType' => 'applications/project/edge/PhabricatorProjectProjectHasObjectEdgeType.php', ··· 2651 2655 'PhabricatorProjectTransactionQuery' => 'applications/project/query/PhabricatorProjectTransactionQuery.php', 2652 2656 'PhabricatorProjectUIEventListener' => 'applications/project/events/PhabricatorProjectUIEventListener.php', 2653 2657 'PhabricatorProjectUpdateController' => 'applications/project/controller/PhabricatorProjectUpdateController.php', 2658 + 'PhabricatorProjectUserFunctionDatasource' => 'applications/project/typeahead/PhabricatorProjectUserFunctionDatasource.php', 2654 2659 'PhabricatorProjectViewController' => 'applications/project/controller/PhabricatorProjectViewController.php', 2655 2660 'PhabricatorProjectWatchController' => 'applications/project/controller/PhabricatorProjectWatchController.php', 2656 2661 'PhabricatorProjectsPolicyRule' => 'applications/policy/rule/PhabricatorProjectsPolicyRule.php', ··· 4158 4163 'DifferentialUpdateRevisionConduitAPIMethod' => 'DifferentialConduitAPIMethod', 4159 4164 'DifferentialViewPolicyField' => 'DifferentialCoreCustomField', 4160 4165 'DiffusionAuditorDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 4166 + 'DiffusionAuditorFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 4161 4167 'DiffusionAuditorsAddAuditorsHeraldAction' => 'DiffusionAuditorsHeraldAction', 4162 4168 'DiffusionAuditorsAddSelfHeraldAction' => 'DiffusionAuditorsHeraldAction', 4163 4169 'DiffusionAuditorsHeraldAction' => 'HeraldAction', ··· 6407 6413 'PhabricatorApplicationTransactionInterface', 6408 6414 ), 6409 6415 'PhabricatorOwnersPackageDatasource' => 'PhabricatorTypeaheadDatasource', 6416 + 'PhabricatorOwnersPackageFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 6417 + 'PhabricatorOwnersPackageOwnerDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 6410 6418 'PhabricatorOwnersPackagePHIDType' => 'PhabricatorPHIDType', 6411 6419 'PhabricatorOwnersPackageQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 6412 6420 'PhabricatorOwnersPackageSearchEngine' => 'PhabricatorApplicationSearchEngine', ··· 6650 6658 'PhabricatorProjectNoProjectsDatasource' => 'PhabricatorTypeaheadDatasource', 6651 6659 'PhabricatorProjectObjectHasProjectEdgeType' => 'PhabricatorEdgeType', 6652 6660 'PhabricatorProjectOrUserDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 6661 + 'PhabricatorProjectOrUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 6653 6662 'PhabricatorProjectProfileController' => 'PhabricatorProjectController', 6654 6663 'PhabricatorProjectProjectHasMemberEdgeType' => 'PhabricatorEdgeType', 6655 6664 'PhabricatorProjectProjectHasObjectEdgeType' => 'PhabricatorEdgeType', ··· 6672 6681 'PhabricatorProjectTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 6673 6682 'PhabricatorProjectUIEventListener' => 'PhabricatorEventListener', 6674 6683 'PhabricatorProjectUpdateController' => 'PhabricatorProjectController', 6684 + 'PhabricatorProjectUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 6675 6685 'PhabricatorProjectViewController' => 'PhabricatorProjectController', 6676 6686 'PhabricatorProjectWatchController' => 'PhabricatorProjectController', 6677 6687 'PhabricatorProjectsPolicyRule' => 'PhabricatorPolicyRule',
+1 -2
src/applications/audit/application/PhabricatorAuditApplication.php
··· 70 70 71 71 $query = id(new DiffusionCommitQuery()) 72 72 ->setViewer($user) 73 - ->withAuditorPHIDs($phids) 73 + ->withNeedsAuditByPHIDs($phids) 74 74 ->withAuditStatus(DiffusionCommitQuery::AUDIT_STATUS_OPEN) 75 - ->withAuditAwaitingUser($user) 76 75 ->setLimit(self::MAX_STATUS_ITEMS); 77 76 $commits = $query->execute(); 78 77
+56 -92
src/applications/audit/query/PhabricatorCommitSearchEngine.php
··· 11 11 return 'PhabricatorDiffusionApplication'; 12 12 } 13 13 14 - public function buildSavedQueryFromRequest(AphrontRequest $request) { 15 - $saved = new PhabricatorSavedQuery(); 16 - 17 - $saved->setParameter( 18 - 'auditorPHIDs', 19 - $this->readPHIDsFromRequest($request, 'auditorPHIDs')); 20 - 21 - $saved->setParameter( 22 - 'commitAuthorPHIDs', 23 - $this->readUsersFromRequest($request, 'authors')); 24 - 25 - $saved->setParameter( 26 - 'auditStatus', 27 - $request->getStr('auditStatus')); 28 - 29 - $saved->setParameter( 30 - 'repositoryPHIDs', 31 - $this->readPHIDsFromRequest($request, 'repositoryPHIDs')); 32 - 33 - // -- TODO - T4173 - file location 34 - 35 - return $saved; 14 + public function newQuery() { 15 + return id(new DiffusionCommitQuery()) 16 + ->needAuditRequests(true) 17 + ->needCommitData(true); 36 18 } 37 19 38 - public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { 39 - $query = id(new DiffusionCommitQuery()) 40 - ->needAuditRequests(true) 41 - ->needCommitData(true); 20 + protected function buildQueryFromParameters(array $map) { 21 + $query = $this->newQuery(); 42 22 43 - $auditor_phids = $saved->getParameter('auditorPHIDs', array()); 44 - if ($auditor_phids) { 45 - $query->withAuditorPHIDs($auditor_phids); 23 + if ($map['needsAuditByPHIDs']) { 24 + $query->withNeedsAuditByPHIDs($map['needsAuditByPHIDs']); 46 25 } 47 26 48 - $commit_author_phids = $saved->getParameter('commitAuthorPHIDs', array()); 49 - if ($commit_author_phids) { 50 - $query->withAuthorPHIDs($commit_author_phids); 27 + if ($map['auditorPHIDs']) { 28 + $query->withAuditorPHIDs($map['auditorPHIDs']); 51 29 } 52 30 53 - $audit_status = $saved->getParameter('auditStatus', null); 54 - if ($audit_status) { 55 - $query->withAuditStatus($audit_status); 31 + if ($map['commitAuthorPHIDs']) { 32 + $query->withAuthorPHIDs($map['commitAuthorPHIDs']); 56 33 } 57 34 58 - $awaiting_user_phid = $saved->getParameter('awaitingUserPHID', null); 59 - if ($awaiting_user_phid) { 60 - // This is used only for the built-in "needs attention" filter, 61 - // so cheat and just use the already-loaded viewer rather than reloading 62 - // it. 63 - $query->withAuditAwaitingUser($this->requireViewer()); 35 + if ($map['auditStatus']) { 36 + $query->withAuditStatus($map['auditStatus']); 64 37 } 65 38 66 - $repository_phids = $saved->getParameter('repositoryPHIDs', array()); 67 - if ($repository_phids) { 68 - $query->withRepositoryPHIDs($repository_phids); 39 + if ($map['repositoryPHIDs']) { 40 + $query->withRepositoryPHIDs($map['repositoryPHIDs']); 69 41 } 70 42 71 43 return $query; 72 44 } 73 45 74 - public function buildSearchForm( 75 - AphrontFormView $form, 76 - PhabricatorSavedQuery $saved) { 77 - 78 - $auditor_phids = $saved->getParameter('auditorPHIDs', array()); 79 - $commit_author_phids = $saved->getParameter( 80 - 'commitAuthorPHIDs', 81 - array()); 82 - $audit_status = $saved->getParameter('auditStatus', null); 83 - $repository_phids = $saved->getParameter('repositoryPHIDs', array()); 84 - 85 - $form 86 - ->appendControl( 87 - id(new AphrontFormTokenizerControl()) 88 - ->setDatasource(new DiffusionAuditorDatasource()) 89 - ->setName('auditorPHIDs') 90 - ->setLabel(pht('Auditors')) 91 - ->setValue($auditor_phids)) 92 - ->appendControl( 93 - id(new AphrontFormTokenizerControl()) 94 - ->setDatasource(new PhabricatorPeopleDatasource()) 95 - ->setName('authors') 96 - ->setLabel(pht('Commit Authors')) 97 - ->setValue($commit_author_phids)) 98 - ->appendChild( 99 - id(new AphrontFormSelectControl()) 100 - ->setName('auditStatus') 101 - ->setLabel(pht('Audit Status')) 102 - ->setOptions($this->getAuditStatusOptions()) 103 - ->setValue($audit_status)) 104 - ->appendControl( 105 - id(new AphrontFormTokenizerControl()) 106 - ->setLabel(pht('Repositories')) 107 - ->setName('repositoryPHIDs') 108 - ->setDatasource(new DiffusionRepositoryDatasource()) 109 - ->setValue($repository_phids)); 110 - 46 + protected function buildCustomSearchFields() { 47 + return array( 48 + id(new PhabricatorSearchDatasourceField()) 49 + ->setLabel(pht('Needs Audit By')) 50 + ->setKey('needsAuditByPHIDs') 51 + ->setAliases(array('needs', 'need')) 52 + ->setDatasource(new DiffusionAuditorFunctionDatasource()), 53 + id(new PhabricatorSearchDatasourceField()) 54 + ->setLabel(pht('Auditors')) 55 + ->setKey('auditorPHIDs') 56 + ->setAliases(array('auditor', 'auditors')) 57 + ->setDatasource(new DiffusionAuditorFunctionDatasource()), 58 + id(new PhabricatorUsersSearchField()) 59 + ->setLabel(pht('Authors')) 60 + ->setKey('commitAuthorPHIDs') 61 + ->setAliases(array('author', 'authors')), 62 + id(new PhabricatorSearchSelectField()) 63 + ->setLabel(pht('Audit Status')) 64 + ->setKey('auditStatus') 65 + ->setAliases(array('status')) 66 + ->setOptions($this->getAuditStatusOptions()), 67 + id(new PhabricatorSearchDatasourceField()) 68 + ->setLabel(pht('Repositories')) 69 + ->setKey('repositoryPHIDs') 70 + ->setAliases(array('repository', 'repositories')) 71 + ->setDatasource(new DiffusionRepositoryDatasource()), 72 + ); 111 73 } 112 74 113 75 protected function getURI($path) { ··· 118 80 $names = array(); 119 81 120 82 if ($this->requireViewer()->isLoggedIn()) { 121 - $names['need'] = pht('Need Attention'); 83 + $names['need'] = pht('Needs Audit'); 122 84 $names['problem'] = pht('Problem Commits'); 123 85 } 124 86 ··· 138 100 $query->setQueryKey($query_key); 139 101 $viewer = $this->requireViewer(); 140 102 103 + $viewer_phid = $viewer->getPHID(); 104 + $status_open = DiffusionCommitQuery::AUDIT_STATUS_OPEN; 105 + 141 106 switch ($query_key) { 142 107 case 'all': 143 108 return $query; 144 109 case 'open': 145 - $query->setParameter( 146 - 'auditStatus', 147 - DiffusionCommitQuery::AUDIT_STATUS_OPEN); 110 + $query->setParameter('auditStatus', $status_open); 148 111 return $query; 149 112 case 'need': 150 - $query->setParameter('awaitingUserPHID', $viewer->getPHID()); 151 - $query->setParameter( 152 - 'auditStatus', 153 - DiffusionCommitQuery::AUDIT_STATUS_OPEN); 154 - $query->setParameter( 155 - 'auditorPHIDs', 156 - PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($viewer)); 113 + $needs_tokens = array( 114 + $viewer_phid, 115 + 'projects('.$viewer_phid.')', 116 + 'packages('.$viewer_phid.')', 117 + ); 118 + 119 + $query->setParameter('needsAuditByPHIDs', $needs_tokens); 120 + $query->setParameter('auditStatus', $status_open); 157 121 return $query; 158 122 case 'authored': 159 123 $query->setParameter('commitAuthorPHIDs', array($viewer->getPHID()));
+122 -108
src/applications/diffusion/query/DiffusionCommitQuery.php
··· 15 15 private $needAuditRequests; 16 16 private $auditIDs; 17 17 private $auditorPHIDs; 18 - private $auditAwaitingUser; 18 + private $needsAuditByPHIDs; 19 19 private $auditStatus; 20 20 private $epochMin; 21 21 private $epochMax; ··· 103 103 return $this; 104 104 } 105 105 106 - /** 107 - * Returns true if we should join the audit table, either because we're 108 - * interested in the information if it's available or because matching rows 109 - * must always have it. 110 - */ 111 - private function shouldJoinAudits() { 112 - return $this->auditStatus || 113 - $this->rowsMustHaveAudits(); 114 - } 115 - 116 - /** 117 - * Return true if we should `JOIN` (vs `LEFT JOIN`) the audit table, because 118 - * matching commits will always have audit rows. 119 - */ 120 - private function rowsMustHaveAudits() { 121 - return 122 - $this->auditIDs || 123 - $this->auditorPHIDs || 124 - $this->auditAwaitingUser; 125 - } 126 - 127 106 public function withAuditIDs(array $ids) { 128 107 $this->auditIDs = $ids; 129 108 return $this; ··· 134 113 return $this; 135 114 } 136 115 137 - public function withAuditAwaitingUser(PhabricatorUser $user) { 138 - $this->auditAwaitingUser = $user; 116 + public function withNeedsAuditByPHIDs(array $needs_phids) { 117 + $this->needsAuditByPHIDs = $needs_phids; 139 118 return $this; 140 119 } 141 120 ··· 175 154 } 176 155 } 177 156 178 - protected function loadPage() { 179 - $table = new PhabricatorRepositoryCommit(); 180 - $conn_r = $table->establishConnection('r'); 157 + public function newResultObject() { 158 + return new PhabricatorRepositoryCommit(); 159 + } 181 160 182 - $data = queryfx_all( 183 - $conn_r, 184 - 'SELECT commit.* FROM %T commit %Q %Q %Q %Q %Q', 185 - $table->getTableName(), 186 - $this->buildJoinClause($conn_r), 187 - $this->buildWhereClause($conn_r), 188 - $this->buildGroupClause($conn_r), 189 - $this->buildOrderClause($conn_r), 190 - $this->buildLimitClause($conn_r)); 191 - 192 - return $table->loadAllFromArray($data); 161 + protected function loadPage() { 162 + return $this->loadStandardPage($this->newResultObject()); 193 163 } 194 164 195 165 protected function willFilterPage(array $commits) { ··· 296 266 return $commits; 297 267 } 298 268 299 - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { 300 - $where = array(); 269 + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { 270 + $where = parent::buildWhereClauseParts($conn); 301 271 302 272 if ($this->repositoryPHIDs !== null) { 303 273 $map_repositories = id(new PhabricatorRepositoryQuery()) ··· 317 287 318 288 if ($this->ids !== null) { 319 289 $where[] = qsprintf( 320 - $conn_r, 290 + $conn, 321 291 'commit.id IN (%Ld)', 322 292 $this->ids); 323 293 } 324 294 325 295 if ($this->phids !== null) { 326 296 $where[] = qsprintf( 327 - $conn_r, 297 + $conn, 328 298 'commit.phid IN (%Ls)', 329 299 $this->phids); 330 300 } 331 301 332 302 if ($this->repositoryIDs !== null) { 333 303 $where[] = qsprintf( 334 - $conn_r, 304 + $conn, 335 305 'commit.repositoryID IN (%Ld)', 336 306 $this->repositoryIDs); 337 307 } 338 308 339 309 if ($this->authorPHIDs !== null) { 340 310 $where[] = qsprintf( 341 - $conn_r, 311 + $conn, 342 312 'commit.authorPHID IN (%Ls)', 343 313 $this->authorPHIDs); 344 314 } 345 315 346 316 if ($this->epochMin !== null) { 347 317 $where[] = qsprintf( 348 - $conn_r, 318 + $conn, 349 319 'commit.epoch >= %d', 350 320 $this->epochMin); 351 321 } 352 322 353 323 if ($this->epochMax !== null) { 354 324 $where[] = qsprintf( 355 - $conn_r, 325 + $conn, 356 326 'commit.epoch <= %d', 357 327 $this->epochMax); 358 328 } ··· 360 330 if ($this->importing !== null) { 361 331 if ($this->importing) { 362 332 $where[] = qsprintf( 363 - $conn_r, 333 + $conn, 364 334 '(commit.importStatus & %d) != %d', 365 335 PhabricatorRepositoryCommit::IMPORTED_ALL, 366 336 PhabricatorRepositoryCommit::IMPORTED_ALL); 367 337 } else { 368 338 $where[] = qsprintf( 369 - $conn_r, 339 + $conn, 370 340 '(commit.importStatus & %d) = %d', 371 341 PhabricatorRepositoryCommit::IMPORTED_ALL, 372 342 PhabricatorRepositoryCommit::IMPORTED_ALL); ··· 409 379 410 380 foreach ($bare as $identifier) { 411 381 $sql[] = qsprintf( 412 - $conn_r, 382 + $conn, 413 383 '(commit.commitIdentifier LIKE %> AND '. 414 384 'LENGTH(commit.commitIdentifier) = 40)', 415 385 $identifier); ··· 437 407 continue; 438 408 } 439 409 $sql[] = qsprintf( 440 - $conn_r, 410 + $conn, 441 411 '(commit.repositoryID = %d AND commit.commitIdentifier = %s)', 442 412 $repo->getID(), 443 413 // NOTE: Because the 'commitIdentifier' column is a string, MySQL ··· 449 419 continue; 450 420 } 451 421 $sql[] = qsprintf( 452 - $conn_r, 422 + $conn, 453 423 '(commit.repositoryID = %d AND commit.commitIdentifier LIKE %>)', 454 424 $repo->getID(), 455 425 $ref['identifier']); ··· 470 440 471 441 if ($this->auditIDs !== null) { 472 442 $where[] = qsprintf( 473 - $conn_r, 443 + $conn, 474 444 'audit.id IN (%Ld)', 475 445 $this->auditIDs); 476 446 } 477 447 478 448 if ($this->auditorPHIDs !== null) { 479 449 $where[] = qsprintf( 480 - $conn_r, 450 + $conn, 481 451 'audit.auditorPHID IN (%Ls)', 482 452 $this->auditorPHIDs); 483 453 } 484 454 485 - if ($this->auditAwaitingUser) { 486 - $awaiting_user_phid = $this->auditAwaitingUser->getPHID(); 487 - // Exclude package and project audits associated with commits where 488 - // the user is the author. 455 + if ($this->needsAuditByPHIDs !== null) { 489 456 $where[] = qsprintf( 490 - $conn_r, 491 - '(commit.authorPHID IS NULL OR commit.authorPHID != %s) 492 - OR (audit.auditorPHID = %s)', 493 - $awaiting_user_phid, 494 - $awaiting_user_phid); 457 + $conn, 458 + 'needs.auditorPHID IN (%Ls)', 459 + $this->needsAuditByPHIDs); 495 460 } 496 461 497 462 $status = $this->auditStatus; ··· 499 464 switch ($status) { 500 465 case self::AUDIT_STATUS_PARTIAL: 501 466 $where[] = qsprintf( 502 - $conn_r, 467 + $conn, 503 468 'commit.auditStatus = %d', 504 469 PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED); 505 470 break; 506 471 case self::AUDIT_STATUS_ACCEPTED: 507 472 $where[] = qsprintf( 508 - $conn_r, 473 + $conn, 509 474 'commit.auditStatus = %d', 510 475 PhabricatorAuditCommitStatusConstants::FULLY_AUDITED); 511 476 break; 512 477 case self::AUDIT_STATUS_CONCERN: 513 478 $where[] = qsprintf( 514 - $conn_r, 515 - 'audit.auditStatus = %s', 479 + $conn, 480 + 'status.auditStatus = %s', 516 481 PhabricatorAuditStatusConstants::CONCERNED); 517 482 break; 518 483 case self::AUDIT_STATUS_OPEN: 519 484 $where[] = qsprintf( 520 - $conn_r, 521 - 'audit.auditStatus in (%Ls)', 485 + $conn, 486 + 'status.auditStatus in (%Ls)', 522 487 PhabricatorAuditStatusConstants::getOpenStatusConstants()); 523 - if ($this->auditAwaitingUser) { 524 - $where[] = qsprintf( 525 - $conn_r, 526 - 'awaiting.auditStatus IS NULL OR awaiting.auditStatus != %s', 527 - PhabricatorAuditStatusConstants::RESIGNED); 528 - } 529 488 break; 530 489 case self::AUDIT_STATUS_ANY: 531 490 break; ··· 545 504 } 546 505 } 547 506 548 - $where[] = $this->buildPagingClause($conn_r); 549 - 550 - return $this->formatWhereClause($where); 507 + return $where; 551 508 } 552 509 553 510 protected function didFilterResults(array $filtered) { ··· 560 517 } 561 518 } 562 519 563 - protected function buildJoinClause(AphrontDatabaseConnection $conn_r) { 564 - $joins = array(); 520 + private function shouldJoinStatus() { 521 + return $this->auditStatus; 522 + } 523 + 524 + private function shouldJoinAudits() { 525 + return $this->auditIDs || $this->auditorPHIDs; 526 + } 527 + 528 + private function shouldJoinNeeds() { 529 + return $this->needsAuditByPHIDs; 530 + } 531 + 532 + protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { 533 + $join = parent::buildJoinClauseParts($conn); 565 534 $audit_request = new PhabricatorRepositoryAuditRequest(); 566 535 536 + if ($this->shouldJoinStatus()) { 537 + $join[] = qsprintf( 538 + $conn, 539 + 'LEFT JOIN %T status ON commit.phid = status.commitPHID', 540 + $audit_request->getTableName()); 541 + } 542 + 567 543 if ($this->shouldJoinAudits()) { 568 - $joins[] = qsprintf( 569 - $conn_r, 570 - '%Q %T audit ON commit.phid = audit.commitPHID', 571 - ($this->rowsMustHaveAudits() ? 'JOIN' : 'LEFT JOIN'), 544 + $join[] = qsprintf( 545 + $conn, 546 + 'JOIN %T audit ON commit.phid = audit.commitPHID', 572 547 $audit_request->getTableName()); 573 548 } 574 549 575 - if ($this->auditAwaitingUser) { 576 - // Join the request table on the awaiting user's requests, so we can 577 - // filter out package and project requests which the user has resigned 578 - // from. 579 - $joins[] = qsprintf( 580 - $conn_r, 581 - 'LEFT JOIN %T awaiting ON audit.commitPHID = awaiting.commitPHID AND 582 - awaiting.auditorPHID = %s', 550 + if ($this->shouldJoinNeeds()) { 551 + $join[] = qsprintf( 552 + $conn, 553 + 'JOIN %T needs ON commit.phid = needs.commitPHID 554 + AND needs.auditStatus IN (%Ls)', 583 555 $audit_request->getTableName(), 584 - $this->auditAwaitingUser->getPHID()); 556 + array( 557 + PhabricatorAuditStatusConstants::AUDIT_REQUESTED, 558 + PhabricatorAuditStatusConstants::AUDIT_REQUIRED, 559 + )); 585 560 } 586 561 587 - if ($joins) { 588 - return implode(' ', $joins); 589 - } else { 590 - return ''; 591 - } 562 + return $join; 592 563 } 593 564 594 - protected function buildGroupClause(AphrontDatabaseConnection $conn_r) { 595 - $should_group = $this->shouldJoinAudits(); 565 + protected function shouldGroupQueryResultRows() { 566 + if ($this->shouldJoinStatus()) { 567 + return true; 568 + } 596 569 597 - // TODO: Currently, the audit table is missing a unique key, so we may 598 - // require a GROUP BY if we perform this join. See T1768. This can be 599 - // removed once the table has the key. 600 - if ($this->auditAwaitingUser) { 601 - $should_group = true; 570 + if ($this->shouldJoinAudits()) { 571 + return true; 602 572 } 603 573 604 - if ($should_group) { 605 - return 'GROUP BY commit.id'; 606 - } else { 607 - return ''; 574 + if ($this->shouldJoinNeeds()) { 575 + return true; 608 576 } 577 + 578 + return parent::shouldGroupQueryResultRows(); 609 579 } 610 580 611 581 public function getQueryApplicationClass() { 612 582 return 'PhabricatorDiffusionApplication'; 613 583 } 584 + 585 + public function getOrderableColumns() { 586 + return parent::getOrderableColumns() + array( 587 + 'epoch' => array( 588 + 'table' => $this->getPrimaryTableAlias(), 589 + 'column' => 'epoch', 590 + 'type' => 'int', 591 + 'reverse' => false, 592 + ), 593 + ); 594 + } 595 + 596 + protected function getPagingValueMap($cursor, array $keys) { 597 + $commit = $this->loadCursorObject($cursor); 598 + return array( 599 + 'id' => $commit->getID(), 600 + 'epoch' => $commit->getEpoch(), 601 + ); 602 + } 603 + 604 + public function getBuiltinOrders() { 605 + $parent = parent::getBuiltinOrders(); 606 + 607 + // Rename the default ID-based orders. 608 + $parent['importnew'] = array( 609 + 'name' => pht('Import Date (Newest First)'), 610 + ) + $parent['newest']; 611 + 612 + $parent['importold'] = array( 613 + 'name' => pht('Import Date (Oldest First)'), 614 + ) + $parent['oldest']; 615 + 616 + return array( 617 + 'newest' => array( 618 + 'vector' => array('epoch', 'id'), 619 + 'name' => pht('Commit Date (Newest First)'), 620 + ), 621 + 'oldest' => array( 622 + 'vector' => array('-epoch', '-id'), 623 + 'name' => pht('Commit Date (Oldest First)'), 624 + ), 625 + ) + $parent; 626 + } 627 + 614 628 615 629 }
+25
src/applications/diffusion/typeahead/DiffusionAuditorFunctionDatasource.php
··· 1 + <?php 2 + 3 + final class DiffusionAuditorFunctionDatasource 4 + extends PhabricatorTypeaheadCompositeDatasource { 5 + 6 + public function getBrowseTitle() { 7 + return pht('Browse Auditors'); 8 + } 9 + 10 + public function getPlaceholderText() { 11 + return pht('Type a user, project, package name or function...'); 12 + } 13 + 14 + public function getDatasourceApplicationClass() { 15 + return 'PhabricatorDiffusionApplication'; 16 + } 17 + 18 + public function getComponentDatasources() { 19 + return array( 20 + new PhabricatorProjectOrUserFunctionDatasource(), 21 + new PhabricatorOwnersPackageFunctionDatasource(), 22 + ); 23 + } 24 + 25 + }
+1 -2
src/applications/home/controller/PhabricatorHomeMainController.php
··· 349 349 350 350 $query = id(new DiffusionCommitQuery()) 351 351 ->setViewer($user) 352 - ->withAuditorPHIDs($phids) 352 + ->withNeedsAuditByPHIDs($phids) 353 353 ->withAuditStatus(DiffusionCommitQuery::AUDIT_STATUS_OPEN) 354 - ->withAuditAwaitingUser($user) 355 354 ->needAuditRequests(true) 356 355 ->needCommitData(true) 357 356 ->setLimit(10);
+25
src/applications/owners/typeahead/PhabricatorOwnersPackageFunctionDatasource.php
··· 1 + <?php 2 + 3 + final class PhabricatorOwnersPackageFunctionDatasource 4 + extends PhabricatorTypeaheadCompositeDatasource { 5 + 6 + public function getBrowseTitle() { 7 + return pht('Browse Packages'); 8 + } 9 + 10 + public function getPlaceholderText() { 11 + return pht('Type a package name or function...'); 12 + } 13 + 14 + public function getDatasourceApplicationClass() { 15 + return 'PhabricatorOwnersApplication'; 16 + } 17 + 18 + public function getComponentDatasources() { 19 + return array( 20 + new PhabricatorOwnersPackageDatasource(), 21 + new PhabricatorOwnersPackageOwnerDatasource(), 22 + ); 23 + } 24 + 25 + }
+140
src/applications/owners/typeahead/PhabricatorOwnersPackageOwnerDatasource.php
··· 1 + <?php 2 + 3 + final class PhabricatorOwnersPackageOwnerDatasource 4 + extends PhabricatorTypeaheadCompositeDatasource { 5 + 6 + public function getBrowseTitle() { 7 + return pht('Browse Packages by Owner'); 8 + } 9 + 10 + public function getPlaceholderText() { 11 + return pht('Type packages(<user>) or packages(<project>)...'); 12 + } 13 + 14 + public function getDatasourceApplicationClass() { 15 + return 'PhabricatorOwnersApplication'; 16 + } 17 + 18 + public function getComponentDatasources() { 19 + return array( 20 + new PhabricatorPeopleDatasource(), 21 + new PhabricatorProjectDatasource(), 22 + ); 23 + } 24 + 25 + public function getDatasourceFunctions() { 26 + return array( 27 + 'packages' => array( 28 + 'name' => pht('Packages: ...'), 29 + 'arguments' => pht('owner'), 30 + 'summary' => pht("Find results in any of an owner's projects."), 31 + 'description' => pht( 32 + "This function allows you to find results associated with any ". 33 + "of the packages a specified user or project is an owner of. ". 34 + "For example, this will find results associated with all of ". 35 + "the projects `%s` owns:\n\n%s\n\n", 36 + 'alincoln', 37 + '> packages(alincoln)'), 38 + ), 39 + ); 40 + } 41 + 42 + protected function didLoadResults(array $results) { 43 + foreach ($results as $result) { 44 + $result 45 + ->setColor(null) 46 + ->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION) 47 + ->setIcon('fa-asterisk') 48 + ->setPHID('packages('.$result->getPHID().')') 49 + ->setDisplayName(pht('Packages: %s', $result->getDisplayName())) 50 + ->setName($result->getName().' packages'); 51 + } 52 + 53 + return $results; 54 + } 55 + 56 + protected function evaluateFunction($function, array $argv_list) { 57 + $phids = array(); 58 + foreach ($argv_list as $argv) { 59 + $phids[] = head($argv); 60 + } 61 + 62 + $phids = $this->resolvePHIDs($phids); 63 + 64 + $user_phids = array(); 65 + foreach ($phids as $phid) { 66 + if (phid_get_type($phid) == PhabricatorPeopleUserPHIDType::TYPECONST) { 67 + $user_phids[] = $phid; 68 + } 69 + } 70 + 71 + if ($user_phids) { 72 + $projects = id(new PhabricatorProjectQuery()) 73 + ->setViewer($this->getViewer()) 74 + ->withMemberPHIDs($user_phids) 75 + ->execute(); 76 + foreach ($projects as $project) { 77 + $phids[] = $project->getPHID(); 78 + } 79 + } 80 + 81 + return $phids; 82 + } 83 + 84 + public function renderFunctionTokens($function, array $argv_list) { 85 + $phids = array(); 86 + foreach ($argv_list as $argv) { 87 + $phids[] = head($argv); 88 + } 89 + 90 + $phids = $this->resolvePHIDs($phids); 91 + 92 + $tokens = $this->renderTokens($phids); 93 + foreach ($tokens as $token) { 94 + $token->setColor(null); 95 + if ($token->isInvalid()) { 96 + $token 97 + ->setValue(pht('Packages: Invalid Owner')); 98 + } else { 99 + $token 100 + ->setIcon('fa-asterisk') 101 + ->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION) 102 + ->setKey('packages('.$token->getKey().')') 103 + ->setValue(pht('Packages: %s', $token->getValue())); 104 + } 105 + } 106 + 107 + return $tokens; 108 + } 109 + 110 + private function resolvePHIDs(array $phids) { 111 + 112 + // TODO: It would be nice for this to handle `packages(#project)` from a 113 + // query string or eventually via Conduit. This could also share code with 114 + // PhabricatorProjectLogicalUserDatasource. 115 + 116 + $usernames = array(); 117 + foreach ($phids as $key => $phid) { 118 + if (phid_get_type($phid) != PhabricatorPeopleUserPHIDType::TYPECONST) { 119 + $usernames[$key] = $phid; 120 + } 121 + } 122 + 123 + if ($usernames) { 124 + $users = id(new PhabricatorPeopleQuery()) 125 + ->setViewer($this->getViewer()) 126 + ->withUsernames($usernames) 127 + ->execute(); 128 + $users = mpull($users, null, 'getUsername'); 129 + foreach ($usernames as $key => $username) { 130 + $user = idx($users, $username); 131 + if ($user) { 132 + $phids[$key] = $user->getPHID(); 133 + } 134 + } 135 + } 136 + 137 + return $phids; 138 + } 139 + 140 + }
+25
src/applications/project/typeahead/PhabricatorProjectOrUserFunctionDatasource.php
··· 1 + <?php 2 + 3 + final class PhabricatorProjectOrUserFunctionDatasource 4 + extends PhabricatorTypeaheadCompositeDatasource { 5 + 6 + public function getBrowseTitle() { 7 + return pht('Browse Users and Projects'); 8 + } 9 + 10 + public function getPlaceholderText() { 11 + return pht('Type a user, project name, or function...'); 12 + } 13 + 14 + public function getComponentDatasources() { 15 + return array( 16 + new PhabricatorViewerDatasource(), 17 + new PhabricatorPeopleDatasource(), 18 + new PhabricatorProjectDatasource(), 19 + new PhabricatorProjectMembersDatasource(), 20 + new PhabricatorProjectUserFunctionDatasource(), 21 + ); 22 + } 23 + 24 + 25 + }
+36
src/applications/project/typeahead/PhabricatorProjectUserFunctionDatasource.php
··· 1 + <?php 2 + 3 + final class PhabricatorProjectUserFunctionDatasource 4 + extends PhabricatorTypeaheadCompositeDatasource { 5 + 6 + public function getBrowseTitle() { 7 + return pht('Browse User Projects'); 8 + } 9 + 10 + public function getPlaceholderText() { 11 + return pht('Type projects(<user>)...'); 12 + } 13 + 14 + public function getDatasourceApplicationClass() { 15 + return 'PhabricatorProjectApplication'; 16 + } 17 + 18 + public function getComponentDatasources() { 19 + return array( 20 + new PhabricatorProjectLogicalUserDatasource(), 21 + ); 22 + } 23 + 24 + protected function evaluateFunction($function, array $argv_list) { 25 + $result = parent::evaluateFunction($function, $argv_list); 26 + 27 + foreach ($result as $k => $v) { 28 + if ($v instanceof PhabricatorQueryConstraint) { 29 + $result[$k] = $v->getValue(); 30 + } 31 + } 32 + 33 + return $result; 34 + } 35 + 36 + }
+6
src/applications/repository/storage/PhabricatorRepositoryCommit.php
··· 98 98 'columns' => array('commitIdentifier', 'repositoryID'), 99 99 'unique' => true, 100 100 ), 101 + 'key_epoch' => array( 102 + 'columns' => array('epoch'), 103 + ), 104 + 'key_author' => array( 105 + 'columns' => array('authorPHID', 'epoch'), 106 + ), 101 107 ), 102 108 self::CONFIG_NO_MUTATE => array( 103 109 'importStatus',