diff --git a/src/applications/almanac/query/AlmanacBindingQuery.php b/src/applications/almanac/query/AlmanacBindingQuery.php index 574967f7bb..c3c7940525 100644 --- a/src/applications/almanac/query/AlmanacBindingQuery.php +++ b/src/applications/almanac/query/AlmanacBindingQuery.php @@ -1,179 +1,175 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withServicePHIDs(array $phids) { $this->servicePHIDs = $phids; return $this; } public function withDevicePHIDs(array $phids) { $this->devicePHIDs = $phids; return $this; } public function withInterfacePHIDs(array $phids) { $this->interfacePHIDs = $phids; return $this; } public function withIsActive($active) { $this->isActive = $active; return $this; } public function newResultObject() { return new AlmanacBinding(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $bindings) { $service_phids = mpull($bindings, 'getServicePHID'); $device_phids = mpull($bindings, 'getDevicePHID'); $interface_phids = mpull($bindings, 'getInterfacePHID'); $services = id(new AlmanacServiceQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($service_phids) ->needProperties($this->getNeedProperties()) ->execute(); $services = mpull($services, null, 'getPHID'); $devices = id(new AlmanacDeviceQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($device_phids) ->needProperties($this->getNeedProperties()) ->execute(); $devices = mpull($devices, null, 'getPHID'); $interfaces = id(new AlmanacInterfaceQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($interface_phids) ->needProperties($this->getNeedProperties()) ->execute(); $interfaces = mpull($interfaces, null, 'getPHID'); foreach ($bindings as $key => $binding) { $service = idx($services, $binding->getServicePHID()); $device = idx($devices, $binding->getDevicePHID()); $interface = idx($interfaces, $binding->getInterfacePHID()); if (!$service || !$device || !$interface) { $this->didRejectResult($binding); unset($bindings[$key]); continue; } $binding->attachService($service); $binding->attachDevice($device); $binding->attachInterface($interface); } return $bindings; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'binding.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'binding.phid IN (%Ls)', $this->phids); } if ($this->servicePHIDs !== null) { $where[] = qsprintf( $conn, 'binding.servicePHID IN (%Ls)', $this->servicePHIDs); } if ($this->devicePHIDs !== null) { $where[] = qsprintf( $conn, 'binding.devicePHID IN (%Ls)', $this->devicePHIDs); } if ($this->interfacePHIDs !== null) { $where[] = qsprintf( $conn, 'binding.interfacePHID IN (%Ls)', $this->interfacePHIDs); } if ($this->isActive !== null) { if ($this->isActive) { $where[] = qsprintf( $conn, '(binding.isDisabled = 0) AND (device.status IN (%Ls))', AlmanacDeviceStatus::getActiveStatusList()); } else { $where[] = qsprintf( $conn, '(binding.isDisabled = 1) OR (device.status IN (%Ls))', AlmanacDeviceStatus::getDisabledStatusList()); } } return $where; } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); if ($this->shouldJoinDeviceTable()) { $device_table = new AlmanacDevice(); $joins[] = qsprintf( $conn, 'JOIN %R device ON binding.devicePHID = device.phid', $device_table); } return $joins; } private function shouldJoinDeviceTable() { if ($this->isActive !== null) { return true; } return false; } protected function getPrimaryTableAlias() { return 'binding'; } } diff --git a/src/applications/almanac/query/AlmanacDeviceQuery.php b/src/applications/almanac/query/AlmanacDeviceQuery.php index 7fcf219571..42796c4839 100644 --- a/src/applications/almanac/query/AlmanacDeviceQuery.php +++ b/src/applications/almanac/query/AlmanacDeviceQuery.php @@ -1,158 +1,154 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withNames(array $names) { $this->names = $names; return $this; } public function withNamePrefix($prefix) { $this->namePrefix = $prefix; return $this; } public function withNameSuffix($suffix) { $this->nameSuffix = $suffix; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function withNameNgrams($ngrams) { return $this->withNgramsConstraint( new AlmanacDeviceNameNgrams(), $ngrams); } public function withIsClusterDevice($is_cluster_device) { $this->isClusterDevice = $is_cluster_device; return $this; } public function newResultObject() { return new AlmanacDevice(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'device.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'device.phid IN (%Ls)', $this->phids); } if ($this->names !== null) { $hashes = array(); foreach ($this->names as $name) { $hashes[] = PhabricatorHash::digestForIndex($name); } $where[] = qsprintf( $conn, 'device.nameIndex IN (%Ls)', $hashes); } if ($this->namePrefix !== null) { $where[] = qsprintf( $conn, 'device.name LIKE %>', $this->namePrefix); } if ($this->nameSuffix !== null) { $where[] = qsprintf( $conn, 'device.name LIKE %<', $this->nameSuffix); } if ($this->isClusterDevice !== null) { $where[] = qsprintf( $conn, 'device.isBoundToClusterService = %d', (int)$this->isClusterDevice); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'device.status IN (%Ls)', $this->statuses); } return $where; } protected function getPrimaryTableAlias() { return 'device'; } public function getOrderableColumns() { return parent::getOrderableColumns() + array( 'name' => array( 'table' => $this->getPrimaryTableAlias(), 'column' => 'name', 'type' => 'string', 'unique' => true, 'reverse' => true, ), ); } protected function newPagingMapFromPartialObject($object) { return array( 'id' => (int)$object->getID(), 'name' => $object->getName(), ); } public function getBuiltinOrders() { return array( 'name' => array( 'vector' => array('name'), 'name' => pht('Device Name'), ), ) + parent::getBuiltinOrders(); } public function getQueryApplicationClass() { return 'PhabricatorAlmanacApplication'; } } diff --git a/src/applications/almanac/query/AlmanacInterfaceQuery.php b/src/applications/almanac/query/AlmanacInterfaceQuery.php index 5738108ffc..dbbc0cd53e 100644 --- a/src/applications/almanac/query/AlmanacInterfaceQuery.php +++ b/src/applications/almanac/query/AlmanacInterfaceQuery.php @@ -1,211 +1,207 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withNetworkPHIDs(array $phids) { $this->networkPHIDs = $phids; return $this; } public function withDevicePHIDs(array $phids) { $this->devicePHIDs = $phids; return $this; } public function withAddresses(array $addresses) { $this->addresses = $addresses; return $this; } public function newResultObject() { return new AlmanacInterface(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $interfaces) { $network_phids = mpull($interfaces, 'getNetworkPHID'); $device_phids = mpull($interfaces, 'getDevicePHID'); $networks = id(new AlmanacNetworkQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($network_phids) ->needProperties($this->getNeedProperties()) ->execute(); $networks = mpull($networks, null, 'getPHID'); $devices = id(new AlmanacDeviceQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($device_phids) ->needProperties($this->getNeedProperties()) ->execute(); $devices = mpull($devices, null, 'getPHID'); foreach ($interfaces as $key => $interface) { $network = idx($networks, $interface->getNetworkPHID()); $device = idx($devices, $interface->getDevicePHID()); if (!$network || !$device) { $this->didRejectResult($interface); unset($interfaces[$key]); continue; } $interface->attachNetwork($network); $interface->attachDevice($device); } return $interfaces; } protected function buildSelectClauseParts(AphrontDatabaseConnection $conn) { $select = parent::buildSelectClauseParts($conn); if ($this->shouldJoinDeviceTable()) { $select[] = qsprintf($conn, 'device.name'); } return $select; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'interface.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'interface.phid IN (%Ls)', $this->phids); } if ($this->networkPHIDs !== null) { $where[] = qsprintf( $conn, 'interface.networkPHID IN (%Ls)', $this->networkPHIDs); } if ($this->devicePHIDs !== null) { $where[] = qsprintf( $conn, 'interface.devicePHID IN (%Ls)', $this->devicePHIDs); } if ($this->addresses !== null) { $parts = array(); foreach ($this->addresses as $address) { $parts[] = qsprintf( $conn, '(interface.networkPHID = %s '. 'AND interface.address = %s '. 'AND interface.port = %d)', $address->getNetworkPHID(), $address->getAddress(), $address->getPort()); } $where[] = qsprintf($conn, '%LO', $parts); } return $where; } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); if ($this->shouldJoinDeviceTable()) { $joins[] = qsprintf( $conn, 'JOIN %T device ON device.phid = interface.devicePHID', id(new AlmanacDevice())->getTableName()); } return $joins; } protected function shouldGroupQueryResultRows() { if ($this->shouldJoinDeviceTable()) { return true; } return parent::shouldGroupQueryResultRows(); } private function shouldJoinDeviceTable() { $vector = $this->getOrderVector(); if ($vector->containsKey('name')) { return true; } return false; } protected function getPrimaryTableAlias() { return 'interface'; } public function getQueryApplicationClass() { return 'PhabricatorAlmanacApplication'; } public function getBuiltinOrders() { return array( 'name' => array( 'vector' => array('name', 'id'), 'name' => pht('Device Name'), ), ) + parent::getBuiltinOrders(); } public function getOrderableColumns() { return parent::getOrderableColumns() + array( 'name' => array( 'table' => 'device', 'column' => 'name', 'type' => 'string', 'reverse' => true, ), ); } protected function newPagingMapFromCursorObject( PhabricatorQueryCursor $cursor, array $keys) { $interface = $cursor->getObject(); return array( 'id' => (int)$interface->getID(), 'name' => $cursor->getRawRowProperty('device.name'), ); } } diff --git a/src/applications/almanac/query/AlmanacNamespaceQuery.php b/src/applications/almanac/query/AlmanacNamespaceQuery.php index d4378e17c7..e6c99bf6ea 100644 --- a/src/applications/almanac/query/AlmanacNamespaceQuery.php +++ b/src/applications/almanac/query/AlmanacNamespaceQuery.php @@ -1,102 +1,98 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withNames(array $names) { $this->names = $names; return $this; } public function withNameNgrams($ngrams) { return $this->withNgramsConstraint( new AlmanacNamespaceNameNgrams(), $ngrams); } public function newResultObject() { return new AlmanacNamespace(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'namespace.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'namespace.phid IN (%Ls)', $this->phids); } if ($this->names !== null) { $where[] = qsprintf( $conn, 'namespace.name IN (%Ls)', $this->names); } return $where; } protected function getPrimaryTableAlias() { return 'namespace'; } public function getOrderableColumns() { return parent::getOrderableColumns() + array( 'name' => array( 'table' => $this->getPrimaryTableAlias(), 'column' => 'name', 'type' => 'string', 'unique' => true, 'reverse' => true, ), ); } protected function newPagingMapFromPartialObject($object) { return array( 'id' => (int)$object->getID(), 'name' => $object->getName(), ); } public function getBuiltinOrders() { return array( 'name' => array( 'vector' => array('name'), 'name' => pht('Namespace Name'), ), ) + parent::getBuiltinOrders(); } public function getQueryApplicationClass() { return 'PhabricatorAlmanacApplication'; } } diff --git a/src/applications/almanac/query/AlmanacNetworkQuery.php b/src/applications/almanac/query/AlmanacNetworkQuery.php index 13176f7de1..7af9db8585 100644 --- a/src/applications/almanac/query/AlmanacNetworkQuery.php +++ b/src/applications/almanac/query/AlmanacNetworkQuery.php @@ -1,74 +1,70 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function newResultObject() { return new AlmanacNetwork(); } public function withNames(array $names) { $this->names = $names; return $this; } public function withNameNgrams($ngrams) { return $this->withNgramsConstraint( new AlmanacNetworkNameNgrams(), $ngrams); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'network.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'network.phid IN (%Ls)', $this->phids); } if ($this->names !== null) { $where[] = qsprintf( $conn, 'network.name IN (%Ls)', $this->names); } return $where; } protected function getPrimaryTableAlias() { return 'network'; } public function getQueryApplicationClass() { return 'PhabricatorAlmanacApplication'; } } diff --git a/src/applications/almanac/query/AlmanacPropertyQuery.php b/src/applications/almanac/query/AlmanacPropertyQuery.php index 17f1e87c07..4261c70fec 100644 --- a/src/applications/almanac/query/AlmanacPropertyQuery.php +++ b/src/applications/almanac/query/AlmanacPropertyQuery.php @@ -1,109 +1,105 @@ ids = $ids; return $this; } public function withObjectPHIDs(array $phids) { $this->objectPHIDs = $phids; return $this; } public function withObjects(array $objects) { $this->objects = mpull($objects, null, 'getPHID'); $this->objectPHIDs = array_keys($this->objects); return $this; } public function withNames(array $names) { $this->names = $names; return $this; } public function newResultObject() { return new AlmanacProperty(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $properties) { $object_phids = mpull($properties, 'getObjectPHID'); $object_phids = array_fuse($object_phids); if ($this->objects !== null) { $object_phids = array_diff_key($object_phids, $this->objects); } if ($object_phids) { $objects = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($object_phids) ->execute(); $objects = mpull($objects, null, 'getPHID'); } else { $objects = array(); } $objects += $this->objects; foreach ($properties as $key => $property) { $object = idx($objects, $property->getObjectPHID()); if (!$object) { unset($properties[$key]); continue; } $property->attachObject($object); } return $properties; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->objectPHIDs !== null) { $where[] = qsprintf( $conn, 'objectPHID IN (%Ls)', $this->objectPHIDs); } if ($this->names !== null) { $hashes = array(); foreach ($this->names as $name) { $hashes[] = PhabricatorHash::digestForIndex($name); } $where[] = qsprintf( $conn, 'fieldIndex IN (%Ls)', $hashes); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorAlmanacApplication'; } } diff --git a/src/applications/almanac/query/AlmanacServiceQuery.php b/src/applications/almanac/query/AlmanacServiceQuery.php index 4e374ec90c..eaca9d4a76 100644 --- a/src/applications/almanac/query/AlmanacServiceQuery.php +++ b/src/applications/almanac/query/AlmanacServiceQuery.php @@ -1,248 +1,244 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withNames(array $names) { $this->names = $names; return $this; } public function withServiceTypes(array $types) { $this->serviceTypes = $types; return $this; } public function withDevicePHIDs(array $phids) { $this->devicePHIDs = $phids; return $this; } public function withNamePrefix($prefix) { $this->namePrefix = $prefix; return $this; } public function withNameSuffix($suffix) { $this->nameSuffix = $suffix; return $this; } public function withNameNgrams($ngrams) { return $this->withNgramsConstraint( new AlmanacServiceNameNgrams(), $ngrams); } public function needBindings($need_bindings) { $this->needBindings = $need_bindings; return $this; } public function needActiveBindings($need_active) { $this->needActiveBindings = $need_active; return $this; } public function newResultObject() { return new AlmanacService(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); if ($this->shouldJoinBindingTable()) { $joins[] = qsprintf( $conn, 'JOIN %T binding ON service.phid = binding.servicePHID', id(new AlmanacBinding())->getTableName()); } return $joins; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'service.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'service.phid IN (%Ls)', $this->phids); } if ($this->names !== null) { $hashes = array(); foreach ($this->names as $name) { $hashes[] = PhabricatorHash::digestForIndex($name); } $where[] = qsprintf( $conn, 'service.nameIndex IN (%Ls)', $hashes); } if ($this->serviceTypes !== null) { $where[] = qsprintf( $conn, 'service.serviceType IN (%Ls)', $this->serviceTypes); } if ($this->devicePHIDs !== null) { $where[] = qsprintf( $conn, 'binding.devicePHID IN (%Ls)', $this->devicePHIDs); } if ($this->namePrefix !== null) { $where[] = qsprintf( $conn, 'service.name LIKE %>', $this->namePrefix); } if ($this->nameSuffix !== null) { $where[] = qsprintf( $conn, 'service.name LIKE %<', $this->nameSuffix); } return $where; } protected function willFilterPage(array $services) { $service_map = AlmanacServiceType::getAllServiceTypes(); foreach ($services as $key => $service) { $implementation = idx($service_map, $service->getServiceType()); if (!$implementation) { $this->didRejectResult($service); unset($services[$key]); continue; } $implementation = clone $implementation; $service->attachServiceImplementation($implementation); } return $services; } protected function didFilterPage(array $services) { $need_all = $this->needBindings; $need_active = $this->needActiveBindings; $need_any = ($need_all || $need_active); $only_active = ($need_active && !$need_all); if ($need_any) { $service_phids = mpull($services, 'getPHID'); $bindings_query = id(new AlmanacBindingQuery()) ->setViewer($this->getViewer()) ->withServicePHIDs($service_phids) ->needProperties($this->getNeedProperties()); if ($only_active) { $bindings_query->withIsActive(true); } $bindings = $bindings_query->execute(); $bindings = mgroup($bindings, 'getServicePHID'); foreach ($services as $service) { $service_bindings = idx($bindings, $service->getPHID(), array()); if ($only_active) { $service->attachActiveBindings($service_bindings); } else { $service->attachBindings($service_bindings); } } } return parent::didFilterPage($services); } private function shouldJoinBindingTable() { return ($this->devicePHIDs !== null); } protected function shouldGroupQueryResultRows() { if ($this->shouldJoinBindingTable()) { return true; } return parent::shouldGroupQueryResultRows(); } protected function getPrimaryTableAlias() { return 'service'; } public function getOrderableColumns() { return parent::getOrderableColumns() + array( 'name' => array( 'table' => $this->getPrimaryTableAlias(), 'column' => 'name', 'type' => 'string', 'unique' => true, 'reverse' => true, ), ); } protected function newPagingMapFromPartialObject($object) { return array( 'id' => (int)$object->getID(), 'name' => $object->getName(), ); } public function getBuiltinOrders() { return array( 'name' => array( 'vector' => array('name'), 'name' => pht('Service Name'), ), ) + parent::getBuiltinOrders(); } } diff --git a/src/applications/auth/query/PhabricatorAuthChallengeQuery.php b/src/applications/auth/query/PhabricatorAuthChallengeQuery.php index 195abe0884..c6abead11f 100644 --- a/src/applications/auth/query/PhabricatorAuthChallengeQuery.php +++ b/src/applications/auth/query/PhabricatorAuthChallengeQuery.php @@ -1,99 +1,95 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withUserPHIDs(array $user_phids) { $this->userPHIDs = $user_phids; return $this; } public function withFactorPHIDs(array $factor_phids) { $this->factorPHIDs = $factor_phids; return $this; } public function withChallengeTTLBetween($challenge_min, $challenge_max) { $this->challengeTTLMin = $challenge_min; $this->challengeTTLMax = $challenge_max; return $this; } public function newResultObject() { return new PhabricatorAuthChallenge(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->userPHIDs !== null) { $where[] = qsprintf( $conn, 'userPHID IN (%Ls)', $this->userPHIDs); } if ($this->factorPHIDs !== null) { $where[] = qsprintf( $conn, 'factorPHID IN (%Ls)', $this->factorPHIDs); } if ($this->challengeTTLMin !== null) { $where[] = qsprintf( $conn, 'challengeTTL >= %d', $this->challengeTTLMin); } if ($this->challengeTTLMax !== null) { $where[] = qsprintf( $conn, 'challengeTTL <= %d', $this->challengeTTLMax); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorAuthApplication'; } } diff --git a/src/applications/auth/query/PhabricatorAuthContactNumberQuery.php b/src/applications/auth/query/PhabricatorAuthContactNumberQuery.php index 77b3b559dd..ba507c757b 100644 --- a/src/applications/auth/query/PhabricatorAuthContactNumberQuery.php +++ b/src/applications/auth/query/PhabricatorAuthContactNumberQuery.php @@ -1,103 +1,99 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withObjectPHIDs(array $object_phids) { $this->objectPHIDs = $object_phids; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function withUniqueKeys(array $unique_keys) { $this->uniqueKeys = $unique_keys; return $this; } public function withIsPrimary($is_primary) { $this->isPrimary = $is_primary; return $this; } public function newResultObject() { return new PhabricatorAuthContactNumber(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->objectPHIDs !== null) { $where[] = qsprintf( $conn, 'objectPHID IN (%Ls)', $this->objectPHIDs); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'status IN (%Ls)', $this->statuses); } if ($this->uniqueKeys !== null) { $where[] = qsprintf( $conn, 'uniqueKey IN (%Ls)', $this->uniqueKeys); } if ($this->isPrimary !== null) { $where[] = qsprintf( $conn, 'isPrimary = %d', (int)$this->isPrimary); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorAuthApplication'; } } diff --git a/src/applications/auth/query/PhabricatorAuthFactorConfigQuery.php b/src/applications/auth/query/PhabricatorAuthFactorConfigQuery.php index 5f838f66ba..dd73d0b081 100644 --- a/src/applications/auth/query/PhabricatorAuthFactorConfigQuery.php +++ b/src/applications/auth/query/PhabricatorAuthFactorConfigQuery.php @@ -1,131 +1,127 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withUserPHIDs(array $user_phids) { $this->userPHIDs = $user_phids; return $this; } public function withFactorProviderPHIDs(array $provider_phids) { $this->factorProviderPHIDs = $provider_phids; return $this; } public function withFactorProviderStatuses(array $statuses) { $this->factorProviderStatuses = $statuses; return $this; } public function newResultObject() { return new PhabricatorAuthFactorConfig(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'config.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'config.phid IN (%Ls)', $this->phids); } if ($this->userPHIDs !== null) { $where[] = qsprintf( $conn, 'config.userPHID IN (%Ls)', $this->userPHIDs); } if ($this->factorProviderPHIDs !== null) { $where[] = qsprintf( $conn, 'config.factorProviderPHID IN (%Ls)', $this->factorProviderPHIDs); } if ($this->factorProviderStatuses !== null) { $where[] = qsprintf( $conn, 'provider.status IN (%Ls)', $this->factorProviderStatuses); } return $where; } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); if ($this->factorProviderStatuses !== null) { $joins[] = qsprintf( $conn, 'JOIN %R provider ON config.factorProviderPHID = provider.phid', new PhabricatorAuthFactorProvider()); } return $joins; } protected function willFilterPage(array $configs) { $provider_phids = mpull($configs, 'getFactorProviderPHID'); $providers = id(new PhabricatorAuthFactorProviderQuery()) ->setViewer($this->getViewer()) ->withPHIDs($provider_phids) ->execute(); $providers = mpull($providers, null, 'getPHID'); foreach ($configs as $key => $config) { $provider = idx($providers, $config->getFactorProviderPHID()); if (!$provider) { unset($configs[$key]); $this->didRejectResult($config); continue; } $config->attachFactorProvider($provider); } return $configs; } protected function getPrimaryTableAlias() { return 'config'; } public function getQueryApplicationClass() { return 'PhabricatorAuthApplication'; } } diff --git a/src/applications/auth/query/PhabricatorAuthFactorProviderQuery.php b/src/applications/auth/query/PhabricatorAuthFactorProviderQuery.php index 57b554885c..7083545e3e 100644 --- a/src/applications/auth/query/PhabricatorAuthFactorProviderQuery.php +++ b/src/applications/auth/query/PhabricatorAuthFactorProviderQuery.php @@ -1,94 +1,90 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withProviderFactorKeys(array $keys) { $this->providerFactorKeys = $keys; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function newResultObject() { return new PhabricatorAuthFactorProvider(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'status IN (%Ls)', $this->statuses); } if ($this->providerFactorKeys !== null) { $where[] = qsprintf( $conn, 'providerFactorKey IN (%Ls)', $this->providerFactorKeys); } return $where; } protected function willFilterPage(array $providers) { $map = PhabricatorAuthFactor::getAllFactors(); foreach ($providers as $key => $provider) { $factor_key = $provider->getProviderFactorKey(); $factor = idx($map, $factor_key); if (!$factor) { unset($providers[$key]); continue; } $provider->attachFactor($factor); } return $providers; } public function getQueryApplicationClass() { return 'PhabricatorAuthApplication'; } } diff --git a/src/applications/auth/query/PhabricatorAuthMessageQuery.php b/src/applications/auth/query/PhabricatorAuthMessageQuery.php index 384c8de23b..7158d03a00 100644 --- a/src/applications/auth/query/PhabricatorAuthMessageQuery.php +++ b/src/applications/auth/query/PhabricatorAuthMessageQuery.php @@ -1,83 +1,79 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withMessageKeys(array $keys) { $this->messageKeys = $keys; return $this; } public function newResultObject() { return new PhabricatorAuthMessage(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->messageKeys !== null) { $where[] = qsprintf( $conn, 'messageKey IN (%Ls)', $this->messageKeys); } return $where; } protected function willFilterPage(array $messages) { $message_types = PhabricatorAuthMessageType::getAllMessageTypes(); foreach ($messages as $key => $message) { $message_key = $message->getMessageKey(); $message_type = idx($message_types, $message_key); if (!$message_type) { unset($messages[$key]); $this->didRejectResult($message); continue; } $message->attachMessageType($message_type); } return $messages; } public function getQueryApplicationClass() { return 'PhabricatorAuthApplication'; } } diff --git a/src/applications/auth/query/PhabricatorAuthPasswordQuery.php b/src/applications/auth/query/PhabricatorAuthPasswordQuery.php index 483936fb30..a77fd54b13 100644 --- a/src/applications/auth/query/PhabricatorAuthPasswordQuery.php +++ b/src/applications/auth/query/PhabricatorAuthPasswordQuery.php @@ -1,114 +1,110 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withObjectPHIDs(array $object_phids) { $this->objectPHIDs = $object_phids; return $this; } public function withPasswordTypes(array $types) { $this->passwordTypes = $types; return $this; } public function withIsRevoked($is_revoked) { $this->isRevoked = $is_revoked; return $this; } public function newResultObject() { return new PhabricatorAuthPassword(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->objectPHIDs !== null) { $where[] = qsprintf( $conn, 'objectPHID IN (%Ls)', $this->objectPHIDs); } if ($this->passwordTypes !== null) { $where[] = qsprintf( $conn, 'passwordType IN (%Ls)', $this->passwordTypes); } if ($this->isRevoked !== null) { $where[] = qsprintf( $conn, 'isRevoked = %d', (int)$this->isRevoked); } return $where; } protected function willFilterPage(array $passwords) { $object_phids = mpull($passwords, 'getObjectPHID'); $objects = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($object_phids) ->execute(); $objects = mpull($objects, null, 'getPHID'); foreach ($passwords as $key => $password) { $object = idx($objects, $password->getObjectPHID()); if (!$object) { unset($passwords[$key]); $this->didRejectResult($password); continue; } $password->attachObject($object); } return $passwords; } public function getQueryApplicationClass() { return 'PhabricatorAuthApplication'; } } diff --git a/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php b/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php index ee073e3ac1..30e5dad113 100644 --- a/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php +++ b/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php @@ -1,90 +1,86 @@ phids = $phids; return $this; } public function withIDs(array $ids) { $this->ids = $ids; return $this; } public function withProviderClasses(array $classes) { $this->providerClasses = $classes; return $this; } public function withIsEnabled($is_enabled) { $this->isEnabled = $is_enabled; return $this; } public function newResultObject() { return new PhabricatorAuthProviderConfig(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->providerClasses !== null) { $where[] = qsprintf( $conn, 'providerClass IN (%Ls)', $this->providerClasses); } if ($this->isEnabled !== null) { $where[] = qsprintf( $conn, 'isEnabled = %d', (int)$this->isEnabled); } return $where; } protected function willFilterPage(array $configs) { foreach ($configs as $key => $config) { $provider = $config->getProvider(); if (!$provider) { unset($configs[$key]); continue; } } return $configs; } public function getQueryApplicationClass() { return 'PhabricatorAuthApplication'; } } diff --git a/src/applications/auth/query/PhabricatorAuthSSHKeyQuery.php b/src/applications/auth/query/PhabricatorAuthSSHKeyQuery.php index 3a310ed173..7d4aac4a01 100644 --- a/src/applications/auth/query/PhabricatorAuthSSHKeyQuery.php +++ b/src/applications/auth/query/PhabricatorAuthSSHKeyQuery.php @@ -1,138 +1,134 @@ deleteKey($authfile_key); } public function withIDs(array $ids) { $this->ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withObjectPHIDs(array $object_phids) { $this->objectPHIDs = $object_phids; return $this; } public function withKeys(array $keys) { assert_instances_of($keys, 'PhabricatorAuthSSHPublicKey'); $this->keys = $keys; return $this; } public function withIsActive($active) { $this->isActive = $active; return $this; } public function newResultObject() { return new PhabricatorAuthSSHKey(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $keys) { $object_phids = mpull($keys, 'getObjectPHID'); $objects = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($object_phids) ->execute(); $objects = mpull($objects, null, 'getPHID'); foreach ($keys as $key => $ssh_key) { $object = idx($objects, $ssh_key->getObjectPHID()); // We must have an object, and that object must be a valid object for // SSH keys. if (!$object || !($object instanceof PhabricatorSSHPublicKeyInterface)) { $this->didRejectResult($ssh_key); unset($keys[$key]); continue; } $ssh_key->attachObject($object); } return $keys; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->objectPHIDs !== null) { $where[] = qsprintf( $conn, 'objectPHID IN (%Ls)', $this->objectPHIDs); } if ($this->keys !== null) { $sql = array(); foreach ($this->keys as $key) { $sql[] = qsprintf( $conn, '(keyType = %s AND keyIndex = %s)', $key->getType(), $key->getHash()); } $where[] = qsprintf($conn, '%LO', $sql); } if ($this->isActive !== null) { if ($this->isActive) { $where[] = qsprintf( $conn, 'isActive = %d', 1); } else { $where[] = qsprintf( $conn, 'isActive IS NULL'); } } return $where; } public function getQueryApplicationClass() { return 'PhabricatorAuthApplication'; } } diff --git a/src/applications/auth/query/PhabricatorAuthSessionQuery.php b/src/applications/auth/query/PhabricatorAuthSessionQuery.php index 00a663e964..4bc7eba73f 100644 --- a/src/applications/auth/query/PhabricatorAuthSessionQuery.php +++ b/src/applications/auth/query/PhabricatorAuthSessionQuery.php @@ -1,117 +1,113 @@ identityPHIDs = $identity_phids; return $this; } public function withSessionKeys(array $keys) { $this->sessionKeys = $keys; return $this; } public function withSessionTypes(array $types) { $this->sessionTypes = $types; return $this; } public function withIDs(array $ids) { $this->ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function newResultObject() { return new PhabricatorAuthSession(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $sessions) { $identity_phids = mpull($sessions, 'getUserPHID'); $identity_objects = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($identity_phids) ->execute(); $identity_objects = mpull($identity_objects, null, 'getPHID'); foreach ($sessions as $key => $session) { $identity_object = idx($identity_objects, $session->getUserPHID()); if (!$identity_object) { unset($sessions[$key]); } else { $session->attachIdentityObject($identity_object); } } return $sessions; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->identityPHIDs !== null) { $where[] = qsprintf( $conn, 'userPHID IN (%Ls)', $this->identityPHIDs); } if ($this->sessionKeys !== null) { $hashes = array(); foreach ($this->sessionKeys as $session_key) { $hashes[] = PhabricatorAuthSession::newSessionDigest( new PhutilOpaqueEnvelope($session_key)); } $where[] = qsprintf( $conn, 'sessionKey IN (%Ls)', $hashes); } if ($this->sessionTypes !== null) { $where[] = qsprintf( $conn, 'type IN (%Ls)', $this->sessionTypes); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorAuthApplication'; } } diff --git a/src/applications/auth/query/PhabricatorAuthTemporaryTokenQuery.php b/src/applications/auth/query/PhabricatorAuthTemporaryTokenQuery.php index 72141f75f0..c5cb39096c 100644 --- a/src/applications/auth/query/PhabricatorAuthTemporaryTokenQuery.php +++ b/src/applications/auth/query/PhabricatorAuthTemporaryTokenQuery.php @@ -1,110 +1,106 @@ ids = $ids; return $this; } public function withTokenResources(array $resources) { $this->tokenResources = $resources; return $this; } public function withTokenTypes(array $types) { $this->tokenTypes = $types; return $this; } public function withExpired($expired) { $this->expired = $expired; return $this; } public function withTokenCodes(array $codes) { $this->tokenCodes = $codes; return $this; } public function withUserPHIDs(array $phids) { $this->userPHIDs = $phids; return $this; } public function newResultObject() { return new PhabricatorAuthTemporaryToken(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->tokenResources !== null) { $where[] = qsprintf( $conn, 'tokenResource IN (%Ls)', $this->tokenResources); } if ($this->tokenTypes !== null) { $where[] = qsprintf( $conn, 'tokenType IN (%Ls)', $this->tokenTypes); } if ($this->expired !== null) { if ($this->expired) { $where[] = qsprintf( $conn, 'tokenExpires <= %d', time()); } else { $where[] = qsprintf( $conn, 'tokenExpires > %d', time()); } } if ($this->tokenCodes !== null) { $where[] = qsprintf( $conn, 'tokenCode IN (%Ls)', $this->tokenCodes); } if ($this->userPHIDs !== null) { $where[] = qsprintf( $conn, 'userPHID IN (%Ls)', $this->userPHIDs); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorAuthApplication'; } } diff --git a/src/applications/auth/query/PhabricatorExternalAccountIdentifierQuery.php b/src/applications/auth/query/PhabricatorExternalAccountIdentifierQuery.php index c44e0aed4f..b5c5b6eaa2 100644 --- a/src/applications/auth/query/PhabricatorExternalAccountIdentifierQuery.php +++ b/src/applications/auth/query/PhabricatorExternalAccountIdentifierQuery.php @@ -1,94 +1,90 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withProviderConfigPHIDs(array $phids) { $this->providerConfigPHIDs = $phids; return $this; } public function withExternalAccountPHIDs(array $phids) { $this->externalAccountPHIDs = $phids; return $this; } public function withRawIdentifiers(array $identifiers) { $this->rawIdentifiers = $identifiers; return $this; } public function newResultObject() { return new PhabricatorExternalAccountIdentifier(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->providerConfigPHIDs !== null) { $where[] = qsprintf( $conn, 'providerConfigPHID IN (%Ls)', $this->providerConfigPHIDs); } if ($this->externalAccountPHIDs !== null) { $where[] = qsprintf( $conn, 'externalAccountPHID IN (%Ls)', $this->externalAccountPHIDs); } if ($this->rawIdentifiers !== null) { $hashes = array(); foreach ($this->rawIdentifiers as $raw_identifier) { $hashes[] = PhabricatorHash::digestForIndex($raw_identifier); } $where[] = qsprintf( $conn, 'identifierHash IN (%Ls)', $hashes); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorPeopleApplication'; } } diff --git a/src/applications/auth/query/PhabricatorExternalAccountQuery.php b/src/applications/auth/query/PhabricatorExternalAccountQuery.php index bdc030f20c..f44821d7a9 100644 --- a/src/applications/auth/query/PhabricatorExternalAccountQuery.php +++ b/src/applications/auth/query/PhabricatorExternalAccountQuery.php @@ -1,251 +1,247 @@ userPHIDs = $user_phids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withIDs($ids) { $this->ids = $ids; return $this; } public function withAccountSecrets(array $secrets) { $this->accountSecrets = $secrets; return $this; } public function needImages($need) { $this->needImages = $need; return $this; } public function needAccountIdentifiers($need) { $this->needAccountIdentifiers = $need; return $this; } public function withProviderConfigPHIDs(array $phids) { $this->providerConfigPHIDs = $phids; return $this; } public function withRawAccountIdentifiers(array $identifiers) { $this->rawAccountIdentifiers = $identifiers; return $this; } public function newResultObject() { return new PhabricatorExternalAccount(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $accounts) { $viewer = $this->getViewer(); $configs = id(new PhabricatorAuthProviderConfigQuery()) ->setViewer($viewer) ->withPHIDs(mpull($accounts, 'getProviderConfigPHID')) ->execute(); $configs = mpull($configs, null, 'getPHID'); foreach ($accounts as $key => $account) { $config_phid = $account->getProviderConfigPHID(); $config = idx($configs, $config_phid); if (!$config) { unset($accounts[$key]); continue; } $account->attachProviderConfig($config); } if ($this->needImages) { $file_phids = mpull($accounts, 'getProfileImagePHID'); $file_phids = array_filter($file_phids); if ($file_phids) { // NOTE: We use the omnipotent viewer here because these files are // usually created during registration and can't be associated with // the correct policies, since the relevant user account does not exist // yet. In effect, if you can see an ExternalAccount, you can see its // profile image. $files = id(new PhabricatorFileQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withPHIDs($file_phids) ->execute(); $files = mpull($files, null, 'getPHID'); } else { $files = array(); } $default_file = null; foreach ($accounts as $account) { $image_phid = $account->getProfileImagePHID(); if ($image_phid && isset($files[$image_phid])) { $account->attachProfileImageFile($files[$image_phid]); } else { if ($default_file === null) { $default_file = PhabricatorFile::loadBuiltin( $this->getViewer(), 'profile.png'); } $account->attachProfileImageFile($default_file); } } } if ($this->needAccountIdentifiers) { $account_phids = mpull($accounts, 'getPHID'); $identifiers = id(new PhabricatorExternalAccountIdentifierQuery()) ->setViewer($viewer) ->setParentQuery($this) ->withExternalAccountPHIDs($account_phids) ->execute(); $identifiers = mgroup($identifiers, 'getExternalAccountPHID'); foreach ($accounts as $account) { $account_phid = $account->getPHID(); $account_identifiers = idx($identifiers, $account_phid, array()); $account->attachAccountIdentifiers($account_identifiers); } } return $accounts; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'account.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'account.phid IN (%Ls)', $this->phids); } if ($this->userPHIDs !== null) { $where[] = qsprintf( $conn, 'account.userPHID IN (%Ls)', $this->userPHIDs); } if ($this->accountSecrets !== null) { $where[] = qsprintf( $conn, 'account.accountSecret IN (%Ls)', $this->accountSecrets); } if ($this->providerConfigPHIDs !== null) { $where[] = qsprintf( $conn, 'account.providerConfigPHID IN (%Ls)', $this->providerConfigPHIDs); // If we have a list of ProviderConfig PHIDs and are joining the // identifiers table, also include the list as an additional constraint // on the identifiers table. // This does not change the query results (an Account and its // Identifiers always have the same ProviderConfig PHID) but it allows // us to use keys on the Identifier table more efficiently. if ($this->shouldJoinIdentifiersTable()) { $where[] = qsprintf( $conn, 'identifier.providerConfigPHID IN (%Ls)', $this->providerConfigPHIDs); } } if ($this->rawAccountIdentifiers !== null) { $hashes = array(); foreach ($this->rawAccountIdentifiers as $raw_identifier) { $hashes[] = PhabricatorHash::digestForIndex($raw_identifier); } $where[] = qsprintf( $conn, 'identifier.identifierHash IN (%Ls)', $hashes); } return $where; } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); if ($this->shouldJoinIdentifiersTable()) { $joins[] = qsprintf( $conn, 'JOIN %R identifier ON account.phid = identifier.externalAccountPHID', new PhabricatorExternalAccountIdentifier()); } return $joins; } protected function shouldJoinIdentifiersTable() { return ($this->rawAccountIdentifiers !== null); } protected function shouldGroupQueryResultRows() { if ($this->shouldJoinIdentifiersTable()) { return true; } return parent::shouldGroupQueryResultRows(); } protected function getPrimaryTableAlias() { return 'account'; } public function getQueryApplicationClass() { return 'PhabricatorPeopleApplication'; } } diff --git a/src/applications/badges/query/PhabricatorBadgesAwardQuery.php b/src/applications/badges/query/PhabricatorBadgesAwardQuery.php index 347462de4d..57e53a5a30 100644 --- a/src/applications/badges/query/PhabricatorBadgesAwardQuery.php +++ b/src/applications/badges/query/PhabricatorBadgesAwardQuery.php @@ -1,125 +1,121 @@ $award) { $badge_phids[] = $award->getBadgePHID(); } $badges = id(new PhabricatorBadgesQuery()) ->setViewer($this->getViewer()) ->withPHIDs($badge_phids) ->execute(); $badges = mpull($badges, null, 'getPHID'); foreach ($awards as $key => $award) { $award_badge = idx($badges, $award->getBadgePHID()); if (!$award_badge) { unset($awards[$key]); $this->didRejectResult($award); continue; } $award->attachBadge($award_badge); } return $awards; } public function withBadgePHIDs(array $phids) { $this->badgePHIDs = $phids; return $this; } public function withRecipientPHIDs(array $phids) { $this->recipientPHIDs = $phids; return $this; } public function withAwarderPHIDs(array $phids) { $this->awarderPHIDs = $phids; return $this; } public function withBadgeStatuses(array $statuses) { $this->badgeStatuses = $statuses; return $this; } private function shouldJoinBadge() { return (bool)$this->badgeStatuses; } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - public function newResultObject() { return new PhabricatorBadgesAward(); } protected function getPrimaryTableAlias() { return 'badges_award'; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->badgePHIDs !== null) { $where[] = qsprintf( $conn, 'badges_award.badgePHID IN (%Ls)', $this->badgePHIDs); } if ($this->recipientPHIDs !== null) { $where[] = qsprintf( $conn, 'badges_award.recipientPHID IN (%Ls)', $this->recipientPHIDs); } if ($this->awarderPHIDs !== null) { $where[] = qsprintf( $conn, 'badges_award.awarderPHID IN (%Ls)', $this->awarderPHIDs); } if ($this->badgeStatuses !== null) { $where[] = qsprintf( $conn, 'badges_badge.status IN (%Ls)', $this->badgeStatuses); } return $where; } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $join = parent::buildJoinClauseParts($conn); $badges = new PhabricatorBadgesBadge(); if ($this->shouldJoinBadge()) { $join[] = qsprintf( $conn, 'JOIN %T badges_badge ON badges_award.badgePHID = badges_badge.phid', $badges->getTableName()); } return $join; } public function getQueryApplicationClass() { return 'PhabricatorBadgesApplication'; } } diff --git a/src/applications/badges/query/PhabricatorBadgesQuery.php b/src/applications/badges/query/PhabricatorBadgesQuery.php index dcadf881fe..cc59465f67 100644 --- a/src/applications/badges/query/PhabricatorBadgesQuery.php +++ b/src/applications/badges/query/PhabricatorBadgesQuery.php @@ -1,119 +1,115 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withQualities(array $qualities) { $this->qualities = $qualities; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function withNameNgrams($ngrams) { return $this->withNgramsConstraint( id(new PhabricatorBadgesBadgeNameNgrams()), $ngrams); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function getPrimaryTableAlias() { return 'badges'; } public function newResultObject() { return new PhabricatorBadgesBadge(); } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'badges.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'badges.phid IN (%Ls)', $this->phids); } if ($this->qualities !== null) { $where[] = qsprintf( $conn, 'badges.quality IN (%Ls)', $this->qualities); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'badges.status IN (%Ls)', $this->statuses); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorBadgesApplication'; } public function getBuiltinOrders() { return array( 'quality' => array( 'vector' => array('quality', 'id'), 'name' => pht('Rarity (Rarest First)'), ), 'shoddiness' => array( 'vector' => array('-quality', '-id'), 'name' => pht('Rarity (Most Common First)'), ), ) + parent::getBuiltinOrders(); } public function getOrderableColumns() { return array( 'quality' => array( 'table' => $this->getPrimaryTableAlias(), 'column' => 'quality', 'reverse' => true, 'type' => 'int', ), ) + parent::getOrderableColumns(); } protected function newPagingMapFromPartialObject($object) { return array( 'id' => (int)$object->getID(), 'quality' => $object->getQuality(), ); } } diff --git a/src/applications/calendar/query/PhabricatorCalendarExportQuery.php b/src/applications/calendar/query/PhabricatorCalendarExportQuery.php index c51671c806..7ef970216f 100644 --- a/src/applications/calendar/query/PhabricatorCalendarExportQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarExportQuery.php @@ -1,94 +1,90 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAuthorPHIDs(array $phids) { $this->authorPHIDs = $phids; return $this; } public function withIsDisabled($is_disabled) { $this->isDisabled = $is_disabled; return $this; } public function withSecretKeys(array $keys) { $this->secretKeys = $keys; return $this; } public function newResultObject() { return new PhabricatorCalendarExport(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'export.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'export.phid IN (%Ls)', $this->phids); } if ($this->authorPHIDs !== null) { $where[] = qsprintf( $conn, 'export.authorPHID IN (%Ls)', $this->authorPHIDs); } if ($this->isDisabled !== null) { $where[] = qsprintf( $conn, 'export.isDisabled = %d', (int)$this->isDisabled); } if ($this->secretKeys !== null) { $where[] = qsprintf( $conn, 'export.secretKey IN (%Ls)', $this->secretKeys); } return $where; } protected function getPrimaryTableAlias() { return 'export'; } public function getQueryApplicationClass() { return 'PhabricatorCalendarApplication'; } } diff --git a/src/applications/calendar/query/PhabricatorCalendarExternalInviteeQuery.php b/src/applications/calendar/query/PhabricatorCalendarExternalInviteeQuery.php index ea7200e614..35891cfd28 100644 --- a/src/applications/calendar/query/PhabricatorCalendarExternalInviteeQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarExternalInviteeQuery.php @@ -1,68 +1,64 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withNames(array $names) { $this->names = $names; return $this; } public function newResultObject() { return new PhabricatorCalendarExternalInvitee(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->names !== null) { $name_indexes = array(); foreach ($this->names as $name) { $name_indexes[] = PhabricatorHash::digestForIndex($name); } $where[] = qsprintf( $conn, 'nameIndex IN (%Ls)', $name_indexes); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorCalendarApplication'; } } diff --git a/src/applications/calendar/query/PhabricatorCalendarImportLogQuery.php b/src/applications/calendar/query/PhabricatorCalendarImportLogQuery.php index 81f309bdca..731b1209cc 100644 --- a/src/applications/calendar/query/PhabricatorCalendarImportLogQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarImportLogQuery.php @@ -1,111 +1,107 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withImportPHIDs(array $phids) { $this->importPHIDs = $phids; return $this; } public function newResultObject() { return new PhabricatorCalendarImportLog(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'log.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'log.phid IN (%Ls)', $this->phids); } if ($this->importPHIDs !== null) { $where[] = qsprintf( $conn, 'log.importPHID IN (%Ls)', $this->importPHIDs); } return $where; } protected function willFilterPage(array $page) { $viewer = $this->getViewer(); $type_map = PhabricatorCalendarImportLogType::getAllLogTypes(); foreach ($page as $log) { $type_constant = $log->getParameter('type'); $type_object = idx($type_map, $type_constant); if (!$type_object) { $type_object = new PhabricatorCalendarImportDefaultLogType(); } $type_object = clone $type_object; $log->attachLogType($type_object); } $import_phids = mpull($page, 'getImportPHID'); if ($import_phids) { $imports = id(new PhabricatorCalendarImportQuery()) ->setViewer($viewer) ->withPHIDs($import_phids) ->execute(); $imports = mpull($imports, null, 'getPHID'); } else { $imports = array(); } foreach ($page as $key => $log) { $import = idx($imports, $log->getImportPHID()); if (!$import) { $this->didRejectResult($import); unset($page[$key]); continue; } $log->attachImport($import); } return $page; } protected function getPrimaryTableAlias() { return 'log'; } public function getQueryApplicationClass() { return 'PhabricatorCalendarApplication'; } } diff --git a/src/applications/calendar/query/PhabricatorCalendarImportQuery.php b/src/applications/calendar/query/PhabricatorCalendarImportQuery.php index 5d44657994..0e3dbbf387 100644 --- a/src/applications/calendar/query/PhabricatorCalendarImportQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarImportQuery.php @@ -1,99 +1,95 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAuthorPHIDs(array $phids) { $this->authorPHIDs = $phids; return $this; } public function withIsDisabled($is_disabled) { $this->isDisabled = $is_disabled; return $this; } public function newResultObject() { return new PhabricatorCalendarImport(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'import.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'import.phid IN (%Ls)', $this->phids); } if ($this->authorPHIDs !== null) { $where[] = qsprintf( $conn, 'import.authorPHID IN (%Ls)', $this->authorPHIDs); } if ($this->isDisabled !== null) { $where[] = qsprintf( $conn, 'import.isDisabled = %d', (int)$this->isDisabled); } return $where; } protected function willFilterPage(array $page) { $engines = PhabricatorCalendarImportEngine::getAllImportEngines(); foreach ($page as $key => $import) { $engine_type = $import->getEngineType(); $engine = idx($engines, $engine_type); if (!$engine) { unset($page[$key]); $this->didRejectResult($import); continue; } $import->attachEngine(clone $engine); } return $page; } protected function getPrimaryTableAlias() { return 'import'; } public function getQueryApplicationClass() { return 'PhabricatorCalendarApplication'; } } diff --git a/src/applications/conduit/query/PhabricatorConduitLogQuery.php b/src/applications/conduit/query/PhabricatorConduitLogQuery.php index 23a5b46786..19cb31a812 100644 --- a/src/applications/conduit/query/PhabricatorConduitLogQuery.php +++ b/src/applications/conduit/query/PhabricatorConduitLogQuery.php @@ -1,117 +1,113 @@ ids = $ids; return $this; } public function withCallerPHIDs(array $phids) { $this->callerPHIDs = $phids; return $this; } public function withMethods(array $methods) { $this->methods = $methods; return $this; } public function withMethodStatuses(array $statuses) { $this->methodStatuses = $statuses; return $this; } public function withEpochBetween($epoch_min, $epoch_max) { $this->epochMin = $epoch_min; $this->epochMax = $epoch_max; return $this; } public function newResultObject() { return new PhabricatorConduitMethodCallLog(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->callerPHIDs !== null) { $where[] = qsprintf( $conn, 'callerPHID IN (%Ls)', $this->callerPHIDs); } if ($this->methods !== null) { $where[] = qsprintf( $conn, 'method IN (%Ls)', $this->methods); } if ($this->methodStatuses !== null) { $statuses = array_fuse($this->methodStatuses); $methods = id(new PhabricatorConduitMethodQuery()) ->setViewer($this->getViewer()) ->execute(); $method_names = array(); foreach ($methods as $method) { $status = $method->getMethodStatus(); if (isset($statuses[$status])) { $method_names[] = $method->getAPIMethodName(); } } if (!$method_names) { throw new PhabricatorEmptyQueryException(); } $where[] = qsprintf( $conn, 'method IN (%Ls)', $method_names); } if ($this->epochMin !== null) { $where[] = qsprintf( $conn, 'dateCreated >= %d', $this->epochMin); } if ($this->epochMax !== null) { $where[] = qsprintf( $conn, 'dateCreated <= %d', $this->epochMax); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorConduitApplication'; } } diff --git a/src/applications/conduit/query/PhabricatorConduitTokenQuery.php b/src/applications/conduit/query/PhabricatorConduitTokenQuery.php index ef35b006a7..384efccadf 100644 --- a/src/applications/conduit/query/PhabricatorConduitTokenQuery.php +++ b/src/applications/conduit/query/PhabricatorConduitTokenQuery.php @@ -1,119 +1,115 @@ expired = $expired; return $this; } public function withIDs(array $ids) { $this->ids = $ids; return $this; } public function withObjectPHIDs(array $phids) { $this->objectPHIDs = $phids; return $this; } public function withTokens(array $tokens) { $this->tokens = $tokens; return $this; } public function withTokenTypes(array $types) { $this->tokenTypes = $types; return $this; } public function newResultObject() { return new PhabricatorConduitToken(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->objectPHIDs !== null) { $where[] = qsprintf( $conn, 'objectPHID IN (%Ls)', $this->objectPHIDs); } if ($this->tokens !== null) { $where[] = qsprintf( $conn, 'token IN (%Ls)', $this->tokens); } if ($this->tokenTypes !== null) { $where[] = qsprintf( $conn, 'tokenType IN (%Ls)', $this->tokenTypes); } if ($this->expired !== null) { if ($this->expired) { $where[] = qsprintf( $conn, 'expires <= %d', PhabricatorTime::getNow()); } else { $where[] = qsprintf( $conn, 'expires IS NULL OR expires > %d', PhabricatorTime::getNow()); } } return $where; } protected function willFilterPage(array $tokens) { $object_phids = mpull($tokens, 'getObjectPHID'); $objects = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($object_phids) ->execute(); $objects = mpull($objects, null, 'getPHID'); foreach ($tokens as $key => $token) { $object = idx($objects, $token->getObjectPHID(), null); if (!$object) { $this->didRejectResult($token); unset($tokens[$key]); continue; } $token->attachObject($object); } return $tokens; } public function getQueryApplicationClass() { return 'PhabricatorConduitApplication'; } } diff --git a/src/applications/countdown/query/PhabricatorCountdownQuery.php b/src/applications/countdown/query/PhabricatorCountdownQuery.php index 67a2f3a9e3..4a6df16e8e 100644 --- a/src/applications/countdown/query/PhabricatorCountdownQuery.php +++ b/src/applications/countdown/query/PhabricatorCountdownQuery.php @@ -1,107 +1,103 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAuthorPHIDs(array $author_phids) { $this->authorPHIDs = $author_phids; return $this; } public function withUpcoming() { $this->upcoming = true; return $this; } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - public function newResultObject() { return new PhabricatorCountdown(); } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->authorPHIDs !== null) { $where[] = qsprintf( $conn, 'authorPHID in (%Ls)', $this->authorPHIDs); } if ($this->upcoming !== null) { $where[] = qsprintf( $conn, 'epoch >= %d', PhabricatorTime::getNow()); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorCountdownApplication'; } public function getBuiltinOrders() { return array( 'ending' => array( 'vector' => array('-epoch', '-id'), 'name' => pht('End Date (Past to Future)'), ), 'unending' => array( 'vector' => array('epoch', 'id'), 'name' => pht('End Date (Future to Past)'), ), ) + parent::getBuiltinOrders(); } public function getOrderableColumns() { return array( 'epoch' => array( 'table' => $this->getPrimaryTableAlias(), 'column' => 'epoch', 'type' => 'int', ), ) + parent::getOrderableColumns(); } protected function newPagingMapFromPartialObject($object) { return array( 'id' => (int)$object->getID(), 'epoch' => (int)$object->getEpoch(), ); } } diff --git a/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php b/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php index dd32d1c9dc..c67b756262 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php +++ b/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php @@ -1,102 +1,98 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withArchived($archived) { $this->archived = $archived; return $this; } public function withPanelTypes(array $types) { $this->panelTypes = $types; return $this; } public function withAuthorPHIDs(array $authors) { $this->authorPHIDs = $authors; return $this; } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - public function newResultObject() { // TODO: If we don't do this, SearchEngine explodes when trying to // enumerate custom fields. For now, just give the panel a default panel // type so custom fields work. In the long run, we may want to find a // cleaner or more general approach for this. $text_type = id(new PhabricatorDashboardTextPanelType()) ->getPanelTypeKey(); return id(new PhabricatorDashboardPanel()) ->setPanelType($text_type); } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'panel.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'panel.phid IN (%Ls)', $this->phids); } if ($this->archived !== null) { $where[] = qsprintf( $conn, 'panel.isArchived = %d', (int)$this->archived); } if ($this->panelTypes !== null) { $where[] = qsprintf( $conn, 'panel.panelType IN (%Ls)', $this->panelTypes); } if ($this->authorPHIDs !== null) { $where[] = qsprintf( $conn, 'panel.authorPHID IN (%Ls)', $this->authorPHIDs); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorDashboardApplication'; } protected function getPrimaryTableAlias() { return 'panel'; } } diff --git a/src/applications/dashboard/query/PhabricatorDashboardPortalQuery.php b/src/applications/dashboard/query/PhabricatorDashboardPortalQuery.php index 857c4dc215..418262c745 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardPortalQuery.php +++ b/src/applications/dashboard/query/PhabricatorDashboardPortalQuery.php @@ -1,68 +1,64 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function newResultObject() { return new PhabricatorDashboardPortal(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'portal.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'portal.phid IN (%Ls)', $this->phids); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'portal.status IN (%Ls)', $this->statuses); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorDashboardApplication'; } protected function getPrimaryTableAlias() { return 'portal'; } } diff --git a/src/applications/dashboard/query/PhabricatorDashboardQuery.php b/src/applications/dashboard/query/PhabricatorDashboardQuery.php index 76854bffc1..954a565ac6 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardQuery.php +++ b/src/applications/dashboard/query/PhabricatorDashboardQuery.php @@ -1,103 +1,99 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function withAuthorPHIDs(array $authors) { $this->authorPHIDs = $authors; return $this; } public function withCanEdit($can_edit) { $this->canEdit = $can_edit; return $this; } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - public function newResultObject() { return new PhabricatorDashboard(); } protected function didFilterPage(array $dashboards) { $phids = mpull($dashboards, 'getPHID'); if ($this->canEdit) { $dashboards = id(new PhabricatorPolicyFilter()) ->setViewer($this->getViewer()) ->requireCapabilities(array( PhabricatorPolicyCapability::CAN_EDIT, )) ->apply($dashboards); } return $dashboards; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'dashboard.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'dashboard.phid IN (%Ls)', $this->phids); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'dashboard.status IN (%Ls)', $this->statuses); } if ($this->authorPHIDs !== null) { $where[] = qsprintf( $conn, 'dashboard.authorPHID IN (%Ls)', $this->authorPHIDs); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorDashboardApplication'; } protected function getPrimaryTableAlias() { return 'dashboard'; } } diff --git a/src/applications/differential/query/DifferentialChangesetQuery.php b/src/applications/differential/query/DifferentialChangesetQuery.php index e2357f9278..ee29aa1cf8 100644 --- a/src/applications/differential/query/DifferentialChangesetQuery.php +++ b/src/applications/differential/query/DifferentialChangesetQuery.php @@ -1,182 +1,178 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withDiffs(array $diffs) { assert_instances_of($diffs, 'DifferentialDiff'); $this->diffs = $diffs; return $this; } public function withDiffPHIDs(array $phids) { $this->diffPHIDs = $phids; return $this; } public function needAttachToDiffs($attach) { $this->needAttachToDiffs = $attach; return $this; } public function needHunks($need) { $this->needHunks = $need; return $this; } protected function willExecute() { // If we fail to load any changesets (which is possible in the case of an // empty commit) we'll never call didFilterPage(). Attach empty changeset // lists now so that we end up with the right result. if ($this->needAttachToDiffs) { foreach ($this->diffs as $diff) { $diff->attachChangesets(array()); } } } public function newResultObject() { return new DifferentialChangeset(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $changesets) { // First, attach all the diffs we already have. We can just do this // directly without worrying about querying for them. When we don't have // a diff, record that we need to load it. if ($this->diffs) { $have_diffs = mpull($this->diffs, null, 'getID'); } else { $have_diffs = array(); } $must_load = array(); foreach ($changesets as $key => $changeset) { $diff_id = $changeset->getDiffID(); if (isset($have_diffs[$diff_id])) { $changeset->attachDiff($have_diffs[$diff_id]); } else { $must_load[$key] = $changeset; } } // Load all the diffs we don't have. $need_diff_ids = mpull($must_load, 'getDiffID'); $more_diffs = array(); if ($need_diff_ids) { $more_diffs = id(new DifferentialDiffQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withIDs($need_diff_ids) ->execute(); $more_diffs = mpull($more_diffs, null, 'getID'); } // Attach the diffs we loaded. foreach ($must_load as $key => $changeset) { $diff_id = $changeset->getDiffID(); if (isset($more_diffs[$diff_id])) { $changeset->attachDiff($more_diffs[$diff_id]); } else { // We didn't have the diff, and could not load it (it does not exist, // or we can't see it), so filter this result out. unset($changesets[$key]); } } return $changesets; } protected function didFilterPage(array $changesets) { if ($this->needAttachToDiffs) { $changeset_groups = mgroup($changesets, 'getDiffID'); foreach ($this->diffs as $diff) { $diff_changesets = idx($changeset_groups, $diff->getID(), array()); $diff->attachChangesets($diff_changesets); } } if ($this->needHunks) { id(new DifferentialHunkQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withChangesets($changesets) ->needAttachToChangesets(true) ->execute(); } return $changesets; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->diffs !== null) { $where[] = qsprintf( $conn, 'diffID IN (%Ld)', mpull($this->diffs, 'getID')); } if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->diffPHIDs !== null) { $diff_ids = queryfx_all( $conn, 'SELECT id FROM %R WHERE phid IN (%Ls)', new DifferentialDiff(), $this->diffPHIDs); $diff_ids = ipull($diff_ids, 'id', null); if (!$diff_ids) { throw new PhabricatorEmptyQueryException(); } $where[] = qsprintf( $conn, 'diffID IN (%Ld)', $diff_ids); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorDifferentialApplication'; } } diff --git a/src/applications/differential/query/DifferentialDiffQuery.php b/src/applications/differential/query/DifferentialDiffQuery.php index 3b1dd3ae62..04019df1e0 100644 --- a/src/applications/differential/query/DifferentialDiffQuery.php +++ b/src/applications/differential/query/DifferentialDiffQuery.php @@ -1,195 +1,191 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withRevisionIDs(array $revision_ids) { $this->revisionIDs = $revision_ids; return $this; } public function withRevisionPHIDs(array $revision_phids) { $this->revisionPHIDs = $revision_phids; return $this; } public function withCommitPHIDs(array $phids) { $this->commitPHIDs = $phids; return $this; } public function withHasRevision($has_revision) { $this->hasRevision = $has_revision; return $this; } public function needChangesets($bool) { $this->needChangesets = $bool; return $this; } public function needProperties($need_properties) { $this->needProperties = $need_properties; return $this; } public function newResultObject() { return new DifferentialDiff(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $diffs) { $revision_ids = array_filter(mpull($diffs, 'getRevisionID')); $revisions = array(); if ($revision_ids) { $revisions = id(new DifferentialRevisionQuery()) ->setViewer($this->getViewer()) ->withIDs($revision_ids) ->execute(); } foreach ($diffs as $key => $diff) { if (!$diff->getRevisionID()) { continue; } $revision = idx($revisions, $diff->getRevisionID()); if ($revision) { $diff->attachRevision($revision); continue; } unset($diffs[$key]); } if ($diffs && $this->needChangesets) { $diffs = $this->loadChangesets($diffs); } return $diffs; } protected function didFilterPage(array $diffs) { if ($this->needProperties) { $properties = id(new DifferentialDiffProperty())->loadAllWhere( 'diffID IN (%Ld)', mpull($diffs, 'getID')); $properties = mgroup($properties, 'getDiffID'); foreach ($diffs as $diff) { $map = idx($properties, $diff->getID(), array()); $map = mpull($map, 'getData', 'getName'); $diff->attachDiffProperties($map); } } return $diffs; } private function loadChangesets(array $diffs) { id(new DifferentialChangesetQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withDiffs($diffs) ->needAttachToDiffs(true) ->needHunks(true) ->execute(); return $diffs; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->revisionIDs !== null) { $where[] = qsprintf( $conn, 'revisionID IN (%Ld)', $this->revisionIDs); } if ($this->commitPHIDs !== null) { $where[] = qsprintf( $conn, 'commitPHID IN (%Ls)', $this->commitPHIDs); } if ($this->hasRevision !== null) { if ($this->hasRevision) { $where[] = qsprintf( $conn, 'revisionID IS NOT NULL'); } else { $where[] = qsprintf( $conn, 'revisionID IS NULL'); } } if ($this->revisionPHIDs !== null) { $viewer = $this->getViewer(); $revisions = id(new DifferentialRevisionQuery()) ->setViewer($viewer) ->setParentQuery($this) ->withPHIDs($this->revisionPHIDs) ->execute(); $revision_ids = mpull($revisions, 'getID'); if (!$revision_ids) { throw new PhabricatorEmptyQueryException(); } $where[] = qsprintf( $conn, 'revisionID IN (%Ls)', $revision_ids); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorDifferentialApplication'; } } diff --git a/src/applications/differential/query/DifferentialHunkQuery.php b/src/applications/differential/query/DifferentialHunkQuery.php index 981c2b4782..21787f04e5 100644 --- a/src/applications/differential/query/DifferentialHunkQuery.php +++ b/src/applications/differential/query/DifferentialHunkQuery.php @@ -1,93 +1,89 @@ changesets = $changesets; return $this; } public function needAttachToChangesets($attach) { $this->shouldAttachToChangesets = $attach; return $this; } protected function willExecute() { // If we fail to load any hunks at all (for example, because all of // the requested changesets are directories or empty files and have no // hunks) we'll never call didFilterPage(), and thus never have an // opportunity to attach hunks. Attach empty hunk lists now so that we // end up with the right result. if ($this->shouldAttachToChangesets) { foreach ($this->changesets as $changeset) { $changeset->attachHunks(array()); } } } public function newResultObject() { return new DifferentialHunk(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $hunks) { $changesets = mpull($this->changesets, null, 'getID'); foreach ($hunks as $key => $hunk) { $changeset = idx($changesets, $hunk->getChangesetID()); if (!$changeset) { unset($hunks[$key]); } $hunk->attachChangeset($changeset); } return $hunks; } protected function didFilterPage(array $hunks) { if ($this->shouldAttachToChangesets) { $hunk_groups = mgroup($hunks, 'getChangesetID'); foreach ($this->changesets as $changeset) { $hunks = idx($hunk_groups, $changeset->getID(), array()); $changeset->attachHunks($hunks); } } return $hunks; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if (!$this->changesets) { throw new Exception( pht( 'You must load hunks via changesets, with %s!', 'withChangesets()')); } $where[] = qsprintf( $conn, 'changesetID IN (%Ld)', mpull($this->changesets, 'getID')); return $where; } public function getQueryApplicationClass() { return 'PhabricatorDifferentialApplication'; } protected function getDefaultOrderVector() { // TODO: Do we need this? return array('-id'); } } diff --git a/src/applications/differential/query/DifferentialViewStateQuery.php b/src/applications/differential/query/DifferentialViewStateQuery.php index 6ef63721e4..604a6de1db 100644 --- a/src/applications/differential/query/DifferentialViewStateQuery.php +++ b/src/applications/differential/query/DifferentialViewStateQuery.php @@ -1,64 +1,60 @@ ids = $ids; return $this; } public function withViewerPHIDs(array $phids) { $this->viewerPHIDs = $phids; return $this; } public function withObjectPHIDs(array $phids) { $this->objectPHIDs = $phids; return $this; } public function newResultObject() { return new DifferentialViewState(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->viewerPHIDs !== null) { $where[] = qsprintf( $conn, 'viewerPHID IN (%Ls)', $this->viewerPHIDs); } if ($this->objectPHIDs !== null) { $where[] = qsprintf( $conn, 'objectPHID IN (%Ls)', $this->objectPHIDs); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorDifferentialApplication'; } } diff --git a/src/applications/diffusion/query/DiffusionCommitHintQuery.php b/src/applications/diffusion/query/DiffusionCommitHintQuery.php index bfd8045131..c0794a4387 100644 --- a/src/applications/diffusion/query/DiffusionCommitHintQuery.php +++ b/src/applications/diffusion/query/DiffusionCommitHintQuery.php @@ -1,116 +1,112 @@ ids = $ids; return $this; } public function withRepositoryPHIDs(array $phids) { $this->repositoryPHIDs = $phids; return $this; } public function withOldCommitIdentifiers(array $identifiers) { $this->oldCommitIdentifiers = $identifiers; return $this; } public function withCommits(array $commits) { assert_instances_of($commits, 'PhabricatorRepositoryCommit'); $repository_phids = array(); foreach ($commits as $commit) { $repository_phids[] = $commit->getRepository()->getPHID(); } $this->repositoryPHIDs = $repository_phids; $this->oldCommitIdentifiers = mpull($commits, 'getCommitIdentifier'); $this->commits = $commits; return $this; } public function getCommitMap() { if ($this->commitMap === null) { throw new PhutilInvalidStateException('execute'); } return $this->commitMap; } public function newResultObject() { return new PhabricatorRepositoryCommitHint(); } protected function willExecute() { $this->commitMap = array(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->repositoryPHIDs !== null) { $where[] = qsprintf( $conn, 'repositoryPHID IN (%Ls)', $this->repositoryPHIDs); } if ($this->oldCommitIdentifiers !== null) { $where[] = qsprintf( $conn, 'oldCommitIdentifier IN (%Ls)', $this->oldCommitIdentifiers); } return $where; } protected function didFilterPage(array $hints) { if ($this->commits) { $map = array(); foreach ($this->commits as $commit) { $repository_phid = $commit->getRepository()->getPHID(); $identifier = $commit->getCommitIdentifier(); $map[$repository_phid][$identifier] = $commit->getPHID(); } foreach ($hints as $hint) { $repository_phid = $hint->getRepositoryPHID(); $identifier = $hint->getOldCommitIdentifier(); if (isset($map[$repository_phid][$identifier])) { $commit_phid = $map[$repository_phid][$identifier]; $this->commitMap[$commit_phid] = $hint; } } } return $hints; } public function getQueryApplicationClass() { return 'PhabricatorDiffusionApplication'; } } diff --git a/src/applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php b/src/applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php index bc794cc629..2a33206831 100644 --- a/src/applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php +++ b/src/applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php @@ -1,51 +1,47 @@ phids = $phids; return $this; } public function withObjectKeys(array $keys) { $this->objectKeys = $keys; return $this; } public function newResultObject() { return new DoorkeeperExternalObject(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->objectKeys !== null) { $where[] = qsprintf( $conn, 'objectKey IN (%Ls)', $this->objectKeys); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorDoorkeeperApplication'; } } diff --git a/src/applications/drydock/query/DrydockAuthorizationQuery.php b/src/applications/drydock/query/DrydockAuthorizationQuery.php index d6436fc93a..8c5c58c853 100644 --- a/src/applications/drydock/query/DrydockAuthorizationQuery.php +++ b/src/applications/drydock/query/DrydockAuthorizationQuery.php @@ -1,175 +1,171 @@ setViewer(PhabricatorUser::getOmnipotentUser()) ->withObjectPHIDs(array($object_phid)) ->withBlueprintPHIDs($blueprint_phids) ->execute(); $authorizations = mpull($authorizations, null, 'getBlueprintPHID'); foreach ($blueprint_phids as $phid) { $authorization = idx($authorizations, $phid); if (!$authorization) { return false; } if (!$authorization->isAuthorized()) { return false; } } return true; } public function withIDs(array $ids) { $this->ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withBlueprintPHIDs(array $phids) { $this->blueprintPHIDs = $phids; return $this; } public function withObjectPHIDs(array $phids) { $this->objectPHIDs = $phids; return $this; } public function withBlueprintStates(array $states) { $this->blueprintStates = $states; return $this; } public function withObjectStates(array $states) { $this->objectStates = $states; return $this; } public function newResultObject() { return new DrydockAuthorization(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $authorizations) { $blueprint_phids = mpull($authorizations, 'getBlueprintPHID'); if ($blueprint_phids) { $blueprints = id(new DrydockBlueprintQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($blueprint_phids) ->execute(); $blueprints = mpull($blueprints, null, 'getPHID'); } else { $blueprints = array(); } foreach ($authorizations as $key => $authorization) { $blueprint = idx($blueprints, $authorization->getBlueprintPHID()); if (!$blueprint) { $this->didRejectResult($authorization); unset($authorizations[$key]); continue; } $authorization->attachBlueprint($blueprint); } $object_phids = mpull($authorizations, 'getObjectPHID'); if ($object_phids) { $objects = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($object_phids) ->execute(); $objects = mpull($objects, null, 'getPHID'); } else { $objects = array(); } foreach ($authorizations as $key => $authorization) { $object = idx($objects, $authorization->getObjectPHID()); if (!$object) { $this->didRejectResult($authorization); unset($authorizations[$key]); continue; } $authorization->attachObject($object); } return $authorizations; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->blueprintPHIDs !== null) { $where[] = qsprintf( $conn, 'blueprintPHID IN (%Ls)', $this->blueprintPHIDs); } if ($this->objectPHIDs !== null) { $where[] = qsprintf( $conn, 'objectPHID IN (%Ls)', $this->objectPHIDs); } if ($this->blueprintStates !== null) { $where[] = qsprintf( $conn, 'blueprintAuthorizationState IN (%Ls)', $this->blueprintStates); } if ($this->objectStates !== null) { $where[] = qsprintf( $conn, 'objectAuthorizationState IN (%Ls)', $this->objectStates); } return $where; } } diff --git a/src/applications/drydock/query/DrydockBlueprintQuery.php b/src/applications/drydock/query/DrydockBlueprintQuery.php index a5df734e19..c163103d16 100644 --- a/src/applications/drydock/query/DrydockBlueprintQuery.php +++ b/src/applications/drydock/query/DrydockBlueprintQuery.php @@ -1,241 +1,237 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withBlueprintClasses(array $classes) { $this->blueprintClasses = $classes; return $this; } public function withDatasourceQuery($query) { $this->datasourceQuery = $query; return $this; } public function withDisabled($disabled) { $this->disabled = $disabled; return $this; } public function withAuthorizedPHIDs(array $phids) { $this->authorizedPHIDs = $phids; return $this; } public function withNameNgrams($ngrams) { return $this->withNgramsConstraint( new DrydockBlueprintNameNgrams(), $ngrams); } public function withIdentifiers(array $identifiers) { if (!$identifiers) { throw new Exception( pht( 'Can not issue a query with an empty identifier list.')); } $this->identifiers = $identifiers; $ids = array(); $phids = array(); foreach ($identifiers as $identifier) { if (ctype_digit($identifier)) { $ids[] = $identifier; } else { $phids[] = $identifier; } } $this->identifierIDs = $ids; $this->identifierPHIDs = $phids; return $this; } public function getIdentifierMap() { if ($this->identifierMap === null) { throw new Exception( pht( 'Execute a query with identifiers before getting the '. 'identifier map.')); } return $this->identifierMap; } public function newResultObject() { return new DrydockBlueprint(); } protected function getPrimaryTableAlias() { return 'blueprint'; } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willExecute() { if ($this->identifiers) { $this->identifierMap = array(); } else { $this->identifierMap = null; } } protected function willFilterPage(array $blueprints) { $impls = DrydockBlueprintImplementation::getAllBlueprintImplementations(); foreach ($blueprints as $key => $blueprint) { $impl = idx($impls, $blueprint->getClassName()); if (!$impl) { $this->didRejectResult($blueprint); unset($blueprints[$key]); continue; } $impl = clone $impl; $blueprint->attachImplementation($impl); } if ($this->identifiers) { $id_map = mpull($blueprints, null, 'getID'); $phid_map = mpull($blueprints, null, 'getPHID'); $map = $this->identifierMap; foreach ($this->identifierIDs as $id) { if (isset($id_map[$id])) { $map[$id] = $id_map[$id]; } } foreach ($this->identifierPHIDs as $phid) { if (isset($phid_map[$phid])) { $map[$phid] = $phid_map[$phid]; } } // Just for consistency, reorder the map to match input order. $map = array_select_keys($map, $this->identifiers); $this->identifierMap = $map; } return $blueprints; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'blueprint.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'blueprint.phid IN (%Ls)', $this->phids); } if ($this->datasourceQuery !== null) { $where[] = qsprintf( $conn, 'blueprint.blueprintName LIKE %>', $this->datasourceQuery); } if ($this->blueprintClasses !== null) { $where[] = qsprintf( $conn, 'blueprint.className IN (%Ls)', $this->blueprintClasses); } if ($this->disabled !== null) { $where[] = qsprintf( $conn, 'blueprint.isDisabled = %d', (int)$this->disabled); } if ($this->identifiers !== null) { $parts = array(); if ($this->identifierIDs) { $parts[] = qsprintf( $conn, 'blueprint.id IN (%Ld)', $this->identifierIDs); } if ($this->identifierPHIDs) { $parts[] = qsprintf( $conn, 'blueprint.phid IN (%Ls)', $this->identifierPHIDs); } $where[] = qsprintf( $conn, '%LO', $parts); } return $where; } protected function shouldGroupQueryResultRows() { if ($this->authorizedPHIDs !== null) { return true; } return parent::shouldGroupQueryResultRows(); } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); if ($this->authorizedPHIDs !== null) { $joins[] = qsprintf( $conn, 'JOIN %T authorization ON authorization.blueprintPHID = blueprint.phid AND authorization.objectPHID IN (%Ls) AND authorization.objectAuthorizationState = %s AND authorization.blueprintAuthorizationState = %s', id(new DrydockAuthorization())->getTableName(), $this->authorizedPHIDs, DrydockAuthorization::OBJECTAUTH_ACTIVE, DrydockAuthorization::BLUEPRINTAUTH_AUTHORIZED); } return $joins; } } diff --git a/src/applications/drydock/query/DrydockCommandQuery.php b/src/applications/drydock/query/DrydockCommandQuery.php index 0d71288a85..fef6170ec9 100644 --- a/src/applications/drydock/query/DrydockCommandQuery.php +++ b/src/applications/drydock/query/DrydockCommandQuery.php @@ -1,82 +1,78 @@ ids = $ids; return $this; } public function withTargetPHIDs(array $phids) { $this->targetPHIDs = $phids; return $this; } public function withConsumed($consumed) { $this->consumed = $consumed; return $this; } public function newResultObject() { return new DrydockCommand(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $commands) { $target_phids = mpull($commands, 'getTargetPHID'); $targets = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($target_phids) ->execute(); $targets = mpull($targets, null, 'getPHID'); foreach ($commands as $key => $command) { $target = idx($targets, $command->getTargetPHID()); if (!$target) { $this->didRejectResult($command); unset($commands[$key]); continue; } $command->attachCommandTarget($target); } return $commands; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->targetPHIDs !== null) { $where[] = qsprintf( $conn, 'targetPHID IN (%Ls)', $this->targetPHIDs); } if ($this->consumed !== null) { $where[] = qsprintf( $conn, 'isConsumed = %d', (int)$this->consumed); } return $where; } } diff --git a/src/applications/drydock/query/DrydockLeaseQuery.php b/src/applications/drydock/query/DrydockLeaseQuery.php index 6bfb8deefe..4950764486 100644 --- a/src/applications/drydock/query/DrydockLeaseQuery.php +++ b/src/applications/drydock/query/DrydockLeaseQuery.php @@ -1,174 +1,170 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withResourcePHIDs(array $phids) { $this->resourcePHIDs = $phids; return $this; } public function withOwnerPHIDs(array $phids) { $this->ownerPHIDs = $phids; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function withDatasourceQuery($query) { $this->datasourceQuery = $query; return $this; } public function withDateModifiedBetween($min_epoch, $max_epoch) { $this->minModified = $min_epoch; $this->maxModified = $max_epoch; return $this; } public function needUnconsumedCommands($need) { $this->needUnconsumedCommands = $need; return $this; } public function newResultObject() { return new DrydockLease(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $leases) { $resource_phids = array_filter(mpull($leases, 'getResourcePHID')); if ($resource_phids) { $resources = id(new DrydockResourceQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs(array_unique($resource_phids)) ->execute(); $resources = mpull($resources, null, 'getPHID'); } else { $resources = array(); } foreach ($leases as $key => $lease) { $resource = null; if ($lease->getResourcePHID()) { $resource = idx($resources, $lease->getResourcePHID()); if (!$resource) { $this->didRejectResult($lease); unset($leases[$key]); continue; } } $lease->attachResource($resource); } return $leases; } protected function didFilterPage(array $leases) { if ($this->needUnconsumedCommands) { $commands = id(new DrydockCommandQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withTargetPHIDs(mpull($leases, 'getPHID')) ->withConsumed(false) ->execute(); $commands = mgroup($commands, 'getTargetPHID'); foreach ($leases as $lease) { $list = idx($commands, $lease->getPHID(), array()); $lease->attachUnconsumedCommands($list); } } return $leases; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->resourcePHIDs !== null) { $where[] = qsprintf( $conn, 'resourcePHID IN (%Ls)', $this->resourcePHIDs); } if ($this->ownerPHIDs !== null) { $where[] = qsprintf( $conn, 'ownerPHID IN (%Ls)', $this->ownerPHIDs); } if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'status IN (%Ls)', $this->statuses); } if ($this->datasourceQuery !== null) { $where[] = qsprintf( $conn, 'id = %d', (int)$this->datasourceQuery); } if ($this->minModified !== null) { $where[] = qsprintf( $conn, 'dateModified >= %d', $this->minModified); } if ($this->maxModified !== null) { $where[] = qsprintf( $conn, 'dateModified <= %d', $this->maxModified); } return $where; } } diff --git a/src/applications/drydock/query/DrydockLogQuery.php b/src/applications/drydock/query/DrydockLogQuery.php index 80f47f584f..d7e1f77ae6 100644 --- a/src/applications/drydock/query/DrydockLogQuery.php +++ b/src/applications/drydock/query/DrydockLogQuery.php @@ -1,173 +1,169 @@ ids = $ids; return $this; } public function withBlueprintPHIDs(array $phids) { $this->blueprintPHIDs = $phids; return $this; } public function withResourcePHIDs(array $phids) { $this->resourcePHIDs = $phids; return $this; } public function withLeasePHIDs(array $phids) { $this->leasePHIDs = $phids; return $this; } public function withOperationPHIDs(array $phids) { $this->operationPHIDs = $phids; return $this; } public function newResultObject() { return new DrydockLog(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function didFilterPage(array $logs) { $blueprint_phids = array_filter(mpull($logs, 'getBlueprintPHID')); if ($blueprint_phids) { $blueprints = id(new DrydockBlueprintQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($blueprint_phids) ->execute(); $blueprints = mpull($blueprints, null, 'getPHID'); } else { $blueprints = array(); } foreach ($logs as $key => $log) { $blueprint = null; $blueprint_phid = $log->getBlueprintPHID(); if ($blueprint_phid) { $blueprint = idx($blueprints, $blueprint_phid); } $log->attachBlueprint($blueprint); } $resource_phids = array_filter(mpull($logs, 'getResourcePHID')); if ($resource_phids) { $resources = id(new DrydockResourceQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($resource_phids) ->execute(); $resources = mpull($resources, null, 'getPHID'); } else { $resources = array(); } foreach ($logs as $key => $log) { $resource = null; $resource_phid = $log->getResourcePHID(); if ($resource_phid) { $resource = idx($resources, $resource_phid); } $log->attachResource($resource); } $lease_phids = array_filter(mpull($logs, 'getLeasePHID')); if ($lease_phids) { $leases = id(new DrydockLeaseQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($lease_phids) ->execute(); $leases = mpull($leases, null, 'getPHID'); } else { $leases = array(); } foreach ($logs as $key => $log) { $lease = null; $lease_phid = $log->getLeasePHID(); if ($lease_phid) { $lease = idx($leases, $lease_phid); } $log->attachLease($lease); } $operation_phids = array_filter(mpull($logs, 'getOperationPHID')); if ($operation_phids) { $operations = id(new DrydockRepositoryOperationQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($operation_phids) ->execute(); $operations = mpull($operations, null, 'getPHID'); } else { $operations = array(); } foreach ($logs as $key => $log) { $operation = null; $operation_phid = $log->getOperationPHID(); if ($operation_phid) { $operation = idx($operations, $operation_phid); } $log->attachOperation($operation); } return $logs; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ls)', $this->ids); } if ($this->blueprintPHIDs !== null) { $where[] = qsprintf( $conn, 'blueprintPHID IN (%Ls)', $this->blueprintPHIDs); } if ($this->resourcePHIDs !== null) { $where[] = qsprintf( $conn, 'resourcePHID IN (%Ls)', $this->resourcePHIDs); } if ($this->leasePHIDs !== null) { $where[] = qsprintf( $conn, 'leasePHID IN (%Ls)', $this->leasePHIDs); } if ($this->operationPHIDs !== null) { $where[] = qsprintf( $conn, 'operationPHID IN (%Ls)', $this->operationPHIDs); } return $where; } } diff --git a/src/applications/drydock/query/DrydockRepositoryOperationQuery.php b/src/applications/drydock/query/DrydockRepositoryOperationQuery.php index a5fbe0acc9..6ef5d1b295 100644 --- a/src/applications/drydock/query/DrydockRepositoryOperationQuery.php +++ b/src/applications/drydock/query/DrydockRepositoryOperationQuery.php @@ -1,189 +1,185 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withObjectPHIDs(array $object_phids) { $this->objectPHIDs = $object_phids; return $this; } public function withRepositoryPHIDs(array $repository_phids) { $this->repositoryPHIDs = $repository_phids; return $this; } public function withOperationStates(array $states) { $this->operationStates = $states; return $this; } public function withOperationTypes(array $types) { $this->operationTypes = $types; return $this; } public function withIsDismissed($dismissed) { $this->isDismissed = $dismissed; return $this; } public function withAuthorPHIDs(array $phids) { $this->authorPHIDs = $phids; return $this; } public function newResultObject() { return new DrydockRepositoryOperation(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $operations) { $implementations = DrydockRepositoryOperationType::getAllOperationTypes(); $viewer = $this->getViewer(); foreach ($operations as $key => $operation) { $impl = idx($implementations, $operation->getOperationType()); if (!$impl) { $this->didRejectResult($operation); unset($operations[$key]); continue; } $impl = id(clone $impl) ->setViewer($viewer) ->setOperation($operation); $operation->attachImplementation($impl); } $repository_phids = mpull($operations, 'getRepositoryPHID'); if ($repository_phids) { $repositories = id(new PhabricatorRepositoryQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($repository_phids) ->execute(); $repositories = mpull($repositories, null, 'getPHID'); } else { $repositories = array(); } foreach ($operations as $key => $operation) { $repository = idx($repositories, $operation->getRepositoryPHID()); if (!$repository) { $this->didRejectResult($operation); unset($operations[$key]); continue; } $operation->attachRepository($repository); } return $operations; } protected function didFilterPage(array $operations) { $object_phids = mpull($operations, 'getObjectPHID'); if ($object_phids) { $objects = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($object_phids) ->execute(); $objects = mpull($objects, null, 'getPHID'); } else { $objects = array(); } foreach ($operations as $key => $operation) { $object = idx($objects, $operation->getObjectPHID()); $operation->attachObject($object); } return $operations; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->objectPHIDs !== null) { $where[] = qsprintf( $conn, 'objectPHID IN (%Ls)', $this->objectPHIDs); } if ($this->repositoryPHIDs !== null) { $where[] = qsprintf( $conn, 'repositoryPHID IN (%Ls)', $this->repositoryPHIDs); } if ($this->operationStates !== null) { $where[] = qsprintf( $conn, 'operationState IN (%Ls)', $this->operationStates); } if ($this->operationTypes !== null) { $where[] = qsprintf( $conn, 'operationType IN (%Ls)', $this->operationTypes); } if ($this->isDismissed !== null) { $where[] = qsprintf( $conn, 'isDismissed = %d', (int)$this->isDismissed); } if ($this->authorPHIDs !== null) { $where[] = qsprintf( $conn, 'authorPHID IN (%Ls)', $this->authorPHIDs); } return $where; } } diff --git a/src/applications/drydock/query/DrydockResourceQuery.php b/src/applications/drydock/query/DrydockResourceQuery.php index bcbff03663..395e758a23 100644 --- a/src/applications/drydock/query/DrydockResourceQuery.php +++ b/src/applications/drydock/query/DrydockResourceQuery.php @@ -1,149 +1,145 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withTypes(array $types) { $this->types = $types; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function withBlueprintPHIDs(array $blueprint_phids) { $this->blueprintPHIDs = $blueprint_phids; return $this; } public function withDatasourceQuery($query) { $this->datasourceQuery = $query; return $this; } public function needUnconsumedCommands($need) { $this->needUnconsumedCommands = $need; return $this; } public function newResultObject() { return new DrydockResource(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $resources) { $blueprint_phids = mpull($resources, 'getBlueprintPHID'); $blueprints = id(new DrydockBlueprintQuery()) ->setViewer($this->getViewer()) ->withPHIDs($blueprint_phids) ->execute(); $blueprints = mpull($blueprints, null, 'getPHID'); foreach ($resources as $key => $resource) { $blueprint = idx($blueprints, $resource->getBlueprintPHID()); if (!$blueprint) { $this->didRejectResult($resource); unset($resources[$key]); continue; } $resource->attachBlueprint($blueprint); } return $resources; } protected function didFilterPage(array $resources) { if ($this->needUnconsumedCommands) { $commands = id(new DrydockCommandQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withTargetPHIDs(mpull($resources, 'getPHID')) ->withConsumed(false) ->execute(); $commands = mgroup($commands, 'getTargetPHID'); foreach ($resources as $resource) { $list = idx($commands, $resource->getPHID(), array()); $resource->attachUnconsumedCommands($list); } } return $resources; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'resource.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'resource.phid IN (%Ls)', $this->phids); } if ($this->types !== null) { $where[] = qsprintf( $conn, 'resource.type IN (%Ls)', $this->types); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'resource.status IN (%Ls)', $this->statuses); } if ($this->blueprintPHIDs !== null) { $where[] = qsprintf( $conn, 'resource.blueprintPHID IN (%Ls)', $this->blueprintPHIDs); } if ($this->datasourceQuery !== null) { $where[] = qsprintf( $conn, 'resource.name LIKE %>', $this->datasourceQuery); } return $where; } protected function getPrimaryTableAlias() { return 'resource'; } } diff --git a/src/applications/files/query/PhabricatorFileAttachmentQuery.php b/src/applications/files/query/PhabricatorFileAttachmentQuery.php index 6367fc20f8..5fb37dc32e 100644 --- a/src/applications/files/query/PhabricatorFileAttachmentQuery.php +++ b/src/applications/files/query/PhabricatorFileAttachmentQuery.php @@ -1,135 +1,131 @@ objectPHIDs = $object_phids; return $this; } public function withFilePHIDs(array $file_phids) { $this->filePHIDs = $file_phids; return $this; } public function withVisibleFiles($visible_files) { $this->visibleFiles = $visible_files; return $this; } public function needFiles($need) { $this->needFiles = $need; return $this; } public function newResultObject() { return new PhabricatorFileAttachment(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->objectPHIDs !== null) { $where[] = qsprintf( $conn, 'attachments.objectPHID IN (%Ls)', $this->objectPHIDs); } if ($this->filePHIDs !== null) { $where[] = qsprintf( $conn, 'attachments.filePHID IN (%Ls)', $this->filePHIDs); } return $where; } protected function willFilterPage(array $attachments) { $viewer = $this->getViewer(); $object_phids = array(); foreach ($attachments as $attachment) { $object_phid = $attachment->getObjectPHID(); $object_phids[$object_phid] = $object_phid; } if ($object_phids) { $objects = id(new PhabricatorObjectQuery()) ->setViewer($viewer) ->setParentQuery($this) ->withPHIDs($object_phids) ->execute(); $objects = mpull($objects, null, 'getPHID'); } else { $objects = array(); } foreach ($attachments as $key => $attachment) { $object_phid = $attachment->getObjectPHID(); $object = idx($objects, $object_phid); if (!$object) { $this->didRejectResult($attachment); unset($attachments[$key]); continue; } $attachment->attachObject($object); } if ($this->needFiles) { $file_phids = array(); foreach ($attachments as $attachment) { $file_phid = $attachment->getFilePHID(); $file_phids[$file_phid] = $file_phid; } if ($file_phids) { $files = id(new PhabricatorFileQuery()) ->setViewer($viewer) ->setParentQuery($this) ->withPHIDs($file_phids) ->execute(); $files = mpull($files, null, 'getPHID'); } else { $files = array(); } foreach ($attachments as $key => $attachment) { $file_phid = $attachment->getFilePHID(); $file = idx($files, $file_phid); if ($this->visibleFiles && !$file) { $this->didRejectResult($attachment); unset($attachments[$key]); continue; } $attachment->attachFile($file); } } return $attachments; } protected function getPrimaryTableAlias() { return 'attachments'; } public function getQueryApplicationClass() { return 'PhabricatorFilesApplication'; } } diff --git a/src/applications/fund/query/FundInitiativeQuery.php b/src/applications/fund/query/FundInitiativeQuery.php index bbb4ef2746..4b18597873 100644 --- a/src/applications/fund/query/FundInitiativeQuery.php +++ b/src/applications/fund/query/FundInitiativeQuery.php @@ -1,81 +1,77 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withOwnerPHIDs(array $phids) { $this->ownerPHIDs = $phids; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function newResultObject() { return new FundInitiative(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'i.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'i.phid IN (%Ls)', $this->phids); } if ($this->ownerPHIDs !== null) { $where[] = qsprintf( $conn, 'i.ownerPHID IN (%Ls)', $this->ownerPHIDs); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'i.status IN (%Ls)', $this->statuses); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorFundApplication'; } protected function getPrimaryTableAlias() { return 'i'; } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildArtifactQuery.php b/src/applications/harbormaster/query/HarbormasterBuildArtifactQuery.php index de35c05aa6..59d4c07d2f 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildArtifactQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildArtifactQuery.php @@ -1,117 +1,113 @@ ids = $ids; return $this; } public function withBuildTargetPHIDs(array $build_target_phids) { $this->buildTargetPHIDs = $build_target_phids; return $this; } public function withArtifactTypes(array $artifact_types) { $this->artifactTypes = $artifact_types; return $this; } public function withArtifactIndexes(array $artifact_indexes) { $this->artifactIndexes = $artifact_indexes; return $this; } public function withIsReleased($released) { $this->isReleased = $released; return $this; } public function newResultObject() { return new HarbormasterBuildArtifact(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $page) { $build_targets = array(); $build_target_phids = array_filter(mpull($page, 'getBuildTargetPHID')); if ($build_target_phids) { $build_targets = id(new HarbormasterBuildTargetQuery()) ->setViewer($this->getViewer()) ->withPHIDs($build_target_phids) ->setParentQuery($this) ->execute(); $build_targets = mpull($build_targets, null, 'getPHID'); } foreach ($page as $key => $build_log) { $build_target_phid = $build_log->getBuildTargetPHID(); if (empty($build_targets[$build_target_phid])) { unset($page[$key]); continue; } $build_log->attachBuildTarget($build_targets[$build_target_phid]); } return $page; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->buildTargetPHIDs !== null) { $where[] = qsprintf( $conn, 'buildTargetPHID IN (%Ls)', $this->buildTargetPHIDs); } if ($this->artifactTypes !== null) { $where[] = qsprintf( $conn, 'artifactType in (%Ls)', $this->artifactTypes); } if ($this->artifactIndexes !== null) { $where[] = qsprintf( $conn, 'artifactIndex IN (%Ls)', $this->artifactIndexes); } if ($this->isReleased !== null) { $where[] = qsprintf( $conn, 'isReleased = %d', (int)$this->isReleased); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorHarbormasterApplication'; } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildLogQuery.php b/src/applications/harbormaster/query/HarbormasterBuildLogQuery.php index ad27d09582..f62919f989 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildLogQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildLogQuery.php @@ -1,90 +1,86 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withBuildTargetPHIDs(array $build_target_phids) { $this->buildTargetPHIDs = $build_target_phids; return $this; } public function newResultObject() { return new HarbormasterBuildLog(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $page) { $build_targets = array(); $build_target_phids = array_filter(mpull($page, 'getBuildTargetPHID')); if ($build_target_phids) { $build_targets = id(new HarbormasterBuildTargetQuery()) ->setViewer($this->getViewer()) ->withPHIDs($build_target_phids) ->setParentQuery($this) ->execute(); $build_targets = mpull($build_targets, null, 'getPHID'); } foreach ($page as $key => $build_log) { $build_target_phid = $build_log->getBuildTargetPHID(); if (empty($build_targets[$build_target_phid])) { unset($page[$key]); continue; } $build_log->attachBuildTarget($build_targets[$build_target_phid]); } return $page; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->buildTargetPHIDs !== null) { $where[] = qsprintf( $conn, 'buildTargetPHID IN (%Ls)', $this->buildTargetPHIDs); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorHarbormasterApplication'; } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildMessageQuery.php b/src/applications/harbormaster/query/HarbormasterBuildMessageQuery.php index 134c68ce04..393ca0a493 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildMessageQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildMessageQuery.php @@ -1,92 +1,88 @@ ids = $ids; return $this; } public function withReceiverPHIDs(array $phids) { $this->receiverPHIDs = $phids; return $this; } public function withConsumed($consumed) { $this->consumed = $consumed; return $this; } public function newResultObject() { return new HarbormasterBuildMessage(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $page) { $receiver_phids = array_filter(mpull($page, 'getReceiverPHID')); if ($receiver_phids) { $receivers = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->withPHIDs($receiver_phids) ->setParentQuery($this) ->execute(); $receivers = mpull($receivers, null, 'getPHID'); } else { $receivers = array(); } foreach ($page as $key => $message) { $receiver_phid = $message->getReceiverPHID(); if (empty($receivers[$receiver_phid])) { unset($page[$key]); $this->didRejectResult($message); continue; } $message->attachReceiver($receivers[$receiver_phid]); } return $page; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->receiverPHIDs !== null) { $where[] = qsprintf( $conn, 'receiverPHID IN (%Ls)', $this->receiverPHIDs); } if ($this->consumed !== null) { $where[] = qsprintf( $conn, 'isConsumed = %d', (int)$this->consumed); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorHarbormasterApplication'; } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildPlanQuery.php b/src/applications/harbormaster/query/HarbormasterBuildPlanQuery.php index 262aa12d7b..c8514b2186 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildPlanQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildPlanQuery.php @@ -1,152 +1,148 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function withDatasourceQuery($query) { $this->datasourceQuery = $query; return $this; } public function withPlanAutoKeys(array $keys) { $this->planAutoKeys = $keys; return $this; } public function withNameNgrams($ngrams) { return $this->withNgramsConstraint( new HarbormasterBuildPlanNameNgrams(), $ngrams); } public function needBuildSteps($need) { $this->needBuildSteps = $need; return $this; } public function newResultObject() { return new HarbormasterBuildPlan(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function didFilterPage(array $page) { if ($this->needBuildSteps) { $plan_phids = mpull($page, 'getPHID'); $steps = id(new HarbormasterBuildStepQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withBuildPlanPHIDs($plan_phids) ->execute(); $steps = mgroup($steps, 'getBuildPlanPHID'); foreach ($page as $plan) { $plan_steps = idx($steps, $plan->getPHID(), array()); $plan->attachBuildSteps($plan_steps); } } return $page; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'plan.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'plan.phid IN (%Ls)', $this->phids); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'plan.planStatus IN (%Ls)', $this->statuses); } if (!phutil_nonempty_string($this->datasourceQuery)) { $where[] = qsprintf( $conn, 'plan.name LIKE %>', $this->datasourceQuery); } if ($this->planAutoKeys !== null) { $where[] = qsprintf( $conn, 'plan.planAutoKey IN (%Ls)', $this->planAutoKeys); } return $where; } protected function getPrimaryTableAlias() { return 'plan'; } public function getQueryApplicationClass() { return 'PhabricatorHarbormasterApplication'; } public function getOrderableColumns() { return parent::getOrderableColumns() + array( 'name' => array( 'column' => 'name', 'type' => 'string', 'reverse' => true, ), ); } protected function newPagingMapFromPartialObject($object) { return array( 'id' => (int)$object->getID(), 'name' => $object->getName(), ); } public function getBuiltinOrders() { return array( 'name' => array( 'vector' => array('name', 'id'), 'name' => pht('Name'), ), ) + parent::getBuiltinOrders(); } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildQuery.php b/src/applications/harbormaster/query/HarbormasterBuildQuery.php index 5ff5fe2eb3..a397f5968a 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildQuery.php @@ -1,233 +1,229 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withBuildStatuses(array $build_statuses) { $this->buildStatuses = $build_statuses; return $this; } public function withBuildablePHIDs(array $buildable_phids) { $this->buildablePHIDs = $buildable_phids; return $this; } public function withBuildPlanPHIDs(array $build_plan_phids) { $this->buildPlanPHIDs = $build_plan_phids; return $this; } public function withInitiatorPHIDs(array $initiator_phids) { $this->initiatorPHIDs = $initiator_phids; return $this; } public function withAutobuilds($with_autobuilds) { $this->autobuilds = $with_autobuilds; return $this; } public function needBuildTargets($need_targets) { $this->needBuildTargets = $need_targets; return $this; } public function newResultObject() { return new HarbormasterBuild(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $page) { $buildables = array(); $buildable_phids = array_filter(mpull($page, 'getBuildablePHID')); if ($buildable_phids) { $buildables = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->withPHIDs($buildable_phids) ->setParentQuery($this) ->execute(); $buildables = mpull($buildables, null, 'getPHID'); } foreach ($page as $key => $build) { $buildable_phid = $build->getBuildablePHID(); if (empty($buildables[$buildable_phid])) { unset($page[$key]); continue; } $build->attachBuildable($buildables[$buildable_phid]); } return $page; } protected function didFilterPage(array $page) { $plans = array(); $plan_phids = array_filter(mpull($page, 'getBuildPlanPHID')); if ($plan_phids) { $plans = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->withPHIDs($plan_phids) ->setParentQuery($this) ->execute(); $plans = mpull($plans, null, 'getPHID'); } foreach ($page as $key => $build) { $plan_phid = $build->getBuildPlanPHID(); $build->attachBuildPlan(idx($plans, $plan_phid)); } $build_phids = mpull($page, 'getPHID'); $messages = id(new HarbormasterBuildMessage())->loadAllWhere( 'receiverPHID IN (%Ls) AND isConsumed = 0 ORDER BY id ASC', $build_phids); $messages = mgroup($messages, 'getReceiverPHID'); foreach ($page as $build) { $unprocessed_messages = idx($messages, $build->getPHID(), array()); $build->attachUnprocessedMessages($unprocessed_messages); } if ($this->needBuildTargets) { $targets = id(new HarbormasterBuildTargetQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withBuildPHIDs($build_phids) ->execute(); // TODO: Some day, when targets have dependencies, we should toposort // these. For now, just put them into chronological order. $targets = array_reverse($targets); $targets = mgroup($targets, 'getBuildPHID'); foreach ($page as $build) { $build_targets = idx($targets, $build->getPHID(), array()); foreach ($build_targets as $phid => $target) { if ($target->getBuildGeneration() !== $build->getBuildGeneration()) { unset($build_targets[$phid]); } } $build->attachBuildTargets($build_targets); } } return $page; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'b.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'b.phid in (%Ls)', $this->phids); } if ($this->buildStatuses !== null) { $where[] = qsprintf( $conn, 'b.buildStatus in (%Ls)', $this->buildStatuses); } if ($this->buildablePHIDs !== null) { $where[] = qsprintf( $conn, 'b.buildablePHID IN (%Ls)', $this->buildablePHIDs); } if ($this->buildPlanPHIDs !== null) { $where[] = qsprintf( $conn, 'b.buildPlanPHID IN (%Ls)', $this->buildPlanPHIDs); } if ($this->initiatorPHIDs !== null) { $where[] = qsprintf( $conn, 'b.initiatorPHID IN (%Ls)', $this->initiatorPHIDs); } if ($this->autobuilds !== null) { if ($this->autobuilds) { $where[] = qsprintf( $conn, 'p.planAutoKey IS NOT NULL'); } else { $where[] = qsprintf( $conn, 'p.planAutoKey IS NULL'); } } return $where; } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); if ($this->shouldJoinPlanTable()) { $joins[] = qsprintf( $conn, 'JOIN %T p ON b.buildPlanPHID = p.phid', id(new HarbormasterBuildPlan())->getTableName()); } return $joins; } private function shouldJoinPlanTable() { if ($this->autobuilds !== null) { return true; } return false; } public function getQueryApplicationClass() { return 'PhabricatorHarbormasterApplication'; } protected function getPrimaryTableAlias() { return 'b'; } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildStepQuery.php b/src/applications/harbormaster/query/HarbormasterBuildStepQuery.php index 992dd4fad1..93010071e2 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildStepQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildStepQuery.php @@ -1,89 +1,85 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withBuildPlanPHIDs(array $phids) { $this->buildPlanPHIDs = $phids; return $this; } public function newResultObject() { return new HarbormasterBuildStep(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid in (%Ls)', $this->phids); } if ($this->buildPlanPHIDs !== null) { $where[] = qsprintf( $conn, 'buildPlanPHID in (%Ls)', $this->buildPlanPHIDs); } return $where; } protected function willFilterPage(array $page) { $plans = array(); $buildplan_phids = array_filter(mpull($page, 'getBuildPlanPHID')); if ($buildplan_phids) { $plans = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->withPHIDs($buildplan_phids) ->setParentQuery($this) ->execute(); $plans = mpull($plans, null, 'getPHID'); } foreach ($page as $key => $build) { $buildable_phid = $build->getBuildPlanPHID(); if (empty($plans[$buildable_phid])) { unset($page[$key]); continue; } $build->attachBuildPlan($plans[$buildable_phid]); } return $page; } public function getQueryApplicationClass() { return 'PhabricatorHarbormasterApplication'; } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildTargetQuery.php b/src/applications/harbormaster/query/HarbormasterBuildTargetQuery.php index 54b9c4ee16..a93aff60bd 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildTargetQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildTargetQuery.php @@ -1,213 +1,209 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withBuildPHIDs(array $build_phids) { $this->buildPHIDs = $build_phids; return $this; } public function withBuildGenerations(array $build_generations) { $this->buildGenerations = $build_generations; return $this; } public function withDateCreatedBetween($min, $max) { $this->dateCreatedMin = $min; $this->dateCreatedMax = $max; return $this; } public function withDateStartedBetween($min, $max) { $this->dateStartedMin = $min; $this->dateStartedMax = $max; return $this; } public function withDateCompletedBetween($min, $max) { $this->dateCompletedMin = $min; $this->dateCompletedMax = $max; return $this; } public function withTargetStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function needBuildSteps($need_build_steps) { $this->needBuildSteps = $need_build_steps; return $this; } public function newResultObject() { return new HarbormasterBuildTarget(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid in (%Ls)', $this->phids); } if ($this->buildPHIDs !== null) { $where[] = qsprintf( $conn, 'buildPHID in (%Ls)', $this->buildPHIDs); } if ($this->buildGenerations !== null) { $where[] = qsprintf( $conn, 'buildGeneration in (%Ld)', $this->buildGenerations); } if ($this->dateCreatedMin !== null) { $where[] = qsprintf( $conn, 'dateCreated >= %d', $this->dateCreatedMin); } if ($this->dateCreatedMax !== null) { $where[] = qsprintf( $conn, 'dateCreated <= %d', $this->dateCreatedMax); } if ($this->dateStartedMin !== null) { $where[] = qsprintf( $conn, 'dateStarted >= %d', $this->dateStartedMin); } if ($this->dateStartedMax !== null) { $where[] = qsprintf( $conn, 'dateStarted <= %d', $this->dateStartedMax); } if ($this->dateCompletedMin !== null) { $where[] = qsprintf( $conn, 'dateCompleted >= %d', $this->dateCompletedMin); } if ($this->dateCompletedMax !== null) { $where[] = qsprintf( $conn, 'dateCompleted <= %d', $this->dateCompletedMax); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'targetStatus IN (%Ls)', $this->statuses); } return $where; } protected function didFilterPage(array $page) { if ($this->needBuildSteps) { $step_phids = array(); foreach ($page as $target) { $step_phids[] = $target->getBuildStepPHID(); } $steps = id(new HarbormasterBuildStepQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($step_phids) ->execute(); $steps = mpull($steps, null, 'getPHID'); foreach ($page as $target) { $target->attachBuildStep( idx($steps, $target->getBuildStepPHID())); } } return $page; } protected function willFilterPage(array $page) { $builds = array(); $build_phids = array_filter(mpull($page, 'getBuildPHID')); if ($build_phids) { $builds = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->withPHIDs($build_phids) ->setParentQuery($this) ->execute(); $builds = mpull($builds, null, 'getPHID'); } foreach ($page as $key => $build_target) { $build_phid = $build_target->getBuildPHID(); if (empty($builds[$build_phid])) { unset($page[$key]); continue; } $build_target->attachBuild($builds[$build_phid]); } return $page; } public function getQueryApplicationClass() { return 'PhabricatorHarbormasterApplication'; } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildUnitMessageQuery.php b/src/applications/harbormaster/query/HarbormasterBuildUnitMessageQuery.php index f73016a29f..edfe102ca2 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildUnitMessageQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildUnitMessageQuery.php @@ -1,95 +1,91 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withBuildTargetPHIDs(array $target_phids) { $this->targetPHIDs = $target_phids; return $this; } public function newResultObject() { return new HarbormasterBuildUnitMessage(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid in (%Ls)', $this->phids); } if ($this->targetPHIDs !== null) { $where[] = qsprintf( $conn, 'buildTargetPHID in (%Ls)', $this->targetPHIDs); } return $where; } protected function didFilterPage(array $messages) { $indexes = array(); foreach ($messages as $message) { $index = $message->getNameIndex(); if (strlen($index)) { $indexes[$index] = $index; } } if ($indexes) { $map = HarbormasterString::newIndexMap($indexes); foreach ($messages as $message) { $index = $message->getNameIndex(); if (!strlen($index)) { continue; } $name = idx($map, $index); if ($name === null) { $name = pht('Unknown Unit Message ("%s")', $index); } $message->setName($name); } } return $messages; } public function getQueryApplicationClass() { return 'PhabricatorHarbormasterApplication'; } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildableQuery.php b/src/applications/harbormaster/query/HarbormasterBuildableQuery.php index b1a643cac7..cf907a2dc3 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildableQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildableQuery.php @@ -1,184 +1,180 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withBuildablePHIDs(array $buildable_phids) { $this->buildablePHIDs = $buildable_phids; return $this; } public function withContainerPHIDs(array $container_phids) { $this->containerPHIDs = $container_phids; return $this; } public function withManualBuildables($manual) { $this->manualBuildables = $manual; return $this; } public function needContainerObjects($need) { $this->needContainerObjects = $need; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function needBuilds($need) { $this->needBuilds = $need; return $this; } public function needTargets($need) { $this->needTargets = $need; return $this; } public function newResultObject() { return new HarbormasterBuildable(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $page) { $buildables = array(); $buildable_phids = array_filter(mpull($page, 'getBuildablePHID')); if ($buildable_phids) { $buildables = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->withPHIDs($buildable_phids) ->setParentQuery($this) ->execute(); $buildables = mpull($buildables, null, 'getPHID'); } foreach ($page as $key => $buildable) { $buildable_phid = $buildable->getBuildablePHID(); if (empty($buildables[$buildable_phid])) { unset($page[$key]); continue; } $buildable->attachBuildableObject($buildables[$buildable_phid]); } return $page; } protected function didFilterPage(array $page) { if ($this->needContainerObjects) { $container_phids = array_filter(mpull($page, 'getContainerPHID')); if ($container_phids) { $containers = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->withPHIDs($container_phids) ->setParentQuery($this) ->execute(); $containers = mpull($containers, null, 'getPHID'); } else { $containers = array(); } foreach ($page as $key => $buildable) { $container_phid = $buildable->getContainerPHID(); $buildable->attachContainerObject(idx($containers, $container_phid)); } } if ($this->needBuilds || $this->needTargets) { $builds = id(new HarbormasterBuildQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withBuildablePHIDs(mpull($page, 'getPHID')) ->needBuildTargets($this->needTargets) ->execute(); $builds = mgroup($builds, 'getBuildablePHID'); foreach ($page as $key => $buildable) { $buildable->attachBuilds(idx($builds, $buildable->getPHID(), array())); } } return $page; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->buildablePHIDs !== null) { $where[] = qsprintf( $conn, 'buildablePHID IN (%Ls)', $this->buildablePHIDs); } if ($this->containerPHIDs !== null) { $where[] = qsprintf( $conn, 'containerPHID in (%Ls)', $this->containerPHIDs); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'buildableStatus in (%Ls)', $this->statuses); } if ($this->manualBuildables !== null) { $where[] = qsprintf( $conn, 'isManualBuildable = %d', (int)$this->manualBuildables); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorHarbormasterApplication'; } } diff --git a/src/applications/herald/query/HeraldRuleQuery.php b/src/applications/herald/query/HeraldRuleQuery.php index e346a998d4..e104c44122 100644 --- a/src/applications/herald/query/HeraldRuleQuery.php +++ b/src/applications/herald/query/HeraldRuleQuery.php @@ -1,341 +1,337 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAuthorPHIDs(array $author_phids) { $this->authorPHIDs = $author_phids; return $this; } public function withRuleTypes(array $types) { $this->ruleTypes = $types; return $this; } public function withContentTypes(array $types) { $this->contentTypes = $types; return $this; } public function withDisabled($disabled) { $this->disabled = $disabled; return $this; } public function withActive($active) { $this->active = $active; return $this; } public function withDatasourceQuery($query) { $this->datasourceQuery = $query; return $this; } public function withTriggerObjectPHIDs(array $phids) { $this->triggerObjectPHIDs = $phids; return $this; } public function withAffectedObjectPHIDs(array $phids) { $this->affectedObjectPHIDs = $phids; return $this; } public function needConditionsAndActions($need) { $this->needConditionsAndActions = $need; return $this; } public function needAppliedToPHIDs(array $phids) { $this->needAppliedToPHIDs = $phids; return $this; } public function needValidateAuthors($need) { $this->needValidateAuthors = $need; return $this; } public function newResultObject() { return new HeraldRule(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $rules) { $rule_ids = mpull($rules, 'getID'); // Filter out any rules that have invalid adapters, or have adapters the // viewer isn't permitted to see or use (for example, Differential rules // if the user can't use Differential or Differential is not installed). $types = HeraldAdapter::getEnabledAdapterMap($this->getViewer()); foreach ($rules as $key => $rule) { if (empty($types[$rule->getContentType()])) { $this->didRejectResult($rule); unset($rules[$key]); } } if ($this->needValidateAuthors || ($this->active !== null)) { $this->validateRuleAuthors($rules); } if ($this->active !== null) { $need_active = (bool)$this->active; foreach ($rules as $key => $rule) { if ($rule->getIsDisabled()) { $is_active = false; } else if (!$rule->hasValidAuthor()) { $is_active = false; } else { $is_active = true; } if ($is_active != $need_active) { unset($rules[$key]); } } } if (!$rules) { return array(); } if ($this->needConditionsAndActions) { $conditions = id(new HeraldCondition())->loadAllWhere( 'ruleID IN (%Ld)', $rule_ids); $conditions = mgroup($conditions, 'getRuleID'); $actions = id(new HeraldActionRecord())->loadAllWhere( 'ruleID IN (%Ld)', $rule_ids); $actions = mgroup($actions, 'getRuleID'); foreach ($rules as $rule) { $rule->attachActions(idx($actions, $rule->getID(), array())); $rule->attachConditions(idx($conditions, $rule->getID(), array())); } } if ($this->needAppliedToPHIDs) { $conn_r = id(new HeraldRule())->establishConnection('r'); $applied = queryfx_all( $conn_r, 'SELECT * FROM %T WHERE ruleID IN (%Ld) AND phid IN (%Ls)', HeraldRule::TABLE_RULE_APPLIED, $rule_ids, $this->needAppliedToPHIDs); $map = array(); foreach ($applied as $row) { $map[$row['ruleID']][$row['phid']] = true; } foreach ($rules as $rule) { foreach ($this->needAppliedToPHIDs as $phid) { $rule->setRuleApplied( $phid, isset($map[$rule->getID()][$phid])); } } } $object_phids = array(); foreach ($rules as $rule) { if ($rule->isObjectRule()) { $object_phids[] = $rule->getTriggerObjectPHID(); } } if ($object_phids) { $objects = id(new PhabricatorObjectQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($object_phids) ->execute(); $objects = mpull($objects, null, 'getPHID'); } else { $objects = array(); } foreach ($rules as $key => $rule) { if ($rule->isObjectRule()) { $object = idx($objects, $rule->getTriggerObjectPHID()); if (!$object) { unset($rules[$key]); continue; } $rule->attachTriggerObject($object); } } return $rules; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'rule.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'rule.phid IN (%Ls)', $this->phids); } if ($this->authorPHIDs !== null) { $where[] = qsprintf( $conn, 'rule.authorPHID IN (%Ls)', $this->authorPHIDs); } if ($this->ruleTypes !== null) { $where[] = qsprintf( $conn, 'rule.ruleType IN (%Ls)', $this->ruleTypes); } if ($this->contentTypes !== null) { $where[] = qsprintf( $conn, 'rule.contentType IN (%Ls)', $this->contentTypes); } if ($this->disabled !== null) { $where[] = qsprintf( $conn, 'rule.isDisabled = %d', (int)$this->disabled); } if ($this->active !== null) { $where[] = qsprintf( $conn, 'rule.isDisabled = %d', (int)(!$this->active)); } if ($this->datasourceQuery !== null) { $where[] = qsprintf( $conn, 'rule.name LIKE %>', $this->datasourceQuery); } if ($this->triggerObjectPHIDs !== null) { $where[] = qsprintf( $conn, 'rule.triggerObjectPHID IN (%Ls)', $this->triggerObjectPHIDs); } if ($this->affectedObjectPHIDs !== null) { $where[] = qsprintf( $conn, 'edge_affects.dst IN (%Ls)', $this->affectedObjectPHIDs); } return $where; } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); if ($this->affectedObjectPHIDs !== null) { $joins[] = qsprintf( $conn, 'JOIN %T edge_affects ON rule.phid = edge_affects.src AND edge_affects.type = %d', PhabricatorEdgeConfig::TABLE_NAME_EDGE, HeraldRuleActionAffectsObjectEdgeType::EDGECONST); } return $joins; } private function validateRuleAuthors(array $rules) { // "Global" and "Object" rules always have valid authors. foreach ($rules as $key => $rule) { if ($rule->isGlobalRule() || $rule->isObjectRule()) { $rule->attachValidAuthor(true); unset($rules[$key]); continue; } } if (!$rules) { return; } // For personal rules, the author needs to exist and not be disabled. $user_phids = mpull($rules, 'getAuthorPHID'); $users = id(new PhabricatorPeopleQuery()) ->setViewer($this->getViewer()) ->withPHIDs($user_phids) ->execute(); $users = mpull($users, null, 'getPHID'); foreach ($rules as $key => $rule) { $author_phid = $rule->getAuthorPHID(); if (empty($users[$author_phid])) { $rule->attachValidAuthor(false); continue; } if (!$users[$author_phid]->isUserActivated()) { $rule->attachValidAuthor(false); continue; } $rule->attachValidAuthor(true); $rule->attachAuthor($users[$author_phid]); } } public function getQueryApplicationClass() { return 'PhabricatorHeraldApplication'; } protected function getPrimaryTableAlias() { return 'rule'; } } diff --git a/src/applications/herald/query/HeraldWebhookQuery.php b/src/applications/herald/query/HeraldWebhookQuery.php index ca46880613..77307a71e6 100644 --- a/src/applications/herald/query/HeraldWebhookQuery.php +++ b/src/applications/herald/query/HeraldWebhookQuery.php @@ -1,64 +1,60 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function newResultObject() { return new HeraldWebhook(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'status IN (%Ls)', $this->statuses); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorHeraldApplication'; } } diff --git a/src/applications/herald/query/HeraldWebhookRequestQuery.php b/src/applications/herald/query/HeraldWebhookRequestQuery.php index 4c71d48e05..f0a61a2dc5 100644 --- a/src/applications/herald/query/HeraldWebhookRequestQuery.php +++ b/src/applications/herald/query/HeraldWebhookRequestQuery.php @@ -1,126 +1,122 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withWebhookPHIDs(array $phids) { $this->webhookPHIDs = $phids; return $this; } public function newResultObject() { return new HeraldWebhookRequest(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - public function withLastRequestEpochBetween($epoch_min, $epoch_max) { $this->lastRequestEpochMin = $epoch_min; $this->lastRequestEpochMax = $epoch_max; return $this; } public function withLastRequestResults(array $results) { $this->lastRequestResults = $results; return $this; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->webhookPHIDs !== null) { $where[] = qsprintf( $conn, 'webhookPHID IN (%Ls)', $this->webhookPHIDs); } if ($this->lastRequestEpochMin !== null) { $where[] = qsprintf( $conn, 'lastRequestEpoch >= %d', $this->lastRequestEpochMin); } if ($this->lastRequestEpochMax !== null) { $where[] = qsprintf( $conn, 'lastRequestEpoch <= %d', $this->lastRequestEpochMax); } if ($this->lastRequestResults !== null) { $where[] = qsprintf( $conn, 'lastRequestResult IN (%Ls)', $this->lastRequestResults); } return $where; } protected function willFilterPage(array $requests) { $hook_phids = mpull($requests, 'getWebhookPHID'); $hooks = id(new HeraldWebhookQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($hook_phids) ->execute(); $hooks = mpull($hooks, null, 'getPHID'); foreach ($requests as $key => $request) { $hook_phid = $request->getWebhookPHID(); $hook = idx($hooks, $hook_phid); if (!$hook) { unset($requests[$key]); $this->didRejectResult($request); continue; } $request->attachWebhook($hook); } return $requests; } public function getQueryApplicationClass() { return 'PhabricatorHeraldApplication'; } } diff --git a/src/applications/legalpad/query/LegalpadDocumentQuery.php b/src/applications/legalpad/query/LegalpadDocumentQuery.php index 3d79e9f3a1..854a187fab 100644 --- a/src/applications/legalpad/query/LegalpadDocumentQuery.php +++ b/src/applications/legalpad/query/LegalpadDocumentQuery.php @@ -1,273 +1,269 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withCreatorPHIDs(array $phids) { $this->creatorPHIDs = $phids; return $this; } public function withContributorPHIDs(array $phids) { $this->contributorPHIDs = $phids; return $this; } public function withSignerPHIDs(array $phids) { $this->signerPHIDs = $phids; return $this; } public function withSignatureRequired($bool) { $this->signatureRequired = $bool; return $this; } public function needDocumentBodies($need_bodies) { $this->needDocumentBodies = $need_bodies; return $this; } public function needContributors($need_contributors) { $this->needContributors = $need_contributors; return $this; } public function needSignatures($need_signatures) { $this->needSignatures = $need_signatures; return $this; } public function withDateCreatedBefore($date_created_before) { $this->dateCreatedBefore = $date_created_before; return $this; } public function withDateCreatedAfter($date_created_after) { $this->dateCreatedAfter = $date_created_after; return $this; } public function needViewerSignatures($need) { $this->needViewerSignatures = $need; return $this; } public function newResultObject() { return new LegalpadDocument(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $documents) { if ($this->needDocumentBodies) { $documents = $this->loadDocumentBodies($documents); } if ($this->needContributors) { $documents = $this->loadContributors($documents); } if ($this->needSignatures) { $documents = $this->loadSignatures($documents); } if ($this->needViewerSignatures) { if ($documents) { if ($this->getViewer()->getPHID()) { $signatures = id(new LegalpadDocumentSignatureQuery()) ->setViewer($this->getViewer()) ->withSignerPHIDs(array($this->getViewer()->getPHID())) ->withDocumentPHIDs(mpull($documents, 'getPHID')) ->execute(); $signatures = mpull($signatures, null, 'getDocumentPHID'); } else { $signatures = array(); } foreach ($documents as $document) { $signature = idx($signatures, $document->getPHID()); $document->attachUserSignature( $this->getViewer()->getPHID(), $signature); } } } return $documents; } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); if ($this->contributorPHIDs !== null) { $joins[] = qsprintf( $conn, 'JOIN edge contributor ON contributor.src = d.phid AND contributor.type = %d', PhabricatorObjectHasContributorEdgeType::EDGECONST); } if ($this->signerPHIDs !== null) { $joins[] = qsprintf( $conn, 'JOIN %T signer ON signer.documentPHID = d.phid AND signer.signerPHID IN (%Ls)', id(new LegalpadDocumentSignature())->getTableName(), $this->signerPHIDs); } return $joins; } protected function shouldGroupQueryResultRows() { if ($this->contributorPHIDs) { return true; } if ($this->signerPHIDs) { return true; } return parent::shouldGroupQueryResultRows(); } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'd.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'd.phid IN (%Ls)', $this->phids); } if ($this->creatorPHIDs !== null) { $where[] = qsprintf( $conn, 'd.creatorPHID IN (%Ls)', $this->creatorPHIDs); } if ($this->dateCreatedAfter !== null) { $where[] = qsprintf( $conn, 'd.dateCreated >= %d', $this->dateCreatedAfter); } if ($this->dateCreatedBefore !== null) { $where[] = qsprintf( $conn, 'd.dateCreated <= %d', $this->dateCreatedBefore); } if ($this->contributorPHIDs !== null) { $where[] = qsprintf( $conn, 'contributor.dst IN (%Ls)', $this->contributorPHIDs); } if ($this->signatureRequired !== null) { $where[] = qsprintf( $conn, 'd.requireSignature = %d', $this->signatureRequired); } return $where; } private function loadDocumentBodies(array $documents) { $body_phids = mpull($documents, 'getDocumentBodyPHID'); $bodies = id(new LegalpadDocumentBody())->loadAllWhere( 'phid IN (%Ls)', $body_phids); $bodies = mpull($bodies, null, 'getPHID'); foreach ($documents as $document) { $body = idx($bodies, $document->getDocumentBodyPHID()); $document->attachDocumentBody($body); } return $documents; } private function loadContributors(array $documents) { $document_map = mpull($documents, null, 'getPHID'); $edge_type = PhabricatorObjectHasContributorEdgeType::EDGECONST; $contributor_data = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(array_keys($document_map)) ->withEdgeTypes(array($edge_type)) ->execute(); foreach ($document_map as $document_phid => $document) { $data = $contributor_data[$document_phid]; $contributors = array_keys(idx($data, $edge_type, array())); $document->attachContributors($contributors); } return $documents; } private function loadSignatures(array $documents) { $document_map = mpull($documents, null, 'getPHID'); $signatures = id(new LegalpadDocumentSignatureQuery()) ->setViewer($this->getViewer()) ->withDocumentPHIDs(array_keys($document_map)) ->execute(); $signatures = mgroup($signatures, 'getDocumentPHID'); foreach ($documents as $document) { $sigs = idx($signatures, $document->getPHID(), array()); $document->attachSignatures($sigs); } return $documents; } public function getQueryApplicationClass() { return 'PhabricatorLegalpadApplication'; } protected function getPrimaryTableAlias() { return 'd'; } } diff --git a/src/applications/macro/query/PhabricatorMacroQuery.php b/src/applications/macro/query/PhabricatorMacroQuery.php index 7261803888..70e7f7e688 100644 --- a/src/applications/macro/query/PhabricatorMacroQuery.php +++ b/src/applications/macro/query/PhabricatorMacroQuery.php @@ -1,268 +1,264 @@ pht('Active Macros'), self::STATUS_DISABLED => pht('Disabled Macros'), self::STATUS_ANY => pht('Active and Disabled Macros'), ); } public static function getFlagColorsOptions() { $options = array( '-1' => pht('(No Filtering)'), '-2' => pht('(Marked With Any Flag)'), ); foreach (PhabricatorFlagColor::getColorNameMap() as $color => $name) { $options[$color] = $name; } return $options; } public function withIDs(array $ids) { $this->ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAuthorPHIDs(array $author_phids) { $this->authorPHIDs = $author_phids; return $this; } public function withNameLike($name) { $this->nameLike = $name; return $this; } public function withNames(array $names) { $this->names = $names; return $this; } public function withNamePrefix($prefix) { $this->namePrefix = $prefix; return $this; } public function withStatus($status) { $this->status = $status; return $this; } public function withDateCreatedBefore($date_created_before) { $this->dateCreatedBefore = $date_created_before; return $this; } public function withDateCreatedAfter($date_created_after) { $this->dateCreatedAfter = $date_created_after; return $this; } public function withFlagColor($flag_color) { $this->flagColor = $flag_color; return $this; } public function needFiles($need_files) { $this->needFiles = $need_files; return $this; } public function newResultObject() { return new PhabricatorFileImageMacro(); } - protected function loadPage() { - return $this->loadStandardPage(new PhabricatorFileImageMacro()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'm.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'm.phid IN (%Ls)', $this->phids); } if ($this->authorPHIDs !== null) { $where[] = qsprintf( $conn, 'm.authorPHID IN (%Ls)', $this->authorPHIDs); } if (($this->nameLike !== null) && strlen($this->nameLike)) { $where[] = qsprintf( $conn, 'm.name LIKE %~', $this->nameLike); } if ($this->names !== null) { $where[] = qsprintf( $conn, 'm.name IN (%Ls)', $this->names); } if (($this->namePrefix !== null) && strlen($this->namePrefix)) { $where[] = qsprintf( $conn, 'm.name LIKE %>', $this->namePrefix); } switch ($this->status) { case self::STATUS_ACTIVE: $where[] = qsprintf( $conn, 'm.isDisabled = 0'); break; case self::STATUS_DISABLED: $where[] = qsprintf( $conn, 'm.isDisabled = 1'); break; case self::STATUS_ANY: break; default: throw new Exception(pht("Unknown status '%s'!", $this->status)); } if ($this->dateCreatedAfter) { $where[] = qsprintf( $conn, 'm.dateCreated >= %d', $this->dateCreatedAfter); } if ($this->dateCreatedBefore) { $where[] = qsprintf( $conn, 'm.dateCreated <= %d', $this->dateCreatedBefore); } if ($this->flagColor != '-1' && $this->flagColor !== null) { if ($this->flagColor == '-2') { $flag_colors = array_keys(PhabricatorFlagColor::getColorNameMap()); } else { $flag_colors = array($this->flagColor); } $flags = id(new PhabricatorFlagQuery()) ->withOwnerPHIDs(array($this->getViewer()->getPHID())) ->withTypes(array(PhabricatorMacroMacroPHIDType::TYPECONST)) ->withColors($flag_colors) ->setViewer($this->getViewer()) ->execute(); if (empty($flags)) { throw new PhabricatorEmptyQueryException(pht('No matching flags.')); } else { $where[] = qsprintf( $conn, 'm.phid IN (%Ls)', mpull($flags, 'getObjectPHID')); } } return $where; } protected function didFilterPage(array $macros) { if ($this->needFiles) { $file_phids = mpull($macros, 'getFilePHID'); $files = id(new PhabricatorFileQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($file_phids) ->execute(); $files = mpull($files, null, 'getPHID'); foreach ($macros as $key => $macro) { $file = idx($files, $macro->getFilePHID()); if (!$file) { unset($macros[$key]); continue; } $macro->attachFile($file); } } return $macros; } protected function getPrimaryTableAlias() { return 'm'; } public function getQueryApplicationClass() { return 'PhabricatorMacroApplication'; } public function getOrderableColumns() { return parent::getOrderableColumns() + array( 'name' => array( 'table' => 'm', 'column' => 'name', 'type' => 'string', 'reverse' => true, 'unique' => true, ), ); } protected function newPagingMapFromPartialObject($object) { return array( 'id' => (int)$object->getID(), 'name' => $object->getName(), ); } public function getBuiltinOrders() { return array( 'name' => array( 'vector' => array('name'), 'name' => pht('Name'), ), ) + parent::getBuiltinOrders(); } } diff --git a/src/applications/metamta/query/PhabricatorMetaMTAMailPropertiesQuery.php b/src/applications/metamta/query/PhabricatorMetaMTAMailPropertiesQuery.php index e6ec09f4ff..b7dd3e5ee4 100644 --- a/src/applications/metamta/query/PhabricatorMetaMTAMailPropertiesQuery.php +++ b/src/applications/metamta/query/PhabricatorMetaMTAMailPropertiesQuery.php @@ -1,51 +1,47 @@ ids = $ids; return $this; } public function withObjectPHIDs(array $object_phids) { $this->objectPHIDs = $object_phids; return $this; } public function newResultObject() { return new PhabricatorMetaMTAMailProperties(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->objectPHIDs !== null) { $where[] = qsprintf( $conn, 'objectPHID IN (%Ls)', $this->objectPHIDs); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorMetaMTAApplication'; } } diff --git a/src/applications/metamta/query/PhabricatorMetaMTAMailQuery.php b/src/applications/metamta/query/PhabricatorMetaMTAMailQuery.php index 903b385ceb..d1f0235c90 100644 --- a/src/applications/metamta/query/PhabricatorMetaMTAMailQuery.php +++ b/src/applications/metamta/query/PhabricatorMetaMTAMailQuery.php @@ -1,132 +1,128 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withActorPHIDs(array $phids) { $this->actorPHIDs = $phids; return $this; } public function withRecipientPHIDs(array $phids) { $this->recipientPHIDs = $phids; return $this; } public function withDateCreatedBetween($min, $max) { $this->createdMin = $min; $this->createdMax = $max; return $this; } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'mail.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'mail.phid IN (%Ls)', $this->phids); } if ($this->actorPHIDs !== null) { $where[] = qsprintf( $conn, 'mail.actorPHID IN (%Ls)', $this->actorPHIDs); } if ($this->createdMin !== null) { $where[] = qsprintf( $conn, 'mail.dateCreated >= %d', $this->createdMin); } if ($this->createdMax !== null) { $where[] = qsprintf( $conn, 'mail.dateCreated <= %d', $this->createdMax); } return $where; } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); if ($this->shouldJoinRecipients()) { $joins[] = qsprintf( $conn, 'JOIN %T recipient ON mail.phid = recipient.src AND recipient.type = %d AND recipient.dst IN (%Ls)', PhabricatorEdgeConfig::TABLE_NAME_EDGE, PhabricatorMetaMTAMailHasRecipientEdgeType::EDGECONST, $this->recipientPHIDs); } return $joins; } private function shouldJoinRecipients() { if ($this->recipientPHIDs === null) { return false; } return true; } protected function getPrimaryTableAlias() { return 'mail'; } public function newResultObject() { return new PhabricatorMetaMTAMail(); } public function getQueryApplicationClass() { return 'PhabricatorMetaMTAApplication'; } protected function shouldGroupQueryResultRows() { if ($this->shouldJoinRecipients()) { if (count($this->recipientPHIDs) > 1) { return true; } } return parent::shouldGroupQueryResultRows(); } } diff --git a/src/applications/nuance/query/NuanceImportCursorDataQuery.php b/src/applications/nuance/query/NuanceImportCursorDataQuery.php index ae451abfb9..133c940b3f 100644 --- a/src/applications/nuance/query/NuanceImportCursorDataQuery.php +++ b/src/applications/nuance/query/NuanceImportCursorDataQuery.php @@ -1,60 +1,56 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withSourcePHIDs(array $source_phids) { $this->sourcePHIDs = $source_phids; return $this; } public function newResultObject() { return new NuanceImportCursorData(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->sourcePHIDs !== null) { $where[] = qsprintf( $conn, 'sourcePHID IN (%Ls)', $this->sourcePHIDs); } if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } return $where; } } diff --git a/src/applications/nuance/query/NuanceItemCommandQuery.php b/src/applications/nuance/query/NuanceItemCommandQuery.php index 27137cf8f6..a694657120 100644 --- a/src/applications/nuance/query/NuanceItemCommandQuery.php +++ b/src/applications/nuance/query/NuanceItemCommandQuery.php @@ -1,60 +1,56 @@ ids = $ids; return $this; } public function withItemPHIDs(array $item_phids) { $this->itemPHIDs = $item_phids; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function newResultObject() { return new NuanceItemCommand(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->itemPHIDs !== null) { $where[] = qsprintf( $conn, 'itemPHID IN (%Ls)', $this->itemPHIDs); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'status IN (%Ls)', $this->statuses); } return $where; } } diff --git a/src/applications/nuance/query/NuanceItemQuery.php b/src/applications/nuance/query/NuanceItemQuery.php index 834e81ca72..e2e32c84b4 100644 --- a/src/applications/nuance/query/NuanceItemQuery.php +++ b/src/applications/nuance/query/NuanceItemQuery.php @@ -1,196 +1,192 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withSourcePHIDs(array $source_phids) { $this->sourcePHIDs = $source_phids; return $this; } public function withQueuePHIDs(array $queue_phids) { $this->queuePHIDs = $queue_phids; return $this; } public function withItemTypes(array $item_types) { $this->itemTypes = $item_types; return $this; } public function withItemKeys(array $item_keys) { $this->itemKeys = $item_keys; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function withItemContainerKeys(array $container_keys) { $this->containerKeys = $container_keys; return $this; } public function newResultObject() { return new NuanceItem(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $items) { $viewer = $this->getViewer(); $source_phids = mpull($items, 'getSourcePHID'); $sources = id(new NuanceSourceQuery()) ->setViewer($viewer) ->withPHIDs($source_phids) ->execute(); $sources = mpull($sources, null, 'getPHID'); foreach ($items as $key => $item) { $source = idx($sources, $item->getSourcePHID()); if (!$source) { $this->didRejectResult($items[$key]); unset($items[$key]); continue; } $item->attachSource($source); } $type_map = NuanceItemType::getAllItemTypes(); foreach ($items as $key => $item) { $type = idx($type_map, $item->getItemType()); if (!$type) { $this->didRejectResult($items[$key]); unset($items[$key]); continue; } $item->attachImplementation($type); } $queue_phids = array(); foreach ($items as $item) { $queue_phid = $item->getQueuePHID(); if ($queue_phid) { $queue_phids[$queue_phid] = $queue_phid; } } if ($queue_phids) { $queues = id(new NuanceQueueQuery()) ->setViewer($viewer) ->withPHIDs($queue_phids) ->execute(); $queues = mpull($queues, null, 'getPHID'); } else { $queues = array(); } foreach ($items as $key => $item) { $queue_phid = $item->getQueuePHID(); if (!$queue_phid) { $item->attachQueue(null); continue; } $queue = idx($queues, $queue_phid); if (!$queue) { unset($items[$key]); $this->didRejectResult($item); continue; } $item->attachQueue($queue); } return $items; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->sourcePHIDs !== null) { $where[] = qsprintf( $conn, 'sourcePHID IN (%Ls)', $this->sourcePHIDs); } if ($this->queuePHIDs !== null) { $where[] = qsprintf( $conn, 'queuePHID IN (%Ls)', $this->queuePHIDs); } if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'status IN (%Ls)', $this->statuses); } if ($this->itemTypes !== null) { $where[] = qsprintf( $conn, 'itemType IN (%Ls)', $this->itemTypes); } if ($this->itemKeys !== null) { $where[] = qsprintf( $conn, 'itemKey IN (%Ls)', $this->itemKeys); } if ($this->containerKeys !== null) { $where[] = qsprintf( $conn, 'itemContainerKey IN (%Ls)', $this->containerKeys); } return $where; } } diff --git a/src/applications/nuance/query/NuanceQueueQuery.php b/src/applications/nuance/query/NuanceQueueQuery.php index 10f761d189..acb69665e0 100644 --- a/src/applications/nuance/query/NuanceQueueQuery.php +++ b/src/applications/nuance/query/NuanceQueueQuery.php @@ -1,47 +1,43 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function newResultObject() { return new NuanceQueue(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } return $where; } } diff --git a/src/applications/nuance/query/NuanceSourceQuery.php b/src/applications/nuance/query/NuanceSourceQuery.php index 907d9c314f..9186d47cdd 100644 --- a/src/applications/nuance/query/NuanceSourceQuery.php +++ b/src/applications/nuance/query/NuanceSourceQuery.php @@ -1,137 +1,133 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withTypes($types) { $this->types = $types; return $this; } public function withIsDisabled($disabled) { $this->isDisabled = $disabled; return $this; } public function withHasImportCursors($has_cursors) { $this->hasCursors = $has_cursors; return $this; } public function withNameNgrams($ngrams) { return $this->withNgramsConstraint( new NuanceSourceNameNgrams(), $ngrams); } public function newResultObject() { return new NuanceSource(); } protected function getPrimaryTableAlias() { return 'source'; } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $sources) { $all_types = NuanceSourceDefinition::getAllDefinitions(); foreach ($sources as $key => $source) { $definition = idx($all_types, $source->getType()); if (!$definition) { $this->didRejectResult($source); unset($sources[$key]); continue; } $source->attachDefinition($definition); } return $sources; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->types !== null) { $where[] = qsprintf( $conn, 'source.type IN (%Ls)', $this->types); } if ($this->ids !== null) { $where[] = qsprintf( $conn, 'source.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'source.phid IN (%Ls)', $this->phids); } if ($this->isDisabled !== null) { $where[] = qsprintf( $conn, 'source.isDisabled = %d', (int)$this->isDisabled); } if ($this->hasCursors !== null) { $cursor_types = array(); $definitions = NuanceSourceDefinition::getAllDefinitions(); foreach ($definitions as $key => $definition) { if ($definition->hasImportCursors()) { $cursor_types[] = $key; } } if ($this->hasCursors) { if (!$cursor_types) { throw new PhabricatorEmptyQueryException(); } else { $where[] = qsprintf( $conn, 'source.type IN (%Ls)', $cursor_types); } } else { if (!$cursor_types) { // Apply no constraint. } else { $where[] = qsprintf( $conn, 'source.type NOT IN (%Ls)', $cursor_types); } } } return $where; } } diff --git a/src/applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php b/src/applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php index a746008f55..63a62e8cd9 100644 --- a/src/applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php +++ b/src/applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php @@ -1,89 +1,85 @@ phids = $phids; return $this; } public function withUserPHIDs(array $phids) { $this->userPHIDs = $phids; return $this; } public function withClientPHIDs(array $phids) { $this->clientPHIDs = $phids; return $this; } public function newResultObject() { return new PhabricatorOAuthClientAuthorization(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $authorizations) { $client_phids = mpull($authorizations, 'getClientPHID'); $clients = id(new PhabricatorOAuthServerClientQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($client_phids) ->execute(); $clients = mpull($clients, null, 'getPHID'); foreach ($authorizations as $key => $authorization) { $client = idx($clients, $authorization->getClientPHID()); if (!$client) { $this->didRejectResult($authorization); unset($authorizations[$key]); continue; } $authorization->attachClient($client); } return $authorizations; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->userPHIDs !== null) { $where[] = qsprintf( $conn, 'userPHID IN (%Ls)', $this->userPHIDs); } if ($this->clientPHIDs !== null) { $where[] = qsprintf( $conn, 'clientPHID IN (%Ls)', $this->clientPHIDs); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorOAuthServerApplication'; } } diff --git a/src/applications/owners/query/PhabricatorOwnersPackageQuery.php b/src/applications/owners/query/PhabricatorOwnersPackageQuery.php index 9e5e4c3234..a80b972548 100644 --- a/src/applications/owners/query/PhabricatorOwnersPackageQuery.php +++ b/src/applications/owners/query/PhabricatorOwnersPackageQuery.php @@ -1,464 +1,460 @@ ownerPHIDs = $phids; return $this; } /** * Query owner authority. This will expand authorities, so a user PHID will * match both packages they own directly and packages owned by a project they * are a member of. */ public function withAuthorityPHIDs(array $phids) { $this->authorityPHIDs = $phids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withIDs(array $ids) { $this->ids = $ids; return $this; } public function withRepositoryPHIDs(array $phids) { $this->repositoryPHIDs = $phids; return $this; } public function withPaths(array $paths) { $this->paths = $paths; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function withControl($repository_phid, array $paths) { if (empty($this->controlMap[$repository_phid])) { $this->controlMap[$repository_phid] = array(); } foreach ($paths as $path) { $path = (string)$path; $this->controlMap[$repository_phid][$path] = $path; } // We need to load paths to execute control queries. $this->needPaths = true; return $this; } public function withAuthorityModes(array $modes) { $this->authorityModes = $modes; return $this; } public function withNameNgrams($ngrams) { return $this->withNgramsConstraint( new PhabricatorOwnersPackageNameNgrams(), $ngrams); } public function needPaths($need_paths) { $this->needPaths = $need_paths; return $this; } public function newResultObject() { return new PhabricatorOwnersPackage(); } protected function willExecute() { $this->controlResults = array(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $packages) { $package_ids = mpull($packages, 'getID'); $owners = id(new PhabricatorOwnersOwner())->loadAllWhere( 'packageID IN (%Ld)', $package_ids); $owners = mgroup($owners, 'getPackageID'); foreach ($packages as $package) { $package->attachOwners(idx($owners, $package->getID(), array())); } return $packages; } protected function didFilterPage(array $packages) { $package_ids = mpull($packages, 'getID'); if ($this->needPaths) { $paths = id(new PhabricatorOwnersPath())->loadAllWhere( 'packageID IN (%Ld)', $package_ids); $paths = mgroup($paths, 'getPackageID'); foreach ($packages as $package) { $package->attachPaths(idx($paths, $package->getID(), array())); } } if ($this->controlMap) { foreach ($packages as $package) { // If this package is archived, it's no longer a controlling package // for any path. In particular, it can not force active packages with // weak dominion to give up control. if ($package->isArchived()) { continue; } $this->controlResults[$package->getID()] = $package; } } return $packages; } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); if ($this->shouldJoinOwnersTable()) { $joins[] = qsprintf( $conn, 'JOIN %T o ON o.packageID = p.id', id(new PhabricatorOwnersOwner())->getTableName()); } if ($this->shouldJoinPathTable()) { $joins[] = qsprintf( $conn, 'JOIN %T rpath ON rpath.packageID = p.id', id(new PhabricatorOwnersPath())->getTableName()); } return $joins; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->phids !== null) { $where[] = qsprintf( $conn, 'p.phid IN (%Ls)', $this->phids); } if ($this->ids !== null) { $where[] = qsprintf( $conn, 'p.id IN (%Ld)', $this->ids); } if ($this->repositoryPHIDs !== null) { $where[] = qsprintf( $conn, 'rpath.repositoryPHID IN (%Ls)', $this->repositoryPHIDs); } if ($this->authorityPHIDs !== null) { $authority_phids = $this->expandAuthority($this->authorityPHIDs); $where[] = qsprintf( $conn, 'o.userPHID IN (%Ls)', $authority_phids); } if ($this->ownerPHIDs !== null) { $where[] = qsprintf( $conn, 'o.userPHID IN (%Ls)', $this->ownerPHIDs); } if ($this->paths !== null) { $where[] = qsprintf( $conn, 'rpath.pathIndex IN (%Ls)', $this->getFragmentIndexesForPaths($this->paths)); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'p.status IN (%Ls)', $this->statuses); } if ($this->controlMap) { $clauses = array(); foreach ($this->controlMap as $repository_phid => $paths) { $indexes = $this->getFragmentIndexesForPaths($paths); $clauses[] = qsprintf( $conn, '(rpath.repositoryPHID = %s AND rpath.pathIndex IN (%Ls))', $repository_phid, $indexes); } $where[] = qsprintf($conn, '%LO', $clauses); } if ($this->authorityModes !== null) { $where[] = qsprintf( $conn, 'authorityMode IN (%Ls)', $this->authorityModes); } return $where; } protected function shouldGroupQueryResultRows() { if ($this->shouldJoinOwnersTable()) { return true; } if ($this->shouldJoinPathTable()) { return true; } return parent::shouldGroupQueryResultRows(); } public function getBuiltinOrders() { return array( 'name' => array( 'vector' => array('name'), 'name' => pht('Name'), ), ) + parent::getBuiltinOrders(); } public function getOrderableColumns() { return parent::getOrderableColumns() + array( 'name' => array( 'table' => $this->getPrimaryTableAlias(), 'column' => 'name', 'type' => 'string', 'unique' => true, 'reverse' => true, ), ); } protected function newPagingMapFromPartialObject($object) { return array( 'id' => (int)$object->getID(), 'name' => $object->getName(), ); } public function getQueryApplicationClass() { return 'PhabricatorOwnersApplication'; } protected function getPrimaryTableAlias() { return 'p'; } private function shouldJoinOwnersTable() { if ($this->ownerPHIDs !== null) { return true; } if ($this->authorityPHIDs !== null) { return true; } return false; } private function shouldJoinPathTable() { if ($this->repositoryPHIDs !== null) { return true; } if ($this->paths !== null) { return true; } if ($this->controlMap) { return true; } return false; } private function expandAuthority(array $phids) { $projects = id(new PhabricatorProjectQuery()) ->setViewer($this->getViewer()) ->withMemberPHIDs($phids) ->execute(); $project_phids = mpull($projects, 'getPHID'); return array_fuse($phids) + array_fuse($project_phids); } private function getFragmentsForPaths(array $paths) { $fragments = array(); foreach ($paths as $path) { foreach (PhabricatorOwnersPackage::splitPath($path) as $fragment) { $fragments[$fragment] = $fragment; } } return $fragments; } private function getFragmentIndexesForPaths(array $paths) { $indexes = array(); foreach ($this->getFragmentsForPaths($paths) as $fragment) { $indexes[] = PhabricatorHash::digestForIndex($fragment); } return $indexes; } /* -( Path Control )------------------------------------------------------- */ /** * Get a list of all packages which control a path or its parent directories, * ordered from weakest to strongest. * * The first package has the most specific claim on the path; the last * package has the most general claim. Multiple packages may have claims of * equal strength, so this ordering is primarily one of usability and * convenience. * * @return list List of controlling packages. */ public function getControllingPackagesForPath( $repository_phid, $path, $ignore_dominion = false) { $path = (string)$path; if (!isset($this->controlMap[$repository_phid][$path])) { throw new PhutilInvalidStateException('withControl'); } if ($this->controlResults === null) { throw new PhutilInvalidStateException('execute'); } $packages = $this->controlResults; $weak_dominion = PhabricatorOwnersPackage::DOMINION_WEAK; $path_fragments = PhabricatorOwnersPackage::splitPath($path); $fragment_count = count($path_fragments); $matches = array(); foreach ($packages as $package_id => $package) { $best_match = null; $include = false; $repository_paths = $package->getPathsForRepository($repository_phid); foreach ($repository_paths as $package_path) { $strength = $package_path->getPathMatchStrength( $path_fragments, $fragment_count); if ($strength > $best_match) { $best_match = $strength; $include = !$package_path->getExcluded(); } } if ($best_match && $include) { if ($ignore_dominion) { $is_weak = false; } else { $is_weak = ($package->getDominion() == $weak_dominion); } $matches[$package_id] = array( 'strength' => $best_match, 'weak' => $is_weak, 'package' => $package, ); } } // At each strength level, drop weak packages if there are also strong // packages of the same strength. $strength_map = igroup($matches, 'strength'); foreach ($strength_map as $strength => $package_list) { $any_strong = false; foreach ($package_list as $package_id => $package) { if (!$package['weak']) { $any_strong = true; break; } } if ($any_strong) { foreach ($package_list as $package_id => $package) { if ($package['weak']) { unset($matches[$package_id]); } } } } $matches = isort($matches, 'strength'); $matches = array_reverse($matches); $strongest = null; foreach ($matches as $package_id => $match) { if ($strongest === null) { $strongest = $match['strength']; } if ($match['strength'] === $strongest) { continue; } if ($match['weak']) { unset($matches[$package_id]); } } return array_values(ipull($matches, 'package')); } } diff --git a/src/applications/packages/query/PhabricatorPackagesPackageQuery.php b/src/applications/packages/query/PhabricatorPackagesPackageQuery.php index 67cd3954c9..c97e3b244c 100644 --- a/src/applications/packages/query/PhabricatorPackagesPackageQuery.php +++ b/src/applications/packages/query/PhabricatorPackagesPackageQuery.php @@ -1,135 +1,131 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withPublisherPHIDs(array $phids) { $this->publisherPHIDs = $phids; return $this; } public function withPackageKeys(array $keys) { $this->packageKeys = $keys; return $this; } public function withFullKeys(array $keys) { $this->fullKeys = $keys; return $this; } public function withNameNgrams($ngrams) { return $this->withNgramsConstraint( new PhabricatorPackagesPackageNameNgrams(), $ngrams); } public function newResultObject() { return new PhabricatorPackagesPackage(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'p.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'p.phid IN (%Ls)', $this->phids); } if ($this->publisherPHIDs !== null) { $where[] = qsprintf( $conn, 'p.publisherPHID IN (%Ls)', $this->publisherPHIDs); } if ($this->packageKeys !== null) { $where[] = qsprintf( $conn, 'p.packageKey IN (%Ls)', $this->packageKeys); } if ($this->fullKeys !== null) { $parts = $this->buildFullKeyClauseParts($conn, $this->fullKeys); $where[] = qsprintf($conn, '%Q', $parts); } return $where; } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); $join_publisher = ($this->fullKeys !== null); if ($join_publisher) { $publisher_table = new PhabricatorPackagesPublisher(); $joins[] = qsprintf( $conn, 'JOIN %T u ON u.phid = p.publisherPHID', $publisher_table->getTableName()); } return $joins; } protected function willFilterPage(array $packages) { $publisher_phids = mpull($packages, 'getPublisherPHID'); $publishers = id(new PhabricatorPackagesPublisherQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($publisher_phids) ->execute(); $publishers = mpull($publishers, null, 'getPHID'); foreach ($packages as $key => $package) { $publisher = idx($publishers, $package->getPublisherPHID()); if (!$publisher) { unset($packages[$key]); $this->didRejectResult($package); continue; } $package->attachPublisher($publisher); } return $packages; } protected function getPrimaryTableAlias() { return 'p'; } } diff --git a/src/applications/packages/query/PhabricatorPackagesPublisherQuery.php b/src/applications/packages/query/PhabricatorPackagesPublisherQuery.php index d46535c20d..7d31e5fec6 100644 --- a/src/applications/packages/query/PhabricatorPackagesPublisherQuery.php +++ b/src/applications/packages/query/PhabricatorPackagesPublisherQuery.php @@ -1,70 +1,66 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withPublisherKeys(array $keys) { $this->publisherKeys = $keys; return $this; } public function withNameNgrams($ngrams) { return $this->withNgramsConstraint( new PhabricatorPackagesPublisherNameNgrams(), $ngrams); } public function newResultObject() { return new PhabricatorPackagesPublisher(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'u.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'u.phid IN (%Ls)', $this->phids); } if ($this->publisherKeys !== null) { $where[] = qsprintf( $conn, 'u.publisherKey IN (%Ls)', $this->publisherKeys); } return $where; } protected function getPrimaryTableAlias() { return 'u'; } } diff --git a/src/applications/packages/query/PhabricatorPackagesVersionQuery.php b/src/applications/packages/query/PhabricatorPackagesVersionQuery.php index 6f417e2f77..447907c4a0 100644 --- a/src/applications/packages/query/PhabricatorPackagesVersionQuery.php +++ b/src/applications/packages/query/PhabricatorPackagesVersionQuery.php @@ -1,147 +1,143 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withPackagePHIDs(array $phids) { $this->packagePHIDs = $phids; return $this; } public function withFullKeys(array $keys) { $this->fullKeys = $keys; return $this; } public function withNames(array $names) { $this->names = $names; return $this; } public function withNameNgrams($ngrams) { return $this->withNgramsConstraint( new PhabricatorPackagesVersionNameNgrams(), $ngrams); } public function newResultObject() { return new PhabricatorPackagesVersion(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'v.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'v.phid IN (%Ls)', $this->phids); } if ($this->packagePHIDs !== null) { $where[] = qsprintf( $conn, 'v.packagePHID IN (%Ls)', $this->packagePHIDs); } if ($this->names !== null) { $where[] = qsprintf( $conn, 'v.name IN (%Ls)', $this->names); } if ($this->fullKeys !== null) { $parts = $this->buildFullKeyClauseParts($conn, $this->fullKeys); $where[] = qsprintf($conn, '%Q', $parts); } return $where; } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); $join_publisher = ($this->fullKeys !== null); $join_package = ($this->fullKeys !== null) || $join_publisher; if ($join_package) { $package_table = new PhabricatorPackagesPackage(); $joins[] = qsprintf( $conn, 'JOIN %T p ON v.packagePHID = p.phid', $package_table->getTableName()); } if ($join_publisher) { $publisher_table = new PhabricatorPackagesPublisher(); $joins[] = qsprintf( $conn, 'JOIN %T u ON u.phid = p.publisherPHID', $publisher_table->getTableName()); } return $joins; } protected function willFilterPage(array $versions) { $package_phids = mpull($versions, 'getPackagePHID'); $packages = id(new PhabricatorPackagesPackageQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($package_phids) ->execute(); $packages = mpull($packages, null, 'getPHID'); foreach ($versions as $key => $version) { $package = idx($packages, $version->getPackagePHID()); if (!$package) { unset($versions[$key]); $this->didRejectResult($version); continue; } $version->attachPackage($package); } return $versions; } protected function getPrimaryTableAlias() { return 'v'; } } diff --git a/src/applications/passphrase/query/PassphraseCredentialQuery.php b/src/applications/passphrase/query/PassphraseCredentialQuery.php index 24f5f7440c..98ae9429a8 100644 --- a/src/applications/passphrase/query/PassphraseCredentialQuery.php +++ b/src/applications/passphrase/query/PassphraseCredentialQuery.php @@ -1,169 +1,165 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withCredentialTypes(array $credential_types) { $this->credentialTypes = $credential_types; return $this; } public function withProvidesTypes(array $provides_types) { $this->providesTypes = $provides_types; return $this; } public function withIsDestroyed($destroyed) { $this->isDestroyed = $destroyed; return $this; } public function withAllowConduit($allow_conduit) { $this->allowConduit = $allow_conduit; return $this; } public function withNameContains($name_contains) { $this->nameContains = $name_contains; return $this; } public function needSecrets($need_secrets) { $this->needSecrets = $need_secrets; return $this; } public function newResultObject() { return new PassphraseCredential(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $page) { if ($this->needSecrets) { $secret_ids = mpull($page, 'getSecretID'); $secret_ids = array_filter($secret_ids); $secrets = array(); if ($secret_ids) { $secret_objects = id(new PassphraseSecret())->loadAllWhere( 'id IN (%Ld)', $secret_ids); foreach ($secret_objects as $secret) { $secret_data = $secret->getSecretData(); $secrets[$secret->getID()] = new PhutilOpaqueEnvelope($secret_data); } } foreach ($page as $key => $credential) { $secret_id = $credential->getSecretID(); if (!$secret_id) { $credential->attachSecret(null); } else if (isset($secrets[$secret_id])) { $credential->attachSecret($secrets[$secret_id]); } else { unset($page[$key]); } } } foreach ($page as $key => $credential) { $type = PassphraseCredentialType::getTypeByConstant( $credential->getCredentialType()); if (!$type) { unset($page[$key]); continue; } $credential->attachImplementation(clone $type); } return $page; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'c.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'c.phid IN (%Ls)', $this->phids); } if ($this->credentialTypes !== null) { $where[] = qsprintf( $conn, 'c.credentialType in (%Ls)', $this->credentialTypes); } if ($this->providesTypes !== null) { $where[] = qsprintf( $conn, 'c.providesType IN (%Ls)', $this->providesTypes); } if ($this->isDestroyed !== null) { $where[] = qsprintf( $conn, 'c.isDestroyed = %d', (int)$this->isDestroyed); } if ($this->allowConduit !== null) { $where[] = qsprintf( $conn, 'c.allowConduit = %d', (int)$this->allowConduit); } if (phutil_nonempty_string($this->nameContains)) { $where[] = qsprintf( $conn, 'LOWER(c.name) LIKE %~', phutil_utf8_strtolower($this->nameContains)); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorPassphraseApplication'; } protected function getPrimaryTableAlias() { return 'c'; } } diff --git a/src/applications/paste/query/PhabricatorPasteQuery.php b/src/applications/paste/query/PhabricatorPasteQuery.php index d90ef3b1d2..e841bff300 100644 --- a/src/applications/paste/query/PhabricatorPasteQuery.php +++ b/src/applications/paste/query/PhabricatorPasteQuery.php @@ -1,400 +1,396 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAuthorPHIDs(array $phids) { $this->authorPHIDs = $phids; return $this; } public function withParentPHIDs(array $phids) { $this->parentPHIDs = $phids; return $this; } public function needContent($need_content) { $this->needContent = $need_content; return $this; } public function needRawContent($need_raw_content) { $this->needRawContent = $need_raw_content; return $this; } public function needSnippets($need_snippets) { $this->needSnippets = $need_snippets; return $this; } public function withLanguages(array $languages) { $this->includeNoLanguage = false; foreach ($languages as $key => $language) { if ($language === null) { $languages[$key] = ''; continue; } } $this->languages = $languages; return $this; } public function withDateCreatedBefore($date_created_before) { $this->dateCreatedBefore = $date_created_before; return $this; } public function withDateCreatedAfter($date_created_after) { $this->dateCreatedAfter = $date_created_after; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function newResultObject() { return new PhabricatorPaste(); } - protected function loadPage() { - return $this->loadStandardPage(new PhabricatorPaste()); - } - protected function didFilterPage(array $pastes) { if ($this->needRawContent) { $pastes = $this->loadRawContent($pastes); } if ($this->needContent) { $pastes = $this->loadContent($pastes); } if ($this->needSnippets) { $pastes = $this->loadSnippets($pastes); } return $pastes; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'paste.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'paste.phid IN (%Ls)', $this->phids); } if ($this->authorPHIDs !== null) { $where[] = qsprintf( $conn, 'paste.authorPHID IN (%Ls)', $this->authorPHIDs); } if ($this->parentPHIDs !== null) { $where[] = qsprintf( $conn, 'paste.parentPHID IN (%Ls)', $this->parentPHIDs); } if ($this->languages !== null) { $where[] = qsprintf( $conn, 'paste.language IN (%Ls)', $this->languages); } if ($this->dateCreatedAfter !== null) { $where[] = qsprintf( $conn, 'paste.dateCreated >= %d', $this->dateCreatedAfter); } if ($this->dateCreatedBefore !== null) { $where[] = qsprintf( $conn, 'paste.dateCreated <= %d', $this->dateCreatedBefore); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'paste.status IN (%Ls)', $this->statuses); } return $where; } protected function getPrimaryTableAlias() { return 'paste'; } private function getContentCacheKey(PhabricatorPaste $paste) { return implode( ':', array( 'P'.$paste->getID(), $paste->getFilePHID(), $paste->getLanguage(), PhabricatorHash::digestForIndex($paste->getTitle()), )); } private function getSnippetCacheKey(PhabricatorPaste $paste) { return implode( ':', array( 'P'.$paste->getID(), $paste->getFilePHID(), $paste->getLanguage(), 'snippet', 'v2.1', PhabricatorHash::digestForIndex($paste->getTitle()), )); } private function loadRawContent(array $pastes) { $file_phids = mpull($pastes, 'getFilePHID'); $files = id(new PhabricatorFileQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($file_phids) ->execute(); $files = mpull($files, null, 'getPHID'); foreach ($pastes as $key => $paste) { $file = idx($files, $paste->getFilePHID()); if (!$file) { unset($pastes[$key]); continue; } try { $paste->attachRawContent($file->loadFileData()); } catch (Exception $ex) { // We can hit various sorts of file storage issues here. Just drop the // paste if the file is dead. unset($pastes[$key]); continue; } } return $pastes; } private function loadContent(array $pastes) { $cache = new PhabricatorKeyValueDatabaseCache(); $cache = new PhutilKeyValueCacheProfiler($cache); $cache->setProfiler(PhutilServiceProfiler::getInstance()); $keys = array(); foreach ($pastes as $paste) { $keys[] = $this->getContentCacheKey($paste); } $caches = $cache->getKeys($keys); $need_raw = array(); $have_cache = array(); foreach ($pastes as $paste) { $key = $this->getContentCacheKey($paste); if (isset($caches[$key])) { $paste->attachContent(phutil_safe_html($caches[$key])); $have_cache[$paste->getPHID()] = true; } else { $need_raw[$key] = $paste; } } if (!$need_raw) { return $pastes; } $write_data = array(); $have_raw = $this->loadRawContent($need_raw); $have_raw = mpull($have_raw, null, 'getPHID'); foreach ($pastes as $key => $paste) { $paste_phid = $paste->getPHID(); if (isset($have_cache[$paste_phid])) { continue; } if (empty($have_raw[$paste_phid])) { unset($pastes[$key]); continue; } $content = $this->buildContent($paste); $paste->attachContent($content); $write_data[$this->getContentCacheKey($paste)] = (string)$content; } if ($write_data) { $cache->setKeys($write_data); } return $pastes; } private function loadSnippets(array $pastes) { $cache = new PhabricatorKeyValueDatabaseCache(); $cache = new PhutilKeyValueCacheProfiler($cache); $cache->setProfiler(PhutilServiceProfiler::getInstance()); $keys = array(); foreach ($pastes as $paste) { $keys[] = $this->getSnippetCacheKey($paste); } $caches = $cache->getKeys($keys); $need_raw = array(); $have_cache = array(); foreach ($pastes as $paste) { $key = $this->getSnippetCacheKey($paste); if (isset($caches[$key])) { $snippet_data = phutil_json_decode($caches[$key]); $snippet = new PhabricatorPasteSnippet( phutil_safe_html($snippet_data['content']), $snippet_data['type'], $snippet_data['contentLineCount']); $paste->attachSnippet($snippet); $have_cache[$paste->getPHID()] = true; } else { $need_raw[$key] = $paste; } } if (!$need_raw) { return $pastes; } $write_data = array(); $have_raw = $this->loadRawContent($need_raw); $have_raw = mpull($have_raw, null, 'getPHID'); foreach ($pastes as $key => $paste) { $paste_phid = $paste->getPHID(); if (isset($have_cache[$paste_phid])) { continue; } if (empty($have_raw[$paste_phid])) { unset($pastes[$key]); continue; } $snippet = $this->buildSnippet($paste); $paste->attachSnippet($snippet); $snippet_data = array( 'content' => (string)$snippet->getContent(), 'type' => (string)$snippet->getType(), 'contentLineCount' => $snippet->getContentLineCount(), ); $write_data[$this->getSnippetCacheKey($paste)] = phutil_json_encode( $snippet_data); } if ($write_data) { $cache->setKeys($write_data); } return $pastes; } private function buildContent(PhabricatorPaste $paste) { return $this->highlightSource( $paste->getRawContent(), $paste->getTitle(), $paste->getLanguage()); } private function buildSnippet(PhabricatorPaste $paste) { $snippet_type = PhabricatorPasteSnippet::FULL; $snippet = $paste->getRawContent(); $lines = phutil_split_lines($snippet); $line_count = count($lines); if (strlen($snippet) > 1024) { $snippet_type = PhabricatorPasteSnippet::FIRST_BYTES; $snippet = id(new PhutilUTF8StringTruncator()) ->setMaximumBytes(1024) ->setTerminator('') ->truncateString($snippet); } if ($line_count > 5) { $snippet_type = PhabricatorPasteSnippet::FIRST_LINES; $snippet = implode('', array_slice($lines, 0, 5)); } return new PhabricatorPasteSnippet( $this->highlightSource( $snippet, $paste->getTitle(), $paste->getLanguage()), $snippet_type, $line_count); } private function highlightSource($source, $title, $language) { if (empty($language)) { return PhabricatorSyntaxHighlighter::highlightWithFilename( $title, $source); } else { return PhabricatorSyntaxHighlighter::highlightWithLanguage( $language, $source); } } public function getQueryApplicationClass() { return 'PhabricatorPasteApplication'; } } diff --git a/src/applications/people/query/PhabricatorPeopleLogQuery.php b/src/applications/people/query/PhabricatorPeopleLogQuery.php index 203f79579a..e87fb33660 100644 --- a/src/applications/people/query/PhabricatorPeopleLogQuery.php +++ b/src/applications/people/query/PhabricatorPeopleLogQuery.php @@ -1,139 +1,135 @@ ids = $ids; return $this; } public function withActorPHIDs(array $actor_phids) { $this->actorPHIDs = $actor_phids; return $this; } public function withUserPHIDs(array $user_phids) { $this->userPHIDs = $user_phids; return $this; } public function withRelatedPHIDs(array $related_phids) { $this->relatedPHIDs = $related_phids; return $this; } public function withSessionKeys(array $session_keys) { $this->sessionKeys = $session_keys; return $this; } public function withActions(array $actions) { $this->actions = $actions; return $this; } public function withRemoteAddressPrefix($remote_address_prefix) { $this->remoteAddressPrefix = $remote_address_prefix; return $this; } public function withDateCreatedBetween($min, $max) { $this->dateCreatedMin = $min; $this->dateCreatedMax = $max; return $this; } public function newResultObject() { return new PhabricatorUserLog(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->actorPHIDs !== null) { $where[] = qsprintf( $conn, 'actorPHID IN (%Ls)', $this->actorPHIDs); } if ($this->userPHIDs !== null) { $where[] = qsprintf( $conn, 'userPHID IN (%Ls)', $this->userPHIDs); } if ($this->relatedPHIDs !== null) { $where[] = qsprintf( $conn, '(actorPHID IN (%Ls) OR userPHID IN (%Ls))', $this->relatedPHIDs, $this->relatedPHIDs); } if ($this->sessionKeys !== null) { $where[] = qsprintf( $conn, 'session IN (%Ls)', $this->sessionKeys); } if ($this->actions !== null) { $where[] = qsprintf( $conn, 'action IN (%Ls)', $this->actions); } if ($this->remoteAddressPrefix !== null) { $where[] = qsprintf( $conn, 'remoteAddr LIKE %>', $this->remoteAddressPrefix); } if ($this->dateCreatedMin !== null) { $where[] = qsprintf( $conn, 'dateCreated >= %d', $this->dateCreatedMin); } if ($this->dateCreatedMax !== null) { $where[] = qsprintf( $conn, 'dateCreated <= %d', $this->dateCreatedMax); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorPeopleApplication'; } } diff --git a/src/applications/people/query/PhabricatorPeopleQuery.php b/src/applications/people/query/PhabricatorPeopleQuery.php index e122be0b2e..b74b936ba8 100644 --- a/src/applications/people/query/PhabricatorPeopleQuery.php +++ b/src/applications/people/query/PhabricatorPeopleQuery.php @@ -1,634 +1,630 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withEmails(array $emails) { $this->emails = $emails; return $this; } public function withRealnames(array $realnames) { $this->realnames = $realnames; return $this; } public function withUsernames(array $usernames) { $this->usernames = $usernames; return $this; } public function withDateCreatedBefore($date_created_before) { $this->dateCreatedBefore = $date_created_before; return $this; } public function withDateCreatedAfter($date_created_after) { $this->dateCreatedAfter = $date_created_after; return $this; } public function withIsAdmin($admin) { $this->isAdmin = $admin; return $this; } public function withIsSystemAgent($system_agent) { $this->isSystemAgent = $system_agent; return $this; } public function withIsMailingList($mailing_list) { $this->isMailingList = $mailing_list; return $this; } public function withIsDisabled($disabled) { $this->isDisabled = $disabled; return $this; } public function withIsApproved($approved) { $this->isApproved = $approved; return $this; } public function withNameLike($like) { $this->nameLike = $like; return $this; } public function withNameTokens(array $tokens) { $this->nameTokens = array_values($tokens); return $this; } public function withNamePrefixes(array $prefixes) { $this->namePrefixes = $prefixes; return $this; } public function withIsEnrolledInMultiFactor($enrolled) { $this->isEnrolledInMultiFactor = $enrolled; return $this; } public function needPrimaryEmail($need) { $this->needPrimaryEmail = $need; return $this; } public function needProfile($need) { $this->needProfile = $need; return $this; } public function needProfileImage($need) { $cache_key = PhabricatorUserProfileImageCacheType::KEY_URI; if ($need) { $this->cacheKeys[$cache_key] = true; } else { unset($this->cacheKeys[$cache_key]); } return $this; } public function needAvailability($need) { $this->needAvailability = $need; return $this; } public function needUserSettings($need) { $cache_key = PhabricatorUserPreferencesCacheType::KEY_PREFERENCES; if ($need) { $this->cacheKeys[$cache_key] = true; } else { unset($this->cacheKeys[$cache_key]); } return $this; } public function needBadgeAwards($need) { $cache_key = PhabricatorUserBadgesCacheType::KEY_BADGES; if ($need) { $this->cacheKeys[$cache_key] = true; } else { unset($this->cacheKeys[$cache_key]); } return $this; } public function newResultObject() { return new PhabricatorUser(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function didFilterPage(array $users) { if ($this->needProfile) { $user_list = mpull($users, null, 'getPHID'); $profiles = new PhabricatorUserProfile(); $profiles = $profiles->loadAllWhere( 'userPHID IN (%Ls)', array_keys($user_list)); $profiles = mpull($profiles, null, 'getUserPHID'); foreach ($user_list as $user_phid => $user) { $profile = idx($profiles, $user_phid); if (!$profile) { $profile = PhabricatorUserProfile::initializeNewProfile($user); } $user->attachUserProfile($profile); } } if ($this->needAvailability) { $rebuild = array(); foreach ($users as $user) { $cache = $user->getAvailabilityCache(); if ($cache !== null) { $user->attachAvailability($cache); } else { $rebuild[] = $user; } } if ($rebuild) { $this->rebuildAvailabilityCache($rebuild); } } $this->fillUserCaches($users); return $users; } protected function shouldGroupQueryResultRows() { if ($this->nameTokens) { return true; } return parent::shouldGroupQueryResultRows(); } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); if ($this->emails) { $email_table = new PhabricatorUserEmail(); $joins[] = qsprintf( $conn, 'JOIN %T email ON email.userPHID = user.PHID', $email_table->getTableName()); } if ($this->nameTokens) { foreach ($this->nameTokens as $key => $token) { $token_table = 'token_'.$key; $joins[] = qsprintf( $conn, 'JOIN %T %T ON %T.userID = user.id AND %T.token LIKE %>', PhabricatorUser::NAMETOKEN_TABLE, $token_table, $token_table, $token_table, $token); } } return $joins; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->usernames !== null) { $where[] = qsprintf( $conn, 'user.userName IN (%Ls)', $this->usernames); } if ($this->namePrefixes) { $parts = array(); foreach ($this->namePrefixes as $name_prefix) { $parts[] = qsprintf( $conn, 'user.username LIKE %>', $name_prefix); } $where[] = qsprintf($conn, '%LO', $parts); } if ($this->emails !== null) { $where[] = qsprintf( $conn, 'email.address IN (%Ls)', $this->emails); } if ($this->realnames !== null) { $where[] = qsprintf( $conn, 'user.realName IN (%Ls)', $this->realnames); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'user.phid IN (%Ls)', $this->phids); } if ($this->ids !== null) { $where[] = qsprintf( $conn, 'user.id IN (%Ld)', $this->ids); } if ($this->dateCreatedAfter) { $where[] = qsprintf( $conn, 'user.dateCreated >= %d', $this->dateCreatedAfter); } if ($this->dateCreatedBefore) { $where[] = qsprintf( $conn, 'user.dateCreated <= %d', $this->dateCreatedBefore); } if ($this->isAdmin !== null) { $where[] = qsprintf( $conn, 'user.isAdmin = %d', (int)$this->isAdmin); } if ($this->isDisabled !== null) { $where[] = qsprintf( $conn, 'user.isDisabled = %d', (int)$this->isDisabled); } if ($this->isApproved !== null) { $where[] = qsprintf( $conn, 'user.isApproved = %d', (int)$this->isApproved); } if ($this->isSystemAgent !== null) { $where[] = qsprintf( $conn, 'user.isSystemAgent = %d', (int)$this->isSystemAgent); } if ($this->isMailingList !== null) { $where[] = qsprintf( $conn, 'user.isMailingList = %d', (int)$this->isMailingList); } if ($this->nameLike !== null) { $where[] = qsprintf( $conn, 'user.username LIKE %~ OR user.realname LIKE %~', $this->nameLike, $this->nameLike); } if ($this->isEnrolledInMultiFactor !== null) { $where[] = qsprintf( $conn, 'user.isEnrolledInMultiFactor = %d', (int)$this->isEnrolledInMultiFactor); } return $where; } protected function getPrimaryTableAlias() { return 'user'; } public function getQueryApplicationClass() { return 'PhabricatorPeopleApplication'; } public function getOrderableColumns() { return parent::getOrderableColumns() + array( 'username' => array( 'table' => 'user', 'column' => 'username', 'type' => 'string', 'reverse' => true, 'unique' => true, ), ); } protected function newPagingMapFromPartialObject($object) { return array( 'id' => (int)$object->getID(), 'username' => $object->getUsername(), ); } private function rebuildAvailabilityCache(array $rebuild) { $rebuild = mpull($rebuild, null, 'getPHID'); // Limit the window we look at because far-future events are largely // irrelevant and this makes the cache cheaper to build and allows it to // self-heal over time. $min_range = PhabricatorTime::getNow(); $max_range = $min_range + phutil_units('72 hours in seconds'); // NOTE: We don't need to generate ghosts here, because we only care if // the user is attending, and you can't attend a ghost event: RSVP'ing // to it creates a real event. $events = id(new PhabricatorCalendarEventQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withInvitedPHIDs(array_keys($rebuild)) ->withIsCancelled(false) ->withDateRange($min_range, $max_range) ->execute(); // Group all the events by invited user. Only examine events that users // are actually attending. $map = array(); $invitee_map = array(); foreach ($events as $event) { foreach ($event->getInvitees() as $invitee) { if (!$invitee->isAttending()) { continue; } // If the user is set to "Available" for this event, don't consider it // when computing their away status. if (!$invitee->getDisplayAvailability($event)) { continue; } $invitee_phid = $invitee->getInviteePHID(); if (!isset($rebuild[$invitee_phid])) { continue; } $map[$invitee_phid][] = $event; $event_phid = $event->getPHID(); $invitee_map[$invitee_phid][$event_phid] = $invitee; } } // We need to load these users' timezone settings to figure out their // availability if they're attending all-day events. $this->needUserSettings(true); $this->fillUserCaches($rebuild); foreach ($rebuild as $phid => $user) { $events = idx($map, $phid, array()); // We loaded events with the omnipotent user, but want to shift them // into the user's timezone before building the cache because they will // be unavailable during their own local day. foreach ($events as $event) { $event->applyViewerTimezone($user); } $cursor = $min_range; $next_event = null; if ($events) { // Find the next time when the user has no meetings. If we move forward // because of an event, we check again for events after that one ends. while (true) { foreach ($events as $event) { $from = $event->getStartDateTimeEpochForCache(); $to = $event->getEndDateTimeEpochForCache(); if (($from <= $cursor) && ($to > $cursor)) { $cursor = $to; if (!$next_event) { $next_event = $event; } continue 2; } } break; } } if ($cursor > $min_range) { $invitee = $invitee_map[$phid][$next_event->getPHID()]; $availability_type = $invitee->getDisplayAvailability($next_event); $availability = array( 'until' => $cursor, 'eventPHID' => $next_event->getPHID(), 'availability' => $availability_type, ); // We only cache this availability until the end of the current event, // since the event PHID (and possibly the availability type) are only // valid for that long. // NOTE: This doesn't handle overlapping events with the greatest // possible care. In theory, if you're attending multiple events // simultaneously we should accommodate that. However, it's complex // to compute, rare, and probably not confusing most of the time. $availability_ttl = $next_event->getEndDateTimeEpochForCache(); } else { $availability = array( 'until' => null, 'eventPHID' => null, 'availability' => null, ); // Cache that the user is available until the next event they are // invited to starts. $availability_ttl = $max_range; foreach ($events as $event) { $from = $event->getStartDateTimeEpochForCache(); if ($from > $cursor) { $availability_ttl = min($from, $availability_ttl); } } } // Never TTL the cache to longer than the maximum range we examined. $availability_ttl = min($availability_ttl, $max_range); $user->writeAvailabilityCache($availability, $availability_ttl); $user->attachAvailability($availability); } } private function fillUserCaches(array $users) { if (!$this->cacheKeys) { return; } $user_map = mpull($users, null, 'getPHID'); $keys = array_keys($this->cacheKeys); $hashes = array(); foreach ($keys as $key) { $hashes[] = PhabricatorHash::digestForIndex($key); } $types = PhabricatorUserCacheType::getAllCacheTypes(); // First, pull any available caches. If we wanted to be particularly clever // we could do this with JOINs in the main query. $cache_table = new PhabricatorUserCache(); $cache_conn = $cache_table->establishConnection('r'); $cache_data = queryfx_all( $cache_conn, 'SELECT cacheKey, userPHID, cacheData, cacheType FROM %T WHERE cacheIndex IN (%Ls) AND userPHID IN (%Ls)', $cache_table->getTableName(), $hashes, array_keys($user_map)); $skip_validation = array(); // After we read caches from the database, discard any which have data that // invalid or out of date. This allows cache types to implement TTLs or // versions instead of or in addition to explicit cache clears. foreach ($cache_data as $row_key => $row) { $cache_type = $row['cacheType']; if (isset($skip_validation[$cache_type])) { continue; } if (empty($types[$cache_type])) { unset($cache_data[$row_key]); continue; } $type = $types[$cache_type]; if (!$type->shouldValidateRawCacheData()) { $skip_validation[$cache_type] = true; continue; } $user = $user_map[$row['userPHID']]; $raw_data = $row['cacheData']; if (!$type->isRawCacheDataValid($user, $row['cacheKey'], $raw_data)) { unset($cache_data[$row_key]); continue; } } $need = array(); $cache_data = igroup($cache_data, 'userPHID'); foreach ($user_map as $user_phid => $user) { $raw_rows = idx($cache_data, $user_phid, array()); $raw_data = ipull($raw_rows, 'cacheData', 'cacheKey'); foreach ($keys as $key) { if (isset($raw_data[$key]) || array_key_exists($key, $raw_data)) { continue; } $need[$key][$user_phid] = $user; } $user->attachRawCacheData($raw_data); } // If we missed any cache values, bulk-construct them now. This is // usually much cheaper than generating them on-demand for each user // record. if (!$need) { return; } $writes = array(); foreach ($need as $cache_key => $need_users) { $type = PhabricatorUserCacheType::getCacheTypeForKey($cache_key); if (!$type) { continue; } $data = $type->newValueForUsers($cache_key, $need_users); foreach ($data as $user_phid => $raw_value) { $data[$user_phid] = $raw_value; $writes[] = array( 'userPHID' => $user_phid, 'key' => $cache_key, 'type' => $type, 'value' => $raw_value, ); } foreach ($need_users as $user_phid => $user) { if (isset($data[$user_phid]) || array_key_exists($user_phid, $data)) { $user->attachRawCacheData( array( $cache_key => $data[$user_phid], )); } } } PhabricatorUserCache::writeCaches($writes); } } diff --git a/src/applications/people/query/PhabricatorPeopleUserEmailQuery.php b/src/applications/people/query/PhabricatorPeopleUserEmailQuery.php index 6e2627a96d..ead44a56dc 100644 --- a/src/applications/people/query/PhabricatorPeopleUserEmailQuery.php +++ b/src/applications/people/query/PhabricatorPeopleUserEmailQuery.php @@ -1,81 +1,77 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function newResultObject() { return new PhabricatorUserEmail(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function getPrimaryTableAlias() { return 'email'; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'email.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'email.phid IN (%Ls)', $this->phids); } return $where; } protected function willLoadPage(array $page) { $user_phids = mpull($page, 'getUserPHID'); $users = id(new PhabricatorPeopleQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($user_phids) ->execute(); $users = mpull($users, null, 'getPHID'); foreach ($page as $key => $address) { $user = idx($users, $address->getUserPHID()); if (!$user) { unset($page[$key]); $this->didRejectResult($address); continue; } $address->attachUser($user); } return $page; } public function getQueryApplicationClass() { return 'PhabricatorPeopleApplication'; } } diff --git a/src/applications/phame/query/PhameBlogQuery.php b/src/applications/phame/query/PhameBlogQuery.php index b4018c78eb..b730f3c4aa 100644 --- a/src/applications/phame/query/PhameBlogQuery.php +++ b/src/applications/phame/query/PhameBlogQuery.php @@ -1,150 +1,146 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withDomain($domain) { $this->domain = $domain; return $this; } public function withStatuses(array $status) { $this->statuses = $status; return $this; } public function needProfileImage($need) { $this->needProfileImage = $need; return $this; } public function needHeaderImage($need) { $this->needHeaderImage = $need; return $this; } public function newResultObject() { return new PhameBlog(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'b.status IN (%Ls)', $this->statuses); } if ($this->ids !== null) { $where[] = qsprintf( $conn, 'b.id IN (%Ls)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'b.phid IN (%Ls)', $this->phids); } if ($this->domain !== null) { $where[] = qsprintf( $conn, 'b.domain = %s', $this->domain); } return $where; } protected function didFilterPage(array $blogs) { if ($this->needProfileImage) { $default = null; $file_phids = mpull($blogs, 'getProfileImagePHID'); $file_phids = array_filter($file_phids); if ($file_phids) { $files = id(new PhabricatorFileQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($file_phids) ->execute(); $files = mpull($files, null, 'getPHID'); } else { $files = array(); } foreach ($blogs as $blog) { $file = idx($files, $blog->getProfileImagePHID()); if (!$file) { if (!$default) { $default = PhabricatorFile::loadBuiltin( $this->getViewer(), 'blog.png'); } $file = $default; } $blog->attachProfileImageFile($file); } } if ($this->needHeaderImage) { $file_phids = mpull($blogs, 'getHeaderImagePHID'); $file_phids = array_filter($file_phids); if ($file_phids) { $files = id(new PhabricatorFileQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($file_phids) ->execute(); $files = mpull($files, null, 'getPHID'); } else { $files = array(); } foreach ($blogs as $blog) { $file = idx($files, $blog->getHeaderImagePHID()); if ($file) { $blog->attachHeaderImageFile($file); } } } return $blogs; } public function getQueryApplicationClass() { // TODO: Can we set this without breaking public blogs? return null; } protected function getPrimaryTableAlias() { return 'b'; } } diff --git a/src/applications/phame/query/PhamePostQuery.php b/src/applications/phame/query/PhamePostQuery.php index d7396e553f..61ee4f2a25 100644 --- a/src/applications/phame/query/PhamePostQuery.php +++ b/src/applications/phame/query/PhamePostQuery.php @@ -1,190 +1,186 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withBloggerPHIDs(array $blogger_phids) { $this->bloggerPHIDs = $blogger_phids; return $this; } public function withBlogPHIDs(array $blog_phids) { $this->blogPHIDs = $blog_phids; return $this; } public function withVisibility(array $visibility) { $this->visibility = $visibility; return $this; } public function withPublishedAfter($time) { $this->publishedAfter = $time; return $this; } public function needHeaderImage($need) { $this->needHeaderImage = $need; return $this; } public function newResultObject() { return new PhamePost(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $posts) { // We require blogs to do visibility checks, so load them unconditionally. $blog_phids = mpull($posts, 'getBlogPHID'); $blogs = id(new PhameBlogQuery()) ->setViewer($this->getViewer()) ->needProfileImage(true) ->withPHIDs($blog_phids) ->execute(); $blogs = mpull($blogs, null, 'getPHID'); foreach ($posts as $key => $post) { $blog_phid = $post->getBlogPHID(); $blog = idx($blogs, $blog_phid); if (!$blog) { $this->didRejectResult($post); unset($posts[$key]); continue; } $post->attachBlog($blog); } if ($this->needHeaderImage) { $file_phids = mpull($posts, 'getHeaderImagePHID'); $file_phids = array_filter($file_phids); if ($file_phids) { $files = id(new PhabricatorFileQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($file_phids) ->execute(); $files = mpull($files, null, 'getPHID'); } else { $files = array(); } foreach ($posts as $post) { $file = idx($files, $post->getHeaderImagePHID()); if ($file) { $post->attachHeaderImageFile($file); } } } return $posts; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'p.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'p.phid IN (%Ls)', $this->phids); } if ($this->bloggerPHIDs !== null) { $where[] = qsprintf( $conn, 'p.bloggerPHID IN (%Ls)', $this->bloggerPHIDs); } if ($this->visibility !== null) { $where[] = qsprintf( $conn, 'p.visibility IN (%Ld)', $this->visibility); } if ($this->publishedAfter !== null) { $where[] = qsprintf( $conn, 'p.datePublished > %d', $this->publishedAfter); } if ($this->blogPHIDs !== null) { $where[] = qsprintf( $conn, 'p.blogPHID in (%Ls)', $this->blogPHIDs); } return $where; } public function getBuiltinOrders() { return array( 'datePublished' => array( 'vector' => array('datePublished', 'id'), 'name' => pht('Publish Date'), ), ) + parent::getBuiltinOrders(); } public function getOrderableColumns() { return parent::getOrderableColumns() + array( 'datePublished' => array( 'table' => $this->getPrimaryTableAlias(), 'column' => 'datePublished', 'type' => 'int', 'reverse' => false, ), ); } protected function newPagingMapFromPartialObject($object) { return array( 'id' => (int)$object->getID(), 'datePublished' => (int)$object->getDatePublished(), ); } public function getQueryApplicationClass() { // TODO: Does setting this break public blogs? return null; } protected function getPrimaryTableAlias() { return 'p'; } } diff --git a/src/applications/pholio/query/PholioImageQuery.php b/src/applications/pholio/query/PholioImageQuery.php index 0d64540f91..69d890ab98 100644 --- a/src/applications/pholio/query/PholioImageQuery.php +++ b/src/applications/pholio/query/PholioImageQuery.php @@ -1,162 +1,158 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withMocks(array $mocks) { assert_instances_of($mocks, 'PholioMock'); $mocks = mpull($mocks, null, 'getPHID'); $this->mocks = $mocks; $this->mockPHIDs = array_keys($mocks); return $this; } public function withMockPHIDs(array $mock_phids) { $this->mockPHIDs = $mock_phids; return $this; } public function needInlineComments($need_inline_comments) { $this->needInlineComments = $need_inline_comments; return $this; } public function newResultObject() { return new PholioImage(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->mockPHIDs !== null) { $where[] = qsprintf( $conn, 'mockPHID IN (%Ls)', $this->mockPHIDs); } return $where; } protected function willFilterPage(array $images) { assert_instances_of($images, 'PholioImage'); $mock_phids = array(); foreach ($images as $image) { if (!$image->hasMock()) { continue; } $mock_phids[] = $image->getMockPHID(); } if ($mock_phids) { if ($this->mocks) { $mocks = $this->mocks; } else { $mocks = id(new PholioMockQuery()) ->setViewer($this->getViewer()) ->withPHIDs($mock_phids) ->execute(); } $mocks = mpull($mocks, null, 'getPHID'); foreach ($images as $key => $image) { if (!$image->hasMock()) { continue; } $mock = idx($mocks, $image->getMockPHID()); if (!$mock) { unset($images[$key]); $this->didRejectResult($image); continue; } $image->attachMock($mock); } } return $images; } protected function didFilterPage(array $images) { assert_instances_of($images, 'PholioImage'); $file_phids = mpull($images, 'getFilePHID'); $all_files = id(new PhabricatorFileQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($file_phids) ->execute(); $all_files = mpull($all_files, null, 'getPHID'); if ($this->needInlineComments) { // Only load inline comments the viewer has permission to see. $all_inline_comments = id(new PholioTransactionComment())->loadAllWhere( 'imageID IN (%Ld) AND (transactionPHID IS NOT NULL OR authorPHID = %s)', mpull($images, 'getID'), $this->getViewer()->getPHID()); $all_inline_comments = mgroup($all_inline_comments, 'getImageID'); } foreach ($images as $image) { $file = idx($all_files, $image->getFilePHID()); if (!$file) { $file = PhabricatorFile::loadBuiltin($this->getViewer(), 'missing.png'); } $image->attachFile($file); if ($this->needInlineComments) { $inlines = idx($all_inline_comments, $image->getID(), array()); $image->attachInlineComments($inlines); } } return $images; } public function getQueryApplicationClass() { return 'PhabricatorPholioApplication'; } } diff --git a/src/applications/phortune/query/PhortuneAccountEmailQuery.php b/src/applications/phortune/query/PhortuneAccountEmailQuery.php index 0bdfdb78dc..0e0a668b8c 100644 --- a/src/applications/phortune/query/PhortuneAccountEmailQuery.php +++ b/src/applications/phortune/query/PhortuneAccountEmailQuery.php @@ -1,117 +1,113 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAccountPHIDs(array $phids) { $this->accountPHIDs = $phids; return $this; } public function withAddressKeys(array $keys) { $this->addressKeys = $keys; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function newResultObject() { return new PhortuneAccountEmail(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $addresses) { $accounts = id(new PhortuneAccountQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs(mpull($addresses, 'getAccountPHID')) ->execute(); $accounts = mpull($accounts, null, 'getPHID'); foreach ($addresses as $key => $address) { $account = idx($accounts, $address->getAccountPHID()); if (!$account) { $this->didRejectResult($addresses[$key]); unset($addresses[$key]); continue; } $address->attachAccount($account); } return $addresses; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'address.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'address.phid IN (%Ls)', $this->phids); } if ($this->accountPHIDs !== null) { $where[] = qsprintf( $conn, 'address.accountPHID IN (%Ls)', $this->accountPHIDs); } if ($this->addressKeys !== null) { $where[] = qsprintf( $conn, 'address.addressKey IN (%Ls)', $this->addressKeys); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'address.status IN (%Ls)', $this->statuses); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorPhortuneApplication'; } protected function getPrimaryTableAlias() { return 'address'; } } diff --git a/src/applications/phortune/query/PhortuneAccountQuery.php b/src/applications/phortune/query/PhortuneAccountQuery.php index 70c12d9722..43c7c5f976 100644 --- a/src/applications/phortune/query/PhortuneAccountQuery.php +++ b/src/applications/phortune/query/PhortuneAccountQuery.php @@ -1,138 +1,134 @@ setViewer($user) ->withMemberPHIDs(array($user->getPHID())) ->execute(); if (!$accounts) { $accounts = array( PhortuneAccount::createNewAccount($user, $content_source), ); } $accounts = mpull($accounts, null, 'getPHID'); return $accounts; } public function withIDs(array $ids) { $this->ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withMemberPHIDs(array $phids) { $this->memberPHIDs = $phids; return $this; } public function newResultObject() { return new PhortuneAccount(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $accounts) { $query = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(mpull($accounts, 'getPHID')) ->withEdgeTypes( array( PhortuneAccountHasMemberEdgeType::EDGECONST, PhortuneAccountHasMerchantEdgeType::EDGECONST, )); $query->execute(); foreach ($accounts as $account) { $member_phids = $query->getDestinationPHIDs( array( $account->getPHID(), ), array( PhortuneAccountHasMemberEdgeType::EDGECONST, )); $member_phids = array_reverse($member_phids); $account->attachMemberPHIDs($member_phids); $merchant_phids = $query->getDestinationPHIDs( array( $account->getPHID(), ), array( PhortuneAccountHasMerchantEdgeType::EDGECONST, )); $merchant_phids = array_reverse($merchant_phids); $account->attachMerchantPHIDs($merchant_phids); } return $accounts; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'a.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'a.phid IN (%Ls)', $this->phids); } if ($this->memberPHIDs !== null) { $where[] = qsprintf( $conn, 'm.dst IN (%Ls)', $this->memberPHIDs); } return $where; } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); if ($this->memberPHIDs !== null) { $joins[] = qsprintf( $conn, 'LEFT JOIN %T m ON a.phid = m.src AND m.type = %d', PhabricatorEdgeConfig::TABLE_NAME_EDGE, PhortuneAccountHasMemberEdgeType::EDGECONST); } return $joins; } public function getQueryApplicationClass() { return 'PhabricatorPhortuneApplication'; } protected function getPrimaryTableAlias() { return 'a'; } } diff --git a/src/applications/phortune/query/PhortuneMerchantQuery.php b/src/applications/phortune/query/PhortuneMerchantQuery.php index aef7d8aaf1..2c9aefc74d 100644 --- a/src/applications/phortune/query/PhortuneMerchantQuery.php +++ b/src/applications/phortune/query/PhortuneMerchantQuery.php @@ -1,193 +1,189 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withMemberPHIDs(array $member_phids) { $this->memberPHIDs = $member_phids; return $this; } public function needProfileImage($need) { $this->needProfileImage = $need; return $this; } public function newResultObject() { return new PhortuneMerchant(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $merchants) { $query = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(mpull($merchants, 'getPHID')) ->withEdgeTypes(array(PhortuneMerchantHasMemberEdgeType::EDGECONST)); $query->execute(); foreach ($merchants as $merchant) { $member_phids = $query->getDestinationPHIDs(array($merchant->getPHID())); $member_phids = array_reverse($member_phids); $merchant->attachMemberPHIDs($member_phids); } if ($this->needProfileImage) { $default = null; $file_phids = mpull($merchants, 'getProfileImagePHID'); $file_phids = array_filter($file_phids); if ($file_phids) { $files = id(new PhabricatorFileQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($file_phids) ->execute(); $files = mpull($files, null, 'getPHID'); } else { $files = array(); } foreach ($merchants as $merchant) { $file = idx($files, $merchant->getProfileImagePHID()); if (!$file) { if (!$default) { $default = PhabricatorFile::loadBuiltin( $this->getViewer(), 'merchant.png'); } $file = $default; } $merchant->attachProfileImageFile($file); } } return $merchants; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'merchant.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'merchant.phid IN (%Ls)', $this->phids); } if ($this->memberPHIDs !== null) { $where[] = qsprintf( $conn, 'e.dst IN (%Ls)', $this->memberPHIDs); } return $where; } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); if ($this->memberPHIDs !== null) { $joins[] = qsprintf( $conn, 'LEFT JOIN %T e ON merchant.phid = e.src AND e.type = %d', PhabricatorEdgeConfig::TABLE_NAME_EDGE, PhortuneMerchantHasMemberEdgeType::EDGECONST); } return $joins; } public function getQueryApplicationClass() { return 'PhabricatorPhortuneApplication'; } protected function getPrimaryTableAlias() { return 'merchant'; } public static function canViewersEditMerchants( array $viewer_phids, array $merchant_phids) { // See T13366 for some discussion. This is an unusual caching construct to // make policy filtering of Accounts easier. foreach ($viewer_phids as $key => $viewer_phid) { if (!$viewer_phid) { unset($viewer_phids[$key]); } } if (!$viewer_phids) { return array(); } $cache_key = 'phortune.merchant.can-edit'; $cache = PhabricatorCaches::getRequestCache(); $cache_data = $cache->getKey($cache_key); if (!$cache_data) { $cache_data = array(); } $load_phids = array(); foreach ($viewer_phids as $viewer_phid) { if (!isset($cache_data[$viewer_phid])) { $load_phids[] = $viewer_phid; } } $did_write = false; foreach ($load_phids as $load_phid) { $merchants = id(new self()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withMemberPHIDs(array($load_phid)) ->execute(); foreach ($merchants as $merchant) { $cache_data[$load_phid][$merchant->getPHID()] = true; $did_write = true; } } if ($did_write) { $cache->setKey($cache_key, $cache_data); } $results = array(); foreach ($viewer_phids as $viewer_phid) { foreach ($merchant_phids as $merchant_phid) { if (!isset($cache_data[$viewer_phid][$merchant_phid])) { continue; } $results[$viewer_phid][$merchant_phid] = true; } } return $results; } } diff --git a/src/applications/phortune/query/PhortunePaymentMethodQuery.php b/src/applications/phortune/query/PhortunePaymentMethodQuery.php index 013fa147ec..b95881d3a7 100644 --- a/src/applications/phortune/query/PhortunePaymentMethodQuery.php +++ b/src/applications/phortune/query/PhortunePaymentMethodQuery.php @@ -1,150 +1,146 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAccountPHIDs(array $phids) { $this->accountPHIDs = $phids; return $this; } public function withMerchantPHIDs(array $phids) { $this->merchantPHIDs = $phids; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function newResultObject() { return new PhortunePaymentMethod(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $methods) { $accounts = id(new PhortuneAccountQuery()) ->setViewer($this->getViewer()) ->withPHIDs(mpull($methods, 'getAccountPHID')) ->execute(); $accounts = mpull($accounts, null, 'getPHID'); foreach ($methods as $key => $method) { $account = idx($accounts, $method->getAccountPHID()); if (!$account) { unset($methods[$key]); $this->didRejectResult($method); continue; } $method->attachAccount($account); } if (!$methods) { return $methods; } $merchants = id(new PhortuneMerchantQuery()) ->setViewer($this->getViewer()) ->withPHIDs(mpull($methods, 'getMerchantPHID')) ->execute(); $merchants = mpull($merchants, null, 'getPHID'); foreach ($methods as $key => $method) { $merchant = idx($merchants, $method->getMerchantPHID()); if (!$merchant) { unset($methods[$key]); $this->didRejectResult($method); continue; } $method->attachMerchant($merchant); } if (!$methods) { return $methods; } $provider_configs = id(new PhortunePaymentProviderConfigQuery()) ->setViewer($this->getViewer()) ->withPHIDs(mpull($methods, 'getProviderPHID')) ->execute(); $provider_configs = mpull($provider_configs, null, 'getPHID'); foreach ($methods as $key => $method) { $provider_config = idx($provider_configs, $method->getProviderPHID()); if (!$provider_config) { unset($methods[$key]); $this->didRejectResult($method); continue; } $method->attachProviderConfig($provider_config); } return $methods; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->accountPHIDs !== null) { $where[] = qsprintf( $conn, 'accountPHID IN (%Ls)', $this->accountPHIDs); } if ($this->merchantPHIDs !== null) { $where[] = qsprintf( $conn, 'merchantPHID IN (%Ls)', $this->merchantPHIDs); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'status IN (%Ls)', $this->statuses); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorPhortuneApplication'; } } diff --git a/src/applications/phortune/query/PhortuneSubscriptionQuery.php b/src/applications/phortune/query/PhortuneSubscriptionQuery.php index de0fee1980..5622578738 100644 --- a/src/applications/phortune/query/PhortuneSubscriptionQuery.php +++ b/src/applications/phortune/query/PhortuneSubscriptionQuery.php @@ -1,204 +1,200 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAccountPHIDs(array $account_phids) { $this->accountPHIDs = $account_phids; return $this; } public function withMerchantPHIDs(array $merchant_phids) { $this->merchantPHIDs = $merchant_phids; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function withPaymentMethodPHIDs(array $method_phids) { $this->paymentMethodPHIDs = $method_phids; return $this; } public function needTriggers($need_triggers) { $this->needTriggers = $need_triggers; return $this; } public function newResultObject() { return new PhortuneSubscription(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $subscriptions) { $accounts = id(new PhortuneAccountQuery()) ->setViewer($this->getViewer()) ->withPHIDs(mpull($subscriptions, 'getAccountPHID')) ->execute(); $accounts = mpull($accounts, null, 'getPHID'); foreach ($subscriptions as $key => $subscription) { $account = idx($accounts, $subscription->getAccountPHID()); if (!$account) { unset($subscriptions[$key]); $this->didRejectResult($subscription); continue; } $subscription->attachAccount($account); } if (!$subscriptions) { return $subscriptions; } $merchants = id(new PhortuneMerchantQuery()) ->setViewer($this->getViewer()) ->withPHIDs(mpull($subscriptions, 'getMerchantPHID')) ->execute(); $merchants = mpull($merchants, null, 'getPHID'); foreach ($subscriptions as $key => $subscription) { $merchant = idx($merchants, $subscription->getMerchantPHID()); if (!$merchant) { unset($subscriptions[$key]); $this->didRejectResult($subscription); continue; } $subscription->attachMerchant($merchant); } if (!$subscriptions) { return $subscriptions; } $implementations = array(); $subscription_map = mgroup($subscriptions, 'getSubscriptionClass'); foreach ($subscription_map as $class => $class_subscriptions) { $sub = newv($class, array()); $impl_objects = $sub->loadImplementationsForRefs( $this->getViewer(), mpull($class_subscriptions, 'getSubscriptionRef')); $implementations += mpull($impl_objects, null, 'getRef'); } foreach ($subscriptions as $key => $subscription) { $ref = $subscription->getSubscriptionRef(); $implementation = idx($implementations, $ref); if (!$implementation) { unset($subscriptions[$key]); $this->didRejectResult($subscription); continue; } $subscription->attachImplementation($implementation); } if (!$subscriptions) { return $subscriptions; } if ($this->needTriggers) { $trigger_phids = mpull($subscriptions, 'getTriggerPHID'); $triggers = id(new PhabricatorWorkerTriggerQuery()) ->setViewer($this->getViewer()) ->withPHIDs($trigger_phids) ->needEvents(true) ->execute(); $triggers = mpull($triggers, null, 'getPHID'); foreach ($subscriptions as $key => $subscription) { $trigger = idx($triggers, $subscription->getTriggerPHID()); if (!$trigger) { unset($subscriptions[$key]); $this->didRejectResult($subscription); continue; } $subscription->attachTrigger($trigger); } } return $subscriptions; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'subscription.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'subscription.phid IN (%Ls)', $this->phids); } if ($this->accountPHIDs !== null) { $where[] = qsprintf( $conn, 'subscription.accountPHID IN (%Ls)', $this->accountPHIDs); } if ($this->merchantPHIDs !== null) { $where[] = qsprintf( $conn, 'subscription.merchantPHID IN (%Ls)', $this->merchantPHIDs); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'subscription.status IN (%Ls)', $this->statuses); } if ($this->paymentMethodPHIDs !== null) { $where[] = qsprintf( $conn, 'subscription.defaultPaymentMethodPHID IN (%Ls)', $this->paymentMethodPHIDs); } return $where; } protected function getPrimaryTableAlias() { return 'subscription'; } public function getQueryApplicationClass() { return 'PhabricatorPhortuneApplication'; } } diff --git a/src/applications/phriction/query/PhrictionContentQuery.php b/src/applications/phriction/query/PhrictionContentQuery.php index 6efab5e1c6..8ac92be351 100644 --- a/src/applications/phriction/query/PhrictionContentQuery.php +++ b/src/applications/phriction/query/PhrictionContentQuery.php @@ -1,128 +1,124 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withDocumentPHIDs(array $phids) { $this->documentPHIDs = $phids; return $this; } public function withVersions(array $versions) { $this->versions = $versions; return $this; } public function newResultObject() { return new PhrictionContent(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'c.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'c.phid IN (%Ls)', $this->phids); } if ($this->versions !== null) { $where[] = qsprintf( $conn, 'version IN (%Ld)', $this->versions); } if ($this->documentPHIDs !== null) { $where[] = qsprintf( $conn, 'd.phid IN (%Ls)', $this->documentPHIDs); } return $where; } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); if ($this->shouldJoinDocumentTable()) { $joins[] = qsprintf( $conn, 'JOIN %T d ON d.phid = c.documentPHID', id(new PhrictionDocument())->getTableName()); } return $joins; } protected function willFilterPage(array $contents) { $document_phids = mpull($contents, 'getDocumentPHID'); $documents = id(new PhrictionDocumentQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($document_phids) ->execute(); $documents = mpull($documents, null, 'getPHID'); foreach ($contents as $key => $content) { $document_phid = $content->getDocumentPHID(); $document = idx($documents, $document_phid); if (!$document) { unset($contents[$key]); $this->didRejectResult($content); continue; } $content->attachDocument($document); } return $contents; } private function shouldJoinDocumentTable() { if ($this->documentPHIDs !== null) { return true; } return false; } protected function getPrimaryTableAlias() { return 'c'; } public function getQueryApplicationClass() { return 'PhabricatorPhrictionApplication'; } } diff --git a/src/applications/phriction/query/PhrictionDocumentQuery.php b/src/applications/phriction/query/PhrictionDocumentQuery.php index e7b5a0529e..298db97dbb 100644 --- a/src/applications/phriction/query/PhrictionDocumentQuery.php +++ b/src/applications/phriction/query/PhrictionDocumentQuery.php @@ -1,402 +1,398 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withSlugs(array $slugs) { $this->slugs = $slugs; return $this; } public function withDepths(array $depths) { $this->depths = $depths; return $this; } public function withSlugPrefix($slug_prefix) { $this->slugPrefix = $slug_prefix; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function withParentPaths(array $paths) { $this->parentPaths = $paths; return $this; } public function withAncestorPaths(array $paths) { $this->ancestorPaths = $paths; return $this; } public function needContent($need_content) { $this->needContent = $need_content; return $this; } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - public function newResultObject() { return new PhrictionDocument(); } protected function willFilterPage(array $documents) { if ($documents) { $ancestor_slugs = array(); foreach ($documents as $key => $document) { $document_slug = $document->getSlug(); foreach (PhabricatorSlug::getAncestry($document_slug) as $ancestor) { $ancestor_slugs[$ancestor][] = $key; } } if ($ancestor_slugs) { $table = new PhrictionDocument(); $conn_r = $table->establishConnection('r'); $ancestors = queryfx_all( $conn_r, 'SELECT * FROM %T WHERE slug IN (%Ls)', $document->getTableName(), array_keys($ancestor_slugs)); $ancestors = $table->loadAllFromArray($ancestors); $ancestors = mpull($ancestors, null, 'getSlug'); foreach ($ancestor_slugs as $ancestor_slug => $document_keys) { $ancestor = idx($ancestors, $ancestor_slug); foreach ($document_keys as $document_key) { $documents[$document_key]->attachAncestor( $ancestor_slug, $ancestor); } } } } // To view a Phriction document, you must also be able to view all of the // ancestor documents. Filter out documents which have ancestors that are // not visible. $document_map = array(); foreach ($documents as $document) { $document_map[$document->getSlug()] = $document; foreach ($document->getAncestors() as $key => $ancestor) { if ($ancestor) { $document_map[$key] = $ancestor; } } } $filtered_map = $this->applyPolicyFilter( $document_map, array(PhabricatorPolicyCapability::CAN_VIEW)); // Filter all of the documents where a parent is not visible. foreach ($documents as $document_key => $document) { // If the document itself is not visible, filter it. if (!isset($filtered_map[$document->getSlug()])) { $this->didRejectResult($documents[$document_key]); unset($documents[$document_key]); continue; } // If an ancestor exists but is not visible, filter the document. foreach ($document->getAncestors() as $ancestor_key => $ancestor) { if (!$ancestor) { continue; } if (!isset($filtered_map[$ancestor_key])) { $this->didRejectResult($documents[$document_key]); unset($documents[$document_key]); break; } } } if (!$documents) { return $documents; } if ($this->needContent) { $contents = id(new PhrictionContentQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs(mpull($documents, 'getContentPHID')) ->execute(); $contents = mpull($contents, null, 'getPHID'); foreach ($documents as $key => $document) { $content_phid = $document->getContentPHID(); if (empty($contents[$content_phid])) { unset($documents[$key]); continue; } $document->attachContent($contents[$content_phid]); } } return $documents; } protected function buildSelectClauseParts(AphrontDatabaseConnection $conn) { $select = parent::buildSelectClauseParts($conn); if ($this->shouldJoinContentTable()) { $select[] = qsprintf($conn, 'c.title'); } return $select; } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); if ($this->shouldJoinContentTable()) { $content_dao = new PhrictionContent(); $joins[] = qsprintf( $conn, 'JOIN %T c ON d.contentPHID = c.phid', $content_dao->getTableName()); } return $joins; } private function shouldJoinContentTable() { if ($this->getOrderVector()->containsKey('title')) { return true; } return false; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'd.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'd.phid IN (%Ls)', $this->phids); } if ($this->slugs !== null) { $where[] = qsprintf( $conn, 'd.slug IN (%Ls)', $this->slugs); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'd.status IN (%Ls)', $this->statuses); } if ($this->slugPrefix !== null) { $where[] = qsprintf( $conn, 'd.slug LIKE %>', $this->slugPrefix); } if ($this->depths !== null) { $where[] = qsprintf( $conn, 'd.depth IN (%Ld)', $this->depths); } if ($this->parentPaths !== null || $this->ancestorPaths !== null) { $sets = array( array( 'paths' => $this->parentPaths, 'parents' => true, ), array( 'paths' => $this->ancestorPaths, 'parents' => false, ), ); $paths = array(); foreach ($sets as $set) { $set_paths = $set['paths']; if ($set_paths === null) { continue; } if (!$set_paths) { throw new PhabricatorEmptyQueryException( pht('No parent/ancestor paths specified.')); } $is_parents = $set['parents']; foreach ($set_paths as $path) { $path_normal = PhabricatorSlug::normalize($path); if ($path !== $path_normal) { throw new Exception( pht( 'Document path "%s" is not a valid path. The normalized '. 'form of this path is "%s".', $path, $path_normal)); } $depth = PhabricatorSlug::getDepth($path_normal); if ($is_parents) { $min_depth = $depth + 1; $max_depth = $depth + 1; } else { $min_depth = $depth + 1; $max_depth = null; } $paths[] = array( $path_normal, $min_depth, $max_depth, ); } } $path_clauses = array(); foreach ($paths as $path) { $parts = array(); list($prefix, $min, $max) = $path; // If we're getting children or ancestors of the root document, they // aren't actually stored with the leading "/" in the database, so // just skip this part of the clause. if ($prefix !== '/') { $parts[] = qsprintf( $conn, 'd.slug LIKE %>', $prefix); } if ($min !== null) { $parts[] = qsprintf( $conn, 'd.depth >= %d', $min); } if ($max !== null) { $parts[] = qsprintf( $conn, 'd.depth <= %d', $max); } if ($parts) { $path_clauses[] = qsprintf($conn, '%LA', $parts); } } if ($path_clauses) { $where[] = qsprintf($conn, '%LO', $path_clauses); } } return $where; } public function getBuiltinOrders() { return parent::getBuiltinOrders() + array( self::ORDER_HIERARCHY => array( 'vector' => array('depth', 'title', 'updated', 'id'), 'name' => pht('Hierarchy'), ), ); } public function getOrderableColumns() { return parent::getOrderableColumns() + array( 'depth' => array( 'table' => 'd', 'column' => 'depth', 'reverse' => true, 'type' => 'int', ), 'title' => array( 'table' => 'c', 'column' => 'title', 'reverse' => true, 'type' => 'string', ), 'updated' => array( 'table' => 'd', 'column' => 'editedEpoch', 'type' => 'int', 'unique' => false, ), ); } protected function newPagingMapFromCursorObject( PhabricatorQueryCursor $cursor, array $keys) { $document = $cursor->getObject(); $map = array( 'id' => (int)$document->getID(), 'depth' => $document->getDepth(), 'updated' => (int)$document->getEditedEpoch(), ); if (isset($keys['title'])) { $map['title'] = $cursor->getRawRowProperty('title'); } return $map; } protected function getPrimaryTableAlias() { return 'd'; } public function getQueryApplicationClass() { return 'PhabricatorPhrictionApplication'; } } diff --git a/src/applications/phurl/query/PhabricatorPhurlURLQuery.php b/src/applications/phurl/query/PhabricatorPhurlURLQuery.php index 6efbbd5b4c..c30cedf09d 100644 --- a/src/applications/phurl/query/PhabricatorPhurlURLQuery.php +++ b/src/applications/phurl/query/PhabricatorPhurlURLQuery.php @@ -1,112 +1,108 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withNames(array $names) { $this->names = $names; return $this; } public function withNameNgrams($ngrams) { return $this->withNgramsConstraint( id(new PhabricatorPhurlURLNameNgrams()), $ngrams); } public function withLongURLs(array $long_urls) { $this->longURLs = $long_urls; return $this; } public function withAliases(array $aliases) { $this->aliases = $aliases; return $this; } public function withAuthorPHIDs(array $author_phids) { $this->authorPHIDs = $author_phids; return $this; } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'url.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'url.phid IN (%Ls)', $this->phids); } if ($this->authorPHIDs !== null) { $where[] = qsprintf( $conn, 'url.authorPHID IN (%Ls)', $this->authorPHIDs); } if ($this->names !== null) { $where[] = qsprintf( $conn, 'url.name IN (%Ls)', $this->names); } if ($this->longURLs !== null) { $where[] = qsprintf( $conn, 'url.longURL IN (%Ls)', $this->longURLs); } if ($this->aliases !== null) { $where[] = qsprintf( $conn, 'url.alias IN (%Ls)', $this->aliases); } return $where; } protected function getPrimaryTableAlias() { return 'url'; } public function getQueryApplicationClass() { return 'PhabricatorPhurlApplication'; } } diff --git a/src/applications/ponder/query/PonderAnswerQuery.php b/src/applications/ponder/query/PonderAnswerQuery.php index 2901f4d6a5..f100f05ae3 100644 --- a/src/applications/ponder/query/PonderAnswerQuery.php +++ b/src/applications/ponder/query/PonderAnswerQuery.php @@ -1,88 +1,84 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAuthorPHIDs(array $phids) { $this->authorPHIDs = $phids; return $this; } public function withQuestionIDs(array $ids) { $this->questionIDs = $ids; return $this; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->authorPHIDs !== null) { $where[] = qsprintf( $conn, 'authorPHID IN (%Ls)', $this->authorPHIDs); } return $where; } public function newResultObject() { return new PonderAnswer(); } - protected function loadPage() { - return $this->loadStandardPage(new PonderAnswer()); - } - protected function willFilterPage(array $answers) { $questions = id(new PonderQuestionQuery()) ->setViewer($this->getViewer()) ->withIDs(mpull($answers, 'getQuestionID')) ->execute(); foreach ($answers as $key => $answer) { $question = idx($questions, $answer->getQuestionID()); if (!$question) { unset($answers[$key]); continue; } $answer->attachQuestion($question); } return $answers; } public function getQueryApplicationClass() { return 'PhabricatorPonderApplication'; } } diff --git a/src/applications/ponder/query/PonderQuestionQuery.php b/src/applications/ponder/query/PonderQuestionQuery.php index b2de14e52c..323f34aac5 100644 --- a/src/applications/ponder/query/PonderQuestionQuery.php +++ b/src/applications/ponder/query/PonderQuestionQuery.php @@ -1,154 +1,150 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAuthorPHIDs(array $phids) { $this->authorPHIDs = $phids; return $this; } public function withStatuses($status) { $this->status = $status; return $this; } public function withAnswererPHIDs(array $phids) { $this->answererPHIDs = $phids; return $this; } public function needAnswers($need_answers) { $this->needAnswers = $need_answers; return $this; } public function needProjectPHIDs($need_projects) { $this->needProjectPHIDs = $need_projects; return $this; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'q.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'q.phid IN (%Ls)', $this->phids); } if ($this->authorPHIDs !== null) { $where[] = qsprintf( $conn, 'q.authorPHID IN (%Ls)', $this->authorPHIDs); } if ($this->status !== null) { $where[] = qsprintf( $conn, 'q.status IN (%Ls)', $this->status); } return $where; } public function newResultObject() { return new PonderQuestion(); } - protected function loadPage() { - return $this->loadStandardPage(new PonderQuestion()); - } - protected function willFilterPage(array $questions) { $phids = mpull($questions, 'getPHID'); if ($this->needAnswers) { $aquery = id(new PonderAnswerQuery()) ->setViewer($this->getViewer()) ->setOrderVector(array('-id')) ->withQuestionIDs(mpull($questions, 'getID')); $answers = $aquery->execute(); $answers = mgroup($answers, 'getQuestionID'); foreach ($questions as $question) { $question_answers = idx($answers, $question->getID(), array()); $question->attachAnswers(mpull($question_answers, null, 'getPHID')); } } if ($this->needProjectPHIDs) { $edge_query = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs($phids) ->withEdgeTypes( array( PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, )); $edge_query->execute(); foreach ($questions as $question) { $project_phids = $edge_query->getDestinationPHIDs( array($question->getPHID())); $question->attachProjectPHIDs($project_phids); } } return $questions; } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); if ($this->answererPHIDs) { $answer_table = new PonderAnswer(); $joins[] = qsprintf( $conn, 'JOIN %T a ON a.questionID = q.id AND a.authorPHID IN (%Ls)', $answer_table->getTableName(), $this->answererPHIDs); } return $joins; } protected function getPrimaryTableAlias() { return 'q'; } public function getQueryApplicationClass() { return 'PhabricatorPonderApplication'; } } diff --git a/src/applications/project/query/PhabricatorProjectColumnPositionQuery.php b/src/applications/project/query/PhabricatorProjectColumnPositionQuery.php index 438c558e6e..2673902780 100644 --- a/src/applications/project/query/PhabricatorProjectColumnPositionQuery.php +++ b/src/applications/project/query/PhabricatorProjectColumnPositionQuery.php @@ -1,77 +1,73 @@ ids = $ids; return $this; } public function withBoardPHIDs(array $board_phids) { $this->boardPHIDs = $board_phids; return $this; } public function withObjectPHIDs(array $object_phids) { $this->objectPHIDs = $object_phids; return $this; } public function withColumnPHIDs(array $column_phids) { $this->columnPHIDs = $column_phids; return $this; } public function newResultObject() { return new PhabricatorProjectColumnPosition(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = array(); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->boardPHIDs !== null) { $where[] = qsprintf( $conn, 'boardPHID IN (%Ls)', $this->boardPHIDs); } if ($this->objectPHIDs !== null) { $where[] = qsprintf( $conn, 'objectPHID IN (%Ls)', $this->objectPHIDs); } if ($this->columnPHIDs !== null) { $where[] = qsprintf( $conn, 'columnPHID IN (%Ls)', $this->columnPHIDs); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorProjectApplication'; } } diff --git a/src/applications/project/query/PhabricatorProjectColumnQuery.php b/src/applications/project/query/PhabricatorProjectColumnQuery.php index 380dab5208..478d872cb4 100644 --- a/src/applications/project/query/PhabricatorProjectColumnQuery.php +++ b/src/applications/project/query/PhabricatorProjectColumnQuery.php @@ -1,235 +1,231 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withProjectPHIDs(array $project_phids) { $this->projectPHIDs = $project_phids; return $this; } public function withProxyPHIDs(array $proxy_phids) { $this->proxyPHIDs = $proxy_phids; return $this; } public function withStatuses(array $status) { $this->statuses = $status; return $this; } public function withIsProxyColumn($is_proxy) { $this->isProxyColumn = $is_proxy; return $this; } public function withTriggerPHIDs(array $trigger_phids) { $this->triggerPHIDs = $trigger_phids; return $this; } public function needTriggers($need_triggers) { $this->needTriggers = true; return $this; } public function newResultObject() { return new PhabricatorProjectColumn(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $page) { $projects = array(); $project_phids = array_filter(mpull($page, 'getProjectPHID')); if ($project_phids) { $projects = id(new PhabricatorProjectQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($project_phids) ->execute(); $projects = mpull($projects, null, 'getPHID'); } foreach ($page as $key => $column) { $phid = $column->getProjectPHID(); $project = idx($projects, $phid); if (!$project) { $this->didRejectResult($page[$key]); unset($page[$key]); continue; } $column->attachProject($project); } $proxy_phids = array_filter(mpull($page, 'getProjectPHID')); return $page; } protected function didFilterPage(array $page) { $proxy_phids = array(); foreach ($page as $column) { $proxy_phid = $column->getProxyPHID(); if ($proxy_phid !== null) { $proxy_phids[$proxy_phid] = $proxy_phid; } } if ($proxy_phids) { $proxies = id(new PhabricatorObjectQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($proxy_phids) ->execute(); $proxies = mpull($proxies, null, 'getPHID'); } else { $proxies = array(); } foreach ($page as $key => $column) { $proxy_phid = $column->getProxyPHID(); if ($proxy_phid !== null) { $proxy = idx($proxies, $proxy_phid); // Only attach valid proxies, so we don't end up getting surprised if // an install somehow gets junk into their database. if (!($proxy instanceof PhabricatorColumnProxyInterface)) { $proxy = null; } if (!$proxy) { $this->didRejectResult($column); unset($page[$key]); continue; } } else { $proxy = null; } $column->attachProxy($proxy); } if ($this->needTriggers) { $trigger_phids = array(); foreach ($page as $column) { if ($column->canHaveTrigger()) { $trigger_phid = $column->getTriggerPHID(); if ($trigger_phid) { $trigger_phids[] = $trigger_phid; } } } if ($trigger_phids) { $triggers = id(new PhabricatorProjectTriggerQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($trigger_phids) ->execute(); $triggers = mpull($triggers, null, 'getPHID'); } else { $triggers = array(); } foreach ($page as $column) { $trigger = null; if ($column->canHaveTrigger()) { $trigger_phid = $column->getTriggerPHID(); if ($trigger_phid) { $trigger = idx($triggers, $trigger_phid); } } $column->attachTrigger($trigger); } } return $page; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->projectPHIDs !== null) { $where[] = qsprintf( $conn, 'projectPHID IN (%Ls)', $this->projectPHIDs); } if ($this->proxyPHIDs !== null) { $where[] = qsprintf( $conn, 'proxyPHID IN (%Ls)', $this->proxyPHIDs); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'status IN (%Ld)', $this->statuses); } if ($this->triggerPHIDs !== null) { $where[] = qsprintf( $conn, 'triggerPHID IN (%Ls)', $this->triggerPHIDs); } if ($this->isProxyColumn !== null) { if ($this->isProxyColumn) { $where[] = qsprintf($conn, 'proxyPHID IS NOT NULL'); } else { $where[] = qsprintf($conn, 'proxyPHID IS NULL'); } } return $where; } public function getQueryApplicationClass() { return 'PhabricatorProjectApplication'; } } diff --git a/src/applications/project/query/PhabricatorProjectQuery.php b/src/applications/project/query/PhabricatorProjectQuery.php index 22e0b59c94..b02fa647a2 100644 --- a/src/applications/project/query/PhabricatorProjectQuery.php +++ b/src/applications/project/query/PhabricatorProjectQuery.php @@ -1,901 +1,897 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withStatus($status) { $this->status = $status; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function withMemberPHIDs(array $member_phids) { $this->memberPHIDs = $member_phids; return $this; } public function withWatcherPHIDs(array $watcher_phids) { $this->watcherPHIDs = $watcher_phids; return $this; } public function withSlugs(array $slugs) { $this->slugs = $slugs; return $this; } public function withNames(array $names) { $this->names = $names; return $this; } public function withNamePrefixes(array $prefixes) { $this->namePrefixes = $prefixes; return $this; } public function withNameTokens(array $tokens) { $this->nameTokens = array_values($tokens); return $this; } public function withIcons(array $icons) { $this->icons = $icons; return $this; } public function withColors(array $colors) { $this->colors = $colors; return $this; } public function withParentProjectPHIDs($parent_phids) { $this->parentPHIDs = $parent_phids; return $this; } public function withAncestorProjectPHIDs($ancestor_phids) { $this->ancestorPHIDs = $ancestor_phids; return $this; } public function withIsMilestone($is_milestone) { $this->isMilestone = $is_milestone; return $this; } public function withHasSubprojects($has_subprojects) { $this->hasSubprojects = $has_subprojects; return $this; } public function withDepthBetween($min, $max) { $this->minDepth = $min; $this->maxDepth = $max; return $this; } public function withMilestoneNumberBetween($min, $max) { $this->minMilestoneNumber = $min; $this->maxMilestoneNumber = $max; return $this; } public function withSubtypes(array $subtypes) { $this->subtypes = $subtypes; return $this; } public function needMembers($need_members) { $this->needMembers = $need_members; return $this; } public function needAncestorMembers($need_ancestor_members) { $this->needAncestorMembers = $need_ancestor_members; return $this; } public function needWatchers($need_watchers) { $this->needWatchers = $need_watchers; return $this; } public function needImages($need_images) { $this->needImages = $need_images; return $this; } public function needSlugs($need_slugs) { $this->needSlugs = $need_slugs; return $this; } public function newResultObject() { return new PhabricatorProject(); } protected function getDefaultOrderVector() { return array('name'); } public function getBuiltinOrders() { return array( 'name' => array( 'vector' => array('name'), 'name' => pht('Name'), ), ) + parent::getBuiltinOrders(); } public function getOrderableColumns() { return parent::getOrderableColumns() + array( 'name' => array( 'table' => $this->getPrimaryTableAlias(), 'column' => 'name', 'reverse' => true, 'type' => 'string', 'unique' => true, ), 'milestoneNumber' => array( 'table' => $this->getPrimaryTableAlias(), 'column' => 'milestoneNumber', 'type' => 'int', ), 'status' => array( 'table' => $this->getPrimaryTableAlias(), 'column' => 'status', 'type' => 'int', ), ); } protected function newPagingMapFromPartialObject($object) { return array( 'id' => (int)$object->getID(), 'name' => $object->getName(), 'status' => $object->getStatus(), ); } public function getSlugMap() { if ($this->slugMap === null) { throw new PhutilInvalidStateException('execute'); } return $this->slugMap; } protected function willExecute() { $this->slugMap = array(); $this->slugNormals = array(); $this->allSlugs = array(); if ($this->slugs) { foreach ($this->slugs as $slug) { if (PhabricatorSlug::isValidProjectSlug($slug)) { $normal = PhabricatorSlug::normalizeProjectSlug($slug); $this->slugNormals[$slug] = $normal; $this->allSlugs[$normal] = $normal; } // NOTE: At least for now, we query for the normalized slugs but also // for the slugs exactly as entered. This allows older projects with // slugs that are no longer valid to continue to work. $this->allSlugs[$slug] = $slug; } } } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $projects) { $ancestor_paths = array(); foreach ($projects as $project) { foreach ($project->getAncestorProjectPaths() as $path) { $ancestor_paths[$path] = $path; } } if ($ancestor_paths) { $ancestors = id(new PhabricatorProject())->loadAllWhere( 'projectPath IN (%Ls)', $ancestor_paths); } else { $ancestors = array(); } $projects = $this->linkProjectGraph($projects, $ancestors); $viewer_phid = $this->getViewer()->getPHID(); $material_type = PhabricatorProjectMaterializedMemberEdgeType::EDGECONST; $watcher_type = PhabricatorObjectHasWatcherEdgeType::EDGECONST; $types = array(); $types[] = $material_type; if ($this->needWatchers) { $types[] = $watcher_type; } $all_graph = $this->getAllReachableAncestors($projects); // See T13484. If the graph is damaged (and contains a cycle or an edge // pointing at a project which has been destroyed), some of the nodes we // started with may be filtered out by reachability tests. If any of the // projects we are linking up don't have available ancestors, filter them // out. foreach ($projects as $key => $project) { $project_phid = $project->getPHID(); if (!isset($all_graph[$project_phid])) { $this->didRejectResult($project); unset($projects[$key]); continue; } } if (!$projects) { return array(); } // NOTE: Although we may not need much information about ancestors, we // always need to test if the viewer is a member, because we will return // ancestor projects to the policy filter via ExtendedPolicy calls. If // we skip populating membership data on a parent, the policy framework // will think the user is not a member of the parent project. $all_sources = array(); foreach ($all_graph as $project) { // For milestones, we need parent members. if ($project->isMilestone()) { $parent_phid = $project->getParentProjectPHID(); $all_sources[$parent_phid] = $parent_phid; } $phid = $project->getPHID(); $all_sources[$phid] = $phid; } $edge_query = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs($all_sources) ->withEdgeTypes($types); $need_all_edges = $this->needMembers || $this->needWatchers || $this->needAncestorMembers; // If we only need to know if the viewer is a member, we can restrict // the query to just their PHID. $any_edges = true; if (!$need_all_edges) { if ($viewer_phid) { $edge_query->withDestinationPHIDs(array($viewer_phid)); } else { // If we don't need members or watchers and don't have a viewer PHID // (viewer is logged-out or omnipotent), they'll never be a member // so we don't need to issue this query at all. $any_edges = false; } } if ($any_edges) { $edge_query->execute(); } $membership_projects = array(); foreach ($all_graph as $project) { $project_phid = $project->getPHID(); if ($project->isMilestone()) { $source_phids = array($project->getParentProjectPHID()); } else { $source_phids = array($project_phid); } if ($any_edges) { $member_phids = $edge_query->getDestinationPHIDs( $source_phids, array($material_type)); } else { $member_phids = array(); } if (in_array($viewer_phid, $member_phids)) { $membership_projects[$project_phid] = $project; } if ($this->needMembers || $this->needAncestorMembers) { $project->attachMemberPHIDs($member_phids); } if ($this->needWatchers) { $watcher_phids = $edge_query->getDestinationPHIDs( array($project_phid), array($watcher_type)); $project->attachWatcherPHIDs($watcher_phids); $project->setIsUserWatcher( $viewer_phid, in_array($viewer_phid, $watcher_phids)); } } // If we loaded ancestor members, we've already populated membership // lists above, so we can skip this step. if (!$this->needAncestorMembers) { $member_graph = $this->getAllReachableAncestors($membership_projects); foreach ($all_graph as $phid => $project) { $is_member = isset($member_graph[$phid]); $project->setIsUserMember($viewer_phid, $is_member); } } return $projects; } protected function didFilterPage(array $projects) { $viewer = $this->getViewer(); if ($this->needImages) { $need_images = $projects; // First, try to load custom profile images for any projects with custom // images. $file_phids = array(); foreach ($need_images as $key => $project) { $image_phid = $project->getProfileImagePHID(); if ($image_phid) { $file_phids[$key] = $image_phid; } } if ($file_phids) { $files = id(new PhabricatorFileQuery()) ->setParentQuery($this) ->setViewer($viewer) ->withPHIDs($file_phids) ->execute(); $files = mpull($files, null, 'getPHID'); foreach ($file_phids as $key => $image_phid) { $file = idx($files, $image_phid); if (!$file) { continue; } $need_images[$key]->attachProfileImageFile($file); unset($need_images[$key]); } } // For projects with default images, or projects where the custom image // failed to load, load a builtin image. if ($need_images) { $builtin_map = array(); $builtins = array(); foreach ($need_images as $key => $project) { $icon = $project->getIcon(); $builtin_name = PhabricatorProjectIconSet::getIconImage($icon); $builtin_name = 'projects/'.$builtin_name; $builtin = id(new PhabricatorFilesOnDiskBuiltinFile()) ->setName($builtin_name); $builtin_key = $builtin->getBuiltinFileKey(); $builtins[] = $builtin; $builtin_map[$key] = $builtin_key; } $builtin_files = PhabricatorFile::loadBuiltins( $viewer, $builtins); foreach ($need_images as $key => $project) { $builtin_key = $builtin_map[$key]; $builtin_file = $builtin_files[$builtin_key]; $project->attachProfileImageFile($builtin_file); } } } $this->loadSlugs($projects); return $projects; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->status != self::STATUS_ANY) { switch ($this->status) { case self::STATUS_OPEN: case self::STATUS_ACTIVE: $filter = array( PhabricatorProjectStatus::STATUS_ACTIVE, ); break; case self::STATUS_CLOSED: case self::STATUS_ARCHIVED: $filter = array( PhabricatorProjectStatus::STATUS_ARCHIVED, ); break; default: throw new Exception( pht( "Unknown project status '%s'!", $this->status)); } $where[] = qsprintf( $conn, 'project.status IN (%Ld)', $filter); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'project.status IN (%Ls)', $this->statuses); } if ($this->ids !== null) { $where[] = qsprintf( $conn, 'project.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'project.phid IN (%Ls)', $this->phids); } if ($this->memberPHIDs !== null) { $where[] = qsprintf( $conn, 'e.dst IN (%Ls)', $this->memberPHIDs); } if ($this->watcherPHIDs !== null) { $where[] = qsprintf( $conn, 'w.dst IN (%Ls)', $this->watcherPHIDs); } if ($this->slugs !== null) { $where[] = qsprintf( $conn, 'slug.slug IN (%Ls)', $this->allSlugs); } if ($this->names !== null) { $where[] = qsprintf( $conn, 'project.name IN (%Ls)', $this->names); } if ($this->namePrefixes) { $parts = array(); foreach ($this->namePrefixes as $name_prefix) { $parts[] = qsprintf( $conn, 'project.name LIKE %>', $name_prefix); } $where[] = qsprintf($conn, '%LO', $parts); } if ($this->icons !== null) { $where[] = qsprintf( $conn, 'project.icon IN (%Ls)', $this->icons); } if ($this->colors !== null) { $where[] = qsprintf( $conn, 'project.color IN (%Ls)', $this->colors); } if ($this->parentPHIDs !== null) { $where[] = qsprintf( $conn, 'project.parentProjectPHID IN (%Ls)', $this->parentPHIDs); } if ($this->ancestorPHIDs !== null) { $ancestor_paths = queryfx_all( $conn, 'SELECT projectPath, projectDepth FROM %T WHERE phid IN (%Ls)', id(new PhabricatorProject())->getTableName(), $this->ancestorPHIDs); if (!$ancestor_paths) { throw new PhabricatorEmptyQueryException(); } $sql = array(); foreach ($ancestor_paths as $ancestor_path) { $sql[] = qsprintf( $conn, '(project.projectPath LIKE %> AND project.projectDepth > %d)', $ancestor_path['projectPath'], $ancestor_path['projectDepth']); } $where[] = qsprintf($conn, '%LO', $sql); $where[] = qsprintf( $conn, 'project.parentProjectPHID IS NOT NULL'); } if ($this->isMilestone !== null) { if ($this->isMilestone) { $where[] = qsprintf( $conn, 'project.milestoneNumber IS NOT NULL'); } else { $where[] = qsprintf( $conn, 'project.milestoneNumber IS NULL'); } } if ($this->hasSubprojects !== null) { $where[] = qsprintf( $conn, 'project.hasSubprojects = %d', (int)$this->hasSubprojects); } if ($this->minDepth !== null) { $where[] = qsprintf( $conn, 'project.projectDepth >= %d', $this->minDepth); } if ($this->maxDepth !== null) { $where[] = qsprintf( $conn, 'project.projectDepth <= %d', $this->maxDepth); } if ($this->minMilestoneNumber !== null) { $where[] = qsprintf( $conn, 'project.milestoneNumber >= %d', $this->minMilestoneNumber); } if ($this->maxMilestoneNumber !== null) { $where[] = qsprintf( $conn, 'project.milestoneNumber <= %d', $this->maxMilestoneNumber); } if ($this->subtypes !== null) { $where[] = qsprintf( $conn, 'project.subtype IN (%Ls)', $this->subtypes); } return $where; } protected function shouldGroupQueryResultRows() { if ($this->memberPHIDs || $this->watcherPHIDs || $this->nameTokens) { return true; } if ($this->slugs) { return true; } return parent::shouldGroupQueryResultRows(); } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); if ($this->memberPHIDs !== null) { $joins[] = qsprintf( $conn, 'JOIN %T e ON e.src = project.phid AND e.type = %d', PhabricatorEdgeConfig::TABLE_NAME_EDGE, PhabricatorProjectMaterializedMemberEdgeType::EDGECONST); } if ($this->watcherPHIDs !== null) { $joins[] = qsprintf( $conn, 'JOIN %T w ON w.src = project.phid AND w.type = %d', PhabricatorEdgeConfig::TABLE_NAME_EDGE, PhabricatorObjectHasWatcherEdgeType::EDGECONST); } if ($this->slugs !== null) { $joins[] = qsprintf( $conn, 'JOIN %T slug on slug.projectPHID = project.phid', id(new PhabricatorProjectSlug())->getTableName()); } if ($this->nameTokens !== null) { $name_tokens = $this->getNameTokensForQuery($this->nameTokens); foreach ($name_tokens as $key => $token) { $token_table = 'token_'.$key; $joins[] = qsprintf( $conn, 'JOIN %T %T ON %T.projectID = project.id AND %T.token LIKE %>', PhabricatorProject::TABLE_DATASOURCE_TOKEN, $token_table, $token_table, $token_table, $token); } } return $joins; } public function getQueryApplicationClass() { return 'PhabricatorProjectApplication'; } protected function getPrimaryTableAlias() { return 'project'; } private function linkProjectGraph(array $projects, array $ancestors) { $ancestor_map = mpull($ancestors, null, 'getPHID'); $projects_map = mpull($projects, null, 'getPHID'); $all_map = $projects_map + $ancestor_map; $done = array(); foreach ($projects as $key => $project) { $seen = array($project->getPHID() => true); if (!$this->linkProject($project, $all_map, $done, $seen)) { $this->didRejectResult($project); unset($projects[$key]); continue; } foreach ($project->getAncestorProjects() as $ancestor) { $seen[$ancestor->getPHID()] = true; } } return $projects; } private function linkProject($project, array $all, array $done, array $seen) { $parent_phid = $project->getParentProjectPHID(); // This project has no parent, so just attach `null` and return. if (!$parent_phid) { $project->attachParentProject(null); return true; } // This project has a parent, but it failed to load. if (empty($all[$parent_phid])) { return false; } // Test for graph cycles. If we encounter one, we're going to hide the // entire cycle since we can't meaningfully resolve it. if (isset($seen[$parent_phid])) { return false; } $seen[$parent_phid] = true; $parent = $all[$parent_phid]; $project->attachParentProject($parent); if (!empty($done[$parent_phid])) { return true; } return $this->linkProject($parent, $all, $done, $seen); } private function getAllReachableAncestors(array $projects) { $ancestors = array(); $seen = mpull($projects, null, 'getPHID'); $stack = $projects; while ($stack) { $project = array_pop($stack); $phid = $project->getPHID(); $ancestors[$phid] = $project; $parent_phid = $project->getParentProjectPHID(); if (!$parent_phid) { continue; } if (isset($seen[$parent_phid])) { continue; } $seen[$parent_phid] = true; $stack[] = $project->getParentProject(); } return $ancestors; } private function loadSlugs(array $projects) { // Build a map from primary slugs to projects. $primary_map = array(); foreach ($projects as $project) { $primary_slug = $project->getPrimarySlug(); if ($primary_slug === null) { continue; } $primary_map[$primary_slug] = $project; } // Link up all of the queried slugs which correspond to primary // slugs. If we can link up everything from this (no slugs were queried, // or only primary slugs were queried) we don't need to load anything // else. $unknown = $this->slugNormals; foreach ($unknown as $input => $normal) { if (isset($primary_map[$input])) { $match = $input; } else if (isset($primary_map[$normal])) { $match = $normal; } else { continue; } $this->slugMap[$input] = array( 'slug' => $match, 'projectPHID' => $primary_map[$match]->getPHID(), ); unset($unknown[$input]); } // If we need slugs, we have to load everything. // If we still have some queried slugs which we haven't mapped, we only // need to look for them. // If we've mapped everything, we don't have to do any work. $project_phids = mpull($projects, 'getPHID'); if ($this->needSlugs) { $slugs = id(new PhabricatorProjectSlug())->loadAllWhere( 'projectPHID IN (%Ls)', $project_phids); } else if ($unknown) { $slugs = id(new PhabricatorProjectSlug())->loadAllWhere( 'projectPHID IN (%Ls) AND slug IN (%Ls)', $project_phids, $unknown); } else { $slugs = array(); } // Link up any slugs we were not able to link up earlier. $extra_map = mpull($slugs, 'getProjectPHID', 'getSlug'); foreach ($unknown as $input => $normal) { if (isset($extra_map[$input])) { $match = $input; } else if (isset($extra_map[$normal])) { $match = $normal; } else { continue; } $this->slugMap[$input] = array( 'slug' => $match, 'projectPHID' => $extra_map[$match], ); unset($unknown[$input]); } if ($this->needSlugs) { $slug_groups = mgroup($slugs, 'getProjectPHID'); foreach ($projects as $project) { $project_slugs = idx($slug_groups, $project->getPHID(), array()); $project->attachSlugs($project_slugs); } } } private function getNameTokensForQuery(array $tokens) { // When querying for projects by name, only actually search for the five // longest tokens. MySQL can get grumpy with a large number of JOINs // with LIKEs and queries for more than 5 tokens are essentially never // legitimate searches for projects, but users copy/pasting nonsense. // See also PHI47. $length_map = array(); foreach ($tokens as $token) { $length_map[$token] = strlen($token); } arsort($length_map); $length_map = array_slice($length_map, 0, 5, true); return array_keys($length_map); } } diff --git a/src/applications/project/query/PhabricatorProjectTriggerQuery.php b/src/applications/project/query/PhabricatorProjectTriggerQuery.php index 452e3e53f1..306fcb50fe 100644 --- a/src/applications/project/query/PhabricatorProjectTriggerQuery.php +++ b/src/applications/project/query/PhabricatorProjectTriggerQuery.php @@ -1,135 +1,131 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function needUsage($need_usage) { $this->needUsage = $need_usage; return $this; } public function withActiveColumnCountBetween($min, $max) { $this->activeColumnMin = $min; $this->activeColumnMax = $max; return $this; } public function newResultObject() { return new PhabricatorProjectTrigger(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'trigger.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'trigger.phid IN (%Ls)', $this->phids); } if ($this->activeColumnMin !== null) { $where[] = qsprintf( $conn, 'trigger_usage.activeColumnCount >= %d', $this->activeColumnMin); } if ($this->activeColumnMax !== null) { $where[] = qsprintf( $conn, 'trigger_usage.activeColumnCount <= %d', $this->activeColumnMax); } return $where; } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); if ($this->shouldJoinUsageTable()) { $joins[] = qsprintf( $conn, 'JOIN %R trigger_usage ON trigger.phid = trigger_usage.triggerPHID', new PhabricatorProjectTriggerUsage()); } return $joins; } private function shouldJoinUsageTable() { if ($this->activeColumnMin !== null) { return true; } if ($this->activeColumnMax !== null) { return true; } return false; } protected function didFilterPage(array $triggers) { if ($this->needUsage) { $usage_map = id(new PhabricatorProjectTriggerUsage())->loadAllWhere( 'triggerPHID IN (%Ls)', mpull($triggers, 'getPHID')); $usage_map = mpull($usage_map, null, 'getTriggerPHID'); foreach ($triggers as $trigger) { $trigger_phid = $trigger->getPHID(); $usage = idx($usage_map, $trigger_phid); if (!$usage) { $usage = id(new PhabricatorProjectTriggerUsage()) ->setTriggerPHID($trigger_phid) ->setExamplePHID(null) ->setColumnCount(0) ->setActiveColumnCount(0); } $trigger->attachUsage($usage); } } return $triggers; } public function getQueryApplicationClass() { return 'PhabricatorProjectApplication'; } protected function getPrimaryTableAlias() { return 'trigger'; } } diff --git a/src/applications/repository/query/PhabricatorRepositoryGitLFSRefQuery.php b/src/applications/repository/query/PhabricatorRepositoryGitLFSRefQuery.php index cd693be605..08f0ada159 100644 --- a/src/applications/repository/query/PhabricatorRepositoryGitLFSRefQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryGitLFSRefQuery.php @@ -1,64 +1,60 @@ ids = $ids; return $this; } public function withRepositoryPHIDs(array $phids) { $this->repositoryPHIDs = $phids; return $this; } public function withObjectHashes(array $hashes) { $this->objectHashes = $hashes; return $this; } public function newResultObject() { return new PhabricatorRepositoryGitLFSRef(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->repositoryPHIDs !== null) { $where[] = qsprintf( $conn, 'repositoryPHID IN (%Ls)', $this->repositoryPHIDs); } if ($this->objectHashes !== null) { $where[] = qsprintf( $conn, 'objectHash IN (%Ls)', $this->objectHashes); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorDiffusionApplication'; } } diff --git a/src/applications/repository/query/PhabricatorRepositoryIdentityQuery.php b/src/applications/repository/query/PhabricatorRepositoryIdentityQuery.php index 7de97de4d6..2b05b542d5 100644 --- a/src/applications/repository/query/PhabricatorRepositoryIdentityQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryIdentityQuery.php @@ -1,160 +1,156 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withIdentityNames(array $names) { $this->identityNames = $names; return $this; } public function withIdentityNameLike($name_like) { $this->identityNameLike = $name_like; return $this; } public function withEmailAddresses(array $addresses) { $this->emailAddresses = $addresses; return $this; } public function withAssignedPHIDs(array $assigned) { $this->assignedPHIDs = $assigned; return $this; } public function withEffectivePHIDs(array $effective) { $this->effectivePHIDs = $effective; return $this; } public function withRelatedPHIDs(array $related) { $this->relatedPHIDs = $related; return $this; } public function withHasEffectivePHID($has_effective_phid) { $this->hasEffectivePHID = $has_effective_phid; return $this; } public function newResultObject() { return new PhabricatorRepositoryIdentity(); } protected function getPrimaryTableAlias() { return 'identity'; } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'identity.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'identity.phid IN (%Ls)', $this->phids); } if ($this->assignedPHIDs !== null) { $where[] = qsprintf( $conn, 'identity.manuallySetUserPHID IN (%Ls)', $this->assignedPHIDs); } if ($this->effectivePHIDs !== null) { $where[] = qsprintf( $conn, 'identity.currentEffectiveUserPHID IN (%Ls)', $this->effectivePHIDs); } if ($this->hasEffectivePHID !== null) { if ($this->hasEffectivePHID) { $where[] = qsprintf( $conn, 'identity.currentEffectiveUserPHID IS NOT NULL'); } else { $where[] = qsprintf( $conn, 'identity.currentEffectiveUserPHID IS NULL'); } } if ($this->identityNames !== null) { $name_hashes = array(); foreach ($this->identityNames as $name) { $name_hashes[] = PhabricatorHash::digestForIndex($name); } $where[] = qsprintf( $conn, 'identity.identityNameHash IN (%Ls)', $name_hashes); } if ($this->emailAddresses !== null) { $where[] = qsprintf( $conn, 'identity.emailAddress IN (%Ls)', $this->emailAddresses); } if ($this->identityNameLike != null) { $where[] = qsprintf( $conn, 'identity.identityNameRaw LIKE %~', $this->identityNameLike); } if ($this->relatedPHIDs !== null) { $where[] = qsprintf( $conn, '(identity.manuallySetUserPHID IN (%Ls) OR identity.currentEffectiveUserPHID IN (%Ls) OR identity.automaticGuessedUserPHID IN (%Ls))', $this->relatedPHIDs, $this->relatedPHIDs, $this->relatedPHIDs); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorDiffusionApplication'; } } diff --git a/src/applications/repository/query/PhabricatorRepositoryPullEventQuery.php b/src/applications/repository/query/PhabricatorRepositoryPullEventQuery.php index ce14a6f831..8d4f14e0ce 100644 --- a/src/applications/repository/query/PhabricatorRepositoryPullEventQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryPullEventQuery.php @@ -1,135 +1,131 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withRepositoryPHIDs(array $repository_phids) { $this->repositoryPHIDs = $repository_phids; return $this; } public function withPullerPHIDs(array $puller_phids) { $this->pullerPHIDs = $puller_phids; return $this; } public function withEpochBetween($min, $max) { $this->epochMin = $min; $this->epochMax = $max; return $this; } public function newResultObject() { return new PhabricatorRepositoryPullEvent(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $events) { // If a pull targets an invalid repository or fails before authenticating, // it may not have an associated repository. $repository_phids = mpull($events, 'getRepositoryPHID'); $repository_phids = array_filter($repository_phids); if ($repository_phids) { $repositories = id(new PhabricatorRepositoryQuery()) ->setViewer($this->getViewer()) ->withPHIDs($repository_phids) ->execute(); $repositories = mpull($repositories, null, 'getPHID'); } else { $repositories = array(); } foreach ($events as $key => $event) { $phid = $event->getRepositoryPHID(); if (!$phid) { $event->attachRepository(null); continue; } if (empty($repositories[$phid])) { unset($events[$key]); $this->didRejectResult($event); continue; } $event->attachRepository($repositories[$phid]); } return $events; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->repositoryPHIDs !== null) { $where[] = qsprintf( $conn, 'repositoryPHID IN (%Ls)', $this->repositoryPHIDs); } if ($this->pullerPHIDs !== null) { $where[] = qsprintf( $conn, 'pullerPHID in (%Ls)', $this->pullerPHIDs); } if ($this->epochMin !== null) { $where[] = qsprintf( $conn, 'epoch >= %d', $this->epochMin); } if ($this->epochMax !== null) { $where[] = qsprintf( $conn, 'epoch <= %d', $this->epochMax); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorDiffusionApplication'; } } diff --git a/src/applications/repository/query/PhabricatorRepositoryPushEventQuery.php b/src/applications/repository/query/PhabricatorRepositoryPushEventQuery.php index f3e5fc62b4..d1ce937b86 100644 --- a/src/applications/repository/query/PhabricatorRepositoryPushEventQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryPushEventQuery.php @@ -1,122 +1,118 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withRepositoryPHIDs(array $repository_phids) { $this->repositoryPHIDs = $repository_phids; return $this; } public function withPusherPHIDs(array $pusher_phids) { $this->pusherPHIDs = $pusher_phids; return $this; } public function needLogs($need_logs) { $this->needLogs = $need_logs; return $this; } public function newResultObject() { return new PhabricatorRepositoryPushEvent(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $events) { $repository_phids = mpull($events, 'getRepositoryPHID'); $repositories = id(new PhabricatorRepositoryQuery()) ->setViewer($this->getViewer()) ->withPHIDs($repository_phids) ->execute(); $repositories = mpull($repositories, null, 'getPHID'); foreach ($events as $key => $event) { $phid = $event->getRepositoryPHID(); if (empty($repositories[$phid])) { unset($events[$key]); continue; } $event->attachRepository($repositories[$phid]); } return $events; } protected function didFilterPage(array $events) { $phids = mpull($events, 'getPHID'); if ($this->needLogs) { $logs = id(new PhabricatorRepositoryPushLogQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPushEventPHIDs($phids) ->execute(); $logs = mgroup($logs, 'getPushEventPHID'); foreach ($events as $key => $event) { $event_logs = idx($logs, $event->getPHID(), array()); $event->attachLogs($event_logs); } } return $events; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->repositoryPHIDs !== null) { $where[] = qsprintf( $conn, 'repositoryPHID IN (%Ls)', $this->repositoryPHIDs); } if ($this->pusherPHIDs !== null) { $where[] = qsprintf( $conn, 'pusherPHID in (%Ls)', $this->pusherPHIDs); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorDiffusionApplication'; } } diff --git a/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php b/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php index d4734f61e6..16897a1e4b 100644 --- a/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php @@ -1,199 +1,195 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withRepositoryPHIDs(array $repository_phids) { $this->repositoryPHIDs = $repository_phids; return $this; } public function withPusherPHIDs(array $pusher_phids) { $this->pusherPHIDs = $pusher_phids; return $this; } public function withRefTypes(array $ref_types) { $this->refTypes = $ref_types; return $this; } public function withNewRefs(array $new_refs) { $this->newRefs = $new_refs; return $this; } public function withPushEventPHIDs(array $phids) { $this->pushEventPHIDs = $phids; return $this; } public function withEpochBetween($min, $max) { $this->epochMin = $min; $this->epochMax = $max; return $this; } public function withBlockingHeraldRulePHIDs(array $phids) { $this->blockingHeraldRulePHIDs = $phids; return $this; } public function newResultObject() { return new PhabricatorRepositoryPushLog(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $logs) { $event_phids = mpull($logs, 'getPushEventPHID'); $events = id(new PhabricatorObjectQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($event_phids) ->execute(); $events = mpull($events, null, 'getPHID'); foreach ($logs as $key => $log) { $event = idx($events, $log->getPushEventPHID()); if (!$event) { unset($logs[$key]); continue; } $log->attachPushEvent($event); } return $logs; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'log.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'log.phid IN (%Ls)', $this->phids); } if ($this->repositoryPHIDs !== null) { $where[] = qsprintf( $conn, 'log.repositoryPHID IN (%Ls)', $this->repositoryPHIDs); } if ($this->pusherPHIDs !== null) { $where[] = qsprintf( $conn, 'log.pusherPHID in (%Ls)', $this->pusherPHIDs); } if ($this->pushEventPHIDs !== null) { $where[] = qsprintf( $conn, 'log.pushEventPHID in (%Ls)', $this->pushEventPHIDs); } if ($this->refTypes !== null) { $where[] = qsprintf( $conn, 'log.refType IN (%Ls)', $this->refTypes); } if ($this->newRefs !== null) { $where[] = qsprintf( $conn, 'log.refNew IN (%Ls)', $this->newRefs); } if ($this->epochMin !== null) { $where[] = qsprintf( $conn, 'log.epoch >= %d', $this->epochMin); } if ($this->epochMax !== null) { $where[] = qsprintf( $conn, 'log.epoch <= %d', $this->epochMax); } if ($this->blockingHeraldRulePHIDs !== null) { $where[] = qsprintf( $conn, '(event.rejectCode = %d AND event.rejectDetails IN (%Ls))', PhabricatorRepositoryPushLog::REJECT_HERALD, $this->blockingHeraldRulePHIDs); } return $where; } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); if ($this->shouldJoinPushEventTable()) { $joins[] = qsprintf( $conn, 'JOIN %T event ON event.phid = log.pushEventPHID', id(new PhabricatorRepositoryPushEvent())->getTableName()); } return $joins; } private function shouldJoinPushEventTable() { if ($this->blockingHeraldRulePHIDs !== null) { return true; } return false; } public function getQueryApplicationClass() { return 'PhabricatorDiffusionApplication'; } protected function getPrimaryTableAlias() { return 'log'; } } diff --git a/src/applications/repository/query/PhabricatorRepositoryRefCursorQuery.php b/src/applications/repository/query/PhabricatorRepositoryRefCursorQuery.php index b3c960e025..5e894333f6 100644 --- a/src/applications/repository/query/PhabricatorRepositoryRefCursorQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryRefCursorQuery.php @@ -1,153 +1,149 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withRepositoryPHIDs(array $phids) { $this->repositoryPHIDs = $phids; return $this; } public function withRefTypes(array $types) { $this->refTypes = $types; return $this; } public function withRefNames(array $names) { $this->refNames = $names; return $this; } public function withDatasourceQuery($query) { $this->datasourceQuery = $query; return $this; } public function needPositions($need) { $this->needPositions = $need; return $this; } public function newResultObject() { return new PhabricatorRepositoryRefCursor(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $refs) { $repository_phids = mpull($refs, 'getRepositoryPHID'); $repositories = id(new PhabricatorRepositoryQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($repository_phids) ->execute(); $repositories = mpull($repositories, null, 'getPHID'); foreach ($refs as $key => $ref) { $repository = idx($repositories, $ref->getRepositoryPHID()); if (!$repository) { $this->didRejectResult($ref); unset($refs[$key]); continue; } $ref->attachRepository($repository); } if (!$refs) { return $refs; } if ($this->needPositions) { $positions = id(new PhabricatorRepositoryRefPosition())->loadAllWhere( 'cursorID IN (%Ld)', mpull($refs, 'getID')); $positions = mgroup($positions, 'getCursorID'); foreach ($refs as $key => $ref) { $ref_positions = idx($positions, $ref->getID(), array()); $ref->attachPositions($ref_positions); } } return $refs; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->repositoryPHIDs !== null) { $where[] = qsprintf( $conn, 'repositoryPHID IN (%Ls)', $this->repositoryPHIDs); } if ($this->refTypes !== null) { $where[] = qsprintf( $conn, 'refType IN (%Ls)', $this->refTypes); } if ($this->refNames !== null) { $name_hashes = array(); foreach ($this->refNames as $name) { $name_hashes[] = PhabricatorHash::digestForIndex($name); } $where[] = qsprintf( $conn, 'refNameHash IN (%Ls)', $name_hashes); } if (strlen($this->datasourceQuery)) { $where[] = qsprintf( $conn, 'refNameRaw LIKE %>', $this->datasourceQuery); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorDiffusionApplication'; } } diff --git a/src/applications/repository/query/PhabricatorRepositorySyncEventQuery.php b/src/applications/repository/query/PhabricatorRepositorySyncEventQuery.php index 542cb5cdc0..cc568ef8e1 100644 --- a/src/applications/repository/query/PhabricatorRepositorySyncEventQuery.php +++ b/src/applications/repository/query/PhabricatorRepositorySyncEventQuery.php @@ -1,115 +1,111 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withRepositoryPHIDs(array $repository_phids) { $this->repositoryPHIDs = $repository_phids; return $this; } public function withEpochBetween($min, $max) { $this->epochMin = $min; $this->epochMax = $max; return $this; } public function newResultObject() { return new PhabricatorRepositorySyncEvent(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $events) { $repository_phids = mpull($events, 'getRepositoryPHID'); $repository_phids = array_filter($repository_phids); if ($repository_phids) { $repositories = id(new PhabricatorRepositoryQuery()) ->setViewer($this->getViewer()) ->withPHIDs($repository_phids) ->execute(); $repositories = mpull($repositories, null, 'getPHID'); } else { $repositories = array(); } foreach ($events as $key => $event) { $phid = $event->getRepositoryPHID(); if (empty($repositories[$phid])) { unset($events[$key]); $this->didRejectResult($event); continue; } $event->attachRepository($repositories[$phid]); } return $events; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->repositoryPHIDs !== null) { $where[] = qsprintf( $conn, 'repositoryPHID IN (%Ls)', $this->repositoryPHIDs); } if ($this->epochMin !== null) { $where[] = qsprintf( $conn, 'epoch >= %d', $this->epochMin); } if ($this->epochMax !== null) { $where[] = qsprintf( $conn, 'epoch <= %d', $this->epochMax); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorDiffusionApplication'; } } diff --git a/src/applications/repository/query/PhabricatorRepositoryURIQuery.php b/src/applications/repository/query/PhabricatorRepositoryURIQuery.php index 71252a6fb7..5b75e1ef63 100644 --- a/src/applications/repository/query/PhabricatorRepositoryURIQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryURIQuery.php @@ -1,101 +1,97 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withRepositoryPHIDs(array $phids) { $this->repositoryPHIDs = $phids; return $this; } public function withRepositories(array $repositories) { $repositories = mpull($repositories, null, 'getPHID'); $this->withRepositoryPHIDs(array_keys($repositories)); $this->repositories = $repositories; return $this; } public function newResultObject() { return new PhabricatorRepositoryURI(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->repositoryPHIDs !== null) { $where[] = qsprintf( $conn, 'repositoryPHID IN (%Ls)', $this->repositoryPHIDs); } return $where; } protected function willFilterPage(array $uris) { $repositories = $this->repositories; $repository_phids = mpull($uris, 'getRepositoryPHID'); $repository_phids = array_fuse($repository_phids); $repository_phids = array_diff_key($repository_phids, $repositories); if ($repository_phids) { $more_repositories = id(new PhabricatorRepositoryQuery()) ->setViewer($this->getViewer()) ->withPHIDs($repository_phids) ->execute(); $repositories += mpull($more_repositories, null, 'getPHID'); } foreach ($uris as $key => $uri) { $repository_phid = $uri->getRepositoryPHID(); $repository = idx($repositories, $repository_phid); if (!$repository) { $this->didRejectResult($uri); unset($uris[$key]); continue; } $uri->attachRepository($repository); } return $uris; } public function getQueryApplicationClass() { return 'PhabricatorDiffusionApplication'; } } diff --git a/src/applications/search/query/PhabricatorNamedQueryConfigQuery.php b/src/applications/search/query/PhabricatorNamedQueryConfigQuery.php index 6c4fad31a3..862f694fc8 100644 --- a/src/applications/search/query/PhabricatorNamedQueryConfigQuery.php +++ b/src/applications/search/query/PhabricatorNamedQueryConfigQuery.php @@ -1,64 +1,60 @@ ids = $ids; return $this; } public function withScopePHIDs(array $scope_phids) { $this->scopePHIDs = $scope_phids; return $this; } public function withEngineClassNames(array $engine_class_names) { $this->engineClassNames = $engine_class_names; return $this; } public function newResultObject() { return new PhabricatorNamedQueryConfig(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->engineClassNames !== null) { $where[] = qsprintf( $conn, 'engineClassName IN (%Ls)', $this->engineClassNames); } if ($this->scopePHIDs !== null) { $where[] = qsprintf( $conn, 'scopePHID IN (%Ls)', $this->scopePHIDs); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorSearchApplication'; } } diff --git a/src/applications/search/query/PhabricatorNamedQueryQuery.php b/src/applications/search/query/PhabricatorNamedQueryQuery.php index 3decff5494..0ed92646e6 100644 --- a/src/applications/search/query/PhabricatorNamedQueryQuery.php +++ b/src/applications/search/query/PhabricatorNamedQueryQuery.php @@ -1,77 +1,73 @@ ids = $ids; return $this; } public function withUserPHIDs(array $user_phids) { $this->userPHIDs = $user_phids; return $this; } public function withEngineClassNames(array $engine_class_names) { $this->engineClassNames = $engine_class_names; return $this; } public function withQueryKeys(array $query_keys) { $this->queryKeys = $query_keys; return $this; } public function newResultObject() { return new PhabricatorNamedQuery(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->engineClassNames !== null) { $where[] = qsprintf( $conn, 'engineClassName IN (%Ls)', $this->engineClassNames); } if ($this->userPHIDs !== null) { $where[] = qsprintf( $conn, 'userPHID IN (%Ls)', $this->userPHIDs); } if ($this->queryKeys !== null) { $where[] = qsprintf( $conn, 'queryKey IN (%Ls)', $this->queryKeys); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorSearchApplication'; } } diff --git a/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php b/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php index 16b5d793a4..9f26a81424 100644 --- a/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php +++ b/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php @@ -1,163 +1,159 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withProfilePHIDs(array $phids) { $this->profilePHIDs = $phids; return $this; } public function withCustomPHIDs(array $phids, $include_global = false) { $this->customPHIDs = $phids; $this->includeGlobal = $include_global; return $this; } public function withAffectedObjectPHIDs(array $phids) { $this->affectedObjectPHIDs = $phids; return $this; } public function newResultObject() { return new PhabricatorProfileMenuItemConfiguration(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'config.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'config.phid IN (%Ls)', $this->phids); } if ($this->profilePHIDs !== null) { $where[] = qsprintf( $conn, 'config.profilePHID IN (%Ls)', $this->profilePHIDs); } if ($this->customPHIDs !== null) { if ($this->customPHIDs && $this->includeGlobal) { $where[] = qsprintf( $conn, 'config.customPHID IN (%Ls) OR config.customPHID IS NULL', $this->customPHIDs); } else if ($this->customPHIDs) { $where[] = qsprintf( $conn, 'config.customPHID IN (%Ls)', $this->customPHIDs); } else { $where[] = qsprintf( $conn, 'config.customPHID IS NULL'); } } if ($this->affectedObjectPHIDs !== null) { $where[] = qsprintf( $conn, 'affected.dst IN (%Ls)', $this->affectedObjectPHIDs); } return $where; } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); if ($this->affectedObjectPHIDs !== null) { $joins[] = qsprintf( $conn, 'JOIN %T affected ON affected.src = config.phid AND affected.type = %d', PhabricatorEdgeConfig::TABLE_NAME_EDGE, PhabricatorProfileMenuItemAffectsObjectEdgeType::EDGECONST); } return $joins; } protected function willFilterPage(array $page) { $items = PhabricatorProfileMenuItem::getAllMenuItems(); foreach ($page as $key => $item) { $item_type = idx($items, $item->getMenuItemKey()); if (!$item_type) { $this->didRejectResult($item); unset($page[$key]); continue; } $item_type = clone $item_type; $item_type->setViewer($this->getViewer()); $item->attachMenuItem($item_type); } if (!$page) { return array(); } $profile_phids = mpull($page, 'getProfilePHID'); $profiles = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($profile_phids) ->execute(); $profiles = mpull($profiles, null, 'getPHID'); foreach ($page as $key => $item) { $profile = idx($profiles, $item->getProfilePHID()); if (!$profile) { $this->didRejectResult($item); unset($page[$key]); continue; } $item->attachProfileObject($profile); } return $page; } public function getQueryApplicationClass() { return 'PhabricatorSearchApplication'; } protected function getPrimaryTableAlias() { return 'config'; } } diff --git a/src/applications/slowvote/query/PhabricatorSlowvoteQuery.php b/src/applications/slowvote/query/PhabricatorSlowvoteQuery.php index 0b7335326c..72368a654a 100644 --- a/src/applications/slowvote/query/PhabricatorSlowvoteQuery.php +++ b/src/applications/slowvote/query/PhabricatorSlowvoteQuery.php @@ -1,174 +1,170 @@ ids = $ids; return $this; } public function withPHIDs($phids) { $this->phids = $phids; return $this; } public function withAuthorPHIDs($author_phids) { $this->authorPHIDs = $author_phids; return $this; } public function withVotesByViewer($with_vote) { $this->withVotesByViewer = $with_vote; return $this; } public function withIsClosed($with_closed) { $this->isClosed = $with_closed; return $this; } public function needOptions($need_options) { $this->needOptions = $need_options; return $this; } public function needChoices($need_choices) { $this->needChoices = $need_choices; return $this; } public function needViewerChoices($need_viewer_choices) { $this->needViewerChoices = $need_viewer_choices; return $this; } public function newResultObject() { return new PhabricatorSlowvotePoll(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $polls) { assert_instances_of($polls, 'PhabricatorSlowvotePoll'); $ids = mpull($polls, 'getID'); $viewer = $this->getViewer(); if ($this->needOptions) { $options = id(new PhabricatorSlowvoteOption())->loadAllWhere( 'pollID IN (%Ld)', $ids); $options = mgroup($options, 'getPollID'); foreach ($polls as $poll) { $poll->attachOptions(idx($options, $poll->getID(), array())); } } if ($this->needChoices) { $choices = id(new PhabricatorSlowvoteChoice())->loadAllWhere( 'pollID IN (%Ld)', $ids); $choices = mgroup($choices, 'getPollID'); foreach ($polls as $poll) { $poll->attachChoices(idx($choices, $poll->getID(), array())); } // If we need the viewer's choices, we can just fill them from the data // we already loaded. if ($this->needViewerChoices) { foreach ($polls as $poll) { $poll->attachViewerChoices( $viewer, idx( mgroup($poll->getChoices(), 'getAuthorPHID'), $viewer->getPHID(), array())); } } } else if ($this->needViewerChoices) { $choices = id(new PhabricatorSlowvoteChoice())->loadAllWhere( 'pollID IN (%Ld) AND authorPHID = %s', $ids, $viewer->getPHID()); $choices = mgroup($choices, 'getPollID'); foreach ($polls as $poll) { $poll->attachViewerChoices( $viewer, idx($choices, $poll->getID(), array())); } } return $polls; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'p.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'p.phid IN (%Ls)', $this->phids); } if ($this->authorPHIDs !== null) { $where[] = qsprintf( $conn, 'p.authorPHID IN (%Ls)', $this->authorPHIDs); } if ($this->isClosed !== null) { $where[] = qsprintf( $conn, 'p.isClosed = %d', (int)$this->isClosed); } return $where; } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); if ($this->withVotesByViewer !== null) { $joins[] = qsprintf( $conn, 'JOIN %T vv ON vv.pollID = p.id AND vv.authorPHID = %s', id(new PhabricatorSlowvoteChoice())->getTableName(), $this->getViewer()->getPHID()); } return $joins; } protected function getPrimaryTableAlias() { return 'p'; } public function getQueryApplicationClass() { return 'PhabricatorSlowvoteApplication'; } } diff --git a/src/applications/spaces/query/PhabricatorSpacesNamespaceQuery.php b/src/applications/spaces/query/PhabricatorSpacesNamespaceQuery.php index ee11dcdd06..388b6ab4d8 100644 --- a/src/applications/spaces/query/PhabricatorSpacesNamespaceQuery.php +++ b/src/applications/spaces/query/PhabricatorSpacesNamespaceQuery.php @@ -1,238 +1,238 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withIsDefaultNamespace($default) { $this->isDefaultNamespace = $default; return $this; } public function withIsArchived($archived) { $this->isArchived = $archived; return $this; } public function getQueryApplicationClass() { return 'PhabricatorSpacesApplication'; } - protected function loadPage() { - return $this->loadStandardPage(new PhabricatorSpacesNamespace()); + public function newResultObject() { + return new PhabricatorSpacesNamespace(); } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->isDefaultNamespace !== null) { if ($this->isDefaultNamespace) { $where[] = qsprintf( $conn, 'isDefaultNamespace = 1'); } else { $where[] = qsprintf( $conn, 'isDefaultNamespace IS NULL'); } } if ($this->isArchived !== null) { $where[] = qsprintf( $conn, 'isArchived = %d', (int)$this->isArchived); } return $where; } public static function destroySpacesCache() { $cache = PhabricatorCaches::getRequestCache(); $cache->deleteKeys( array( self::KEY_ALL, self::KEY_DEFAULT, )); } public static function getSpacesExist() { return (bool)self::getAllSpaces(); } public static function getViewerSpacesExist(PhabricatorUser $viewer) { if (!self::getSpacesExist()) { return false; } // If the viewer has access to only one space, pretend spaces simply don't // exist. $spaces = self::getViewerSpaces($viewer); return (count($spaces) > 1); } public static function getAllSpaces() { $cache = PhabricatorCaches::getRequestCache(); $cache_key = self::KEY_ALL; $spaces = $cache->getKey($cache_key); if ($spaces === null) { $spaces = id(new PhabricatorSpacesNamespaceQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->execute(); $spaces = mpull($spaces, null, 'getPHID'); $cache->setKey($cache_key, $spaces); } return $spaces; } public static function getDefaultSpace() { $cache = PhabricatorCaches::getRequestCache(); $cache_key = self::KEY_DEFAULT; $default_space = $cache->getKey($cache_key, false); if ($default_space === false) { $default_space = null; $spaces = self::getAllSpaces(); foreach ($spaces as $space) { if ($space->getIsDefaultNamespace()) { $default_space = $space; break; } } $cache->setKey($cache_key, $default_space); } return $default_space; } public static function getViewerSpaces(PhabricatorUser $viewer) { $cache = PhabricatorCaches::getRequestCache(); $cache_key = self::KEY_VIEWER.'('.$viewer->getCacheFragment().')'; $result = $cache->getKey($cache_key); if ($result === null) { $spaces = self::getAllSpaces(); $result = array(); foreach ($spaces as $key => $space) { $can_see = PhabricatorPolicyFilter::hasCapability( $viewer, $space, PhabricatorPolicyCapability::CAN_VIEW); if ($can_see) { $result[$key] = $space; } } $cache->setKey($cache_key, $result); } return $result; } public static function getViewerActiveSpaces(PhabricatorUser $viewer) { $spaces = self::getViewerSpaces($viewer); foreach ($spaces as $key => $space) { if ($space->getIsArchived()) { unset($spaces[$key]); } } return $spaces; } public static function getSpaceOptionsForViewer( PhabricatorUser $viewer, $space_phid) { $viewer_spaces = self::getViewerSpaces($viewer); $viewer_spaces = msort($viewer_spaces, 'getNamespaceName'); $map = array(); foreach ($viewer_spaces as $space) { // Skip archived spaces, unless the object is already in that space. if ($space->getIsArchived()) { if ($space->getPHID() != $space_phid) { continue; } } $map[$space->getPHID()] = pht( 'Space %s: %s', $space->getMonogram(), $space->getNamespaceName()); } return $map; } /** * Get the Space PHID for an object, if one exists. * * This is intended to simplify performing a bunch of redundant checks; you * can intentionally pass any value in (including `null`). * * @param wild * @return phid|null */ public static function getObjectSpacePHID($object) { if (!$object) { return null; } if (!($object instanceof PhabricatorSpacesInterface)) { return null; } $space_phid = $object->getSpacePHID(); if ($space_phid === null) { $default_space = self::getDefaultSpace(); if ($default_space) { $space_phid = $default_space->getPHID(); } } return $space_phid; } } diff --git a/src/applications/tokens/query/PhabricatorTokenGivenQuery.php b/src/applications/tokens/query/PhabricatorTokenGivenQuery.php index b4a86428c1..0e6ad9eb54 100644 --- a/src/applications/tokens/query/PhabricatorTokenGivenQuery.php +++ b/src/applications/tokens/query/PhabricatorTokenGivenQuery.php @@ -1,117 +1,113 @@ tokenPHIDs = $token_phids; return $this; } public function withObjectPHIDs(array $object_phids) { $this->objectPHIDs = $object_phids; return $this; } public function withAuthorPHIDs(array $author_phids) { $this->authorPHIDs = $author_phids; return $this; } public function newResultObject() { return new PhabricatorTokenGiven(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->authorPHIDs !== null) { $where[] = qsprintf( $conn, 'authorPHID IN (%Ls)', $this->authorPHIDs); } if ($this->objectPHIDs !== null) { $where[] = qsprintf( $conn, 'objectPHID IN (%Ls)', $this->objectPHIDs); } if ($this->tokenPHIDs !== null) { $where[] = qsprintf( $conn, 'tokenPHID IN (%Ls)', $this->tokenPHIDs); } return $where; } protected function willFilterPage(array $results) { $viewer = $this->getViewer(); $object_phids = mpull($results, 'getObjectPHID'); $objects = id(new PhabricatorObjectQuery()) ->setViewer($viewer) ->withPHIDs($object_phids) ->execute(); $objects = mpull($objects, null, 'getPHID'); foreach ($results as $key => $result) { $object = idx($objects, $result->getObjectPHID()); if ($object) { if ($object instanceof PhabricatorTokenReceiverInterface) { $result->attachObject($object); continue; } } $this->didRejectResult($result); unset($results[$key]); } if (!$results) { return $results; } $token_phids = mpull($results, 'getTokenPHID'); $tokens = id(new PhabricatorTokenQuery()) ->setViewer($viewer) ->withPHIDs($token_phids) ->execute(); $tokens = mpull($tokens, null, 'getPHID'); foreach ($results as $key => $result) { $token_phid = $result->getTokenPHID(); $token = idx($tokens, $token_phid); if (!$token) { $this->didRejectResult($result); unset($results[$key]); continue; } $result->attachToken($token); } return $results; } public function getQueryApplicationClass() { return 'PhabricatorTokensApplication'; } } diff --git a/src/applications/transactions/query/PhabricatorApplicationTransactionCommentQuery.php b/src/applications/transactions/query/PhabricatorApplicationTransactionCommentQuery.php index 4f6f45bea7..eb4d4d8174 100644 --- a/src/applications/transactions/query/PhabricatorApplicationTransactionCommentQuery.php +++ b/src/applications/transactions/query/PhabricatorApplicationTransactionCommentQuery.php @@ -1,123 +1,119 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withTransactionPHIDs(array $transaction_phids) { $this->transactionPHIDs = $transaction_phids; return $this; } public function withAuthorPHIDs(array $phids) { $this->authorPHIDs = $phids; return $this; } public function withIsDeleted($deleted) { $this->isDeleted = $deleted; return $this; } public function withHasTransaction($has_transaction) { $this->hasTransaction = $has_transaction; return $this; } public function newResultObject() { return $this->newApplicationTransactionCommentTemplate(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); $alias = $this->getPrimaryTableAlias(); if ($this->ids !== null) { $where[] = qsprintf( $conn, '%T.id IN (%Ld)', $alias, $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, '%T.phid IN (%Ls)', $alias, $this->phids); } if ($this->authorPHIDs !== null) { $where[] = qsprintf( $conn, '%T.authorPHID IN (%Ls)', $alias, $this->authorPHIDs); } if ($this->transactionPHIDs !== null) { $where[] = qsprintf( $conn, '%T.transactionPHID IN (%Ls)', $alias, $this->transactionPHIDs); } if ($this->isDeleted !== null) { $where[] = qsprintf( $conn, '%T.isDeleted = %d', $alias, (int)$this->isDeleted); } if ($this->hasTransaction !== null) { if ($this->hasTransaction) { $where[] = qsprintf( $conn, '%T.transactionPHID IS NOT NULL', $alias); } else { $where[] = qsprintf( $conn, '%T.transactionPHID IS NULL', $alias); } } return $where; } protected function getPrimaryTableAlias() { return 'xcomment'; } public function getQueryApplicationClass() { // TODO: Figure out the app via the template? return null; } } diff --git a/src/applications/xhprof/query/PhabricatorXHProfSampleQuery.php b/src/applications/xhprof/query/PhabricatorXHProfSampleQuery.php index 3e9643a7fc..2001fec0c7 100644 --- a/src/applications/xhprof/query/PhabricatorXHProfSampleQuery.php +++ b/src/applications/xhprof/query/PhabricatorXHProfSampleQuery.php @@ -1,51 +1,47 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function newResultObject() { return new PhabricatorXHProfSample(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorXHProfApplication'; } } diff --git a/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobQuery.php b/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobQuery.php index 32a9419a33..b359157e06 100644 --- a/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobQuery.php +++ b/src/infrastructure/daemon/workers/query/PhabricatorWorkerBulkJobQuery.php @@ -1,106 +1,102 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAuthorPHIDs(array $author_phids) { $this->authorPHIDs = $author_phids; return $this; } public function withBulkJobTypes(array $job_types) { $this->bulkJobTypes = $job_types; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function newResultObject() { return new PhabricatorWorkerBulkJob(); } - protected function loadPage() { - return $this->loadStandardPage($this->newResultObject()); - } - protected function willFilterPage(array $page) { $map = PhabricatorWorkerBulkJobType::getAllJobTypes(); foreach ($page as $key => $job) { $implementation = idx($map, $job->getJobTypeKey()); if (!$implementation) { $this->didRejectResult($job); unset($page[$key]); continue; } $job->attachJobImplementation($implementation); } return $page; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->authorPHIDs !== null) { $where[] = qsprintf( $conn, 'authorPHID IN (%Ls)', $this->authorPHIDs); } if ($this->bulkJobTypes !== null) { $where[] = qsprintf( $conn, 'bulkJobType IN (%Ls)', $this->bulkJobTypes); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'status IN (%Ls)', $this->statuses); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorDaemonsApplication'; } } diff --git a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php index 80791400e0..42ccad3316 100644 --- a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php +++ b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php @@ -1,3265 +1,3283 @@ getID(); } protected function newInternalCursorFromExternalCursor($cursor) { $viewer = $this->getViewer(); $query = newv(get_class($this), array()); $query ->setParentQuery($this) ->setViewer($viewer); // We're copying our order vector to the subquery so that the subquery // knows it should generate any supplemental information required by the // ordering. // For example, Phriction documents may be ordered by title, but the title // isn't a column in the "document" table: the query must JOIN the // "content" table to perform the ordering. Passing the ordering to the // subquery tells it that we need it to do that JOIN and attach relevant // paging information to the internal cursor object. // We only expect to load a single result, so the actual result order does // not matter. We only want the internal cursor for that result to look // like a cursor this parent query would generate. $query->setOrderVector($this->getOrderVector()); $this->applyExternalCursorConstraintsToQuery($query, $cursor); // If we have a Ferret fulltext query, copy it to the subquery so that we // generate ranking columns appropriately, and compute the correct object // ranking score for the current query. if ($this->ferretEngine) { $query->withFerretConstraint($this->ferretEngine, $this->ferretTokens); } // We're executing the subquery normally to make sure the viewer can // actually see the object, and that it's a completely valid object which // passes all filtering and policy checks. You aren't allowed to use an // object you can't see as a cursor, since this can leak information. $result = $query->executeOne(); if (!$result) { $this->throwCursorException( pht( 'Cursor "%s" does not identify a valid object in query "%s".', $cursor, get_class($this))); } // Now that we made sure the viewer can actually see the object the // external cursor identifies, return the internal cursor the query // generated as a side effect while loading the object. return $query->getInternalCursorObject(); } final protected function throwCursorException($message) { throw new PhabricatorInvalidQueryCursorException($message); } protected function applyExternalCursorConstraintsToQuery( PhabricatorCursorPagedPolicyAwareQuery $subquery, $cursor) { $subquery->withIDs(array($cursor)); } protected function newPagingMapFromCursorObject( PhabricatorQueryCursor $cursor, array $keys) { $object = $cursor->getObject(); return $this->newPagingMapFromPartialObject($object); } protected function newPagingMapFromPartialObject($object) { return array( 'id' => (int)$object->getID(), ); } private function getExternalCursorStringForResult($object) { $cursor = $this->newExternalCursorStringForResult($object); if (!is_string($cursor)) { throw new Exception( pht( 'Expected "newExternalCursorStringForResult()" in class "%s" to '. 'return a string, but got "%s".', get_class($this), phutil_describe_type($cursor))); } return $cursor; } final protected function getExternalCursorString() { return $this->externalCursorString; } private function setExternalCursorString($external_cursor) { $this->externalCursorString = $external_cursor; return $this; } final protected function getIsQueryOrderReversed() { return $this->isQueryOrderReversed; } final protected function setIsQueryOrderReversed($is_reversed) { $this->isQueryOrderReversed = $is_reversed; return $this; } private function getInternalCursorObject() { return $this->internalCursorObject; } private function setInternalCursorObject( PhabricatorQueryCursor $cursor) { $this->internalCursorObject = $cursor; return $this; } private function getInternalCursorFromExternalCursor( $cursor_string) { $cursor_object = $this->newInternalCursorFromExternalCursor($cursor_string); if (!($cursor_object instanceof PhabricatorQueryCursor)) { throw new Exception( pht( 'Expected "newInternalCursorFromExternalCursor()" to return an '. 'object of class "PhabricatorQueryCursor", but got "%s" (in '. 'class "%s").', phutil_describe_type($cursor_object), get_class($this))); } return $cursor_object; } private function getPagingMapFromCursorObject( PhabricatorQueryCursor $cursor, array $keys) { $map = $this->newPagingMapFromCursorObject($cursor, $keys); if (!is_array($map)) { throw new Exception( pht( 'Expected "newPagingMapFromCursorObject()" to return a map of '. 'paging values, but got "%s" (in class "%s").', phutil_describe_type($map), get_class($this))); } if ($this->supportsFerretEngine()) { if ($this->hasFerretOrder()) { $map += array( 'rank' => $cursor->getRawRowProperty(self::FULLTEXT_RANK), 'fulltext-modified' => $cursor->getRawRowProperty(self::FULLTEXT_MODIFIED), 'fulltext-created' => $cursor->getRawRowProperty(self::FULLTEXT_CREATED), ); } } foreach ($keys as $key) { if (!array_key_exists($key, $map)) { throw new Exception( pht( 'Map returned by "newPagingMapFromCursorObject()" in class "%s" '. 'omits required key "%s".', get_class($this), $key)); } } return $map; } final protected function nextPage(array $page) { if (!$page) { return; } $cursor = id(new PhabricatorQueryCursor()) ->setObject(last($page)); if ($this->rawCursorRow) { $cursor->setRawRow($this->rawCursorRow); } $this->setInternalCursorObject($cursor); } final public function getFerretMetadata() { if (!$this->supportsFerretEngine()) { throw new Exception( pht( 'Unable to retrieve Ferret engine metadata, this class ("%s") does '. 'not support the Ferret engine.', get_class($this))); } return $this->ferretMetadata; } + protected function loadPage() { + $object = $this->newResultObject(); + + if (!$object instanceof PhabricatorLiskDAO) { + throw new Exception( + pht( + 'Query class ("%s") did not return the correct type of object '. + 'from "newResultObject()" (expected a subclass of '. + '"PhabricatorLiskDAO", found "%s"). Return an object of the '. + 'expected type (this is common), or implement a custom '. + '"loadPage()" method (this is unusual in modern code).', + get_class($this), + phutil_describe_type($object))); + } + + return $this->loadStandardPage($object); + } + protected function loadStandardPage(PhabricatorLiskDAO $table) { $rows = $this->loadStandardPageRows($table); return $table->loadAllFromArray($rows); } protected function loadStandardPageRows(PhabricatorLiskDAO $table) { $conn = $table->establishConnection('r'); return $this->loadStandardPageRowsWithConnection( $conn, $table->getTableName()); } protected function loadStandardPageRowsWithConnection( AphrontDatabaseConnection $conn, $table_name) { $query = $this->buildStandardPageQuery($conn, $table_name); $rows = queryfx_all($conn, '%Q', $query); $rows = $this->didLoadRawRows($rows); return $rows; } protected function buildStandardPageQuery( AphrontDatabaseConnection $conn, $table_name) { $table_alias = $this->getPrimaryTableAlias(); if ($table_alias === null) { $table_alias = qsprintf($conn, ''); } else { $table_alias = qsprintf($conn, '%T', $table_alias); } return qsprintf( $conn, '%Q FROM %T %Q %Q %Q %Q %Q %Q %Q', $this->buildSelectClause($conn), $table_name, $table_alias, $this->buildJoinClause($conn), $this->buildWhereClause($conn), $this->buildGroupClause($conn), $this->buildHavingClause($conn), $this->buildOrderClause($conn), $this->buildLimitClause($conn)); } protected function didLoadRawRows(array $rows) { $this->rawCursorRow = last($rows); if ($this->ferretEngine) { foreach ($rows as $row) { $phid = $row['phid']; $metadata = id(new PhabricatorFerretMetadata()) ->setPHID($phid) ->setEngine($this->ferretEngine) ->setRelevance(idx($row, self::FULLTEXT_RANK)); $this->ferretMetadata[$phid] = $metadata; unset($row[self::FULLTEXT_RANK]); unset($row[self::FULLTEXT_MODIFIED]); unset($row[self::FULLTEXT_CREATED]); } } return $rows; } final protected function buildLimitClause(AphrontDatabaseConnection $conn) { if ($this->shouldLimitResults()) { $limit = $this->getRawResultLimit(); if ($limit) { return qsprintf($conn, 'LIMIT %d', $limit); } } return qsprintf($conn, ''); } protected function shouldLimitResults() { return true; } final protected function didLoadResults(array $results) { if ($this->getIsQueryOrderReversed()) { $results = array_reverse($results, $preserve_keys = true); } return $results; } final public function newIterator() { return new PhabricatorQueryIterator($this); } final public function executeWithCursorPager(AphrontCursorPagerView $pager) { $limit = $pager->getPageSize(); $this->setLimit($limit + 1); $after_id = phutil_string_cast($pager->getAfterID()); $before_id = phutil_string_cast($pager->getBeforeID()); if (phutil_nonempty_string($after_id)) { $this->setExternalCursorString($after_id); } else if (phutil_nonempty_string($before_id)) { $this->setExternalCursorString($before_id); $this->setIsQueryOrderReversed(true); } $results = $this->execute(); $count = count($results); $sliced_results = $pager->sliceResults($results); if ($sliced_results) { // If we have results, generate external-facing cursors from the visible // results. This stops us from leaking any internal details about objects // which we loaded but which were not visible to the viewer. if ($pager->getBeforeID() || ($count > $limit)) { $last_object = last($sliced_results); $cursor = $this->getExternalCursorStringForResult($last_object); $pager->setNextPageID($cursor); } if ($pager->getAfterID() || ($pager->getBeforeID() && ($count > $limit))) { $head_object = head($sliced_results); $cursor = $this->getExternalCursorStringForResult($head_object); $pager->setPrevPageID($cursor); } } return $sliced_results; } /** * Return the alias this query uses to identify the primary table. * * Some automatic query constructions may need to be qualified with a table * alias if the query performs joins which make column names ambiguous. If * this is the case, return the alias for the primary table the query * uses; generally the object table which has `id` and `phid` columns. * * @return string Alias for the primary table. */ protected function getPrimaryTableAlias() { return null; } public function newResultObject() { return null; } /* -( Building Query Clauses )--------------------------------------------- */ /** * @task clauses */ protected function buildSelectClause(AphrontDatabaseConnection $conn) { $parts = $this->buildSelectClauseParts($conn); return $this->formatSelectClause($conn, $parts); } /** * @task clauses */ protected function buildSelectClauseParts(AphrontDatabaseConnection $conn) { $select = array(); $alias = $this->getPrimaryTableAlias(); if ($alias) { $select[] = qsprintf($conn, '%T.*', $alias); } else { $select[] = qsprintf($conn, '*'); } $select[] = $this->buildEdgeLogicSelectClause($conn); $select[] = $this->buildFerretSelectClause($conn); return $select; } /** * @task clauses */ protected function buildJoinClause(AphrontDatabaseConnection $conn) { $joins = $this->buildJoinClauseParts($conn); return $this->formatJoinClause($conn, $joins); } /** * @task clauses */ protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = array(); $joins[] = $this->buildEdgeLogicJoinClause($conn); $joins[] = $this->buildApplicationSearchJoinClause($conn); $joins[] = $this->buildNgramsJoinClause($conn); $joins[] = $this->buildFerretJoinClause($conn); return $joins; } /** * @task clauses */ protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = $this->buildWhereClauseParts($conn); return $this->formatWhereClause($conn, $where); } /** * @task clauses */ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = array(); $where[] = $this->buildPagingWhereClause($conn); $where[] = $this->buildEdgeLogicWhereClause($conn); $where[] = $this->buildSpacesWhereClause($conn); $where[] = $this->buildNgramsWhereClause($conn); $where[] = $this->buildFerretWhereClause($conn); $where[] = $this->buildApplicationSearchWhereClause($conn); return $where; } /** * @task clauses */ protected function buildHavingClause(AphrontDatabaseConnection $conn) { $having = $this->buildHavingClauseParts($conn); $having[] = $this->buildPagingHavingClause($conn); return $this->formatHavingClause($conn, $having); } /** * @task clauses */ protected function buildHavingClauseParts(AphrontDatabaseConnection $conn) { $having = array(); $having[] = $this->buildEdgeLogicHavingClause($conn); return $having; } /** * @task clauses */ protected function buildGroupClause(AphrontDatabaseConnection $conn) { if (!$this->shouldGroupQueryResultRows()) { return qsprintf($conn, ''); } return qsprintf( $conn, 'GROUP BY %Q', $this->getApplicationSearchObjectPHIDColumn($conn)); } /** * @task clauses */ protected function shouldGroupQueryResultRows() { if ($this->shouldGroupEdgeLogicResultRows()) { return true; } if ($this->getApplicationSearchMayJoinMultipleRows()) { return true; } if ($this->shouldGroupNgramResultRows()) { return true; } if ($this->shouldGroupFerretResultRows()) { return true; } return false; } /* -( Paging )------------------------------------------------------------- */ private function buildPagingWhereClause(AphrontDatabaseConnection $conn) { if ($this->shouldPageWithHavingClause()) { return null; } return $this->buildPagingClause($conn); } private function buildPagingHavingClause(AphrontDatabaseConnection $conn) { if (!$this->shouldPageWithHavingClause()) { return null; } return $this->buildPagingClause($conn); } private function shouldPageWithHavingClause() { // If any of the paging conditions reference dynamic columns, we need to // put the paging conditions in a "HAVING" clause instead of a "WHERE" // clause. // For example, this happens when paging on the Ferret "rank" column, // since the "rank" value is computed dynamically in the SELECT statement. $orderable = $this->getOrderableColumns(); $vector = $this->getOrderVector(); foreach ($vector as $order) { $key = $order->getOrderKey(); $column = $orderable[$key]; if (!empty($column['having'])) { return true; } } return false; } /** * @task paging */ protected function buildPagingClause(AphrontDatabaseConnection $conn) { $orderable = $this->getOrderableColumns(); $vector = $this->getQueryableOrderVector(); // If we don't have a cursor object yet, it means we're trying to load // the first result page. We may need to build a cursor object from the // external string, or we may not need a paging clause yet. $cursor_object = $this->getInternalCursorObject(); if (!$cursor_object) { $external_cursor = $this->getExternalCursorString(); if ($external_cursor !== null) { $cursor_object = $this->getInternalCursorFromExternalCursor( $external_cursor); } } // If we still don't have a cursor object, this is the first result page // and we aren't paging it. We don't need to build a paging clause. if (!$cursor_object) { return qsprintf($conn, ''); } $reversed = $this->getIsQueryOrderReversed(); $keys = array(); foreach ($vector as $order) { $keys[] = $order->getOrderKey(); } $keys = array_fuse($keys); $value_map = $this->getPagingMapFromCursorObject( $cursor_object, $keys); $columns = array(); foreach ($vector as $order) { $key = $order->getOrderKey(); $column = $orderable[$key]; $column['value'] = $value_map[$key]; // If the vector component is reversed, we need to reverse whatever the // order of the column is. if ($order->getIsReversed()) { $column['reverse'] = !idx($column, 'reverse', false); } $columns[] = $column; } return $this->buildPagingClauseFromMultipleColumns( $conn, $columns, array( 'reversed' => $reversed, )); } /** * Simplifies the task of constructing a paging clause across multiple * columns. In the general case, this looks like: * * A > a OR (A = a AND B > b) OR (A = a AND B = b AND C > c) * * To build a clause, specify the name, type, and value of each column * to include: * * $this->buildPagingClauseFromMultipleColumns( * $conn_r, * array( * array( * 'table' => 't', * 'column' => 'title', * 'type' => 'string', * 'value' => $cursor->getTitle(), * 'reverse' => true, * ), * array( * 'table' => 't', * 'column' => 'id', * 'type' => 'int', * 'value' => $cursor->getID(), * ), * ), * array( * 'reversed' => $is_reversed, * )); * * This method will then return a composable clause for inclusion in WHERE. * * @param AphrontDatabaseConnection Connection query will execute on. * @param list Column description dictionaries. * @param map Additional construction options. * @return string Query clause. * @task paging */ final protected function buildPagingClauseFromMultipleColumns( AphrontDatabaseConnection $conn, array $columns, array $options) { foreach ($columns as $column) { PhutilTypeSpec::checkMap( $column, array( 'table' => 'optional string|null', 'column' => 'string', 'value' => 'wild', 'type' => 'string', 'reverse' => 'optional bool', 'unique' => 'optional bool', 'null' => 'optional string|null', 'requires-ferret' => 'optional bool', 'having' => 'optional bool', )); } PhutilTypeSpec::checkMap( $options, array( 'reversed' => 'optional bool', )); $is_query_reversed = idx($options, 'reversed', false); $clauses = array(); $accumulated = array(); $last_key = last_key($columns); foreach ($columns as $key => $column) { $type = $column['type']; $null = idx($column, 'null'); if ($column['value'] === null) { if ($null) { $value = null; } else { throw new Exception( pht( 'Column "%s" has null value, but does not specify a null '. 'behavior.', $key)); } } else { switch ($type) { case 'int': $value = qsprintf($conn, '%d', $column['value']); break; case 'float': $value = qsprintf($conn, '%f', $column['value']); break; case 'string': $value = qsprintf($conn, '%s', $column['value']); break; default: throw new Exception( pht( 'Column "%s" has unknown column type "%s".', $column['column'], $type)); } } $is_column_reversed = idx($column, 'reverse', false); $reverse = ($is_query_reversed xor $is_column_reversed); $clause = $accumulated; $table_name = idx($column, 'table'); $column_name = $column['column']; if ($table_name !== null) { $field = qsprintf($conn, '%T.%T', $table_name, $column_name); } else { $field = qsprintf($conn, '%T', $column_name); } $parts = array(); if ($null) { $can_page_if_null = ($null === 'head'); $can_page_if_nonnull = ($null === 'tail'); if ($reverse) { $can_page_if_null = !$can_page_if_null; $can_page_if_nonnull = !$can_page_if_nonnull; } $subclause = null; if ($can_page_if_null && $value === null) { $parts[] = qsprintf( $conn, '(%Q IS NOT NULL)', $field); } else if ($can_page_if_nonnull && $value !== null) { $parts[] = qsprintf( $conn, '(%Q IS NULL)', $field); } } if ($value !== null) { $parts[] = qsprintf( $conn, '%Q %Q %Q', $field, $reverse ? qsprintf($conn, '>') : qsprintf($conn, '<'), $value); } if ($parts) { $clause[] = qsprintf($conn, '%LO', $parts); } if ($clause) { $clauses[] = qsprintf($conn, '%LA', $clause); } if ($value === null) { $accumulated[] = qsprintf( $conn, '%Q IS NULL', $field); } else { $accumulated[] = qsprintf( $conn, '%Q = %Q', $field, $value); } } if ($clauses) { return qsprintf($conn, '%LO', $clauses); } return qsprintf($conn, ''); } /* -( Result Ordering )---------------------------------------------------- */ /** * Select a result ordering. * * This is a high-level method which selects an ordering from a predefined * list of builtin orders, as provided by @{method:getBuiltinOrders}. These * options are user-facing and not exhaustive, but are generally convenient * and meaningful. * * You can also use @{method:setOrderVector} to specify a low-level ordering * across individual orderable columns. This offers greater control but is * also more involved. * * @param string Key of a builtin order supported by this query. * @return this * @task order */ public function setOrder($order) { $aliases = $this->getBuiltinOrderAliasMap(); if (empty($aliases[$order])) { throw new Exception( pht( 'Query "%s" does not support a builtin order "%s". Supported orders '. 'are: %s.', get_class($this), $order, implode(', ', array_keys($aliases)))); } $this->builtinOrder = $aliases[$order]; $this->orderVector = null; return $this; } /** * Set a grouping order to apply before primary result ordering. * * This allows you to preface the query order vector with additional orders, * so you can effect "group by" queries while still respecting "order by". * * This is a high-level method which works alongside @{method:setOrder}. For * lower-level control over order vectors, use @{method:setOrderVector}. * * @param PhabricatorQueryOrderVector|list List of order keys. * @return this * @task order */ public function setGroupVector($vector) { $this->groupVector = $vector; $this->orderVector = null; return $this; } /** * Get builtin orders for this class. * * In application UIs, we want to be able to present users with a small * selection of meaningful order options (like "Order by Title") rather than * an exhaustive set of column ordering options. * * Meaningful user-facing orders are often really orders across multiple * columns: for example, a "title" ordering is usually implemented as a * "title, id" ordering under the hood. * * Builtin orders provide a mapping from convenient, understandable * user-facing orders to implementations. * * A builtin order should provide these keys: * * - `vector` (`list`): The actual order vector to use. * - `name` (`string`): Human-readable order name. * * @return map Map from builtin order keys to specification. * @task order */ public function getBuiltinOrders() { $orders = array( 'newest' => array( 'vector' => array('id'), 'name' => pht('Creation (Newest First)'), 'aliases' => array('created'), ), 'oldest' => array( 'vector' => array('-id'), 'name' => pht('Creation (Oldest First)'), ), ); $object = $this->newResultObject(); if ($object instanceof PhabricatorCustomFieldInterface) { $list = PhabricatorCustomField::getObjectFields( $object, PhabricatorCustomField::ROLE_APPLICATIONSEARCH); foreach ($list->getFields() as $field) { $index = $field->buildOrderIndex(); if (!$index) { continue; } $legacy_key = 'custom:'.$field->getFieldKey(); $modern_key = $field->getModernFieldKey(); $orders[$modern_key] = array( 'vector' => array($modern_key, 'id'), 'name' => $field->getFieldName(), 'aliases' => array($legacy_key), ); $orders['-'.$modern_key] = array( 'vector' => array('-'.$modern_key, '-id'), 'name' => pht('%s (Reversed)', $field->getFieldName()), ); } } if ($this->supportsFerretEngine()) { $orders['relevance'] = array( 'vector' => array('rank', 'fulltext-modified', 'id'), 'name' => pht('Relevance'), ); } return $orders; } public function getBuiltinOrderAliasMap() { $orders = $this->getBuiltinOrders(); $map = array(); foreach ($orders as $key => $order) { $keys = array(); $keys[] = $key; foreach (idx($order, 'aliases', array()) as $alias) { $keys[] = $alias; } foreach ($keys as $alias) { if (isset($map[$alias])) { throw new Exception( pht( 'Two builtin orders ("%s" and "%s") define the same key or '. 'alias ("%s"). Each order alias and key must be unique and '. 'identify a single order.', $key, $map[$alias], $alias)); } $map[$alias] = $key; } } return $map; } /** * Set a low-level column ordering. * * This is a low-level method which offers granular control over column * ordering. In most cases, applications can more easily use * @{method:setOrder} to choose a high-level builtin order. * * To set an order vector, specify a list of order keys as provided by * @{method:getOrderableColumns}. * * @param PhabricatorQueryOrderVector|list List of order keys. * @return this * @task order */ public function setOrderVector($vector) { $vector = PhabricatorQueryOrderVector::newFromVector($vector); $orderable = $this->getOrderableColumns(); // Make sure that all the components identify valid columns. $unique = array(); foreach ($vector as $order) { $key = $order->getOrderKey(); if (empty($orderable[$key])) { $valid = implode(', ', array_keys($orderable)); throw new Exception( pht( 'This query ("%s") does not support sorting by order key "%s". '. 'Supported orders are: %s.', get_class($this), $key, $valid)); } $unique[$key] = idx($orderable[$key], 'unique', false); } // Make sure that the last column is unique so that this is a strong // ordering which can be used for paging. $last = last($unique); if ($last !== true) { throw new Exception( pht( 'Order vector "%s" is invalid: the last column in an order must '. 'be a column with unique values, but "%s" is not unique.', $vector->getAsString(), last_key($unique))); } // Make sure that other columns are not unique; an ordering like "id, name" // does not make sense because only "id" can ever have an effect. array_pop($unique); foreach ($unique as $key => $is_unique) { if ($is_unique) { throw new Exception( pht( 'Order vector "%s" is invalid: only the last column in an order '. 'may be unique, but "%s" is a unique column and not the last '. 'column in the order.', $vector->getAsString(), $key)); } } $this->orderVector = $vector; return $this; } /** * Get the effective order vector. * * @return PhabricatorQueryOrderVector Effective vector. * @task order */ protected function getOrderVector() { if (!$this->orderVector) { if ($this->builtinOrder !== null) { $builtin_order = idx($this->getBuiltinOrders(), $this->builtinOrder); $vector = $builtin_order['vector']; } else { $vector = $this->getDefaultOrderVector(); } if ($this->groupVector) { $group = PhabricatorQueryOrderVector::newFromVector($this->groupVector); $group->appendVector($vector); $vector = $group; } $vector = PhabricatorQueryOrderVector::newFromVector($vector); // We call setOrderVector() here to apply checks to the default vector. // This catches any errors in the implementation. $this->setOrderVector($vector); } return $this->orderVector; } /** * @task order */ protected function getDefaultOrderVector() { return array('id'); } /** * @task order */ public function getOrderableColumns() { $cache = PhabricatorCaches::getRequestCache(); $class = get_class($this); $cache_key = 'query.orderablecolumns.'.$class; $columns = $cache->getKey($cache_key); if ($columns !== null) { return $columns; } $columns = array( 'id' => array( 'table' => $this->getPrimaryTableAlias(), 'column' => 'id', 'reverse' => false, 'type' => 'int', 'unique' => true, ), ); $object = $this->newResultObject(); if ($object instanceof PhabricatorCustomFieldInterface) { $list = PhabricatorCustomField::getObjectFields( $object, PhabricatorCustomField::ROLE_APPLICATIONSEARCH); foreach ($list->getFields() as $field) { $index = $field->buildOrderIndex(); if (!$index) { continue; } $digest = $field->getFieldIndex(); $key = $field->getModernFieldKey(); $columns[$key] = array( 'table' => 'appsearch_order_'.$digest, 'column' => 'indexValue', 'type' => $index->getIndexValueType(), 'null' => 'tail', 'customfield' => true, 'customfield.index.table' => $index->getTableName(), 'customfield.index.key' => $digest, ); } } if ($this->supportsFerretEngine()) { $columns['rank'] = array( 'table' => null, 'column' => self::FULLTEXT_RANK, 'type' => 'int', 'requires-ferret' => true, 'having' => true, ); $columns['fulltext-created'] = array( 'table' => null, 'column' => self::FULLTEXT_CREATED, 'type' => 'int', 'requires-ferret' => true, ); $columns['fulltext-modified'] = array( 'table' => null, 'column' => self::FULLTEXT_MODIFIED, 'type' => 'int', 'requires-ferret' => true, ); } $cache->setKey($cache_key, $columns); return $columns; } /** * @task order */ final protected function buildOrderClause( AphrontDatabaseConnection $conn, $for_union = false) { $orderable = $this->getOrderableColumns(); $vector = $this->getQueryableOrderVector(); $parts = array(); foreach ($vector as $order) { $part = $orderable[$order->getOrderKey()]; if ($order->getIsReversed()) { $part['reverse'] = !idx($part, 'reverse', false); } $parts[] = $part; } return $this->formatOrderClause($conn, $parts, $for_union); } /** * @task order */ private function getQueryableOrderVector() { $vector = $this->getOrderVector(); $orderable = $this->getOrderableColumns(); $keep = array(); foreach ($vector as $order) { $column = $orderable[$order->getOrderKey()]; // If this is a Ferret fulltext column but the query doesn't actually // have a fulltext query, we'll skip most of the Ferret stuff and won't // actually have the columns in the result set. Just skip them. if (!empty($column['requires-ferret'])) { if (!$this->getFerretTokens()) { continue; } } $keep[] = $order->getAsScalar(); } return PhabricatorQueryOrderVector::newFromVector($keep); } /** * @task order */ protected function formatOrderClause( AphrontDatabaseConnection $conn, array $parts, $for_union = false) { $is_query_reversed = $this->getIsQueryOrderReversed(); $sql = array(); foreach ($parts as $key => $part) { $is_column_reversed = !empty($part['reverse']); $descending = true; if ($is_query_reversed) { $descending = !$descending; } if ($is_column_reversed) { $descending = !$descending; } $table = idx($part, 'table'); // When we're building an ORDER BY clause for a sequence of UNION // statements, we can't refer to tables from the subqueries. if ($for_union) { $table = null; } $column = $part['column']; if ($table !== null) { $field = qsprintf($conn, '%T.%T', $table, $column); } else { $field = qsprintf($conn, '%T', $column); } $null = idx($part, 'null'); if ($null) { switch ($null) { case 'head': $null_field = qsprintf($conn, '(%Q IS NULL)', $field); break; case 'tail': $null_field = qsprintf($conn, '(%Q IS NOT NULL)', $field); break; default: throw new Exception( pht( 'NULL value "%s" is invalid. Valid values are "head" and '. '"tail".', $null)); } if ($descending) { $sql[] = qsprintf($conn, '%Q DESC', $null_field); } else { $sql[] = qsprintf($conn, '%Q ASC', $null_field); } } if ($descending) { $sql[] = qsprintf($conn, '%Q DESC', $field); } else { $sql[] = qsprintf($conn, '%Q ASC', $field); } } return qsprintf($conn, 'ORDER BY %LQ', $sql); } /* -( Application Search )------------------------------------------------- */ /** * Constrain the query with an ApplicationSearch index, requiring field values * contain at least one of the values in a set. * * This constraint can build the most common types of queries, like: * * - Find users with shirt sizes "X" or "XL". * - Find shoes with size "13". * * @param PhabricatorCustomFieldIndexStorage Table where the index is stored. * @param string|list One or more values to filter by. * @return this * @task appsearch */ public function withApplicationSearchContainsConstraint( PhabricatorCustomFieldIndexStorage $index, $value) { $values = (array)$value; $data_values = array(); $constraint_values = array(); foreach ($values as $value) { if ($value instanceof PhabricatorQueryConstraint) { $constraint_values[] = $value; } else { $data_values[] = $value; } } $alias = 'appsearch_'.count($this->applicationSearchConstraints); $this->applicationSearchConstraints[] = array( 'type' => $index->getIndexValueType(), 'cond' => '=', 'table' => $index->getTableName(), 'index' => $index->getIndexKey(), 'alias' => $alias, 'value' => $values, 'data' => $data_values, 'constraints' => $constraint_values, ); return $this; } /** * Constrain the query with an ApplicationSearch index, requiring values * exist in a given range. * * This constraint is useful for expressing date ranges: * * - Find events between July 1st and July 7th. * * The ends of the range are inclusive, so a `$min` of `3` and a `$max` of * `5` will match fields with values `3`, `4`, or `5`. Providing `null` for * either end of the range will leave that end of the constraint open. * * @param PhabricatorCustomFieldIndexStorage Table where the index is stored. * @param int|null Minimum permissible value, inclusive. * @param int|null Maximum permissible value, inclusive. * @return this * @task appsearch */ public function withApplicationSearchRangeConstraint( PhabricatorCustomFieldIndexStorage $index, $min, $max) { $index_type = $index->getIndexValueType(); if ($index_type != 'int') { throw new Exception( pht( 'Attempting to apply a range constraint to a field with index type '. '"%s", expected type "%s".', $index_type, 'int')); } $alias = 'appsearch_'.count($this->applicationSearchConstraints); $this->applicationSearchConstraints[] = array( 'type' => $index->getIndexValueType(), 'cond' => 'range', 'table' => $index->getTableName(), 'index' => $index->getIndexKey(), 'alias' => $alias, 'value' => array($min, $max), 'data' => null, 'constraints' => null, ); return $this; } /** * Get the name of the query's primary object PHID column, for constructing * JOIN clauses. Normally (and by default) this is just `"phid"`, but it may * be something more exotic. * * See @{method:getPrimaryTableAlias} if the column needs to be qualified with * a table alias. * * @param AphrontDatabaseConnection Connection executing queries. * @return PhutilQueryString Column name. * @task appsearch */ protected function getApplicationSearchObjectPHIDColumn( AphrontDatabaseConnection $conn) { if ($this->getPrimaryTableAlias()) { return qsprintf($conn, '%T.phid', $this->getPrimaryTableAlias()); } else { return qsprintf($conn, 'phid'); } } /** * Determine if the JOINs built by ApplicationSearch might cause each primary * object to return multiple result rows. Generally, this means the query * needs an extra GROUP BY clause. * * @return bool True if the query may return multiple rows for each object. * @task appsearch */ protected function getApplicationSearchMayJoinMultipleRows() { foreach ($this->applicationSearchConstraints as $constraint) { $type = $constraint['type']; $value = $constraint['value']; $cond = $constraint['cond']; switch ($cond) { case '=': switch ($type) { case 'string': case 'int': if (count($value) > 1) { return true; } break; default: throw new Exception(pht('Unknown index type "%s"!', $type)); } break; case 'range': // NOTE: It's possible to write a custom field where multiple rows // match a range constraint, but we don't currently ship any in the // upstream and I can't immediately come up with cases where this // would make sense. break; default: throw new Exception(pht('Unknown constraint condition "%s"!', $cond)); } } return false; } /** * Construct a GROUP BY clause appropriate for ApplicationSearch constraints. * * @param AphrontDatabaseConnection Connection executing the query. * @return string Group clause. * @task appsearch */ protected function buildApplicationSearchGroupClause( AphrontDatabaseConnection $conn) { if ($this->getApplicationSearchMayJoinMultipleRows()) { return qsprintf( $conn, 'GROUP BY %Q', $this->getApplicationSearchObjectPHIDColumn($conn)); } else { return qsprintf($conn, ''); } } /** * Construct a JOIN clause appropriate for applying ApplicationSearch * constraints. * * @param AphrontDatabaseConnection Connection executing the query. * @return string Join clause. * @task appsearch */ protected function buildApplicationSearchJoinClause( AphrontDatabaseConnection $conn) { $joins = array(); foreach ($this->applicationSearchConstraints as $key => $constraint) { $table = $constraint['table']; $alias = $constraint['alias']; $index = $constraint['index']; $cond = $constraint['cond']; $phid_column = $this->getApplicationSearchObjectPHIDColumn($conn); switch ($cond) { case '=': // Figure out whether we need to do a LEFT JOIN or not. We need to // LEFT JOIN if we're going to select "IS NULL" rows. $join_type = qsprintf($conn, 'JOIN'); foreach ($constraint['constraints'] as $query_constraint) { $op = $query_constraint->getOperator(); if ($op === PhabricatorQueryConstraint::OPERATOR_NULL) { $join_type = qsprintf($conn, 'LEFT JOIN'); break; } } $joins[] = qsprintf( $conn, '%Q %T %T ON %T.objectPHID = %Q AND %T.indexKey = %s', $join_type, $table, $alias, $alias, $phid_column, $alias, $index); break; case 'range': list($min, $max) = $constraint['value']; if (($min === null) && ($max === null)) { // If there's no actual range constraint, just move on. break; } if ($min === null) { $constraint_clause = qsprintf( $conn, '%T.indexValue <= %d', $alias, $max); } else if ($max === null) { $constraint_clause = qsprintf( $conn, '%T.indexValue >= %d', $alias, $min); } else { $constraint_clause = qsprintf( $conn, '%T.indexValue BETWEEN %d AND %d', $alias, $min, $max); } $joins[] = qsprintf( $conn, 'JOIN %T %T ON %T.objectPHID = %Q AND %T.indexKey = %s AND (%Q)', $table, $alias, $alias, $phid_column, $alias, $index, $constraint_clause); break; default: throw new Exception(pht('Unknown constraint condition "%s"!', $cond)); } } $phid_column = $this->getApplicationSearchObjectPHIDColumn($conn); $orderable = $this->getOrderableColumns(); $vector = $this->getOrderVector(); foreach ($vector as $order) { $spec = $orderable[$order->getOrderKey()]; if (empty($spec['customfield'])) { continue; } $table = $spec['customfield.index.table']; $alias = $spec['table']; $key = $spec['customfield.index.key']; $joins[] = qsprintf( $conn, 'LEFT JOIN %T %T ON %T.objectPHID = %Q AND %T.indexKey = %s', $table, $alias, $alias, $phid_column, $alias, $key); } if ($joins) { return qsprintf($conn, '%LJ', $joins); } else { return qsprintf($conn, ''); } } /** * Construct a WHERE clause appropriate for applying ApplicationSearch * constraints. * * @param AphrontDatabaseConnection Connection executing the query. * @return list Where clause parts. * @task appsearch */ protected function buildApplicationSearchWhereClause( AphrontDatabaseConnection $conn) { $where = array(); foreach ($this->applicationSearchConstraints as $key => $constraint) { $alias = $constraint['alias']; $cond = $constraint['cond']; $type = $constraint['type']; $data_values = $constraint['data']; $constraint_values = $constraint['constraints']; $constraint_parts = array(); switch ($cond) { case '=': if ($data_values) { switch ($type) { case 'string': $constraint_parts[] = qsprintf( $conn, '%T.indexValue IN (%Ls)', $alias, $data_values); break; case 'int': $constraint_parts[] = qsprintf( $conn, '%T.indexValue IN (%Ld)', $alias, $data_values); break; default: throw new Exception(pht('Unknown index type "%s"!', $type)); } } if ($constraint_values) { foreach ($constraint_values as $value) { $op = $value->getOperator(); switch ($op) { case PhabricatorQueryConstraint::OPERATOR_NULL: $constraint_parts[] = qsprintf( $conn, '%T.indexValue IS NULL', $alias); break; case PhabricatorQueryConstraint::OPERATOR_ANY: $constraint_parts[] = qsprintf( $conn, '%T.indexValue IS NOT NULL', $alias); break; default: throw new Exception( pht( 'No support for applying operator "%s" against '. 'index of type "%s".', $op, $type)); } } } if ($constraint_parts) { $where[] = qsprintf($conn, '%LO', $constraint_parts); } break; } } return $where; } /* -( Integration with CustomField )--------------------------------------- */ /** * @task customfield */ protected function getPagingValueMapForCustomFields( PhabricatorCustomFieldInterface $object) { // We have to get the current field values on the cursor object. $fields = PhabricatorCustomField::getObjectFields( $object, PhabricatorCustomField::ROLE_APPLICATIONSEARCH); $fields->setViewer($this->getViewer()); $fields->readFieldsFromStorage($object); $map = array(); foreach ($fields->getFields() as $field) { $map['custom:'.$field->getFieldKey()] = $field->getValueForStorage(); } return $map; } /** * @task customfield */ protected function isCustomFieldOrderKey($key) { $prefix = 'custom:'; return !strncmp($key, $prefix, strlen($prefix)); } /* -( Ferret )------------------------------------------------------------- */ public function supportsFerretEngine() { $object = $this->newResultObject(); return ($object instanceof PhabricatorFerretInterface); } public function withFerretQuery( PhabricatorFerretEngine $engine, PhabricatorSavedQuery $query) { if (!$this->supportsFerretEngine()) { throw new Exception( pht( 'Query ("%s") does not support the Ferret fulltext engine.', get_class($this))); } $this->ferretEngine = $engine; $this->ferretQuery = $query; return $this; } public function getFerretTokens() { if (!$this->supportsFerretEngine()) { throw new Exception( pht( 'Query ("%s") does not support the Ferret fulltext engine.', get_class($this))); } return $this->ferretTokens; } public function withFerretConstraint( PhabricatorFerretEngine $engine, array $fulltext_tokens) { if (!$this->supportsFerretEngine()) { throw new Exception( pht( 'Query ("%s") does not support the Ferret fulltext engine.', get_class($this))); } if ($this->ferretEngine) { throw new Exception( pht( 'Query may not have multiple fulltext constraints.')); } if (!$fulltext_tokens) { return $this; } $this->ferretEngine = $engine; $this->ferretTokens = $fulltext_tokens; $op_absent = PhutilSearchQueryCompiler::OPERATOR_ABSENT; $default_function = $engine->getDefaultFunctionKey(); $table_map = array(); $idx = 1; foreach ($this->ferretTokens as $fulltext_token) { $raw_token = $fulltext_token->getToken(); $function = $raw_token->getFunction(); if ($function === null) { $function = $default_function; } $function_def = $engine->getFunctionForName($function); // NOTE: The query compiler guarantees that a query can not make a // field both "present" and "absent", so it's safe to just use the // first operator we encounter to determine whether the table is // optional or not. $operator = $raw_token->getOperator(); $is_optional = ($operator === $op_absent); if (!isset($table_map[$function])) { $alias = 'ftfield_'.$idx++; $table_map[$function] = array( 'alias' => $alias, 'function' => $function_def, 'optional' => $is_optional, ); } } // Join the title field separately so we can rank results. $table_map['rank'] = array( 'alias' => 'ft_rank', 'function' => $engine->getFunctionForName('title'), // See T13345. Not every document has a title, so we want to LEFT JOIN // this table to avoid excluding documents with no title that match // the query in other fields. 'optional' => true, ); $this->ferretTables = $table_map; return $this; } protected function buildFerretSelectClause(AphrontDatabaseConnection $conn) { $select = array(); if (!$this->supportsFerretEngine()) { return $select; } if (!$this->hasFerretOrder()) { // We only need to SELECT the virtual rank/relevance columns if we're // actually sorting the results by rank. return $select; } if (!$this->ferretEngine) { $select[] = qsprintf($conn, '0 AS %T', self::FULLTEXT_RANK); $select[] = qsprintf($conn, '0 AS %T', self::FULLTEXT_CREATED); $select[] = qsprintf($conn, '0 AS %T', self::FULLTEXT_MODIFIED); return $select; } $engine = $this->ferretEngine; $stemmer = $engine->newStemmer(); $op_sub = PhutilSearchQueryCompiler::OPERATOR_SUBSTRING; $op_not = PhutilSearchQueryCompiler::OPERATOR_NOT; $table_alias = 'ft_rank'; $parts = array(); foreach ($this->ferretTokens as $fulltext_token) { $raw_token = $fulltext_token->getToken(); $value = $raw_token->getValue(); if ($raw_token->getOperator() == $op_not) { // Ignore "not" terms when ranking, since they aren't useful. continue; } if ($raw_token->getOperator() == $op_sub) { $is_substring = true; } else { $is_substring = false; } if ($is_substring) { $parts[] = qsprintf( $conn, 'IF(%T.rawCorpus LIKE %~, 2, 0)', $table_alias, $value); continue; } if ($raw_token->isQuoted()) { $is_quoted = true; $is_stemmed = false; } else { $is_quoted = false; $is_stemmed = true; } $term_constraints = array(); $term_value = $engine->newTermsCorpus($value); $parts[] = qsprintf( $conn, 'IF(%T.termCorpus LIKE %~, 2, 0)', $table_alias, $term_value); if ($is_stemmed) { $stem_value = $stemmer->stemToken($value); $stem_value = $engine->newTermsCorpus($stem_value); $parts[] = qsprintf( $conn, 'IF(%T.normalCorpus LIKE %~, 1, 0)', $table_alias, $stem_value); } } $parts[] = qsprintf($conn, '%d', 0); $sum = array_shift($parts); foreach ($parts as $part) { $sum = qsprintf( $conn, '%Q + %Q', $sum, $part); } $select[] = qsprintf( $conn, '%Q AS %T', $sum, self::FULLTEXT_RANK); // See D20297. We select these as real columns in the result set so that // constructions like this will work: // // ((SELECT ...) UNION (SELECT ...)) ORDER BY ... // // If the columns aren't part of the result set, the final "ORDER BY" can // not act on them. $select[] = qsprintf( $conn, 'ft_doc.epochCreated AS %T', self::FULLTEXT_CREATED); $select[] = qsprintf( $conn, 'ft_doc.epochModified AS %T', self::FULLTEXT_MODIFIED); return $select; } protected function buildFerretJoinClause(AphrontDatabaseConnection $conn) { if (!$this->ferretEngine) { return array(); } $op_sub = PhutilSearchQueryCompiler::OPERATOR_SUBSTRING; $op_not = PhutilSearchQueryCompiler::OPERATOR_NOT; $op_absent = PhutilSearchQueryCompiler::OPERATOR_ABSENT; $op_present = PhutilSearchQueryCompiler::OPERATOR_PRESENT; $engine = $this->ferretEngine; $stemmer = $engine->newStemmer(); $ngram_table = $engine->getNgramsTableName(); $ngram_engine = $this->getNgramEngine(); $flat = array(); foreach ($this->ferretTokens as $fulltext_token) { $raw_token = $fulltext_token->getToken(); $operator = $raw_token->getOperator(); // If this is a negated term like "-pomegranate", don't join the ngram // table since we aren't looking for documents with this term. (We could // LEFT JOIN the table and require a NULL row, but this is probably more // trouble than it's worth.) if ($operator === $op_not) { continue; } // Neither the "present" or "absent" operators benefit from joining // the ngram table. if ($operator === $op_absent || $operator === $op_present) { continue; } $value = $raw_token->getValue(); $length = count(phutil_utf8v($value)); if ($raw_token->getOperator() == $op_sub) { $is_substring = true; } else { $is_substring = false; } // If the user specified a substring query for a substring which is // shorter than the ngram length, we can't use the ngram index, so // don't do a join. We'll fall back to just doing LIKE on the full // corpus. if ($is_substring) { if ($length < 3) { continue; } } if ($raw_token->isQuoted()) { $is_stemmed = false; } else { $is_stemmed = true; } if ($is_substring) { $ngrams = $ngram_engine->getSubstringNgramsFromString($value); } else { $terms_value = $engine->newTermsCorpus($value); $ngrams = $ngram_engine->getTermNgramsFromString($terms_value); // If this is a stemmed term, only look for ngrams present in both the // unstemmed and stemmed variations. if ($is_stemmed) { // Trim the boundary space characters so the stemmer recognizes this // is (or, at least, may be) a normal word and activates. $terms_value = trim($terms_value, ' '); $stem_value = $stemmer->stemToken($terms_value); $stem_ngrams = $ngram_engine->getTermNgramsFromString($stem_value); $ngrams = array_intersect($ngrams, $stem_ngrams); } } foreach ($ngrams as $ngram) { $flat[] = array( 'table' => $ngram_table, 'ngram' => $ngram, ); } } // Remove common ngrams, like "the", which occur too frequently in // documents to be useful in constraining the query. The best ngrams // are obscure sequences which occur in very few documents. if ($flat) { $common_ngrams = queryfx_all( $conn, 'SELECT ngram FROM %T WHERE ngram IN (%Ls)', $engine->getCommonNgramsTableName(), ipull($flat, 'ngram')); $common_ngrams = ipull($common_ngrams, 'ngram', 'ngram'); foreach ($flat as $key => $spec) { $ngram = $spec['ngram']; if (isset($common_ngrams[$ngram])) { unset($flat[$key]); continue; } // NOTE: MySQL discards trailing whitespace in CHAR(X) columns. $trim_ngram = rtrim($ngram, ' '); if (isset($common_ngrams[$trim_ngram])) { unset($flat[$key]); continue; } } } // MySQL only allows us to join a maximum of 61 tables per query. Each // ngram is going to cost us a join toward that limit, so if the user // specified a very long query string, just pick 16 of the ngrams // at random. if (count($flat) > 16) { shuffle($flat); $flat = array_slice($flat, 0, 16); } $alias = $this->getPrimaryTableAlias(); if ($alias) { $phid_column = qsprintf($conn, '%T.%T', $alias, 'phid'); } else { $phid_column = qsprintf($conn, '%T', 'phid'); } $document_table = $engine->getDocumentTableName(); $field_table = $engine->getFieldTableName(); $joins = array(); $joins[] = qsprintf( $conn, 'JOIN %T ft_doc ON ft_doc.objectPHID = %Q', $document_table, $phid_column); $idx = 1; foreach ($flat as $spec) { $table = $spec['table']; $ngram = $spec['ngram']; $alias = 'ftngram_'.$idx++; $joins[] = qsprintf( $conn, 'JOIN %T %T ON %T.documentID = ft_doc.id AND %T.ngram = %s', $table, $alias, $alias, $alias, $ngram); } $object = $this->newResultObject(); if (!$object) { throw new Exception( pht( 'Query class ("%s") must define "newResultObject()" to use '. 'Ferret constraints.', get_class($this))); } // See T13511. If we have a fulltext query which uses valid field // functions, but at least one of the functions applies to a field which // the object can never have, the query can never match anything. Detect // this and return an empty result set. // (Even if the query is "field is absent" or "field does not contain // such-and-such", the interpretation is that these constraints are // not meaningful when applied to an object which can never have the // field.) $functions = ipull($this->ferretTables, 'function'); $functions = mpull($functions, null, 'getFerretFunctionName'); foreach ($functions as $function) { if (!$function->supportsObject($object)) { throw new PhabricatorEmptyQueryException( pht( 'This query uses a fulltext function which this document '. 'type does not support.')); } } foreach ($this->ferretTables as $table) { $alias = $table['alias']; if (empty($table['optional'])) { $join_type = qsprintf($conn, 'JOIN'); } else { $join_type = qsprintf($conn, 'LEFT JOIN'); } $joins[] = qsprintf( $conn, '%Q %T %T ON ft_doc.id = %T.documentID AND %T.fieldKey = %s', $join_type, $field_table, $alias, $alias, $alias, $table['function']->getFerretFieldKey()); } return $joins; } protected function buildFerretWhereClause(AphrontDatabaseConnection $conn) { if (!$this->ferretEngine) { return array(); } $engine = $this->ferretEngine; $stemmer = $engine->newStemmer(); $table_map = $this->ferretTables; $op_sub = PhutilSearchQueryCompiler::OPERATOR_SUBSTRING; $op_not = PhutilSearchQueryCompiler::OPERATOR_NOT; $op_exact = PhutilSearchQueryCompiler::OPERATOR_EXACT; $op_absent = PhutilSearchQueryCompiler::OPERATOR_ABSENT; $op_present = PhutilSearchQueryCompiler::OPERATOR_PRESENT; $where = array(); $default_function = $engine->getDefaultFunctionKey(); foreach ($this->ferretTokens as $fulltext_token) { $raw_token = $fulltext_token->getToken(); $value = $raw_token->getValue(); $function = $raw_token->getFunction(); if ($function === null) { $function = $default_function; } $operator = $raw_token->getOperator(); $table_alias = $table_map[$function]['alias']; // If this is a "field is present" operator, we've already implicitly // guaranteed this by JOINing the table. We don't need to do any // more work. $is_present = ($operator === $op_present); if ($is_present) { continue; } // If this is a "field is absent" operator, we just want documents // which failed to match to a row when we LEFT JOINed the table. This // means there's no index for the field. $is_absent = ($operator === $op_absent); if ($is_absent) { $where[] = qsprintf( $conn, '(%T.rawCorpus IS NULL)', $table_alias); continue; } $is_not = ($operator === $op_not); if ($operator == $op_sub) { $is_substring = true; } else { $is_substring = false; } // If we're doing exact search, just test the raw corpus. $is_exact = ($operator === $op_exact); if ($is_exact) { if ($is_not) { $where[] = qsprintf( $conn, '(%T.rawCorpus != %s)', $table_alias, $value); } else { $where[] = qsprintf( $conn, '(%T.rawCorpus = %s)', $table_alias, $value); } continue; } // If we're doing substring search, we just match against the raw corpus // and we're done. if ($is_substring) { if ($is_not) { $where[] = qsprintf( $conn, '(%T.rawCorpus NOT LIKE %~)', $table_alias, $value); } else { $where[] = qsprintf( $conn, '(%T.rawCorpus LIKE %~)', $table_alias, $value); } continue; } // Otherwise, we need to match against the term corpus and the normal // corpus, so that searching for "raw" does not find "strawberry". if ($raw_token->isQuoted()) { $is_quoted = true; $is_stemmed = false; } else { $is_quoted = false; $is_stemmed = true; } // Never stem negated queries, since this can exclude results users // did not mean to exclude and generally confuse things. if ($is_not) { $is_stemmed = false; } $term_constraints = array(); $term_value = $engine->newTermsCorpus($value); if ($is_not) { $term_constraints[] = qsprintf( $conn, '(%T.termCorpus NOT LIKE %~)', $table_alias, $term_value); } else { $term_constraints[] = qsprintf( $conn, '(%T.termCorpus LIKE %~)', $table_alias, $term_value); } if ($is_stemmed) { $stem_value = $stemmer->stemToken($value); $stem_value = $engine->newTermsCorpus($stem_value); $term_constraints[] = qsprintf( $conn, '(%T.normalCorpus LIKE %~)', $table_alias, $stem_value); } if ($is_not) { $where[] = qsprintf( $conn, '%LA', $term_constraints); } else if ($is_quoted) { $where[] = qsprintf( $conn, '(%T.rawCorpus LIKE %~ AND %LO)', $table_alias, $value, $term_constraints); } else { $where[] = qsprintf( $conn, '%LO', $term_constraints); } } if ($this->ferretQuery) { $query = $this->ferretQuery; $author_phids = $query->getParameter('authorPHIDs'); if ($author_phids) { $where[] = qsprintf( $conn, 'ft_doc.authorPHID IN (%Ls)', $author_phids); } $with_unowned = $query->getParameter('withUnowned'); $with_any = $query->getParameter('withAnyOwner'); if ($with_any && $with_unowned) { throw new PhabricatorEmptyQueryException( pht( 'This query matches only unowned documents owned by anyone, '. 'which is impossible.')); } $owner_phids = $query->getParameter('ownerPHIDs'); if ($owner_phids && !$with_any) { if ($with_unowned) { $where[] = qsprintf( $conn, 'ft_doc.ownerPHID IN (%Ls) OR ft_doc.ownerPHID IS NULL', $owner_phids); } else { $where[] = qsprintf( $conn, 'ft_doc.ownerPHID IN (%Ls)', $owner_phids); } } else if ($with_unowned) { $where[] = qsprintf( $conn, 'ft_doc.ownerPHID IS NULL'); } if ($with_any) { $where[] = qsprintf( $conn, 'ft_doc.ownerPHID IS NOT NULL'); } $rel_open = PhabricatorSearchRelationship::RELATIONSHIP_OPEN; $statuses = $query->getParameter('statuses'); $is_closed = null; if ($statuses) { $statuses = array_fuse($statuses); if (count($statuses) == 1) { if (isset($statuses[$rel_open])) { $is_closed = 0; } else { $is_closed = 1; } } } if ($is_closed !== null) { $where[] = qsprintf( $conn, 'ft_doc.isClosed = %d', $is_closed); } } return $where; } protected function shouldGroupFerretResultRows() { return (bool)$this->ferretTokens; } /* -( Ngrams )------------------------------------------------------------- */ protected function withNgramsConstraint( PhabricatorSearchNgrams $index, $value) { if (strlen($value)) { $this->ngrams[] = array( 'index' => $index, 'value' => $value, 'length' => count(phutil_utf8v($value)), ); } return $this; } protected function buildNgramsJoinClause(AphrontDatabaseConnection $conn) { $ngram_engine = $this->getNgramEngine(); $flat = array(); foreach ($this->ngrams as $spec) { $length = $spec['length']; if ($length < 3) { continue; } $index = $spec['index']; $value = $spec['value']; $ngrams = $ngram_engine->getSubstringNgramsFromString($value); foreach ($ngrams as $ngram) { $flat[] = array( 'table' => $index->getTableName(), 'ngram' => $ngram, ); } } if (!$flat) { return array(); } // MySQL only allows us to join a maximum of 61 tables per query. Each // ngram is going to cost us a join toward that limit, so if the user // specified a very long query string, just pick 16 of the ngrams // at random. if (count($flat) > 16) { shuffle($flat); $flat = array_slice($flat, 0, 16); } $alias = $this->getPrimaryTableAlias(); if ($alias) { $id_column = qsprintf($conn, '%T.%T', $alias, 'id'); } else { $id_column = qsprintf($conn, '%T', 'id'); } $idx = 1; $joins = array(); foreach ($flat as $spec) { $table = $spec['table']; $ngram = $spec['ngram']; $alias = 'ngm'.$idx++; $joins[] = qsprintf( $conn, 'JOIN %T %T ON %T.objectID = %Q AND %T.ngram = %s', $table, $alias, $alias, $id_column, $alias, $ngram); } return $joins; } protected function buildNgramsWhereClause(AphrontDatabaseConnection $conn) { $where = array(); $ngram_engine = $this->getNgramEngine(); foreach ($this->ngrams as $ngram) { $index = $ngram['index']; $value = $ngram['value']; $column = $index->getColumnName(); $alias = $this->getPrimaryTableAlias(); if ($alias) { $column = qsprintf($conn, '%T.%T', $alias, $column); } else { $column = qsprintf($conn, '%T', $column); } $tokens = $ngram_engine->tokenizeNgramString($value); foreach ($tokens as $token) { $where[] = qsprintf( $conn, '%Q LIKE %~', $column, $token); } } return $where; } protected function shouldGroupNgramResultRows() { return (bool)$this->ngrams; } private function getNgramEngine() { if (!$this->ngramEngine) { $this->ngramEngine = new PhabricatorSearchNgramEngine(); } return $this->ngramEngine; } /* -( Edge Logic )--------------------------------------------------------- */ /** * Convenience method for specifying edge logic constraints with a list of * PHIDs. * * @param const Edge constant. * @param const Constraint operator. * @param list List of PHIDs. * @return this * @task edgelogic */ public function withEdgeLogicPHIDs($edge_type, $operator, array $phids) { $constraints = array(); foreach ($phids as $phid) { $constraints[] = new PhabricatorQueryConstraint($operator, $phid); } return $this->withEdgeLogicConstraints($edge_type, $constraints); } /** * @return this * @task edgelogic */ public function withEdgeLogicConstraints($edge_type, array $constraints) { assert_instances_of($constraints, 'PhabricatorQueryConstraint'); $constraints = mgroup($constraints, 'getOperator'); foreach ($constraints as $operator => $list) { foreach ($list as $item) { $this->edgeLogicConstraints[$edge_type][$operator][] = $item; } } $this->edgeLogicConstraintsAreValid = false; return $this; } /** * @task edgelogic */ public function buildEdgeLogicSelectClause(AphrontDatabaseConnection $conn) { $select = array(); $this->validateEdgeLogicConstraints(); foreach ($this->edgeLogicConstraints as $type => $constraints) { foreach ($constraints as $operator => $list) { $alias = $this->getEdgeLogicTableAlias($operator, $type); switch ($operator) { case PhabricatorQueryConstraint::OPERATOR_AND: if (count($list) > 1) { $select[] = qsprintf( $conn, 'COUNT(DISTINCT(%T.dst)) %T', $alias, $this->buildEdgeLogicTableAliasCount($alias)); } break; case PhabricatorQueryConstraint::OPERATOR_ANCESTOR: // This is tricky. We have a query which specifies multiple // projects, each of which may have an arbitrarily large number // of descendants. // Suppose the projects are "Engineering" and "Operations", and // "Engineering" has subprojects X, Y and Z. // We first use `FIELD(dst, X, Y, Z)` to produce a 0 if a row // is not part of Engineering at all, or some number other than // 0 if it is. // Then we use `IF(..., idx, NULL)` to convert the 0 to a NULL and // any other value to an index (say, 1) for the ancestor. // We build these up for every ancestor, then use `COALESCE(...)` // to select the non-null one, giving us an ancestor which this // row is a member of. // From there, we use `COUNT(DISTINCT(...))` to make sure that // each result row is a member of all ancestors. if (count($list) > 1) { $idx = 1; $parts = array(); foreach ($list as $constraint) { $parts[] = qsprintf( $conn, 'IF(FIELD(%T.dst, %Ls) != 0, %d, NULL)', $alias, (array)$constraint->getValue(), $idx++); } $parts = qsprintf($conn, '%LQ', $parts); $select[] = qsprintf( $conn, 'COUNT(DISTINCT(COALESCE(%Q))) %T', $parts, $this->buildEdgeLogicTableAliasAncestor($alias)); } break; default: break; } } } return $select; } /** * @task edgelogic */ public function buildEdgeLogicJoinClause(AphrontDatabaseConnection $conn) { $edge_table = PhabricatorEdgeConfig::TABLE_NAME_EDGE; $phid_column = $this->getApplicationSearchObjectPHIDColumn($conn); $joins = array(); foreach ($this->edgeLogicConstraints as $type => $constraints) { $op_null = PhabricatorQueryConstraint::OPERATOR_NULL; $has_null = isset($constraints[$op_null]); // If we're going to process an only() operator, build a list of the // acceptable set of PHIDs first. We'll only match results which have // no edges to any other PHIDs. $all_phids = array(); if (isset($constraints[PhabricatorQueryConstraint::OPERATOR_ONLY])) { foreach ($constraints as $operator => $list) { switch ($operator) { case PhabricatorQueryConstraint::OPERATOR_ANCESTOR: case PhabricatorQueryConstraint::OPERATOR_AND: case PhabricatorQueryConstraint::OPERATOR_OR: foreach ($list as $constraint) { $value = (array)$constraint->getValue(); foreach ($value as $v) { $all_phids[$v] = $v; } } break; } } } foreach ($constraints as $operator => $list) { $alias = $this->getEdgeLogicTableAlias($operator, $type); $phids = array(); foreach ($list as $constraint) { $value = (array)$constraint->getValue(); foreach ($value as $v) { $phids[$v] = $v; } } $phids = array_keys($phids); switch ($operator) { case PhabricatorQueryConstraint::OPERATOR_NOT: $joins[] = qsprintf( $conn, 'LEFT JOIN %T %T ON %Q = %T.src AND %T.type = %d AND %T.dst IN (%Ls)', $edge_table, $alias, $phid_column, $alias, $alias, $type, $alias, $phids); break; case PhabricatorQueryConstraint::OPERATOR_ANCESTOR: case PhabricatorQueryConstraint::OPERATOR_AND: case PhabricatorQueryConstraint::OPERATOR_OR: // If we're including results with no matches, we have to degrade // this to a LEFT join. We'll use WHERE to select matching rows // later. if ($has_null) { $join_type = qsprintf($conn, 'LEFT'); } else { $join_type = qsprintf($conn, ''); } $joins[] = qsprintf( $conn, '%Q JOIN %T %T ON %Q = %T.src AND %T.type = %d AND %T.dst IN (%Ls)', $join_type, $edge_table, $alias, $phid_column, $alias, $alias, $type, $alias, $phids); break; case PhabricatorQueryConstraint::OPERATOR_NULL: $joins[] = qsprintf( $conn, 'LEFT JOIN %T %T ON %Q = %T.src AND %T.type = %d', $edge_table, $alias, $phid_column, $alias, $alias, $type); break; case PhabricatorQueryConstraint::OPERATOR_ONLY: $joins[] = qsprintf( $conn, 'LEFT JOIN %T %T ON %Q = %T.src AND %T.type = %d AND %T.dst NOT IN (%Ls)', $edge_table, $alias, $phid_column, $alias, $alias, $type, $alias, $all_phids); break; } } } return $joins; } /** * @task edgelogic */ public function buildEdgeLogicWhereClause(AphrontDatabaseConnection $conn) { $where = array(); foreach ($this->edgeLogicConstraints as $type => $constraints) { $full = array(); $null = array(); $op_null = PhabricatorQueryConstraint::OPERATOR_NULL; $has_null = isset($constraints[$op_null]); foreach ($constraints as $operator => $list) { $alias = $this->getEdgeLogicTableAlias($operator, $type); switch ($operator) { case PhabricatorQueryConstraint::OPERATOR_NOT: case PhabricatorQueryConstraint::OPERATOR_ONLY: $full[] = qsprintf( $conn, '%T.dst IS NULL', $alias); break; case PhabricatorQueryConstraint::OPERATOR_AND: case PhabricatorQueryConstraint::OPERATOR_OR: if ($has_null) { $full[] = qsprintf( $conn, '%T.dst IS NOT NULL', $alias); } break; case PhabricatorQueryConstraint::OPERATOR_NULL: $null[] = qsprintf( $conn, '%T.dst IS NULL', $alias); break; } } if ($full && $null) { $where[] = qsprintf($conn, '(%LA OR %LA)', $full, $null); } else if ($full) { foreach ($full as $condition) { $where[] = $condition; } } else if ($null) { foreach ($null as $condition) { $where[] = $condition; } } } return $where; } /** * @task edgelogic */ public function buildEdgeLogicHavingClause(AphrontDatabaseConnection $conn) { $having = array(); foreach ($this->edgeLogicConstraints as $type => $constraints) { foreach ($constraints as $operator => $list) { $alias = $this->getEdgeLogicTableAlias($operator, $type); switch ($operator) { case PhabricatorQueryConstraint::OPERATOR_AND: if (count($list) > 1) { $having[] = qsprintf( $conn, '%T = %d', $this->buildEdgeLogicTableAliasCount($alias), count($list)); } break; case PhabricatorQueryConstraint::OPERATOR_ANCESTOR: if (count($list) > 1) { $having[] = qsprintf( $conn, '%T = %d', $this->buildEdgeLogicTableAliasAncestor($alias), count($list)); } break; } } } return $having; } /** * @task edgelogic */ public function shouldGroupEdgeLogicResultRows() { foreach ($this->edgeLogicConstraints as $type => $constraints) { foreach ($constraints as $operator => $list) { switch ($operator) { case PhabricatorQueryConstraint::OPERATOR_NOT: case PhabricatorQueryConstraint::OPERATOR_AND: case PhabricatorQueryConstraint::OPERATOR_OR: if (count($list) > 1) { return true; } break; case PhabricatorQueryConstraint::OPERATOR_ANCESTOR: // NOTE: We must always group query results rows when using an // "ANCESTOR" operator because a single task may be related to // two different descendants of a particular ancestor. For // discussion, see T12753. return true; case PhabricatorQueryConstraint::OPERATOR_NULL: case PhabricatorQueryConstraint::OPERATOR_ONLY: return true; } } } return false; } /** * @task edgelogic */ private function getEdgeLogicTableAlias($operator, $type) { return 'edgelogic_'.$operator.'_'.$type; } /** * @task edgelogic */ private function buildEdgeLogicTableAliasCount($alias) { return $alias.'_count'; } /** * @task edgelogic */ private function buildEdgeLogicTableAliasAncestor($alias) { return $alias.'_ancestor'; } /** * Select certain edge logic constraint values. * * @task edgelogic */ protected function getEdgeLogicValues( array $edge_types, array $operators) { $values = array(); $constraint_lists = $this->edgeLogicConstraints; if ($edge_types) { $constraint_lists = array_select_keys($constraint_lists, $edge_types); } foreach ($constraint_lists as $type => $constraints) { if ($operators) { $constraints = array_select_keys($constraints, $operators); } foreach ($constraints as $operator => $list) { foreach ($list as $constraint) { $value = (array)$constraint->getValue(); foreach ($value as $v) { $values[] = $v; } } } } return $values; } /** * Validate edge logic constraints for the query. * * @return this * @task edgelogic */ private function validateEdgeLogicConstraints() { if ($this->edgeLogicConstraintsAreValid) { return $this; } foreach ($this->edgeLogicConstraints as $type => $constraints) { foreach ($constraints as $operator => $list) { switch ($operator) { case PhabricatorQueryConstraint::OPERATOR_EMPTY: throw new PhabricatorEmptyQueryException( pht('This query specifies an empty constraint.')); } } } // This should probably be more modular, eventually, but we only do // project-based edge logic today. $project_phids = $this->getEdgeLogicValues( array( PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, ), array( PhabricatorQueryConstraint::OPERATOR_AND, PhabricatorQueryConstraint::OPERATOR_OR, PhabricatorQueryConstraint::OPERATOR_NOT, PhabricatorQueryConstraint::OPERATOR_ANCESTOR, )); if ($project_phids) { $projects = id(new PhabricatorProjectQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($project_phids) ->execute(); $projects = mpull($projects, null, 'getPHID'); foreach ($project_phids as $phid) { if (empty($projects[$phid])) { throw new PhabricatorEmptyQueryException( pht( 'This query is constrained by a project you do not have '. 'permission to see.')); } } } $op_and = PhabricatorQueryConstraint::OPERATOR_AND; $op_or = PhabricatorQueryConstraint::OPERATOR_OR; $op_ancestor = PhabricatorQueryConstraint::OPERATOR_ANCESTOR; foreach ($this->edgeLogicConstraints as $type => $constraints) { foreach ($constraints as $operator => $list) { switch ($operator) { case PhabricatorQueryConstraint::OPERATOR_ONLY: if (count($list) > 1) { throw new PhabricatorEmptyQueryException( pht( 'This query specifies only() more than once.')); } $have_and = idx($constraints, $op_and); $have_or = idx($constraints, $op_or); $have_ancestor = idx($constraints, $op_ancestor); if (!$have_and && !$have_or && !$have_ancestor) { throw new PhabricatorEmptyQueryException( pht( 'This query specifies only(), but no other constraints '. 'which it can apply to.')); } break; } } } $this->edgeLogicConstraintsAreValid = true; return $this; } /* -( Spaces )------------------------------------------------------------- */ /** * Constrain the query to return results from only specific Spaces. * * Pass a list of Space PHIDs, or `null` to represent the default space. Only * results in those Spaces will be returned. * * Queries are always constrained to include only results from spaces the * viewer has access to. * * @param list * @task spaces */ public function withSpacePHIDs(array $space_phids) { $object = $this->newResultObject(); if (!$object) { throw new Exception( pht( 'This query (of class "%s") does not implement newResultObject(), '. 'but must implement this method to enable support for Spaces.', get_class($this))); } if (!($object instanceof PhabricatorSpacesInterface)) { throw new Exception( pht( 'This query (of class "%s") returned an object of class "%s" from '. 'getNewResultObject(), but it does not implement the required '. 'interface ("%s"). Objects must implement this interface to enable '. 'Spaces support.', get_class($this), get_class($object), 'PhabricatorSpacesInterface')); } $this->spacePHIDs = $space_phids; return $this; } public function withSpaceIsArchived($archived) { $this->spaceIsArchived = $archived; return $this; } /** * Constrain the query to include only results in valid Spaces. * * This method builds part of a WHERE clause which considers the spaces the * viewer has access to see with any explicit constraint on spaces added by * @{method:withSpacePHIDs}. * * @param AphrontDatabaseConnection Database connection. * @return string Part of a WHERE clause. * @task spaces */ private function buildSpacesWhereClause(AphrontDatabaseConnection $conn) { $object = $this->newResultObject(); if (!$object) { return null; } if (!($object instanceof PhabricatorSpacesInterface)) { return null; } $viewer = $this->getViewer(); // If we have an omnipotent viewer and no formal space constraints, don't // emit a clause. This primarily enables older migrations to run cleanly, // without fataling because they try to match a `spacePHID` column which // does not exist yet. See T8743, T8746. if ($viewer->isOmnipotent()) { if ($this->spaceIsArchived === null && $this->spacePHIDs === null) { return null; } } // See T13240. If this query raises policy exceptions, don't filter objects // in the MySQL layer. We want them to reach the application layer so we // can reject them and raise an exception. if ($this->shouldRaisePolicyExceptions()) { return null; } $space_phids = array(); $include_null = false; $all = PhabricatorSpacesNamespaceQuery::getAllSpaces(); if (!$all) { // If there are no spaces at all, implicitly give the viewer access to // the default space. $include_null = true; } else { // Otherwise, give them access to the spaces they have permission to // see. $viewer_spaces = PhabricatorSpacesNamespaceQuery::getViewerSpaces( $viewer); foreach ($viewer_spaces as $viewer_space) { if ($this->spaceIsArchived !== null) { if ($viewer_space->getIsArchived() != $this->spaceIsArchived) { continue; } } $phid = $viewer_space->getPHID(); $space_phids[$phid] = $phid; if ($viewer_space->getIsDefaultNamespace()) { $include_null = true; } } } // If we have additional explicit constraints, evaluate them now. if ($this->spacePHIDs !== null) { $explicit = array(); $explicit_null = false; foreach ($this->spacePHIDs as $phid) { if ($phid === null) { $space = PhabricatorSpacesNamespaceQuery::getDefaultSpace(); } else { $space = idx($all, $phid); } if ($space) { $phid = $space->getPHID(); $explicit[$phid] = $phid; if ($space->getIsDefaultNamespace()) { $explicit_null = true; } } } // If the viewer can see the default space but it isn't on the explicit // list of spaces to query, don't match it. if ($include_null && !$explicit_null) { $include_null = false; } // Include only the spaces common to the viewer and the constraints. $space_phids = array_intersect_key($space_phids, $explicit); } if (!$space_phids && !$include_null) { if ($this->spacePHIDs === null) { throw new PhabricatorEmptyQueryException( pht('You do not have access to any spaces.')); } else { throw new PhabricatorEmptyQueryException( pht( 'You do not have access to any of the spaces this query '. 'is constrained to.')); } } $alias = $this->getPrimaryTableAlias(); if ($alias) { $col = qsprintf($conn, '%T.spacePHID', $alias); } else { $col = qsprintf($conn, 'spacePHID'); } if ($space_phids && $include_null) { return qsprintf( $conn, '(%Q IN (%Ls) OR %Q IS NULL)', $col, $space_phids, $col); } else if ($space_phids) { return qsprintf( $conn, '%Q IN (%Ls)', $col, $space_phids); } else { return qsprintf( $conn, '%Q IS NULL', $col); } } private function hasFerretOrder() { $vector = $this->getOrderVector(); if ($vector->containsKey('rank')) { return true; } if ($vector->containsKey('fulltext-created')) { return true; } if ($vector->containsKey('fulltext-modified')) { return true; } return false; } }