From 30ef575dd395b5257cb0de0bac10c6c59655d4ec Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Sat, 7 Feb 2026 22:41:38 +0200 Subject: [PATCH] Fix and improve Full Transactions View Summary: Fix T16483. 1. Fix the page to actually load things 2. Add a way to query for a specific object 3. Add link from object pages to the full history (under "Advanced") Test Plan: - Visit `/feed/transactions/` and see looots of entries. - enter object names and phids into the query, list is filtered to these objects only. - Visit a random task object, hit Advanced -> View All Transactions, see full history including things that are normally hidden. Reviewers: aklapper, valerio.bozzolan, O1 Blessed Committers, mainframe98 Reviewed By: O1 Blessed Committers, mainframe98 Subscribers: mainframe98, tobiaswiese, Matthew, Cigaryno Maniphest Tasks: T16483 Differential Revision: https://we.phorge.it/D26732 --- .../base/PhabricatorApplication.php | 22 ------- .../PhabricatorFeedApplication.php | 2 +- .../query/PhabricatorFeedTransactionQuery.php | 59 ++++++++++++++++++- ...PhabricatorFeedTransactionSearchEngine.php | 36 +++++++---- .../PhabricatorSystemDebugUIEventListener.php | 5 ++ 5 files changed, 86 insertions(+), 38 deletions(-) diff --git a/src/applications/base/PhabricatorApplication.php b/src/applications/base/PhabricatorApplication.php index 38a0abcfaa..7fee4e6086 100644 --- a/src/applications/base/PhabricatorApplication.php +++ b/src/applications/base/PhabricatorApplication.php @@ -470,28 +470,6 @@ abstract class PhabricatorApplication return $result; } - /** - * Determine if an application is enabled at all, and if a viewer is given - * if the application is available to a viewer, by application class name. - * - * To check if an application is enabled at all, use - * @{method:isClassInstalled}. - * - * @param class-string $class Application class name. - * @param PhabricatorUser|null $viewer Viewing user. - * @return bool True if the class is enabled or if the enabled application is - * available to the viewer when a viewer is given. - * @task meta - */ - final public static function isClassInstalledForViewerIfAny( - $class, - ?PhabricatorUser $viewer) { - - return $viewer - ? self::isClassInstalledForViewer($class, $viewer) - : self::isClassInstalled($class); - } - /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/src/applications/feed/application/PhabricatorFeedApplication.php b/src/applications/feed/application/PhabricatorFeedApplication.php index 7d267394e9..4f9c815722 100644 --- a/src/applications/feed/application/PhabricatorFeedApplication.php +++ b/src/applications/feed/application/PhabricatorFeedApplication.php @@ -33,7 +33,7 @@ final class PhabricatorFeedApplication extends PhabricatorApplication { '(?:query/(?P[^/]+)/)?' => 'PhabricatorFeedListController', 'transactions/' => array( $this->getQueryRoutePattern() - => 'PhabricatorFeedTransactionListController', + => PhabricatorFeedTransactionListController::class, ), ), ); diff --git a/src/applications/feed/query/PhabricatorFeedTransactionQuery.php b/src/applications/feed/query/PhabricatorFeedTransactionQuery.php index a626dcaf58..52d88d5d70 100644 --- a/src/applications/feed/query/PhabricatorFeedTransactionQuery.php +++ b/src/applications/feed/query/PhabricatorFeedTransactionQuery.php @@ -6,6 +6,7 @@ final class PhabricatorFeedTransactionQuery private $phids; private $authorPHIDs; private $objectTypes; + private $objectPHIDs; private $createdMin; private $createdMax; @@ -24,6 +25,11 @@ final class PhabricatorFeedTransactionQuery return $this; } + public function withObjectPHIDs(array $object_phids) { + $this->objectPHIDs = $object_phids; + return $this; + } + public function withDateCreatedBetween($min, $max) { $this->createdMin = $min; $this->createdMax = $max; @@ -39,6 +45,7 @@ final class PhabricatorFeedTransactionQuery } protected function loadPage() { + $this->normalizeObjectPHIDs(); $queries = $this->newTransactionQueries(); $xactions = array(); @@ -80,6 +87,7 @@ final class PhabricatorFeedTransactionQuery $xaction_phids = $this->phids; $author_phids = $this->authorPHIDs; + $object_phids = $this->objectPHIDs; foreach ($queries as $query) { $query->withDateCreatedBetween($created_min, $created_max); @@ -92,6 +100,10 @@ final class PhabricatorFeedTransactionQuery $query->withAuthorPHIDs($author_phids); } + if ($object_phids !== null) { + $query->withObjectPHIDs($object_phids); + } + if ($limit !== null) { $query->setLimit($limit); } @@ -162,8 +174,7 @@ final class PhabricatorFeedTransactionQuery foreach ($queries as $key => $query) { $app = $query->getQueryApplicationClass(); if ($app !== null && - PhabricatorApplication::isClassInstalledForViewerIfAny($app, - $viewer)) { + !PhabricatorApplication::isClassInstalledForViewer($app, $viewer)) { unset($queries[$key]); } } @@ -222,7 +233,51 @@ final class PhabricatorFeedTransactionQuery protected function applyExternalCursorConstraintsToQuery( PhabricatorCursorPagedPolicyAwareQuery $subquery, $cursor) { + $subquery->withPHIDs(array($cursor)); } + private function normalizeObjectPHIDs() { + if (!$this->objectPHIDs) { + return; + } + + $have_non_phids = false; + foreach ($this->objectPHIDs as $name) { + if (strncmp($name, 'PHID-', 5)) { + $have_non_phids = true; + break; + } + } + + if ($have_non_phids) { + + // "Names" field in ObjectQuery also handles PHIDs. + $objects = id(new PhabricatorObjectQuery()) + ->setViewer($this->getViewer()) + ->withNames($this->objectPHIDs) + ->execute(); + + if (!$objects) { + // nothing resolved to anything. + throw new PhabricatorSearchConstraintException( + pht("objectPHID inputs didn't match any known objects.")); + } + + $phids = mpull($objects, 'getPHID'); + + } else { + $phids = $this->objectPHIDs; + } + + $phid_types = array(); + foreach ($phids as $phid) { + $phid_type = phid_get_type($phid); + $phid_types[$phid_type] = $phid_type; + } + + $this->objectPHIDs = $phids; + $this->objectTypes = $phid_types; + } + } diff --git a/src/applications/feed/query/PhabricatorFeedTransactionSearchEngine.php b/src/applications/feed/query/PhabricatorFeedTransactionSearchEngine.php index 8be7f1576d..4b3e700c33 100644 --- a/src/applications/feed/query/PhabricatorFeedTransactionSearchEngine.php +++ b/src/applications/feed/query/PhabricatorFeedTransactionSearchEngine.php @@ -21,6 +21,11 @@ final class PhabricatorFeedTransactionSearchEngine ->setLabel(pht('Authors')) ->setKey('authorPHIDs') ->setAliases(array('author', 'authors')), + id(new PhabricatorSearchStringListField()) + ->setLabel(pht('Object PHIDs')) + ->setKey('objectPHIDs') + ->setAliases(array('objectPhid')) + ->setPlaceholder(pht('Comma separated list of PHIDs or object names.')), id(new PhabricatorSearchDatasourceField()) ->setLabel(pht('Object Types')) ->setKey('objectTypes') @@ -46,6 +51,10 @@ final class PhabricatorFeedTransactionSearchEngine $query->withObjectTypes($map['objectTypes']); } + if ($map['objectPHIDs']) { + $query->withObjectPHIDs($map['objectPHIDs']); + } + $created_min = $map['createdStart']; $created_max = $map['createdEnd']; @@ -91,25 +100,26 @@ final class PhabricatorFeedTransactionSearchEngine } /** - * @param array $objects + * @param array $xactions * @param PhabricatorSavedQuery $query * @param array $handles */ protected function renderResultList( - array $objects, + array $xactions, PhabricatorSavedQuery $query, array $handles) { - assert_instances_of($objects, PhabricatorApplicationTransaction::class); + + assert_instances_of($xactions, PhabricatorApplicationTransaction::class); $viewer = $this->requireViewer(); $handle_phids = array(); - foreach ($objects as $object) { - $author_phid = $object->getAuthorPHID(); + foreach ($xactions as $xaction) { + $author_phid = $xaction->getAuthorPHID(); if ($author_phid !== null) { $handle_phids[] = $author_phid; } - $object_phid = $object->getObjectPHID(); + $object_phid = $xaction->getObjectPHID(); if ($object_phid !== null) { $handle_phids[] = $object_phid; } @@ -118,13 +128,13 @@ final class PhabricatorFeedTransactionSearchEngine $handles = $viewer->loadHandles($handle_phids); $rows = array(); - foreach ($objects as $object) { - $author_phid = $object->getAuthorPHID(); - $object_phid = $object->getObjectPHID(); + foreach ($xactions as $xaction) { + $author_phid = $xaction->getAuthorPHID(); + $object_phid = $xaction->getObjectPHID(); try { - $title = $object->getTitle(); - } catch (Exception $ex) { + $title = $xaction->getTitle(); + } catch (Throwable $ex) { $title = null; } @@ -132,7 +142,7 @@ final class PhabricatorFeedTransactionSearchEngine $handles[$author_phid]->renderLink(), $handles[$object_phid]->renderLink(), AphrontTableView::renderSingleDisplayLine($title), - phabricator_datetime($object->getDateCreated(), $viewer), + phabricator_datetime($xaction->getDateCreated(), $viewer), ); } @@ -214,7 +224,7 @@ final class PhabricatorFeedTransactionSearchEngine $description = $xaction ->setRenderingTarget(PhabricatorApplicationTransaction::TARGET_TEXT) ->getTitle(); - } catch (Exception $ex) { + } catch (Throwable $ex) { $description = null; } $xaction->setRenderingTarget($old_target); diff --git a/src/applications/system/events/PhabricatorSystemDebugUIEventListener.php b/src/applications/system/events/PhabricatorSystemDebugUIEventListener.php index 58b390fb23..e8c21c1641 100644 --- a/src/applications/system/events/PhabricatorSystemDebugUIEventListener.php +++ b/src/applications/system/events/PhabricatorSystemDebugUIEventListener.php @@ -44,6 +44,11 @@ final class PhabricatorSystemDebugUIEventListener ->setName(pht('View Hovercard')) ->setHref(urisprintf('/search/hovercard/?names=%s', $phid)); + $submenu[] = id(new PhabricatorActionView()) + ->setIcon('fa-list') + ->setName(pht('View full transaction history')) + ->setHref(urisprintf('/feed/transactions?objectPHIDs=%s', $phid)); + if ($object instanceof DifferentialRevision) { $submenu[] = id(new PhabricatorActionView()) ->setIcon('fa-database') -- 2.51.2