Page MenuHomestyx hydra

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php b/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php
index 7362bddf7d..46b1c51211 100644
--- a/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php
+++ b/src/applications/auth/query/PhabricatorAuthProviderConfigQuery.php
@@ -1,99 +1,103 @@
<?php
final class PhabricatorAuthProviderConfigQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $providerClasses;
const STATUS_ALL = 'status:all';
const STATUS_ENABLED = 'status:enabled';
private $status = self::STATUS_ALL;
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withStatus($status) {
$this->status = $status;
return $this;
}
public function withProviderClasses(array $classes) {
$this->providerClasses = $classes;
return $this;
}
public static function getStatusOptions() {
return array(
self::STATUS_ALL => pht('All Providers'),
self::STATUS_ENABLED => pht('Enabled Providers'),
);
}
protected function loadPage() {
$table = new PhabricatorAuthProviderConfig();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
if ($this->providerClasses) {
$where[] = qsprintf(
$conn_r,
'providerClass IN (%Ls)',
$this->providerClasses);
}
$status = $this->status;
switch ($status) {
case self::STATUS_ALL:
break;
case self::STATUS_ENABLED:
$where[] = qsprintf(
$conn_r,
'isEnabled = 1');
break;
default:
throw new Exception("Unknown status '{$status}'!");
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationAuth';
+ }
+
}
diff --git a/src/applications/auth/query/PhabricatorExternalAccountQuery.php b/src/applications/auth/query/PhabricatorExternalAccountQuery.php
index b8cb53cc2c..37ec836065 100644
--- a/src/applications/auth/query/PhabricatorExternalAccountQuery.php
+++ b/src/applications/auth/query/PhabricatorExternalAccountQuery.php
@@ -1,166 +1,170 @@
<?php
final class PhabricatorExternalAccountQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $accountTypes;
private $accountDomains;
private $accountIDs;
private $userPHIDs;
private $needImages;
private $accountSecrets;
public function withUserPHIDs(array $user_phids) {
$this->userPHIDs = $user_phids;
return $this;
}
public function withAccountIDs(array $account_ids) {
$this->accountIDs = $account_ids;
return $this;
}
public function withAccountDomains(array $account_domains) {
$this->accountDomains = $account_domains;
return $this;
}
public function withAccountTypes(array $account_types) {
$this->accountTypes = $account_types;
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 loadPage() {
$table = new PhabricatorExternalAccount();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
public function willFilterPage(array $accounts) {
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);
}
}
}
return $accounts;
}
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
$where[] = $this->buildPagingClause($conn_r);
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
if ($this->accountTypes) {
$where[] = qsprintf(
$conn_r,
'accountType IN (%Ls)',
$this->accountTypes);
}
if ($this->accountDomains) {
$where[] = qsprintf(
$conn_r,
'accountDomain IN (%Ls)',
$this->accountDomains);
}
if ($this->accountIDs) {
$where[] = qsprintf(
$conn_r,
'accountID IN (%Ls)',
$this->accountIDs);
}
if ($this->userPHIDs) {
$where[] = qsprintf(
$conn_r,
'userPHID IN (%Ls)',
$this->userPHIDs);
}
if ($this->accountSecrets) {
$where[] = qsprintf(
$conn_r,
'accountSecret IN (%Ls)',
$this->accountSecrets);
}
return $this->formatWhereClause($where);
}
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationPeople';
+ }
+
}
diff --git a/src/applications/chatlog/PhabricatorChatLogChannelQuery.php b/src/applications/chatlog/PhabricatorChatLogChannelQuery.php
index 98d23bd66d..cba9976c79 100644
--- a/src/applications/chatlog/PhabricatorChatLogChannelQuery.php
+++ b/src/applications/chatlog/PhabricatorChatLogChannelQuery.php
@@ -1,58 +1,63 @@
<?php
final class PhabricatorChatLogChannelQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $channels;
private $channelIDs;
public function withChannelNames(array $channels) {
$this->channels = $channels;
return $this;
}
public function withIDs(array $channel_ids) {
$this->channelIDs = $channel_ids;
return $this;
}
protected function loadPage() {
$table = new PhabricatorChatLogChannel();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T c %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
$logs = $table->loadAllFromArray($data);
return $logs;
}
private function buildWhereClause($conn_r) {
$where = array();
$where[] = $this->buildPagingClause($conn_r);
if ($this->channelIDs) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->channelIDs);
}
if ($this->channels) {
$where[] = qsprintf(
$conn_r,
'channelName IN (%Ls)',
$this->channels);
}
return $this->formatWhereClause($where);
}
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationChatlog';
+ }
+
}
diff --git a/src/applications/chatlog/PhabricatorChatLogQuery.php b/src/applications/chatlog/PhabricatorChatLogQuery.php
index 1c5a5ef43c..599a1d5c6d 100644
--- a/src/applications/chatlog/PhabricatorChatLogQuery.php
+++ b/src/applications/chatlog/PhabricatorChatLogQuery.php
@@ -1,57 +1,62 @@
<?php
final class PhabricatorChatLogQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $channelIDs;
private $maximumEpoch;
public function withChannelIDs(array $channel_ids) {
$this->channelIDs = $channel_ids;
return $this;
}
public function withMaximumEpoch($epoch) {
$this->maximumEpoch = $epoch;
return $this;
}
protected function loadPage() {
$table = new PhabricatorChatLogEvent();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T e %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
$logs = $table->loadAllFromArray($data);
return $logs;
}
private function buildWhereClause($conn_r) {
$where = array();
$where[] = $this->buildPagingClause($conn_r);
if ($this->maximumEpoch) {
$where[] = qsprintf(
$conn_r,
'epoch <= %d',
$this->maximumEpoch);
}
if ($this->channelIDs) {
$where[] = qsprintf(
$conn_r,
'channelID IN (%Ld)',
$this->channelIDs);
}
return $this->formatWhereClause($where);
}
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationChatlog';
+ }
+
}
diff --git a/src/applications/conduit/query/PhabricatorConduitLogQuery.php b/src/applications/conduit/query/PhabricatorConduitLogQuery.php
index 092f9d59bc..539d84785f 100644
--- a/src/applications/conduit/query/PhabricatorConduitLogQuery.php
+++ b/src/applications/conduit/query/PhabricatorConduitLogQuery.php
@@ -1,43 +1,47 @@
<?php
final class PhabricatorConduitLogQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $methods;
public function withMethods(array $methods) {
$this->methods = $methods;
return $this;
}
public function loadPage() {
$table = new PhabricatorConduitMethodCallLog();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);;
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->methods) {
$where[] = qsprintf(
$conn_r,
'method IN (%Ls)',
$this->methods);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationConduit';
+ }
+
}
diff --git a/src/applications/conduit/query/PhabricatorConduitMethodQuery.php b/src/applications/conduit/query/PhabricatorConduitMethodQuery.php
index 02236d85b3..40c55a90c9 100644
--- a/src/applications/conduit/query/PhabricatorConduitMethodQuery.php
+++ b/src/applications/conduit/query/PhabricatorConduitMethodQuery.php
@@ -1,124 +1,128 @@
<?php
final class PhabricatorConduitMethodQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $isDeprecated;
private $isStable;
private $isUnstable;
private $applicationNames;
private $nameContains;
private $methods;
public function withMethods(array $methods) {
$this->methods = $methods;
return $this;
}
public function withNameContains($name_contains) {
$this->nameContains = $name_contains;
return $this;
}
public function withApplicationNames(array $application_names) {
$this->applicationNames = $application_names;
return $this;
}
public function withIsStable($is_stable) {
$this->isStable = $is_stable;
return $this;
}
public function withIsUnstable($is_unstable) {
$this->isUnstable = $is_unstable;
return $this;
}
public function withIsDeprecated($is_deprecated) {
$this->isDeprecated = $is_deprecated;
return $this;
}
public function loadPage() {
$methods = $this->getAllMethods();
$methods = $this->filterMethods($methods);
return $methods;
}
private function getAllMethods() {
static $methods;
if ($methods === null) {
$methods = id(new PhutilSymbolLoader())
->setAncestorClass('ConduitAPIMethod')
->loadObjects();
$methods = msort($methods, 'getSortOrder');
}
return $methods;
}
private function filterMethods(array $methods) {
foreach ($methods as $key => $method) {
$application = $method->getApplication();
if (!$application) {
continue;
}
if (!$application->isInstalled()) {
unset($methods[$key]);
}
}
$status = array(
ConduitAPIMethod::METHOD_STATUS_STABLE => $this->isStable,
ConduitAPIMethod::METHOD_STATUS_DEPRECATED => $this->isDeprecated,
ConduitAPIMethod::METHOD_STATUS_UNSTABLE => $this->isUnstable,
);
// Only apply status filters if any of them are set.
if (array_filter($status)) {
foreach ($methods as $key => $method) {
$keep = idx($status, $method->getMethodStatus());
if (!$keep) {
unset($methods[$key]);
}
}
}
if ($this->applicationNames) {
$map = array_fuse($this->applicationNames);
foreach ($methods as $key => $method) {
$needle = $method->getApplicationName();
$needle = phutil_utf8_strtolower($needle);
if (empty($map[$needle])) {
unset($methods[$key]);
}
}
}
if ($this->nameContains) {
$needle = phutil_utf8_strtolower($this->nameContains);
foreach ($methods as $key => $method) {
$haystack = $method->getAPIMethodName();
$haystack = phutil_utf8_strtolower($haystack);
if (strpos($haystack, $needle) === false) {
unset($methods[$key]);
}
}
}
if ($this->methods) {
$map = array_fuse($this->methods);
foreach ($methods as $key => $method) {
$needle = $method->getAPIMethodName();
if (empty($map[$needle])) {
unset($methods[$key]);
}
}
}
return $methods;
}
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationConduit';
+ }
+
}
diff --git a/src/applications/config/phid/PhabricatorConfigPHIDTypeConfig.php b/src/applications/config/phid/PhabricatorConfigPHIDTypeConfig.php
index ae297edbef..204ab5b9a8 100644
--- a/src/applications/config/phid/PhabricatorConfigPHIDTypeConfig.php
+++ b/src/applications/config/phid/PhabricatorConfigPHIDTypeConfig.php
@@ -1,49 +1,46 @@
<?php
final class PhabricatorConfigPHIDTypeConfig extends PhabricatorPHIDType {
const TYPECONST = 'CONF';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Config');
}
public function newObject() {
return new PhabricatorConfigEntry();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorConfigEntryQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$entry = $objects[$phid];
$key = $entry->getConfigKey();
$handle->setName($key);
$handle->setURI("/config/edit/{$key}/");
}
}
public function canLoadNamedObject($name) {
return false;
}
}
diff --git a/src/applications/config/query/PhabricatorConfigEntryQuery.php b/src/applications/config/query/PhabricatorConfigEntryQuery.php
index 1f4026b58c..b23174bcd3 100644
--- a/src/applications/config/query/PhabricatorConfigEntryQuery.php
+++ b/src/applications/config/query/PhabricatorConfigEntryQuery.php
@@ -1,56 +1,60 @@
<?php
final class PhabricatorConfigEntryQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $phids;
private $ids;
public function withIDs($ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs($phids) {
$this->phids = $phids;
return $this;
}
public function loadPage() {
$table = new PhabricatorConfigEntry();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationConfig';
+ }
+
}
diff --git a/src/applications/conpherence/phid/PhabricatorConpherencePHIDTypeThread.php b/src/applications/conpherence/phid/PhabricatorConpherencePHIDTypeThread.php
index dba81f2642..fd94da83a9 100644
--- a/src/applications/conpherence/phid/PhabricatorConpherencePHIDTypeThread.php
+++ b/src/applications/conpherence/phid/PhabricatorConpherencePHIDTypeThread.php
@@ -1,50 +1,47 @@
<?php
/**
* @group conpherence
*/
final class PhabricatorConpherencePHIDTypeThread extends PhabricatorPHIDType {
const TYPECONST = 'CONP';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Conpherence Thread');
}
public function newObject() {
return new ConpherenceThread();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new ConpherenceThreadQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$thread = $objects[$phid];
$name = $thread->getTitle();
if (!strlen($name)) {
$name = pht('[No Title]');
}
$handle->setName($name);
$handle->setFullName($name);
$handle->setURI('/conpherence/'.$thread->getID().'/');
}
}
}
diff --git a/src/applications/conpherence/query/ConpherenceThreadQuery.php b/src/applications/conpherence/query/ConpherenceThreadQuery.php
index 0017b3ed2b..f36d9d8fd8 100644
--- a/src/applications/conpherence/query/ConpherenceThreadQuery.php
+++ b/src/applications/conpherence/query/ConpherenceThreadQuery.php
@@ -1,285 +1,289 @@
<?php
/**
* @group conpherence
*/
final class ConpherenceThreadQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
const TRANSACTION_LIMIT = 100;
private $phids;
private $ids;
private $needWidgetData;
private $needTransactions;
private $needParticipantCache;
private $needFilePHIDs;
private $afterTransactionID;
private $beforeTransactionID;
private $transactionLimit;
public function needFilePHIDs($need_file_phids) {
$this->needFilePHIDs = $need_file_phids;
return $this;
}
public function needParticipantCache($participant_cache) {
$this->needParticipantCache = $participant_cache;
return $this;
}
public function needWidgetData($need_widget_data) {
$this->needWidgetData = $need_widget_data;
return $this;
}
public function needTransactions($need_transactions) {
$this->needTransactions = $need_transactions;
return $this;
}
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function setAfterTransactionID($id) {
$this->afterTransactionID = $id;
return $this;
}
public function setBeforeTransactionID($id) {
$this->beforeTransactionID = $id;
return $this;
}
public function setTransactionLimit($transaction_limit) {
$this->transactionLimit = $transaction_limit;
return $this;
}
public function getTransactionLimit() {
return $this->transactionLimit;
}
protected function loadPage() {
$table = new ConpherenceThread();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT conpherence_thread.* FROM %T conpherence_thread %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
$conpherences = $table->loadAllFromArray($data);
if ($conpherences) {
$conpherences = mpull($conpherences, null, 'getPHID');
$this->loadParticipantsAndInitHandles($conpherences);
if ($this->needParticipantCache) {
$this->loadCoreHandles($conpherences, 'getRecentParticipantPHIDs');
} else if ($this->needWidgetData) {
$this->loadCoreHandles($conpherences, 'getParticipantPHIDs');
}
if ($this->needTransactions) {
$this->loadTransactionsAndHandles($conpherences);
}
if ($this->needFilePHIDs || $this->needWidgetData) {
$this->loadFilePHIDs($conpherences);
}
if ($this->needWidgetData) {
$this->loadWidgetData($conpherences);
}
}
return $conpherences;
}
protected function buildWhereClause($conn_r) {
$where = array();
$where[] = $this->buildPagingClause($conn_r);
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
return $this->formatWhereClause($where);
}
private function loadParticipantsAndInitHandles(array $conpherences) {
$participants = id(new ConpherenceParticipant())
->loadAllWhere('conpherencePHID IN (%Ls)', array_keys($conpherences));
$map = mgroup($participants, 'getConpherencePHID');
foreach ($map as $conpherence_phid => $conpherence_participants) {
$current_conpherence = $conpherences[$conpherence_phid];
$conpherence_participants = mpull(
$conpherence_participants,
null,
'getParticipantPHID');
$current_conpherence->attachParticipants($conpherence_participants);
$current_conpherence->attachHandles(array());
}
return $this;
}
private function loadCoreHandles(
array $conpherences,
$method) {
$handle_phids = array();
foreach ($conpherences as $conpherence) {
$handle_phids[$conpherence->getPHID()] =
$conpherence->$method();
}
$flat_phids = array_mergev($handle_phids);
$handles = id(new PhabricatorHandleQuery())
->setViewer($this->getViewer())
->withPHIDs($flat_phids)
->execute();
foreach ($handle_phids as $conpherence_phid => $phids) {
$conpherence = $conpherences[$conpherence_phid];
$conpherence->attachHandles(array_select_keys($handles, $phids));
}
return $this;
}
private function loadTransactionsAndHandles(array $conpherences) {
$query = id(new ConpherenceTransactionQuery())
->setViewer($this->getViewer())
->withObjectPHIDs(array_keys($conpherences))
->needHandles(true);
// We have to flip these for the underyling query class. The semantics of
// paging are tricky business.
if ($this->afterTransactionID) {
$query->setBeforeID($this->afterTransactionID);
} else if ($this->beforeTransactionID) {
$query->setAfterID($this->beforeTransactionID);
}
if ($this->getTransactionLimit()) {
// fetch an extra for "show older" scenarios
$query->setLimit($this->getTransactionLimit() + 1);
}
$transactions = $query->execute();
$transactions = mgroup($transactions, 'getObjectPHID');
foreach ($conpherences as $phid => $conpherence) {
$current_transactions = $transactions[$phid];
$handles = array();
foreach ($current_transactions as $transaction) {
$handles += $transaction->getHandles();
}
$conpherence->attachHandles($conpherence->getHandles() + $handles);
$conpherence->attachTransactions($transactions[$phid]);
}
return $this;
}
private function loadFilePHIDs(array $conpherences) {
$edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_FILE;
$file_edges = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(array_keys($conpherences))
->withEdgeTypes(array($edge_type))
->execute();
foreach ($file_edges as $conpherence_phid => $data) {
$conpherence = $conpherences[$conpherence_phid];
$conpherence->attachFilePHIDs(array_keys($data[$edge_type]));
}
return $this;
}
private function loadWidgetData(array $conpherences) {
$participant_phids = array();
$file_phids = array();
foreach ($conpherences as $conpherence) {
$participant_phids[] = array_keys($conpherence->getParticipants());
$file_phids[] = $conpherence->getFilePHIDs();
}
$participant_phids = array_mergev($participant_phids);
$file_phids = array_mergev($file_phids);
$epochs = ConpherenceTimeUtil::getCalendarEventEpochs(
$this->getViewer());
$start_epoch = $epochs['start_epoch'];
$end_epoch = $epochs['end_epoch'];
$statuses = id(new PhabricatorUserStatus())
->loadAllWhere(
'userPHID in (%Ls) AND dateTo >= %d AND dateFrom <= %d',
$participant_phids,
$start_epoch,
$end_epoch);
$statuses = mgroup($statuses, 'getUserPHID');
// attached files
$files = array();
$file_author_phids = array();
$authors = array();
if ($file_phids) {
$files = id(new PhabricatorFileQuery())
->setViewer($this->getViewer())
->withPHIDs($file_phids)
->execute();
$files = mpull($files, null, 'getPHID');
$file_author_phids = mpull($files, 'getAuthorPHID', 'getPHID');
$authors = id(new PhabricatorHandleQuery())
->setViewer($this->getViewer())
->withPHIDs($file_author_phids)
->execute();
$authors = mpull($authors, null, 'getPHID');
}
foreach ($conpherences as $phid => $conpherence) {
$participant_phids = array_keys($conpherence->getParticipants());
$statuses = array_select_keys($statuses, $participant_phids);
$statuses = array_mergev($statuses);
$statuses = msort($statuses, 'getDateFrom');
$conpherence_files = array();
$files_authors = array();
foreach ($conpherence->getFilePHIDs() as $curr_phid) {
$curr_file = idx($files, $curr_phid);
if (!$curr_file) {
// this file was deleted or user doesn't have permission to see it
// this is generally weird
continue;
}
$conpherence_files[$curr_phid] = $curr_file;
// some files don't have authors so be careful
$current_author = null;
$current_author_phid = idx($file_author_phids, $curr_phid);
if ($current_author_phid) {
$current_author = $authors[$current_author_phid];
}
$files_authors[$curr_phid] = $current_author;
}
$widget_data = array(
'statuses' => $statuses,
'files' => $conpherence_files,
'files_authors' => $files_authors
);
$conpherence->attachWidgetData($widget_data);
}
return $this;
}
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationConpherence';
+ }
+
}
diff --git a/src/applications/countdown/phid/PhabricatorCountdownPHIDTypeCountdown.php b/src/applications/countdown/phid/PhabricatorCountdownPHIDTypeCountdown.php
index c816a1054d..ef60213d36 100644
--- a/src/applications/countdown/phid/PhabricatorCountdownPHIDTypeCountdown.php
+++ b/src/applications/countdown/phid/PhabricatorCountdownPHIDTypeCountdown.php
@@ -1,76 +1,73 @@
<?php
final class PhabricatorCountdownPHIDTypeCountdown extends PhabricatorPHIDType {
const TYPECONST = 'CDWN';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Countdown');
}
public function newObject() {
return new PhabricatorCountdown();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorCountdownQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$countdown = $objects[$phid];
$name = $countdown->getTitle();
$id = $countdown->getID();
$handle->setName("C{$id}");
$handle->setFullName("C{$id}: {$name}");
$handle->setURI("/countdown/{$id}/");
}
}
public function canLoadNamedObject($name) {
return preg_match('/^C\d*[1-9]\d*$/i', $name);
}
public function loadNamedObjects(
PhabricatorObjectQuery $query,
array $names) {
$id_map = array();
foreach ($names as $name) {
$id = (int)substr($name, 1);
$id_map[$id][] = $name;
}
$objects = id(new PhabricatorCountdownQuery())
->setViewer($query->getViewer())
->withIDs(array_keys($id_map))
->execute();
$results = array();
foreach ($objects as $id => $object) {
foreach (idx($id_map, $id, array()) as $name) {
$results[$name] = $object;
}
}
return $results;
}
}
diff --git a/src/applications/countdown/query/PhabricatorCountdownQuery.php b/src/applications/countdown/query/PhabricatorCountdownQuery.php
index dc3de9c760..6d82fe268b 100644
--- a/src/applications/countdown/query/PhabricatorCountdownQuery.php
+++ b/src/applications/countdown/query/PhabricatorCountdownQuery.php
@@ -1,88 +1,92 @@
<?php
/**
* @group countdown
*/
final class PhabricatorCountdownQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $authorPHIDs;
private $upcoming;
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 withUpcoming($upcoming) {
$this->upcoming = true;
return $this;
}
protected function loadPage() {
$table = new PhabricatorCountdown();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
$countdowns = $table->loadAllFromArray($data);
return $countdowns;
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
$where[] = $this->buildPagingClause($conn_r);
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
if ($this->authorPHIDs) {
$where[] = qsprintf(
$conn_r,
'authorPHID in (%Ls)',
$this->authorPHIDs);
}
if ($this->upcoming) {
$where[] = qsprintf(
$conn_r,
'epoch >= %d',
time());
}
return $this->formatWhereClause($where);
}
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationCountdown';
+ }
+
}
diff --git a/src/applications/daemon/query/PhabricatorDaemonLogQuery.php b/src/applications/daemon/query/PhabricatorDaemonLogQuery.php
index 448c6f8927..656edd7bdc 100644
--- a/src/applications/daemon/query/PhabricatorDaemonLogQuery.php
+++ b/src/applications/daemon/query/PhabricatorDaemonLogQuery.php
@@ -1,145 +1,149 @@
<?php
final class PhabricatorDaemonLogQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
const STATUS_ALL = 'status-all';
const STATUS_ALIVE = 'status-alive';
private $ids;
private $status = self::STATUS_ALL;
private $daemonClasses;
public static function getTimeUntilUnknown() {
return 3 * PhutilDaemonOverseer::HEARTBEAT_WAIT;
}
public static function getTimeUntilDead() {
return 30 * PhutilDaemonOverseer::HEARTBEAT_WAIT;
}
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withStatus($status) {
$this->status = $status;
return $this;
}
public function withDaemonClasses(array $classes) {
$this->daemonClasses = $classes;
return $this;
}
public function loadPage() {
$table = new PhabricatorDaemonLog();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
public function willFilterPage(array $daemons) {
$unknown_delay = PhabricatorDaemonLogQuery::getTimeUntilUnknown();
$dead_delay = PhabricatorDaemonLogQuery::getTimeUntilDead();
$status_running = PhabricatorDaemonLog::STATUS_RUNNING;
$status_unknown = PhabricatorDaemonLog::STATUS_UNKNOWN;
$status_wait = PhabricatorDaemonLog::STATUS_WAIT;
$status_exited = PhabricatorDaemonLog::STATUS_EXITED;
$status_dead = PhabricatorDaemonLog::STATUS_DEAD;
$filter = array_fuse($this->getStatusConstants());
foreach ($daemons as $key => $daemon) {
$status = $daemon->getStatus();
$seen = $daemon->getDateModified();
$is_running = ($status == $status_running) ||
($status == $status_wait);
// If we haven't seen the daemon recently, downgrade its status to
// unknown.
$unknown_time = ($seen + $unknown_delay);
if ($is_running && ($unknown_time < time())) {
$status = $status_unknown;
}
// If the daemon hasn't been seen in quite a while, assume it is dead.
$dead_time = ($seen + $dead_delay);
if (($status == $status_unknown) && ($dead_time < time())) {
$status = $status_dead;
}
// If we changed the daemon's status, update it.
if ($status != $daemon->getStatus()) {
$guard = AphrontWriteGuard::beginScopedUnguardedWrites();
$daemon->setStatus($status)->save();
unset($guard);
}
// If the daemon no longer matches the filter, get rid of it.
if ($filter) {
if (empty($filter[$daemon->getStatus()])) {
unset($daemons[$key]);
}
}
}
return $daemons;
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->getStatusConstants()) {
$where[] = qsprintf(
$conn_r,
'status IN (%Ls)',
$this->getStatusConstants());
}
if ($this->daemonClasses) {
$where[] = qsprintf(
$conn_r,
'daemon IN (%Ls)',
$this->daemonClasses);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
private function getStatusConstants() {
$status = $this->status;
switch ($status) {
case self::STATUS_ALL:
return array();
case self::STATUS_ALIVE:
return array(
PhabricatorDaemonLog::STATUS_UNKNOWN,
PhabricatorDaemonLog::STATUS_RUNNING,
PhabricatorDaemonLog::STATUS_WAIT,
);
default:
throw new Exception("Unknown status '{$status}'!");
}
}
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationDaemons';
+ }
+
}
diff --git a/src/applications/differential/phid/DifferentialPHIDTypeRevision.php b/src/applications/differential/phid/DifferentialPHIDTypeRevision.php
index 414afc7d0d..f13bd6770f 100644
--- a/src/applications/differential/phid/DifferentialPHIDTypeRevision.php
+++ b/src/applications/differential/phid/DifferentialPHIDTypeRevision.php
@@ -1,86 +1,83 @@
<?php
final class DifferentialPHIDTypeRevision extends PhabricatorPHIDType {
const TYPECONST = 'DREV';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Differential Revision');
}
public function newObject() {
return new DifferentialRevision();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new DifferentialRevisionQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
static $closed_statuses = array(
ArcanistDifferentialRevisionStatus::CLOSED => true,
ArcanistDifferentialRevisionStatus::ABANDONED => true,
);
foreach ($handles as $phid => $handle) {
$revision = $objects[$phid];
$title = $revision->getTitle();
$id = $revision->getID();
$status = $revision->getStatus();
$handle->setName("D{$id}");
$handle->setURI("/D{$id}");
$handle->setFullName("D{$id}: {$title}");
if (isset($closed_statuses[$status])) {
$handle->setStatus(PhabricatorObjectHandleStatus::STATUS_CLOSED);
}
}
}
public function canLoadNamedObject($name) {
return preg_match('/^D\d*[1-9]\d*$/i', $name);
}
public function loadNamedObjects(
PhabricatorObjectQuery $query,
array $names) {
$id_map = array();
foreach ($names as $name) {
$id = (int)substr($name, 1);
$id_map[$id][] = $name;
}
$objects = id(new DifferentialRevisionQuery())
->setViewer($query->getViewer())
->withIDs(array_keys($id_map))
->execute();
$results = array();
foreach ($objects as $id => $object) {
foreach (idx($id_map, $id, array()) as $name) {
$results[$name] = $object;
}
}
return $results;
}
}
diff --git a/src/applications/differential/query/DifferentialDiffQuery.php b/src/applications/differential/query/DifferentialDiffQuery.php
index a608a5421e..ec3265caa2 100644
--- a/src/applications/differential/query/DifferentialDiffQuery.php
+++ b/src/applications/differential/query/DifferentialDiffQuery.php
@@ -1,140 +1,144 @@
<?php
final class DifferentialDiffQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $revisionIDs;
private $needChangesets = false;
private $needArcanistProjects = false;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withRevisionIDs(array $revision_ids) {
$this->revisionIDs = $revision_ids;
return $this;
}
public function needChangesets($bool) {
$this->needChangesets = $bool;
return $this;
}
public function needArcanistProjects($bool) {
$this->needArcanistProjects = $bool;
return $this;
}
public function loadPage() {
$table = new DifferentialDiff();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
public 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()) {
$diff->attachRevision(null);
continue;
}
$revision = idx($revisions, $diff->getRevisionID());
if ($revision) {
$diff->attachRevision($revision);
continue;
}
unset($diffs[$key]);
}
if ($this->needChangesets) {
$this->loadChangesets($diffs);
}
if ($this->needArcanistProjects) {
$this->loadArcanistProjects($diffs);
}
return $diffs;
}
private function loadChangesets(array $diffs) {
foreach ($diffs as $diff) {
$diff->attachChangesets(
$diff->loadRelatives(new DifferentialChangeset(), 'diffID'));
foreach ($diff->getChangesets() as $changeset) {
$changeset->attachHunks(
$changeset->loadRelatives(new DifferentialHunk(), 'changesetID'));
}
}
return $diffs;
}
private function loadArcanistProjects(array $diffs) {
$phids = array_filter(mpull($diffs, 'getArcanistProjectPHID'));
$projects = array();
$project_map = array();
if ($phids) {
$projects = id(new PhabricatorRepositoryArcanistProject())
->loadAllWhere(
'phid IN (%Ls)',
$phids);
$project_map = mpull($projects, null, 'getPHID');
}
foreach ($diffs as $diff) {
$project = null;
if ($diff->getArcanistProjectPHID()) {
$project = idx($project_map, $diff->getArcanistProjectPHID());
}
$diff->attachArcanistProject($project);
}
return $diffs;
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->revisionIDs) {
$where[] = qsprintf(
$conn_r,
'revisionID IN (%Ld)',
$this->revisionIDs);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationDifferential';
+ }
+
}
diff --git a/src/applications/differential/query/DifferentialRevisionQuery.php b/src/applications/differential/query/DifferentialRevisionQuery.php
index 556ec6a83c..e124801d11 100644
--- a/src/applications/differential/query/DifferentialRevisionQuery.php
+++ b/src/applications/differential/query/DifferentialRevisionQuery.php
@@ -1,1195 +1,1196 @@
<?php
/**
* Flexible query API for Differential revisions. Example:
*
* // Load open revisions
* $revisions = id(new DifferentialRevisionQuery())
* ->withStatus(DifferentialRevisionQuery::STATUS_OPEN)
* ->execute();
*
* @task config Query Configuration
* @task exec Query Execution
* @task internal Internals
*/
final class DifferentialRevisionQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $pathIDs = array();
private $status = 'status-any';
const STATUS_ANY = 'status-any';
const STATUS_OPEN = 'status-open';
const STATUS_ACCEPTED = 'status-accepted';
const STATUS_NEEDS_REVIEW = 'status-needs-review';
const STATUS_NEEDS_REVISION = 'status-needs-revision';
const STATUS_CLOSED = 'status-closed'; // NOTE: Same as 'committed'
const STATUS_COMMITTED = 'status-committed'; // TODO: Remove.
const STATUS_ABANDONED = 'status-abandoned';
private $authors = array();
private $draftAuthors = array();
private $ccs = array();
private $reviewers = array();
private $revIDs = array();
private $commitHashes = array();
private $phids = array();
private $subscribers = array();
private $responsibles = array();
private $branches = array();
private $arcanistProjectPHIDs = array();
private $draftRevisions = array();
private $repositoryPHIDs;
private $order = 'order-modified';
const ORDER_MODIFIED = 'order-modified';
const ORDER_CREATED = 'order-created';
/**
* This is essentially a denormalized copy of the revision modified time that
* should perform better for path queries with a LIMIT. Critically, when you
* browse "/", every revision in that repository for all time will match so
* the query benefits from being able to stop before fully materializing the
* result set.
*/
const ORDER_PATH_MODIFIED = 'order-path-modified';
private $needRelationships = false;
private $needActiveDiffs = false;
private $needDiffIDs = false;
private $needCommitPHIDs = false;
private $needHashes = false;
private $needReviewerStatus = false;
private $needReviewerAuthority;
private $buildingGlobalOrder;
/* -( Query Configuration )------------------------------------------------ */
/**
* Filter results to revisions which affect a Diffusion path ID in a given
* repository. You can call this multiple times to select revisions for
* several paths.
*
* @param int Diffusion repository ID.
* @param int Diffusion path ID.
* @return this
* @task config
*/
public function withPath($repository_id, $path_id) {
$this->pathIDs[] = array(
'repositoryID' => $repository_id,
'pathID' => $path_id,
);
return $this;
}
/**
* Filter results to revisions authored by one of the given PHIDs. Calling
* this function will clear anything set by previous calls to
* @{method:withAuthors}.
*
* @param array List of PHIDs of authors
* @return this
* @task config
*/
public function withAuthors(array $author_phids) {
$this->authors = $author_phids;
return $this;
}
/**
* Filter results to revisions with comments authored bythe given PHIDs
*
* @param array List of PHIDs of authors
* @return this
* @task config
*/
public function withDraftRepliesByAuthors(array $author_phids) {
$this->draftAuthors = $author_phids;
return $this;
}
/**
* Filter results to revisions which CC one of the listed people. Calling this
* function will clear anything set by previous calls to @{method:withCCs}.
*
* @param array List of PHIDs of subscribers
* @return this
* @task config
*/
public function withCCs(array $cc_phids) {
$this->ccs = $cc_phids;
return $this;
}
/**
* Filter results to revisions that have one of the provided PHIDs as
* reviewers. Calling this function will clear anything set by previous calls
* to @{method:withReviewers}.
*
* @param array List of PHIDs of reviewers
* @return this
* @task config
*/
public function withReviewers(array $reviewer_phids) {
$this->reviewers = $reviewer_phids;
return $this;
}
/**
* Filter results to revisions that have one of the provided commit hashes.
* Calling this function will clear anything set by previous calls to
* @{method:withCommitHashes}.
*
* @param array List of pairs <Class
* ArcanistDifferentialRevisionHash::HASH_$type constant,
* hash>
* @return this
* @task config
*/
public function withCommitHashes(array $commit_hashes) {
$this->commitHashes = $commit_hashes;
return $this;
}
/**
* Filter results to revisions with a given status. Provide a class constant,
* such as ##DifferentialRevisionQuery::STATUS_OPEN##.
*
* @param const Class STATUS constant, like STATUS_OPEN.
* @return this
* @task config
*/
public function withStatus($status_constant) {
$this->status = $status_constant;
return $this;
}
/**
* Filter results to revisions on given branches.
*
* @param list List of branch names.
* @return this
* @task config
*/
public function withBranches(array $branches) {
$this->branches = $branches;
return $this;
}
/**
* Filter results to only return revisions whose ids are in the given set.
*
* @param array List of revision ids
* @return this
* @task config
*/
public function withIDs(array $ids) {
$this->revIDs = $ids;
return $this;
}
/**
* Filter results to only return revisions whose PHIDs are in the given set.
*
* @param array List of revision PHIDs
* @return this
* @task config
*/
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
/**
* Given a set of users, filter results to return only revisions they are
* responsible for (i.e., they are either authors or reviewers).
*
* @param array List of user PHIDs.
* @return this
* @task config
*/
public function withResponsibleUsers(array $responsible_phids) {
$this->responsibles = $responsible_phids;
return $this;
}
/**
* Filter results to only return revisions with a given set of subscribers
* (i.e., they are authors, reviewers or CC'd).
*
* @param array List of user PHIDs.
* @return this
* @task config
*/
public function withSubscribers(array $subscriber_phids) {
$this->subscribers = $subscriber_phids;
return $this;
}
/**
* Filter results to only return revisions with a given set of arcanist
* projects.
*
* @param array List of project PHIDs.
* @return this
* @task config
*/
public function withArcanistProjectPHIDs(array $arc_project_phids) {
$this->arcanistProjectPHIDs = $arc_project_phids;
return $this;
}
public function withRepositoryPHIDs(array $repository_phids) {
$this->repositoryPHIDs = $repository_phids;
return $this;
}
/**
* Set result ordering. Provide a class constant, such as
* ##DifferentialRevisionQuery::ORDER_CREATED##.
*
* @task config
*/
public function setOrder($order_constant) {
$this->order = $order_constant;
return $this;
}
/**
* Set whether or not the query will load and attach relationships.
*
* @param bool True to load and attach relationships.
* @return this
* @task config
*/
public function needRelationships($need_relationships) {
$this->needRelationships = $need_relationships;
return $this;
}
/**
* Set whether or not the query should load the active diff for each
* revision.
*
* @param bool True to load and attach diffs.
* @return this
* @task config
*/
public function needActiveDiffs($need_active_diffs) {
$this->needActiveDiffs = $need_active_diffs;
return $this;
}
/**
* Set whether or not the query should load the associated commit PHIDs for
* each revision.
*
* @param bool True to load and attach diffs.
* @return this
* @task config
*/
public function needCommitPHIDs($need_commit_phids) {
$this->needCommitPHIDs = $need_commit_phids;
return $this;
}
/**
* Set whether or not the query should load associated diff IDs for each
* revision.
*
* @param bool True to load and attach diff IDs.
* @return this
* @task config
*/
public function needDiffIDs($need_diff_ids) {
$this->needDiffIDs = $need_diff_ids;
return $this;
}
/**
* Set whether or not the query should load associated commit hashes for each
* revision.
*
* @param bool True to load and attach commit hashes.
* @return this
* @task config
*/
public function needHashes($need_hashes) {
$this->needHashes = $need_hashes;
return $this;
}
/**
* Set whether or not the query should load associated reviewer status.
*
* @param bool True to load and attach reviewers.
* @return this
* @task config
*/
public function needReviewerStatus($need_reviewer_status) {
$this->needReviewerStatus = $need_reviewer_status;
return $this;
}
/**
* Request information about the viewer's authority to act on behalf of each
* reviewer. In particular, they have authority to act on behalf of projects
* they are a member of.
*
* @param bool True to load and attach authority.
* @return this
* @task config
*/
public function needReviewerAuthority($need_reviewer_authority) {
$this->needReviewerAuthority = $need_reviewer_authority;
return $this;
}
/* -( Query Execution )---------------------------------------------------- */
/**
* Execute the query as configured, returning matching
* @{class:DifferentialRevision} objects.
*
* @return list List of matching DifferentialRevision objects.
* @task exec
*/
public function loadPage() {
$table = new DifferentialRevision();
$conn_r = $table->establishConnection('r');
$data = $this->loadData();
return $table->loadAllFromArray($data);
}
public function willFilterPage(array $revisions) {
$viewer = $this->getViewer();
$repository_phids = mpull($revisions, 'getRepositoryPHID');
$repository_phids = array_filter($repository_phids);
$repositories = array();
if ($repository_phids) {
$repositories = id(new PhabricatorRepositoryQuery())
->setViewer($this->getViewer())
->withPHIDs($repository_phids)
->execute();
$repositories = mpull($repositories, null, 'getPHID');
}
// If a revision is associated with a repository:
//
// - the viewer must be able to see the repository; or
// - the viewer must have an automatic view capability.
//
// In the latter case, we'll load the revision but not load the repository.
$can_view = PhabricatorPolicyCapability::CAN_VIEW;
foreach ($revisions as $key => $revision) {
$repo_phid = $revision->getRepositoryPHID();
if (!$repo_phid) {
// The revision has no associated repository. Attach `null` and move on.
$revision->attachRepository(null);
continue;
}
$repository = idx($repositories, $repo_phid);
if ($repository) {
// The revision has an associated repository, and the viewer can see
// it. Attach it and move on.
$revision->attachRepository($repository);
continue;
}
if ($revision->hasAutomaticCapability($can_view, $viewer)) {
// The revision has an associated repository which the viewer can not
// see, but the viewer has an automatic capability on this revision.
// Load the revision without attaching a repository.
$revision->attachRepository(null);
continue;
}
// The revision has an associated repository, and the viewer can't see
// it, and the viewer has no special capabilities. Filter out this
// revision.
$this->didRejectResult($revision);
unset($revisions[$key]);
}
if (!$revisions) {
return array();
}
$table = new DifferentialRevision();
$conn_r = $table->establishConnection('r');
if ($this->needRelationships) {
$this->loadRelationships($conn_r, $revisions);
}
if ($this->needCommitPHIDs) {
$this->loadCommitPHIDs($conn_r, $revisions);
}
$need_active = $this->needActiveDiffs;
$need_ids = $need_active || $this->needDiffIDs;
if ($need_ids) {
$this->loadDiffIDs($conn_r, $revisions);
}
if ($need_active) {
$this->loadActiveDiffs($conn_r, $revisions);
}
if ($this->needHashes) {
$this->loadHashes($conn_r, $revisions);
}
if ($this->needReviewerStatus || $this->needReviewerAuthority) {
$this->loadReviewers($conn_r, $revisions);
}
return $revisions;
}
private function loadData() {
$table = new DifferentialRevision();
$conn_r = $table->establishConnection('r');
if ($this->draftAuthors) {
$this->draftRevisions = array();
$draft_key = 'differential-comment-';
$drafts = id(new PhabricatorDraft())->loadAllWhere(
'authorPHID IN (%Ls) AND draftKey LIKE %> AND draft != %s',
$this->draftAuthors,
$draft_key,
'');
$len = strlen($draft_key);
foreach ($drafts as $draft) {
$this->draftRevisions[] = substr($draft->getDraftKey(), $len);
}
// TODO: Restore this after drafts are sorted out. It's now very
// expensive to get revision IDs.
/*
$inlines = id(new DifferentialInlineCommentQuery())
->withDraftsByAuthors($this->draftAuthors)
->execute();
foreach ($inlines as $inline) {
$this->draftRevisions[] = $inline->getRevisionID();
}
*/
if (!$this->draftRevisions) {
return array();
}
}
$selects = array();
// NOTE: If the query includes "responsiblePHIDs", we execute it as a
// UNION of revisions they own and revisions they're reviewing. This has
// much better performance than doing it with JOIN/WHERE.
if ($this->responsibles) {
$basic_authors = $this->authors;
$basic_reviewers = $this->reviewers;
$authority_projects = id(new PhabricatorProjectQuery())
->setViewer($this->getViewer())
->withMemberPHIDs($this->responsibles)
->execute();
$authority_phids = mpull($authority_projects, 'getPHID');
try {
// Build the query where the responsible users are authors.
$this->authors = array_merge($basic_authors, $this->responsibles);
$this->reviewers = $basic_reviewers;
$selects[] = $this->buildSelectStatement($conn_r);
// Build the query where the responsible users are reviewers, or
// projects they are members of are reviewers.
$this->authors = $basic_authors;
$this->reviewers = array_merge(
$basic_reviewers,
$this->responsibles,
$authority_phids);
$selects[] = $this->buildSelectStatement($conn_r);
// Put everything back like it was.
$this->authors = $basic_authors;
$this->reviewers = $basic_reviewers;
} catch (Exception $ex) {
$this->authors = $basic_authors;
$this->reviewers = $basic_reviewers;
throw $ex;
}
} else {
$selects[] = $this->buildSelectStatement($conn_r);
}
if (count($selects) > 1) {
$this->buildingGlobalOrder = true;
$query = qsprintf(
$conn_r,
'%Q %Q %Q',
implode(' UNION DISTINCT ', $selects),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
} else {
$query = head($selects);
}
return queryfx_all($conn_r, '%Q', $query);
}
private function buildSelectStatement(AphrontDatabaseConnection $conn_r) {
$table = new DifferentialRevision();
$select = qsprintf(
$conn_r,
'SELECT r.* FROM %T r',
$table->getTableName());
$joins = $this->buildJoinsClause($conn_r);
$where = $this->buildWhereClause($conn_r);
$group_by = $this->buildGroupByClause($conn_r);
$this->buildingGlobalOrder = false;
$order_by = $this->buildOrderClause($conn_r);
$limit = $this->buildLimitClause($conn_r);
return qsprintf(
$conn_r,
'(%Q %Q %Q %Q %Q %Q)',
$select,
$joins,
$where,
$group_by,
$order_by,
$limit);
}
/* -( Internals )---------------------------------------------------------- */
/**
* @task internal
*/
private function buildJoinsClause($conn_r) {
$joins = array();
if ($this->pathIDs) {
$path_table = new DifferentialAffectedPath();
$joins[] = qsprintf(
$conn_r,
'JOIN %T p ON p.revisionID = r.id',
$path_table->getTableName());
}
if ($this->commitHashes) {
$joins[] = qsprintf(
$conn_r,
'JOIN %T hash_rel ON hash_rel.revisionID = r.id',
ArcanistDifferentialRevisionHash::TABLE_NAME);
}
if ($this->ccs) {
$joins[] = qsprintf(
$conn_r,
'JOIN %T cc_rel ON cc_rel.revisionID = r.id '.
'AND cc_rel.relation = %s '.
'AND cc_rel.objectPHID in (%Ls)',
DifferentialRevision::RELATIONSHIP_TABLE,
DifferentialRevision::RELATION_SUBSCRIBED,
$this->ccs);
}
if ($this->reviewers) {
$joins[] = qsprintf(
$conn_r,
'JOIN %T e_reviewers ON e_reviewers.src = r.phid '.
'AND e_reviewers.type = %s '.
'AND e_reviewers.dst in (%Ls)',
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
PhabricatorEdgeConfig::TYPE_DREV_HAS_REVIEWER,
$this->reviewers);
}
if ($this->subscribers) {
// TODO: These can be expressed as a JOIN again (and the corresponding
// WHERE clause removed) once subscribers move to edges.
$joins[] = qsprintf(
$conn_r,
'LEFT JOIN %T sub_rel_cc ON sub_rel_cc.revisionID = r.id '.
'AND sub_rel_cc.relation = %s '.
'AND sub_rel_cc.objectPHID in (%Ls)',
DifferentialRevision::RELATIONSHIP_TABLE,
DifferentialRevision::RELATION_SUBSCRIBED,
$this->subscribers);
$joins[] = qsprintf(
$conn_r,
'LEFT JOIN %T sub_rel_reviewer ON sub_rel_reviewer.src = r.phid '.
'AND sub_rel_reviewer.type = %s '.
'AND sub_rel_reviewer.dst in (%Ls)',
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
PhabricatorEdgeConfig::TYPE_DREV_HAS_REVIEWER,
$this->subscribers);
}
$joins = implode(' ', $joins);
return $joins;
}
/**
* @task internal
*/
private function buildWhereClause($conn_r) {
$where = array();
if ($this->pathIDs) {
$path_clauses = array();
$repo_info = igroup($this->pathIDs, 'repositoryID');
foreach ($repo_info as $repository_id => $paths) {
$path_clauses[] = qsprintf(
$conn_r,
'(p.repositoryID = %d AND p.pathID IN (%Ld))',
$repository_id,
ipull($paths, 'pathID'));
}
$path_clauses = '('.implode(' OR ', $path_clauses).')';
$where[] = $path_clauses;
}
if ($this->authors) {
$where[] = qsprintf(
$conn_r,
'r.authorPHID IN (%Ls)',
$this->authors);
}
if ($this->draftRevisions) {
$where[] = qsprintf(
$conn_r,
'r.id IN (%Ld)',
$this->draftRevisions);
}
if ($this->revIDs) {
$where[] = qsprintf(
$conn_r,
'r.id IN (%Ld)',
$this->revIDs);
}
if ($this->repositoryPHIDs) {
$where[] = qsprintf(
$conn_r,
'r.repositoryPHID IN (%Ls)',
$this->repositoryPHIDs);
}
if ($this->commitHashes) {
$hash_clauses = array();
foreach ($this->commitHashes as $info) {
list($type, $hash) = $info;
$hash_clauses[] = qsprintf(
$conn_r,
'(hash_rel.type = %s AND hash_rel.hash = %s)',
$type,
$hash);
}
$hash_clauses = '('.implode(' OR ', $hash_clauses).')';
$where[] = $hash_clauses;
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'r.phid IN (%Ls)',
$this->phids);
}
if ($this->branches) {
$where[] = qsprintf(
$conn_r,
'r.branchName in (%Ls)',
$this->branches);
}
if ($this->arcanistProjectPHIDs) {
$where[] = qsprintf(
$conn_r,
'r.arcanistProjectPHID in (%Ls)',
$this->arcanistProjectPHIDs);
}
if ($this->subscribers) {
$where[] = qsprintf(
$conn_r,
'(sub_rel_cc.objectPHID IS NOT NULL)
OR (sub_rel_reviewer.dst IS NOT NULL)');
}
switch ($this->status) {
case self::STATUS_ANY:
break;
case self::STATUS_OPEN:
$where[] = qsprintf(
$conn_r,
'r.status IN (%Ld)',
array(
ArcanistDifferentialRevisionStatus::NEEDS_REVIEW,
ArcanistDifferentialRevisionStatus::NEEDS_REVISION,
ArcanistDifferentialRevisionStatus::ACCEPTED,
));
break;
case self::STATUS_NEEDS_REVIEW:
$where[] = qsprintf(
$conn_r,
'r.status IN (%Ld)',
array(
ArcanistDifferentialRevisionStatus::NEEDS_REVIEW,
));
break;
case self::STATUS_NEEDS_REVISION:
$where[] = qsprintf(
$conn_r,
'r.status IN (%Ld)',
array(
ArcanistDifferentialRevisionStatus::NEEDS_REVISION,
));
break;
case self::STATUS_ACCEPTED:
$where[] = qsprintf(
$conn_r,
'r.status IN (%Ld)',
array(
ArcanistDifferentialRevisionStatus::ACCEPTED,
));
break;
case self::STATUS_COMMITTED:
phlog(
"WARNING: DifferentialRevisionQuery using deprecated ".
"STATUS_COMMITTED constant. This will be removed soon. ".
"Use STATUS_CLOSED.");
// fallthrough
case self::STATUS_CLOSED:
$where[] = qsprintf(
$conn_r,
'r.status IN (%Ld)',
array(
ArcanistDifferentialRevisionStatus::CLOSED,
));
break;
case self::STATUS_ABANDONED:
$where[] = qsprintf(
$conn_r,
'r.status IN (%Ld)',
array(
ArcanistDifferentialRevisionStatus::ABANDONED,
));
break;
default:
throw new Exception(
"Unknown revision status filter constant '{$this->status}'!");
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
/**
* @task internal
*/
private function buildGroupByClause($conn_r) {
$join_triggers = array_merge(
$this->pathIDs,
$this->ccs,
$this->reviewers,
$this->subscribers);
$needs_distinct = (count($join_triggers) > 1);
if ($needs_distinct) {
return 'GROUP BY r.id';
} else {
return '';
}
}
private function loadCursorObject($id) {
$results = id(new DifferentialRevisionQuery())
->setViewer($this->getPagingViewer())
->withIDs(array((int)$id))
->execute();
return head($results);
}
protected function buildPagingClause(AphrontDatabaseConnection $conn_r) {
$default = parent::buildPagingClause($conn_r);
$before_id = $this->getBeforeID();
$after_id = $this->getAfterID();
if (!$before_id && !$after_id) {
return $default;
}
if ($before_id) {
$cursor = $this->loadCursorObject($before_id);
} else {
$cursor = $this->loadCursorObject($after_id);
}
if (!$cursor) {
return null;
}
$columns = array();
switch ($this->order) {
case self::ORDER_CREATED:
return $default;
case self::ORDER_MODIFIED:
$columns[] = array(
'name' => 'r.dateModified',
'value' => $cursor->getDateModified(),
'type' => 'int',
);
break;
case self::ORDER_PATH_MODIFIED:
$columns[] = array(
'name' => 'p.epoch',
'value' => $cursor->getDateCreated(),
'type' => 'int',
);
break;
}
$columns[] = array(
'name' => 'r.id',
'value' => $cursor->getID(),
'type' => 'int',
);
return $this->buildPagingClauseFromMultipleColumns(
$conn_r,
$columns,
array(
'reversed' => (bool)($before_id xor $this->getReversePaging()),
));
}
protected function getPagingColumn() {
$is_global = $this->buildingGlobalOrder;
switch ($this->order) {
case self::ORDER_MODIFIED:
if ($is_global) {
return 'dateModified';
}
return 'r.dateModified';
case self::ORDER_CREATED:
if ($is_global) {
return 'id';
}
return 'r.id';
case self::ORDER_PATH_MODIFIED:
if (!$this->pathIDs) {
throw new Exception(
"To use ORDER_PATH_MODIFIED, you must specify withPath().");
}
return 'p.epoch';
default:
throw new Exception("Unknown query order constant '{$this->order}'.");
}
}
private function loadRelationships($conn_r, array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$relationships = queryfx_all(
$conn_r,
'SELECT * FROM %T WHERE revisionID in (%Ld)
AND relation != %s ORDER BY sequence',
DifferentialRevision::RELATIONSHIP_TABLE,
mpull($revisions, 'getID'),
DifferentialRevision::RELATION_REVIEWER);
$relationships = igroup($relationships, 'revisionID');
$type_reviewer = PhabricatorEdgeConfig::TYPE_DREV_HAS_REVIEWER;
$edges = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(mpull($revisions, 'getPHID'))
->withEdgeTypes(array($type_reviewer))
->setOrder(PhabricatorEdgeQuery::ORDER_OLDEST_FIRST)
->execute();
foreach ($revisions as $revision) {
$data = idx($relationships, $revision->getID(), array());
$revision_edges = $edges[$revision->getPHID()][$type_reviewer];
foreach ($revision_edges as $dst_phid => $edge_data) {
$data[] = array(
'relation' => DifferentialRevision::RELATION_REVIEWER,
'objectPHID' => $dst_phid,
'reasonPHID' => null,
);
}
$revision->attachRelationships($data);
}
}
private function loadCommitPHIDs($conn_r, array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$commit_phids = queryfx_all(
$conn_r,
'SELECT * FROM %T WHERE revisionID IN (%Ld)',
DifferentialRevision::TABLE_COMMIT,
mpull($revisions, 'getID'));
$commit_phids = igroup($commit_phids, 'revisionID');
foreach ($revisions as $revision) {
$phids = idx($commit_phids, $revision->getID(), array());
$phids = ipull($phids, 'commitPHID');
$revision->attachCommitPHIDs($phids);
}
}
private function loadDiffIDs($conn_r, array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$diff_table = new DifferentialDiff();
$diff_ids = queryfx_all(
$conn_r,
'SELECT revisionID, id FROM %T WHERE revisionID IN (%Ld)
ORDER BY id DESC',
$diff_table->getTableName(),
mpull($revisions, 'getID'));
$diff_ids = igroup($diff_ids, 'revisionID');
foreach ($revisions as $revision) {
$ids = idx($diff_ids, $revision->getID(), array());
$ids = ipull($ids, 'id');
$revision->attachDiffIDs($ids);
}
}
private function loadActiveDiffs($conn_r, array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$diff_table = new DifferentialDiff();
$load_ids = array();
foreach ($revisions as $revision) {
$diffs = $revision->getDiffIDs();
if ($diffs) {
$load_ids[] = max($diffs);
}
}
$active_diffs = array();
if ($load_ids) {
$active_diffs = $diff_table->loadAllWhere(
'id IN (%Ld)',
$load_ids);
}
$active_diffs = mpull($active_diffs, null, 'getRevisionID');
foreach ($revisions as $revision) {
$revision->attachActiveDiff(idx($active_diffs, $revision->getID()));
}
}
private function loadHashes(
AphrontDatabaseConnection $conn_r,
array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T WHERE revisionID IN (%Ld)',
'differential_revisionhash',
mpull($revisions, 'getID'));
$data = igroup($data, 'revisionID');
foreach ($revisions as $revision) {
$hashes = idx($data, $revision->getID(), array());
$list = array();
foreach ($hashes as $hash) {
$list[] = array($hash['type'], $hash['hash']);
}
$revision->attachHashes($list);
}
}
private function loadReviewers(
AphrontDatabaseConnection $conn_r,
array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$edge_type = PhabricatorEdgeConfig::TYPE_DREV_HAS_REVIEWER;
$edges = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(mpull($revisions, 'getPHID'))
->withEdgeTypes(array($edge_type))
->needEdgeData(true)
->setOrder(PhabricatorEdgeQuery::ORDER_OLDEST_FIRST)
->execute();
$viewer = $this->getViewer();
$viewer_phid = $viewer->getPHID();
// Figure out which of these reviewers the viewer has authority to act as.
if ($this->needReviewerAuthority && $viewer_phid) {
$allow_key = 'differential.allow-self-accept';
$allow_self = PhabricatorEnv::getEnvConfig($allow_key);
$authority = $this->loadReviewerAuthority(
$revisions,
$edges,
$allow_self);
}
foreach ($revisions as $revision) {
$revision_edges = $edges[$revision->getPHID()][$edge_type];
$reviewers = array();
foreach ($revision_edges as $reviewer_phid => $edge) {
$reviewer = new DifferentialReviewer($reviewer_phid, $edge['data']);
if ($this->needReviewerAuthority) {
if (!$viewer_phid) {
// Logged-out users never have authority.
$has_authority = false;
} else if ((!$allow_self) &&
($revision->getAuthorPHID() == $viewer_phid)) {
// The author can never have authority unless we allow self-accept.
$has_authority = false;
} else {
// Otherwise, look up whether th viewer has authority.
$has_authority = isset($authority[$reviewer_phid]);
}
$reviewer->attachAuthority($viewer, $has_authority);
}
$reviewers[$reviewer_phid] = $reviewer;
}
$revision->attachReviewerStatus($reviewers);
}
}
public static function splitResponsible(array $revisions, array $user_phids) {
$blocking = array();
$active = array();
$waiting = array();
$status_review = ArcanistDifferentialRevisionStatus::NEEDS_REVIEW;
// Bucket revisions into $blocking (revisions where you are blocking
// others), $active (revisions you need to do something about) and $waiting
// (revisions you're waiting on someone else to do something about).
foreach ($revisions as $revision) {
$needs_review = ($revision->getStatus() == $status_review);
$filter_is_author = in_array($revision->getAuthorPHID(), $user_phids);
if (!$revision->getReviewers()) {
$needs_review = false;
}
// If exactly one of "needs review" and "the user is the author" is
// true, the user needs to act on it. Otherwise, they're waiting on
// it.
if ($needs_review ^ $filter_is_author) {
if ($needs_review) {
array_unshift($blocking, $revision);
} else {
$active[] = $revision;
}
} else {
$waiting[] = $revision;
}
}
return array($blocking, $active, $waiting);
}
private function loadReviewerAuthority(
array $revisions,
array $edges,
$allow_self) {
$revision_map = mpull($revisions, null, 'getPHID');
$viewer_phid = $this->getViewer()->getPHID();
// Find all the project reviewers which the user may have authority over.
$project_phids = array();
$project_type = PhabricatorProjectPHIDTypeProject::TYPECONST;
$edge_type = PhabricatorEdgeConfig::TYPE_DREV_HAS_REVIEWER;
foreach ($edges as $src => $types) {
if (!$allow_self) {
if ($revision_map[$src]->getAuthorPHID() == $viewer_phid) {
// If self-review isn't permitted, the user will never have
// authority over projects on revisions they authored because you
// can't accept your own revisions, so we don't need to load any
// data about these reviewers.
continue;
}
}
$edge_data = idx($types, $edge_type, array());
foreach ($edge_data as $dst => $data) {
if (phid_get_type($dst) == $project_type) {
$project_phids[] = $dst;
}
}
}
// Now, figure out which of these projects the viewer is actually a
// member of.
$project_authority = array();
if ($project_phids) {
$project_authority = id(new PhabricatorProjectQuery())
->setViewer($this->getViewer())
->withPHIDs($project_phids)
->withMemberPHIDs(array($viewer_phid))
->execute();
$project_authority = mpull($project_authority, 'getPHID');
}
// Finally, the viewer has authority over themselves.
return array(
$viewer_phid => true,
) + array_fuse($project_authority);
}
-
-
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationDifferential';
+ }
}
diff --git a/src/applications/diffusion/query/DiffusionCommitQuery.php b/src/applications/diffusion/query/DiffusionCommitQuery.php
index 36e5a711ea..3522b6e7fa 100644
--- a/src/applications/diffusion/query/DiffusionCommitQuery.php
+++ b/src/applications/diffusion/query/DiffusionCommitQuery.php
@@ -1,253 +1,257 @@
<?php
final class DiffusionCommitQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $identifiers;
private $phids;
private $defaultRepository;
private $identifierMap;
/**
* Load commits by partial or full identifiers, e.g. "rXab82393", "rX1234",
* or "a9caf12". When an identifier matches multiple commits, they will all
* be returned; callers should be prepared to deal with more results than
* they queried for.
*/
public function withIdentifiers(array $identifiers) {
$this->identifiers = $identifiers;
return $this;
}
/**
* If a default repository is provided, ambiguous commit identifiers will
* be assumed to belong to the default repository.
*
* For example, "r123" appearing in a commit message in repository X is
* likely to be unambiguously "rX123". Normally the reference would be
* considered ambiguous, but if you provide a default repository it will
* be correctly resolved.
*/
public function withDefaultRepository(PhabricatorRepository $repository) {
$this->defaultRepository = $repository;
return $this;
}
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function getIdentifierMap() {
if ($this->identifierMap === null) {
throw new Exception(
"You must execute() the query before accessing the identifier map.");
}
return $this->identifierMap;
}
protected function loadPage() {
if ($this->identifierMap === null) {
$this->identifierMap = array();
}
$table = new PhabricatorRepositoryCommit();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
public function willFilterPage(array $commits) {
$repository_ids = mpull($commits, 'getRepositoryID', 'getRepositoryID');
$repos = id(new PhabricatorRepositoryQuery())
->setViewer($this->getViewer())
->withIDs($repository_ids)
->execute();
foreach ($commits as $key => $commit) {
$repo = idx($repos, $commit->getRepositoryID());
if ($repo) {
$commit->attachRepository($repo);
} else {
unset($commits[$key]);
}
}
if ($this->identifiers !== null) {
$ids = array_fuse($this->identifiers);
$min_qualified = PhabricatorRepository::MINIMUM_QUALIFIED_HASH;
$result = array();
foreach ($commits as $commit) {
$prefix = 'r'.$commit->getRepository()->getCallsign();
$suffix = $commit->getCommitIdentifier();
if ($commit->getRepository()->isSVN()) {
if (isset($ids[$prefix.$suffix])) {
$result[$prefix.$suffix][] = $commit;
}
} else {
// This awkward contruction is so we can link the commits up in O(N)
// time instead of O(N^2).
for ($ii = $min_qualified; $ii <= strlen($suffix); $ii++) {
$part = substr($suffix, 0, $ii);
if (isset($ids[$prefix.$part])) {
$result[$prefix.$part][] = $commit;
}
if (isset($ids[$part])) {
$result[$part][] = $commit;
}
}
}
}
foreach ($result as $identifier => $matching_commits) {
if (count($matching_commits) == 1) {
$result[$identifier] = head($matching_commits);
} else {
// This reference is ambiguous (it matches more than one commit) so
// don't link it
unset($result[$identifier]);
}
}
$this->identifierMap += $result;
}
return $commits;
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->identifiers) {
$min_unqualified = PhabricatorRepository::MINIMUM_UNQUALIFIED_HASH;
$min_qualified = PhabricatorRepository::MINIMUM_QUALIFIED_HASH;
$refs = array();
$bare = array();
foreach ($this->identifiers as $identifier) {
$matches = null;
preg_match('/^(?:r([A-Z]+))?(.*)$/', $identifier, $matches);
$repo = nonempty($matches[1], null);
$identifier = nonempty($matches[2], null);
if ($repo === null) {
if ($this->defaultRepository) {
$repo = $this->defaultRepository->getCallsign();
}
}
if ($repo === null) {
if (strlen($identifier) < $min_unqualified) {
continue;
}
$bare[] = $identifier;
} else {
$refs[] = array(
'callsign' => $repo,
'identifier' => $identifier,
);
}
}
$sql = array();
foreach ($bare as $identifier) {
$sql[] = qsprintf(
$conn_r,
'(commitIdentifier LIKE %> AND LENGTH(commitIdentifier) = 40)',
$identifier);
}
if ($refs) {
$callsigns = ipull($refs, 'callsign');
$repos = id(new PhabricatorRepositoryQuery())
->setViewer($this->getViewer())
->withCallsigns($callsigns)
->execute();
$repos = mpull($repos, null, 'getCallsign');
foreach ($refs as $key => $ref) {
$repo = idx($repos, $ref['callsign']);
if (!$repo) {
continue;
}
if ($repo->isSVN()) {
if (!ctype_digit($ref['identifier'])) {
continue;
}
$sql[] = qsprintf(
$conn_r,
'(repositoryID = %d AND commitIdentifier = %s)',
$repo->getID(),
// NOTE: Because the 'commitIdentifier' column is a string, MySQL
// ignores the index if we hand it an integer. Hand it a string.
// See T3377.
(int)$ref['identifier']);
} else {
if (strlen($ref['identifier']) < $min_qualified) {
continue;
}
$sql[] = qsprintf(
$conn_r,
'(repositoryID = %d AND commitIdentifier LIKE %>)',
$repo->getID(),
$ref['identifier']);
}
}
}
if (!$sql) {
// If we discarded all possible identifiers (e.g., they all referenced
// bogus repositories or were all too short), make sure the query finds
// nothing.
throw new PhabricatorEmptyQueryException('No commit identifiers.');
}
$where[] = '('.implode(' OR ', $sql).')';
}
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
return $this->formatWhereClause($where);
}
public function didFilterResults(array $filtered) {
if ($this->identifierMap) {
foreach ($this->identifierMap as $name => $commit) {
if (isset($filtered[$commit->getPHID()])) {
unset($this->identifierMap[$name]);
}
}
}
}
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationDiffusion';
+ }
+
}
diff --git a/src/applications/diviner/phid/DivinerPHIDTypeAtom.php b/src/applications/diviner/phid/DivinerPHIDTypeAtom.php
index a563ec45c6..6a057c0741 100644
--- a/src/applications/diviner/phid/DivinerPHIDTypeAtom.php
+++ b/src/applications/diviner/phid/DivinerPHIDTypeAtom.php
@@ -1,43 +1,40 @@
<?php
final class DivinerPHIDTypeAtom extends PhabricatorPHIDType {
const TYPECONST = 'ATOM';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Atom');
}
public function newObject() {
return new DivinerLiveSymbol();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new DivinerAtomQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$atom = $objects[$phid];
$handle->setName($atom->getTitle());
$handle->setURI($atom->getName());
}
}
}
diff --git a/src/applications/diviner/phid/DivinerPHIDTypeBook.php b/src/applications/diviner/phid/DivinerPHIDTypeBook.php
index 2d80555371..3bd965ee78 100644
--- a/src/applications/diviner/phid/DivinerPHIDTypeBook.php
+++ b/src/applications/diviner/phid/DivinerPHIDTypeBook.php
@@ -1,46 +1,43 @@
<?php
final class DivinerPHIDTypeBook extends PhabricatorPHIDType {
const TYPECONST = 'BOOK';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Book');
}
public function newObject() {
return new DivinerLiveBook();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new DivinerBookQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$book = $objects[$phid];
$name = $book->getName();
$handle->setName($book->getShortTitle());
$handle->setFullName($book->getTitle());
$handle->setURI("/diviner/book/{$name}/");
}
}
}
diff --git a/src/applications/diviner/query/DivinerAtomQuery.php b/src/applications/diviner/query/DivinerAtomQuery.php
index 662bc834d8..45986b7674 100644
--- a/src/applications/diviner/query/DivinerAtomQuery.php
+++ b/src/applications/diviner/query/DivinerAtomQuery.php
@@ -1,408 +1,412 @@
<?php
final class DivinerAtomQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $bookPHIDs;
private $names;
private $types;
private $contexts;
private $indexes;
private $includeUndocumentable;
private $includeGhosts;
private $nodeHashes;
private $needAtoms;
private $needExtends;
private $needChildren;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withBookPHIDs(array $phids) {
$this->bookPHIDs = $phids;
return $this;
}
public function withTypes(array $types) {
$this->types = $types;
return $this;
}
public function withNames(array $names) {
$this->names = $names;
return $this;
}
public function withContexts(array $contexts) {
$this->contexts = $contexts;
return $this;
}
public function withIndexes(array $indexes) {
$this->indexes = $indexes;
return $this;
}
public function withNodeHashes(array $hashes) {
$this->nodeHashes = $hashes;
return $this;
}
public function needAtoms($need) {
$this->needAtoms = $need;
return $this;
}
public function needChildren($need) {
$this->needChildren = $need;
return $this;
}
/**
* Include "ghosts", which are symbols which used to exist but do not exist
* currently (for example, a function which existed in an older version of
* the codebase but was deleted).
*
* These symbols had PHIDs assigned to them, and may have other sorts of
* metadata that we don't want to lose (like comments or flags), so we don't
* delete them outright. They might also come back in the future: the change
* which deleted the symbol might be reverted, or the documentation might
* have been generated incorrectly by accident. In these cases, we can
* restore the original data.
*
* However, most callers are not interested in these symbols, so they are
* excluded by default. You can use this method to include them in results.
*
* @param bool True to include ghosts.
* @return this
*/
public function withIncludeGhosts($include) {
$this->includeGhosts = $include;
return $this;
}
public function needExtends($need) {
$this->needExtends = $need;
return $this;
}
public function withIncludeUndocumentable($include) {
$this->includeUndocumentable = $include;
return $this;
}
protected function loadPage() {
$table = new DivinerLiveSymbol();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
protected function willFilterPage(array $atoms) {
$books = array_unique(mpull($atoms, 'getBookPHID'));
$books = id(new DivinerBookQuery())
->setViewer($this->getViewer())
->withPHIDs($books)
->execute();
$books = mpull($books, null, 'getPHID');
foreach ($atoms as $key => $atom) {
$book = idx($books, $atom->getBookPHID());
if (!$book) {
unset($atoms[$key]);
continue;
}
$atom->attachBook($book);
}
if ($this->needAtoms) {
$atom_data = id(new DivinerLiveAtom())->loadAllWhere(
'symbolPHID IN (%Ls)',
mpull($atoms, 'getPHID'));
$atom_data = mpull($atom_data, null, 'getSymbolPHID');
foreach ($atoms as $key => $atom) {
$data = idx($atom_data, $atom->getPHID());
if (!$data) {
unset($atoms[$key]);
continue;
}
$atom->attachAtom($data);
}
}
// Load all of the symbols this symbol extends, recursively. Commonly,
// this means all the ancestor classes and interfaces it extends and
// implements.
if ($this->needExtends) {
// First, load all the matching symbols by name. This does 99% of the
// work in most cases, assuming things are named at all reasonably.
$names = array();
foreach ($atoms as $atom) {
foreach ($atom->getAtom()->getExtends() as $xref) {
$names[] = $xref->getName();
}
}
if ($names) {
$xatoms = id(new DivinerAtomQuery())
->setViewer($this->getViewer())
->withNames($names)
->needExtends(true)
->needAtoms(true)
->needChildren($this->needChildren)
->execute();
$xatoms = mgroup($xatoms, 'getName', 'getType', 'getBookPHID');
} else {
$xatoms = array();
}
foreach ($atoms as $atom) {
$alang = $atom->getAtom()->getLanguage();
$extends = array();
foreach ($atom->getAtom()->getExtends() as $xref) {
// If there are no symbols of the matching name and type, we can't
// resolve this.
if (empty($xatoms[$xref->getName()][$xref->getType()])) {
continue;
}
// If we found matches in the same documentation book, prefer them
// over other matches. Otherwise, look at all the the matches.
$matches = $xatoms[$xref->getName()][$xref->getType()];
if (isset($matches[$atom->getBookPHID()])) {
$maybe = $matches[$atom->getBookPHID()];
} else {
$maybe = array_mergev($matches);
}
if (!$maybe) {
continue;
}
// Filter out matches in a different language, since, e.g., PHP
// classes can not implement JS classes.
$same_lang = array();
foreach ($maybe as $xatom) {
if ($xatom->getAtom()->getLanguage() == $alang) {
$same_lang[] = $xatom;
}
}
if (!$same_lang) {
continue;
}
// If we have duplicates remaining, just pick the first one. There's
// nothing more we can do to figure out which is the real one.
$extends[] = head($same_lang);
}
$atom->attachExtends($extends);
}
}
if ($this->needChildren) {
$child_hashes = $this->getAllChildHashes($atoms, $this->needExtends);
if ($child_hashes) {
$children = id(new DivinerAtomQuery())
->setViewer($this->getViewer())
->withIncludeUndocumentable(true)
->withNodeHashes($child_hashes)
->needAtoms($this->needAtoms)
->execute();
$children = mpull($children, null, 'getNodeHash');
} else {
$children = array();
}
$this->attachAllChildren($atoms, $children, $this->needExtends);
}
return $atoms;
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
if ($this->bookPHIDs) {
$where[] = qsprintf(
$conn_r,
'bookPHID IN (%Ls)',
$this->bookPHIDs);
}
if ($this->types) {
$where[] = qsprintf(
$conn_r,
'type IN (%Ls)',
$this->types);
}
if ($this->names) {
$where[] = qsprintf(
$conn_r,
'name IN (%Ls)',
$this->names);
}
if ($this->contexts) {
$with_null = false;
$contexts = $this->contexts;
foreach ($contexts as $key => $value) {
if ($value === null) {
unset($contexts[$key]);
$with_null = true;
continue;
}
}
if ($contexts && $with_null) {
$where[] = qsprintf(
$conn_r,
'context IN (%Ls) OR context IS NULL',
$contexts);
} else if ($contexts) {
$where[] = qsprintf(
$conn_r,
'context IN (%Ls)',
$contexts);
} else if ($with_null) {
$where[] = qsprintf(
$conn_r,
'context IS NULL');
}
}
if ($this->indexes) {
$where[] = qsprintf(
$conn_r,
'atomIndex IN (%Ld)',
$this->indexes);
}
if (!$this->includeUndocumentable) {
$where[] = qsprintf(
$conn_r,
'isDocumentable = 1');
}
if (!$this->includeGhosts) {
$where[] = qsprintf(
$conn_r,
'graphHash IS NOT NULL');
}
if ($this->nodeHashes) {
$where[] = qsprintf(
$conn_r,
'nodeHash IN (%Ls)',
$this->nodeHashes);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
/**
* Walk a list of atoms and collect all the node hashes of the atoms'
* children. When recursing, also walk up the tree and collect children of
* atoms they extend.
*
* @param list<DivinerLiveSymbol> List of symbols to collect child hashes of.
* @param bool True to collect children of extended atoms,
* as well.
* @return map<string, string> Hashes of atoms' children.
*/
private function getAllChildHashes(array $symbols, $recurse_up) {
assert_instances_of($symbols, 'DivinerLiveSymbol');
$hashes = array();
foreach ($symbols as $symbol) {
foreach ($symbol->getAtom()->getChildHashes() as $hash) {
$hashes[$hash] = $hash;
}
if ($recurse_up) {
$hashes += $this->getAllChildHashes($symbol->getExtends(), true);
}
}
return $hashes;
}
/**
* Attach child atoms to existing atoms. In recursive mode, also attach child
* atoms to atoms that these atoms extend.
*
* @param list<DivinerLiveSymbol> List of symbols to attach childeren to.
* @param map<string, DivinerLiveSymbol> Map of symbols, keyed by node hash.
* @param bool True to attach children to extended atoms, as well.
* @return void
*/
private function attachAllChildren(
array $symbols,
array $children,
$recurse_up) {
assert_instances_of($symbols, 'DivinerLiveSymbol');
assert_instances_of($children, 'DivinerLiveSymbol');
foreach ($symbols as $symbol) {
$symbol_children = array();
foreach ($symbol->getAtom()->getChildHashes() as $hash) {
if (isset($children[$hash])) {
$symbol_children[] = $children[$hash];
}
}
$symbol->attachChildren($symbol_children);
if ($recurse_up) {
$this->attachAllChildren($symbol->getExtends(), $children, true);
}
}
}
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationDiviner';
+ }
+
}
diff --git a/src/applications/diviner/query/DivinerBookQuery.php b/src/applications/diviner/query/DivinerBookQuery.php
index af6fe1d717..062bc524ca 100644
--- a/src/applications/diviner/query/DivinerBookQuery.php
+++ b/src/applications/diviner/query/DivinerBookQuery.php
@@ -1,69 +1,74 @@
<?php
final class DivinerBookQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $names;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withNames(array $names) {
$this->names = $names;
return $this;
}
protected function loadPage() {
$table = new DivinerLiveBook();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
if ($this->names) {
$where[] = qsprintf(
$conn_r,
'name IN (%Ls)',
$this->names);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationDiviner';
+ }
+
}
diff --git a/src/applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php b/src/applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php
index 0246fdf210..9c410b99b6 100644
--- a/src/applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php
+++ b/src/applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php
@@ -1,55 +1,59 @@
<?php
final class DoorkeeperExternalObjectQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
protected $phids;
protected $objectKeys;
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withObjectKeys(array $keys) {
$this->objectKeys = $keys;
return $this;
}
public function loadPage() {
$table = new DoorkeeperExternalObject();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
if ($this->objectKeys) {
$where[] = qsprintf(
$conn_r,
'objectKey IN (%Ls)',
$this->objectKeys);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationDoorkeeper';
+ }
+
}
diff --git a/src/applications/feed/query/PhabricatorFeedQuery.php b/src/applications/feed/query/PhabricatorFeedQuery.php
index 44a677faa1..2d324f2dcc 100644
--- a/src/applications/feed/query/PhabricatorFeedQuery.php
+++ b/src/applications/feed/query/PhabricatorFeedQuery.php
@@ -1,107 +1,112 @@
<?php
final class PhabricatorFeedQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $filterPHIDs;
private $chronologicalKeys;
public function setFilterPHIDs(array $phids) {
$this->filterPHIDs = $phids;
return $this;
}
public function withChronologicalKeys(array $keys) {
$this->chronologicalKeys = $keys;
return $this;
}
protected function loadPage() {
$story_table = new PhabricatorFeedStoryData();
$conn = $story_table->establishConnection('r');
$data = queryfx_all(
$conn,
'SELECT story.* FROM %T story %Q %Q %Q %Q %Q',
$story_table->getTableName(),
$this->buildJoinClause($conn),
$this->buildWhereClause($conn),
$this->buildGroupClause($conn),
$this->buildOrderClause($conn),
$this->buildLimitClause($conn));
return $data;
}
protected function willFilterPage(array $data) {
return PhabricatorFeedStory::loadAllFromRows($data, $this->getViewer());
}
private function buildJoinClause(AphrontDatabaseConnection $conn_r) {
// NOTE: We perform this join unconditionally (even if we have no filter
// PHIDs) to omit rows which have no story references. These story data
// rows are notifications or realtime alerts.
$ref_table = new PhabricatorFeedStoryReference();
return qsprintf(
$conn_r,
'JOIN %T ref ON ref.chronologicalKey = story.chronologicalKey',
$ref_table->getTableName());
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->filterPHIDs) {
$where[] = qsprintf(
$conn_r,
'ref.objectPHID IN (%Ls)',
$this->filterPHIDs);
}
if ($this->chronologicalKeys) {
// NOTE: We want to use integers in the query so we can take advantage
// of keys, but can't use %d on 32-bit systems. Make sure all the keys
// are integers and then format them raw.
$keys = $this->chronologicalKeys;
foreach ($keys as $key) {
if (!ctype_digit($key)) {
throw new Exception("Key '{$key}' is not a valid chronological key!");
}
}
$where[] = qsprintf(
$conn_r,
'ref.chronologicalKey IN (%Q)',
implode(', ', $keys));
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
private function buildGroupClause(AphrontDatabaseConnection $conn_r) {
return qsprintf(
$conn_r,
'GROUP BY '.($this->filterPHIDs
? 'ref.chronologicalKey'
: 'story.chronologicalKey'));
}
protected function getPagingColumn() {
return ($this->filterPHIDs
? 'ref.chronologicalKey'
: 'story.chronologicalKey');
}
protected function getPagingValue($item) {
if ($item instanceof PhabricatorFeedStory) {
return $item->getChronologicalKey();
}
return $item['chronologicalKey'];
}
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationFeed';
+ }
+
}
diff --git a/src/applications/files/phid/PhabricatorFilePHIDTypeFile.php b/src/applications/files/phid/PhabricatorFilePHIDTypeFile.php
index 50586bee78..1f6fe80df6 100644
--- a/src/applications/files/phid/PhabricatorFilePHIDTypeFile.php
+++ b/src/applications/files/phid/PhabricatorFilePHIDTypeFile.php
@@ -1,77 +1,74 @@
<?php
final class PhabricatorFilePHIDTypeFile extends PhabricatorPHIDType {
const TYPECONST = 'FILE';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('File');
}
public function newObject() {
return new PhabricatorFile();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorFileQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$file = $objects[$phid];
$id = $file->getID();
$name = $file->getName();
$uri = $file->getBestURI();
$handle->setName("F{$id}");
$handle->setFullName("F{$id}: {$name}");
$handle->setURI($uri);
}
}
public function canLoadNamedObject($name) {
return preg_match('/^F\d*[1-9]\d*$/', $name);
}
public function loadNamedObjects(
PhabricatorObjectQuery $query,
array $names) {
$id_map = array();
foreach ($names as $name) {
$id = (int)substr($name, 1);
$id_map[$id][] = $name;
}
$objects = id(new PhabricatorFileQuery())
->setViewer($query->getViewer())
->withIDs(array_keys($id_map))
->execute();
$results = array();
foreach ($objects as $id => $object) {
foreach (idx($id_map, $id, array()) as $name) {
$results[$name] = $object;
}
}
return $results;
}
}
diff --git a/src/applications/files/query/PhabricatorFileQuery.php b/src/applications/files/query/PhabricatorFileQuery.php
index 28393b4502..854f49411e 100644
--- a/src/applications/files/query/PhabricatorFileQuery.php
+++ b/src/applications/files/query/PhabricatorFileQuery.php
@@ -1,238 +1,243 @@
<?php
/**
* @group file
*/
final class PhabricatorFileQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $authorPHIDs;
private $explicitUploads;
private $transforms;
private $dateCreatedAfter;
private $dateCreatedBefore;
public function withIDs(array $ids) {
$this->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 withDateCreatedBefore($date_created_before) {
$this->dateCreatedBefore = $date_created_before;
return $this;
}
public function withDateCreatedAfter($date_created_after) {
$this->dateCreatedAfter = $date_created_after;
return $this;
}
/**
* Select files which are transformations of some other file. For example,
* you can use this query to find previously generated thumbnails of an image
* file.
*
* As a parameter, provide a list of transformation specifications. Each
* specification is a dictionary with the keys `originalPHID` and `transform`.
* The `originalPHID` is the PHID of the original file (the file which was
* transformed) and the `transform` is the name of the transform to query
* for. If you pass `true` as the `transform`, all transformations of the
* file will be selected.
*
* For example:
*
* array(
* array(
* 'originalPHID' => 'PHID-FILE-aaaa',
* 'transform' => 'sepia',
* ),
* array(
* 'originalPHID' => 'PHID-FILE-bbbb',
* 'transform' => true,
* ),
* )
*
* This selects the `"sepia"` transformation of the file with PHID
* `PHID-FILE-aaaa` and all transformations of the file with PHID
* `PHID-FILE-bbbb`.
*
* @param list<dict> List of transform specifications, described above.
* @return this
*/
public function withTransforms(array $specs) {
foreach ($specs as $spec) {
if (!is_array($spec) ||
empty($spec['originalPHID']) ||
empty($spec['transform'])) {
throw new Exception(
"Transform specification must be a dictionary with keys ".
"'originalPHID' and 'transform'!");
}
}
$this->transforms = $specs;
return $this;
}
public function showOnlyExplicitUploads($explicit_uploads) {
$this->explicitUploads = $explicit_uploads;
return $this;
}
protected function loadPage() {
$table = new PhabricatorFile();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT f.* FROM %T f %Q %Q %Q %Q',
$table->getTableName(),
$this->buildJoinClause($conn_r),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
$files = $table->loadAllFromArray($data);
if (!$files) {
return $files;
}
// We need to load attached objects to perform policy checks for files.
// First, load the edges.
$edge_type = PhabricatorEdgeConfig::TYPE_FILE_HAS_OBJECT;
$phids = mpull($files, 'getPHID');
$edges = id(new PhabricatorEdgeQuery())
->withSourcePHIDs($phids)
->withEdgeTypes(array($edge_type))
->execute();
$object_phids = array();
foreach ($files as $file) {
$phids = array_keys($edges[$file->getPHID()][$edge_type]);
$file->attachObjectPHIDs($phids);
foreach ($phids as $phid) {
$object_phids[$phid] = true;
}
}
$object_phids = array_keys($object_phids);
// Now, load the objects.
$objects = array();
if ($object_phids) {
$objects = id(new PhabricatorObjectQuery())
->setParentQuery($this)
->setViewer($this->getViewer())
->withPHIDs($object_phids)
->execute();
$objects = mpull($objects, null, 'getPHID');
}
foreach ($files as $file) {
$file_objects = array_select_keys($objects, $file->getObjectPHIDs());
$file->attachObjects($file_objects);
}
return $files;
}
private function buildJoinClause(AphrontDatabaseConnection $conn_r) {
$joins = array();
if ($this->transforms) {
$joins[] = qsprintf(
$conn_r,
'JOIN %T t ON t.transformedPHID = f.phid',
id(new PhabricatorTransformedFile())->getTableName());
}
return implode(' ', $joins);
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
$where[] = $this->buildPagingClause($conn_r);
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'f.id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'f.phid IN (%Ls)',
$this->phids);
}
if ($this->authorPHIDs) {
$where[] = qsprintf(
$conn_r,
'f.authorPHID IN (%Ls)',
$this->authorPHIDs);
}
if ($this->explicitUploads) {
$where[] = qsprintf(
$conn_r,
'f.isExplicitUpload = true');
}
if ($this->transforms) {
$clauses = array();
foreach ($this->transforms as $transform) {
if ($transform['transform'] === true) {
$clauses[] = qsprintf(
$conn_r,
'(t.originalPHID = %s)',
$transform['originalPHID']);
} else {
$clauses[] = qsprintf(
$conn_r,
'(t.originalPHID = %s AND t.transform = %s)',
$transform['originalPHID'],
$transform['transform']);
}
}
$where[] = qsprintf($conn_r, '(%Q)', implode(') OR (', $clauses));
}
if ($this->dateCreatedAfter) {
$where[] = qsprintf(
$conn_r,
'f.dateCreated >= %d',
$this->dateCreatedAfter);
}
if ($this->dateCreatedBefore) {
$where[] = qsprintf(
$conn_r,
'f.dateCreated <= %d',
$this->dateCreatedBefore);
}
return $this->formatWhereClause($where);
}
protected function getPagingColumn() {
return 'f.id';
}
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationFiles';
+ }
+
}
diff --git a/src/applications/flag/query/PhabricatorFlagQuery.php b/src/applications/flag/query/PhabricatorFlagQuery.php
index 58b1bc4609..c31e4a02c7 100644
--- a/src/applications/flag/query/PhabricatorFlagQuery.php
+++ b/src/applications/flag/query/PhabricatorFlagQuery.php
@@ -1,163 +1,168 @@
<?php
final class PhabricatorFlagQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
const GROUP_COLOR = 'color';
const GROUP_NONE = 'none';
private $ownerPHIDs;
private $types;
private $objectPHIDs;
private $colors;
private $groupBy = self::GROUP_NONE;
private $needHandles;
private $needObjects;
public function withOwnerPHIDs(array $owner_phids) {
$this->ownerPHIDs = $owner_phids;
return $this;
}
public function withTypes(array $types) {
$this->types = $types;
return $this;
}
public function withObjectPHIDs(array $object_phids) {
$this->objectPHIDs = $object_phids;
return $this;
}
public function withColors(array $colors) {
$this->colors = $colors;
return $this;
}
/**
* Note this is done in php and not in mySQL, which means its inappropriate
* for large datasets. Pragmatically, this is fine for user flags which are
* typically well under 100 flags per user.
*/
public function setGroupBy($group) {
$this->groupBy = $group;
return $this;
}
public function needHandles($need) {
$this->needHandles = $need;
return $this;
}
public function needObjects($need) {
$this->needObjects = $need;
return $this;
}
public static function loadUserFlag(PhabricatorUser $user, $object_phid) {
// Specifying the type in the query allows us to use a key.
return id(new PhabricatorFlagQuery())
->setViewer($user)
->withOwnerPHIDs(array($user->getPHID()))
->withTypes(array(phid_get_type($object_phid)))
->withObjectPHIDs(array($object_phid))
->executeOne();
}
public function loadPage() {
$table = new PhabricatorFlag();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T flag %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
public function willFilterPage(array $flags) {
if ($this->needObjects) {
$objects = id(new PhabricatorObjectQuery())
->setViewer($this->getViewer())
->withPHIDs(mpull($flags, 'getObjectPHID'))
->execute();
$objects = mpull($objects, null, 'getPHID');
foreach ($flags as $key => $flag) {
$object = idx($objects, $flag->getObjectPHID());
if ($object) {
$flags[$key]->attachObject($object);
} else {
unset($flags[$key]);
}
}
}
if ($this->needHandles) {
$handles = id(new PhabricatorHandleQuery())
->setViewer($this->getViewer())
->withPHIDs(mpull($flags, 'getObjectPHID'))
->execute();
foreach ($flags as $flag) {
$flag->attachHandle($handles[$flag->getObjectPHID()]);
}
}
switch ($this->groupBy) {
case self::GROUP_COLOR:
$flags = msort($flags, 'getColor');
break;
case self::GROUP_NONE:
break;
default:
throw new Exception("Unknown groupBy parameter: $this->groupBy");
break;
}
return $flags;
}
private function buildWhereClause($conn_r) {
$where = array();
if ($this->ownerPHIDs) {
$where[] = qsprintf(
$conn_r,
'flag.ownerPHID IN (%Ls)',
$this->ownerPHIDs);
}
if ($this->types) {
$where[] = qsprintf(
$conn_r,
'flag.type IN (%Ls)',
$this->types);
}
if ($this->objectPHIDs) {
$where[] = qsprintf(
$conn_r,
'flag.objectPHID IN (%Ls)',
$this->objectPHIDs);
}
if ($this->colors) {
$where[] = qsprintf(
$conn_r,
'flag.color IN (%Ld)',
$this->colors);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationFlags';
+ }
+
}
diff --git a/src/applications/herald/phid/HeraldPHIDTypeRule.php b/src/applications/herald/phid/HeraldPHIDTypeRule.php
index 1c408cbb46..a3022d3c0f 100644
--- a/src/applications/herald/phid/HeraldPHIDTypeRule.php
+++ b/src/applications/herald/phid/HeraldPHIDTypeRule.php
@@ -1,46 +1,43 @@
<?php
final class HeraldPHIDTypeRule extends PhabricatorPHIDType {
const TYPECONST = 'HRUL';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Herald Rule');
}
public function newObject() {
return new HeraldRule();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new HeraldRuleQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$rule = $objects[$phid];
$id = $rule->getID();
$name = $rule->getName();
$handle->setName($name);
$handle->setURI("/herald/rule/{$id}/");
}
}
}
diff --git a/src/applications/herald/query/HeraldRuleQuery.php b/src/applications/herald/query/HeraldRuleQuery.php
index 374bcc0c29..e9d3d05d5f 100644
--- a/src/applications/herald/query/HeraldRuleQuery.php
+++ b/src/applications/herald/query/HeraldRuleQuery.php
@@ -1,232 +1,237 @@
<?php
final class HeraldRuleQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $authorPHIDs;
private $ruleTypes;
private $contentTypes;
private $disabled;
private $needConditionsAndActions;
private $needAppliedToPHIDs;
private $needValidateAuthors;
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 withRuleTypes(array $types) {
$this->ruleTypes = $types;
return $this;
}
public function withContentTypes(array $types) {
$this->contentTypes = $types;
return $this;
}
public function withExecutableRules($executable) {
$this->executable = $executable;
return $this;
}
public function withDisabled($disabled) {
$this->disabled = $disabled;
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 loadPage() {
$table = new HeraldRule();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT rule.* FROM %T rule %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
public 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->validateRuleAuthors($rules);
}
if ($this->needConditionsAndActions) {
$conditions = id(new HeraldCondition())->loadAllWhere(
'ruleID IN (%Ld)',
$rule_ids);
$conditions = mgroup($conditions, 'getRuleID');
$actions = id(new HeraldAction())->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]));
}
}
}
return $rules;
}
private function buildWhereClause($conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'rule.id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'rule.phid IN (%Ls)',
$this->phids);
}
if ($this->authorPHIDs) {
$where[] = qsprintf(
$conn_r,
'rule.authorPHID IN (%Ls)',
$this->authorPHIDs);
}
if ($this->ruleTypes) {
$where[] = qsprintf(
$conn_r,
'rule.ruleType IN (%Ls)',
$this->ruleTypes);
}
if ($this->contentTypes) {
$where[] = qsprintf(
$conn_r,
'rule.contentType IN (%Ls)',
$this->contentTypes);
}
if ($this->disabled !== null) {
$where[] = qsprintf(
$conn_r,
'rule.isDisabled = %d',
(int)$this->disabled);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
private function validateRuleAuthors(array $rules) {
// "Global" rules always have valid authors.
foreach ($rules as $key => $rule) {
if ($rule->isGlobalRule()) {
$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]->getIsDisabled()) {
$rule->attachValidAuthor(false);
continue;
}
$rule->attachValidAuthor(true);
$rule->attachAuthor($users[$author_phid]);
}
}
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationHerald';
+ }
+
}
diff --git a/src/applications/herald/query/HeraldTranscriptQuery.php b/src/applications/herald/query/HeraldTranscriptQuery.php
index 8399218c61..258b2ea898 100644
--- a/src/applications/herald/query/HeraldTranscriptQuery.php
+++ b/src/applications/herald/query/HeraldTranscriptQuery.php
@@ -1,98 +1,101 @@
<?php
final class HeraldTranscriptQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $needPartialRecords;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function needPartialRecords($need_partial) {
$this->needPartialRecords = $need_partial;
return $this;
}
public function loadPage() {
$transcript = new HeraldTranscript();
$conn_r = $transcript->establishConnection('r');
// NOTE: Transcripts include a potentially enormous amount of serialized
// data, so we're loading only some of the fields here if the caller asked
// for partial records.
if ($this->needPartialRecords) {
$fields = implode(
', ',
array(
'id',
'phid',
'objectPHID',
'time',
'duration',
'dryRun',
'host',
));
} else {
$fields = '*';
}
$rows = queryfx_all(
$conn_r,
'SELECT %Q FROM %T t %Q %Q %Q',
$fields,
$transcript->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
$transcripts = $transcript->loadAllFromArray($rows);
if ($this->needPartialRecords) {
// Make sure nothing tries to write these; they aren't complete.
foreach ($transcripts as $transcript) {
$transcript->makeEphemeral();
}
}
return $transcripts;
}
public function willFilterPage(array $transcripts) {
$phids = mpull($transcripts, 'getObjectPHID');
$objects = id(new PhabricatorObjectQuery())
->setViewer($this->getViewer())
->withPHIDs($phids)
->execute();
foreach ($transcripts as $key => $transcript) {
if (empty($objects[$transcript->getObjectPHID()])) {
$this->didRejectResult($transcript);
unset($transcripts[$key]);
}
}
return $transcripts;
}
public function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationHerald';
+ }
}
diff --git a/src/applications/legalpad/phid/PhabricatorLegalpadPHIDTypeDocument.php b/src/applications/legalpad/phid/PhabricatorLegalpadPHIDTypeDocument.php
index d1e1fcc8cc..ad76e1f334 100644
--- a/src/applications/legalpad/phid/PhabricatorLegalpadPHIDTypeDocument.php
+++ b/src/applications/legalpad/phid/PhabricatorLegalpadPHIDTypeDocument.php
@@ -1,77 +1,74 @@
<?php
/**
* @group legalpad
*/
final class PhabricatorLegalpadPHIDTypeDocument extends PhabricatorPHIDType {
const TYPECONST = 'LEGD';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Legalpad Document');
}
public function newObject() {
return new LegalpadDocument();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new LegalpadDocumentQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
->withPHIDs($phids)
- ->needDocumentBodies(true)
- ->execute();
+ ->needDocumentBodies(true);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$document = $objects[$phid];
$name = $document->getDocumentBody()->getTitle();
$handle->setName($name);
$handle->setFullName($name);
$handle->setURI('/legalpad/view/'.$document->getID().'/');
}
}
public function canLoadNamedObject($name) {
return preg_match('/^L\d*[1-9]\d*$/i', $name);
}
public function loadNamedObjects(
PhabricatorObjectQuery $query,
array $names) {
$id_map = array();
foreach ($names as $name) {
$id = (int)substr($name, 1);
$id_map[$id][] = $name;
}
$objects = id(new LegalpadDocumentQuery())
->setViewer($query->getViewer())
->withIDs(array_keys($id_map))
->execute();
$results = array();
foreach ($objects as $id => $object) {
foreach (idx($id_map, $id, array()) as $name) {
$results[$name] = $object;
}
}
return $results;
}
}
diff --git a/src/applications/legalpad/query/LegalpadDocumentQuery.php b/src/applications/legalpad/query/LegalpadDocumentQuery.php
index ccca424de9..22db1808b0 100644
--- a/src/applications/legalpad/query/LegalpadDocumentQuery.php
+++ b/src/applications/legalpad/query/LegalpadDocumentQuery.php
@@ -1,185 +1,188 @@
<?php
/**
* @group legalpad
*/
final class LegalpadDocumentQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $creatorPHIDs;
private $contributorPHIDs;
private $dateCreatedAfter;
private $dateCreatedBefore;
private $needDocumentBodies;
private $needContributors;
public function withIDs(array $ids) {
$this->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 needDocumentBodies($need_bodies) {
$this->needDocumentBodies = $need_bodies;
return $this;
}
public function needContributors($need_contributors) {
$this->needContributors = $need_contributors;
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;
}
protected function loadPage() {
$table = new LegalpadDocument();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT d.* FROM %T d %Q %Q %Q %Q',
$table->getTableName(),
$this->buildJoinClause($conn_r),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
$documents = $table->loadAllFromArray($data);
return $documents;
}
protected function willFilterPage(array $documents) {
if ($this->needDocumentBodies) {
$documents = $this->loadDocumentBodies($documents);
}
if ($this->needContributors) {
$documents = $this->loadContributors($documents);
}
return $documents;
}
private function buildJoinClause($conn_r) {
$joins = array();
if ($this->contributorPHIDs) {
$joins[] = qsprintf(
$conn_r,
'JOIN edge e ON e.src = d.phid');
}
return implode(' ', $joins);
}
protected function buildWhereClause($conn_r) {
$where = array();
$where[] = $this->buildPagingClause($conn_r);
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'd.id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'd.phid IN (%Ls)',
$this->phids);
}
if ($this->creatorPHIDs) {
$where[] = qsprintf(
$conn_r,
'd.creatorPHID IN (%Ls)',
$this->creatorPHIDs);
}
if ($this->dateCreatedAfter) {
$where[] = qsprintf(
$conn_r,
'd.dateCreated >= %d',
$this->dateCreatedAfter);
}
if ($this->dateCreatedBefore) {
$where[] = qsprintf(
$conn_r,
'd.dateCreated <= %d',
$this->dateCreatedBefore);
}
if ($this->contributorPHIDs) {
$where[] = qsprintf(
$conn_r,
'e.type = %s AND e.dst IN (%Ls)',
PhabricatorEdgeConfig::TYPE_OBJECT_HAS_CONTRIBUTOR,
$this->contributorPHIDs);
}
return $this->formatWhereClause($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 = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_CONTRIBUTOR;
$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;
}
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationLegalpad';
+ }
}
diff --git a/src/applications/macro/phid/PhabricatorMacroPHIDTypeMacro.php b/src/applications/macro/phid/PhabricatorMacroPHIDTypeMacro.php
index bc9b7a8d61..b5fbc351b7 100644
--- a/src/applications/macro/phid/PhabricatorMacroPHIDTypeMacro.php
+++ b/src/applications/macro/phid/PhabricatorMacroPHIDTypeMacro.php
@@ -1,51 +1,48 @@
<?php
final class PhabricatorMacroPHIDTypeMacro extends PhabricatorPHIDType {
const TYPECONST = 'MCRO';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Image Macro');
}
public function newObject() {
return new PhabricatorFileImageMacro();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorMacroQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$macro = $objects[$phid];
$id = $macro->getID();
$name = $macro->getName();
$handle->setName($name);
$handle->setFullName(pht('Image Macro "%s"', $name));
$handle->setURI("/macro/view/{$id}/");
}
}
public function canLoadNamedObject($name) {
return false;
}
}
diff --git a/src/applications/macro/query/PhabricatorMacroQuery.php b/src/applications/macro/query/PhabricatorMacroQuery.php
index da7dbd7b83..6ebfb50f9c 100644
--- a/src/applications/macro/query/PhabricatorMacroQuery.php
+++ b/src/applications/macro/query/PhabricatorMacroQuery.php
@@ -1,219 +1,223 @@
<?php
/**
* @group phriction
*/
final class PhabricatorMacroQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $authors;
private $names;
private $nameLike;
private $dateCreatedAfter;
private $dateCreatedBefore;
private $flagColor;
private $status = 'status-any';
const STATUS_ANY = 'status-any';
const STATUS_ACTIVE = 'status-active';
const STATUS_DISABLED = 'status-disabled';
public static function getStatusOptions() {
return array(
self::STATUS_ACTIVE => 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)'));
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 $authors) {
$this->authors = $authors;
return $this;
}
public function withNameLike($name) {
$this->nameLike = $name;
return $this;
}
public function withNames(array $names) {
$this->names = $names;
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;
}
protected function loadPage() {
$macro_table = new PhabricatorFileImageMacro();
$conn = $macro_table->establishConnection('r');
$rows = queryfx_all(
$conn,
'SELECT m.* FROM %T m %Q %Q %Q',
$macro_table->getTableName(),
$this->buildWhereClause($conn),
$this->buildOrderClause($conn),
$this->buildLimitClause($conn));
return $macro_table->loadAllFromArray($rows);
}
protected function buildWhereClause(AphrontDatabaseConnection $conn) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn,
'm.id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn,
'm.phid IN (%Ls)',
$this->phids);
}
if ($this->authors) {
$where[] = qsprintf(
$conn,
'm.authorPHID IN (%Ls)',
$this->authors);
}
if ($this->nameLike) {
$where[] = qsprintf(
$conn,
'm.name LIKE %~',
$this->nameLike);
}
if ($this->names) {
$where[] = qsprintf(
$conn,
'm.name IN (%Ls)',
$this->names);
}
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("Unknown status '{$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) {
$flags = id(new PhabricatorFlagQuery())
->withOwnerPHIDs(array($this->getViewer()->getPHID()))
->withTypes(array(PhabricatorMacroPHIDTypeMacro::TYPECONST))
->withColors(array($this->flagColor))
->setViewer($this->getViewer())
->execute();
if (empty($flags)) {
throw new PhabricatorEmptyQueryException('No matching flags.');
} else {
$where[] = qsprintf(
$conn,
'm.phid IN (%Ls)',
mpull($flags, 'getObjectPHID'));
}
}
$where[] = $this->buildPagingClause($conn);
return $this->formatWhereClause($where);
}
protected function didFilterPage(array $macros) {
$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 getPagingColumn() {
return 'm.id';
}
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationMacro';
+ }
+
}
diff --git a/src/applications/mailinglists/phid/PhabricatorMailingListPHIDTypeList.php b/src/applications/mailinglists/phid/PhabricatorMailingListPHIDTypeList.php
index eee01d277a..ccd14ae2e8 100644
--- a/src/applications/mailinglists/phid/PhabricatorMailingListPHIDTypeList.php
+++ b/src/applications/mailinglists/phid/PhabricatorMailingListPHIDTypeList.php
@@ -1,47 +1,44 @@
<?php
final class PhabricatorMailingListPHIDTypeList extends PhabricatorPHIDType {
const TYPECONST = 'MLST';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Mailing List');
}
public function newObject() {
return new PhabricatorMetaMTAMailingList();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorMailingListQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$list = $objects[$phid];
$handle->setName($list->getName());
$handle->setURI($list->getURI());
}
}
public function canLoadNamedObject($name) {
return false;
}
}
diff --git a/src/applications/mailinglists/query/PhabricatorMailingListQuery.php b/src/applications/mailinglists/query/PhabricatorMailingListQuery.php
index a7f98a4aa9..77551d3984 100644
--- a/src/applications/mailinglists/query/PhabricatorMailingListQuery.php
+++ b/src/applications/mailinglists/query/PhabricatorMailingListQuery.php
@@ -1,56 +1,60 @@
<?php
final class PhabricatorMailingListQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $phids;
private $ids;
public function withIDs($ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs($phids) {
$this->phids = $phids;
return $this;
}
public function loadPage() {
$table = new PhabricatorMetaMTAMailingList();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationMailingLists';
+ }
+
}
diff --git a/src/applications/maniphest/phid/ManiphestPHIDTypeTask.php b/src/applications/maniphest/phid/ManiphestPHIDTypeTask.php
index dd076bc511..2af8135438 100644
--- a/src/applications/maniphest/phid/ManiphestPHIDTypeTask.php
+++ b/src/applications/maniphest/phid/ManiphestPHIDTypeTask.php
@@ -1,79 +1,76 @@
<?php
final class ManiphestPHIDTypeTask extends PhabricatorPHIDType {
const TYPECONST = 'TASK';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Task');
}
public function newObject() {
return new ManiphestTask();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new ManiphestTaskQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$task = $objects[$phid];
$id = $task->getID();
$title = $task->getTitle();
$handle->setName("T{$id}");
$handle->setFullName("T{$id}: {$title}");
$handle->setURI("/T{$id}");
if ($task->getStatus() != ManiphestTaskStatus::STATUS_OPEN) {
$handle->setStatus(PhabricatorObjectHandleStatus::STATUS_CLOSED);
}
}
}
public function canLoadNamedObject($name) {
return preg_match('/^T\d*[1-9]\d*$/i', $name);
}
public function loadNamedObjects(
PhabricatorObjectQuery $query,
array $names) {
$id_map = array();
foreach ($names as $name) {
$id = (int)substr($name, 1);
$id_map[$id][] = $name;
}
$objects = id(new ManiphestTaskQuery())
->setViewer($query->getViewer())
->withIDs(array_keys($id_map))
->execute();
$results = array();
foreach ($objects as $id => $object) {
foreach (idx($id_map, $id, array()) as $name) {
$results[$name] = $object;
}
}
return $results;
}
}
diff --git a/src/applications/maniphest/query/ManiphestTaskQuery.php b/src/applications/maniphest/query/ManiphestTaskQuery.php
index ccda6c0122..82aebd05fb 100644
--- a/src/applications/maniphest/query/ManiphestTaskQuery.php
+++ b/src/applications/maniphest/query/ManiphestTaskQuery.php
@@ -1,889 +1,894 @@
<?php
/**
* Query tasks by specific criteria. This class uses the higher-performance
* but less-general Maniphest indexes to satisfy queries.
*
* @group maniphest
*/
final class ManiphestTaskQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $taskIDs = array();
private $taskPHIDs = array();
private $authorPHIDs = array();
private $ownerPHIDs = array();
private $includeUnowned = null;
private $projectPHIDs = array();
private $xprojectPHIDs = array();
private $subscriberPHIDs = array();
private $anyProjectPHIDs = array();
private $anyUserProjectPHIDs = array();
private $includeNoProject = null;
private $dateCreatedAfter;
private $dateCreatedBefore;
private $fullTextSearch = '';
private $status = 'status-any';
const STATUS_ANY = 'status-any';
const STATUS_OPEN = 'status-open';
const STATUS_CLOSED = 'status-closed';
const STATUS_RESOLVED = 'status-resolved';
const STATUS_WONTFIX = 'status-wontfix';
const STATUS_INVALID = 'status-invalid';
const STATUS_SPITE = 'status-spite';
const STATUS_DUPLICATE = 'status-duplicate';
private $statuses;
private $priorities;
private $groupBy = 'group-none';
const GROUP_NONE = 'group-none';
const GROUP_PRIORITY = 'group-priority';
const GROUP_OWNER = 'group-owner';
const GROUP_STATUS = 'group-status';
const GROUP_PROJECT = 'group-project';
private $orderBy = 'order-modified';
const ORDER_PRIORITY = 'order-priority';
const ORDER_CREATED = 'order-created';
const ORDER_MODIFIED = 'order-modified';
const ORDER_TITLE = 'order-title';
const DEFAULT_PAGE_SIZE = 1000;
public function withAuthors(array $authors) {
$this->authorPHIDs = $authors;
return $this;
}
public function withIDs(array $ids) {
$this->taskIDs = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->taskPHIDs = $phids;
return $this;
}
public function withOwners(array $owners) {
$this->includeUnowned = false;
foreach ($owners as $k => $phid) {
if ($phid == ManiphestTaskOwner::OWNER_UP_FOR_GRABS || $phid === null) {
$this->includeUnowned = true;
unset($owners[$k]);
break;
}
}
$this->ownerPHIDs = $owners;
return $this;
}
public function withAllProjects(array $projects) {
$this->includeNoProject = false;
foreach ($projects as $k => $phid) {
if ($phid == ManiphestTaskOwner::PROJECT_NO_PROJECT) {
$this->includeNoProject = true;
unset($projects[$k]);
}
}
$this->projectPHIDs = $projects;
return $this;
}
public function withoutProjects(array $projects) {
$this->xprojectPHIDs = $projects;
return $this;
}
public function withStatus($status) {
$this->status = $status;
return $this;
}
public function withStatuses(array $statuses) {
$this->statuses = $statuses;
return $this;
}
public function withPriorities(array $priorities) {
$this->priorities = $priorities;
return $this;
}
public function withSubscribers(array $subscribers) {
$this->subscriberPHIDs = $subscribers;
return $this;
}
public function withFullTextSearch($fulltext_search) {
$this->fullTextSearch = $fulltext_search;
return $this;
}
public function setGroupBy($group) {
$this->groupBy = $group;
return $this;
}
public function setOrderBy($order) {
$this->orderBy = $order;
return $this;
}
public function withAnyProjects(array $projects) {
$this->anyProjectPHIDs = $projects;
return $this;
}
public function withAnyUserProjects(array $users) {
$this->anyUserProjectPHIDs = $users;
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 loadPage() {
// TODO: (T603) It is possible for a user to find the PHID of a project
// they can't see, then query for tasks in that project and deduce the
// identity of unknown/invisible projects. Before we allow the user to
// execute a project-based PHID query, we should verify that they
// can see the project.
$task_dao = new ManiphestTask();
$conn = $task_dao->establishConnection('r');
$where = array();
$where[] = $this->buildTaskIDsWhereClause($conn);
$where[] = $this->buildTaskPHIDsWhereClause($conn);
$where[] = $this->buildStatusWhereClause($conn);
$where[] = $this->buildStatusesWhereClause($conn);
$where[] = $this->buildPrioritiesWhereClause($conn);
$where[] = $this->buildAuthorWhereClause($conn);
$where[] = $this->buildOwnerWhereClause($conn);
$where[] = $this->buildSubscriberWhereClause($conn);
$where[] = $this->buildProjectWhereClause($conn);
$where[] = $this->buildAnyProjectWhereClause($conn);
$where[] = $this->buildAnyUserProjectWhereClause($conn);
$where[] = $this->buildXProjectWhereClause($conn);
$where[] = $this->buildFullTextWhereClause($conn);
if ($this->dateCreatedAfter) {
$where[] = qsprintf(
$conn,
'dateCreated >= %d',
$this->dateCreatedAfter);
}
if ($this->dateCreatedBefore) {
$where[] = qsprintf(
$conn,
'dateCreated <= %d',
$this->dateCreatedBefore);
}
$where[] = $this->buildPagingClause($conn);
$where = $this->formatWhereClause($where);
$having = '';
$count = '';
if (count($this->projectPHIDs) > 1) {
// We want to treat the query as an intersection query, not a union
// query. We sum the project count and require it be the same as the
// number of projects we're searching for.
$count = ', COUNT(project.projectPHID) projectCount';
$having = qsprintf(
$conn,
'HAVING projectCount = %d',
count($this->projectPHIDs));
}
$order = $this->buildCustomOrderClause($conn);
// TODO: Clean up this nonstandardness.
if (!$this->getLimit()) {
$this->setLimit(self::DEFAULT_PAGE_SIZE);
}
$group_column = '';
switch ($this->groupBy) {
case self::GROUP_PROJECT:
$group_column = qsprintf(
$conn,
', projectGroupName.indexedObjectPHID projectGroupPHID');
break;
}
$rows = queryfx_all(
$conn,
'SELECT task.* %Q %Q FROM %T task %Q %Q %Q %Q %Q %Q',
$count,
$group_column,
$task_dao->getTableName(),
$this->buildJoinsClause($conn),
$where,
$this->buildGroupClause($conn),
$having,
$order,
$this->buildLimitClause($conn));
switch ($this->groupBy) {
case self::GROUP_PROJECT:
$data = ipull($rows, null, 'id');
break;
default:
$data = $rows;
break;
}
$tasks = $task_dao->loadAllFromArray($data);
switch ($this->groupBy) {
case self::GROUP_PROJECT:
$results = array();
foreach ($rows as $row) {
$task = clone $tasks[$row['id']];
$task->attachGroupByProjectPHID($row['projectGroupPHID']);
$results[] = $task;
}
$tasks = $results;
break;
}
return $tasks;
}
protected function willFilterPage(array $tasks) {
if ($this->groupBy == self::GROUP_PROJECT) {
// We should only return project groups which the user can actually see.
$project_phids = mpull($tasks, 'getGroupByProjectPHID');
$projects = id(new PhabricatorProjectQuery())
->setViewer($this->getViewer())
->withPHIDs($project_phids)
->execute();
$projects = mpull($projects, null, 'getPHID');
foreach ($tasks as $key => $task) {
if (empty($projects[$task->getGroupByProjectPHID()])) {
unset($tasks[$key]);
}
}
}
return $tasks;
}
private function buildTaskIDsWhereClause(AphrontDatabaseConnection $conn) {
if (!$this->taskIDs) {
return null;
}
return qsprintf(
$conn,
'id in (%Ld)',
$this->taskIDs);
}
private function buildTaskPHIDsWhereClause(AphrontDatabaseConnection $conn) {
if (!$this->taskPHIDs) {
return null;
}
return qsprintf(
$conn,
'phid in (%Ls)',
$this->taskPHIDs);
}
private function buildStatusWhereClause(AphrontDatabaseConnection $conn) {
static $map = array(
self::STATUS_RESOLVED => ManiphestTaskStatus::STATUS_CLOSED_RESOLVED,
self::STATUS_WONTFIX => ManiphestTaskStatus::STATUS_CLOSED_WONTFIX,
self::STATUS_INVALID => ManiphestTaskStatus::STATUS_CLOSED_INVALID,
self::STATUS_SPITE => ManiphestTaskStatus::STATUS_CLOSED_SPITE,
self::STATUS_DUPLICATE => ManiphestTaskStatus::STATUS_CLOSED_DUPLICATE,
);
switch ($this->status) {
case self::STATUS_ANY:
return null;
case self::STATUS_OPEN:
return 'status = 0';
case self::STATUS_CLOSED:
return 'status > 0';
default:
$constant = idx($map, $this->status);
if (!$constant) {
throw new Exception("Unknown status query '{$this->status}'!");
}
return qsprintf(
$conn,
'status = %d',
$constant);
}
}
private function buildStatusesWhereClause(AphrontDatabaseConnection $conn) {
if ($this->statuses) {
return qsprintf(
$conn,
'status IN (%Ld)',
$this->statuses);
}
return null;
}
private function buildPrioritiesWhereClause(AphrontDatabaseConnection $conn) {
if ($this->priorities) {
return qsprintf(
$conn,
'priority IN (%Ld)',
$this->priorities);
}
return null;
}
private function buildAuthorWhereClause(AphrontDatabaseConnection $conn) {
if (!$this->authorPHIDs) {
return null;
}
return qsprintf(
$conn,
'authorPHID in (%Ls)',
$this->authorPHIDs);
}
private function buildOwnerWhereClause(AphrontDatabaseConnection $conn) {
if (!$this->ownerPHIDs) {
if ($this->includeUnowned === null) {
return null;
} else if ($this->includeUnowned) {
return qsprintf(
$conn,
'ownerPHID IS NULL');
} else {
return qsprintf(
$conn,
'ownerPHID IS NOT NULL');
}
}
if ($this->includeUnowned) {
return qsprintf(
$conn,
'ownerPHID IN (%Ls) OR ownerPHID IS NULL',
$this->ownerPHIDs);
} else {
return qsprintf(
$conn,
'ownerPHID IN (%Ls)',
$this->ownerPHIDs);
}
}
private function buildFullTextWhereClause(AphrontDatabaseConnection $conn) {
if (!strlen($this->fullTextSearch)) {
return null;
}
// In doing a fulltext search, we first find all the PHIDs that match the
// fulltext search, and then use that to limit the rest of the search
$fulltext_query = new PhabricatorSearchQuery();
$fulltext_query->setQuery($this->fullTextSearch);
// NOTE: Setting this to something larger than 2^53 will raise errors in
// ElasticSearch, and billions of results won't fit in memory anyway.
$fulltext_query->setParameter('limit', 100000);
$fulltext_query->setParameter('type', ManiphestPHIDTypeTask::TYPECONST);
$engine = PhabricatorSearchEngineSelector::newSelector()->newEngine();
$fulltext_results = $engine->executeSearch($fulltext_query);
if (empty($fulltext_results)) {
$fulltext_results = array(null);
}
return qsprintf(
$conn,
'phid IN (%Ls)',
$fulltext_results);
}
private function buildSubscriberWhereClause(AphrontDatabaseConnection $conn) {
if (!$this->subscriberPHIDs) {
return null;
}
return qsprintf(
$conn,
'subscriber.subscriberPHID IN (%Ls)',
$this->subscriberPHIDs);
}
private function buildProjectWhereClause(AphrontDatabaseConnection $conn) {
if (!$this->projectPHIDs && !$this->includeNoProject) {
return null;
}
$parts = array();
if ($this->projectPHIDs) {
$parts[] = qsprintf(
$conn,
'project.projectPHID in (%Ls)',
$this->projectPHIDs);
}
if ($this->includeNoProject) {
$parts[] = qsprintf(
$conn,
'project.projectPHID IS NULL');
}
return '('.implode(') OR (', $parts).')';
}
private function buildAnyProjectWhereClause(AphrontDatabaseConnection $conn) {
if (!$this->anyProjectPHIDs) {
return null;
}
return qsprintf(
$conn,
'anyproject.projectPHID IN (%Ls)',
$this->anyProjectPHIDs);
}
private function buildAnyUserProjectWhereClause(
AphrontDatabaseConnection $conn) {
if (!$this->anyUserProjectPHIDs) {
return null;
}
$projects = id(new PhabricatorProjectQuery())
->setViewer($this->getViewer())
->withMemberPHIDs($this->anyUserProjectPHIDs)
->execute();
$any_user_project_phids = mpull($projects, 'getPHID');
if (!$any_user_project_phids) {
throw new PhabricatorEmptyQueryException();
}
return qsprintf(
$conn,
'anyproject.projectPHID IN (%Ls)',
$any_user_project_phids);
}
private function buildXProjectWhereClause(AphrontDatabaseConnection $conn) {
if (!$this->xprojectPHIDs) {
return null;
}
return qsprintf(
$conn,
'xproject.projectPHID IS NULL');
}
private function buildCustomOrderClause(AphrontDatabaseConnection $conn) {
$order = array();
switch ($this->groupBy) {
case self::GROUP_NONE:
break;
case self::GROUP_PRIORITY:
$order[] = 'priority';
break;
case self::GROUP_OWNER:
$order[] = 'ownerOrdering';
break;
case self::GROUP_STATUS:
$order[] = 'status';
break;
case self::GROUP_PROJECT:
$order[] = '<group.project>';
break;
default:
throw new Exception("Unknown group query '{$this->groupBy}'!");
}
switch ($this->orderBy) {
case self::ORDER_PRIORITY:
$order[] = 'priority';
$order[] = 'subpriority';
$order[] = 'dateModified';
break;
case self::ORDER_CREATED:
$order[] = 'id';
break;
case self::ORDER_MODIFIED:
$order[] = 'dateModified';
break;
case self::ORDER_TITLE:
$order[] = 'title';
break;
default:
throw new Exception("Unknown order query '{$this->orderBy}'!");
}
$order = array_unique($order);
if (empty($order)) {
return null;
}
$reverse = ($this->getBeforeID() xor $this->getReversePaging());
foreach ($order as $k => $column) {
switch ($column) {
case 'subpriority':
case 'ownerOrdering':
case 'title':
if ($reverse) {
$order[$k] = "task.{$column} DESC";
} else {
$order[$k] = "task.{$column} ASC";
}
break;
case '<group.project>':
// Put "No Project" at the end of the list.
if ($reverse) {
$order[$k] =
'projectGroupName.indexedObjectName IS NULL DESC, '.
'projectGroupName.indexedObjectName DESC';
} else {
$order[$k] =
'projectGroupName.indexedObjectName IS NULL ASC, '.
'projectGroupName.indexedObjectName ASC';
}
break;
default:
if ($reverse) {
$order[$k] = "task.{$column} ASC";
} else {
$order[$k] = "task.{$column} DESC";
}
break;
}
}
return 'ORDER BY '.implode(', ', $order);
}
private function buildJoinsClause(AphrontDatabaseConnection $conn_r) {
$project_dao = new ManiphestTaskProject();
$joins = array();
if ($this->projectPHIDs || $this->includeNoProject) {
$joins[] = qsprintf(
$conn_r,
'%Q JOIN %T project ON project.taskPHID = task.phid',
($this->includeNoProject ? 'LEFT' : ''),
$project_dao->getTableName());
}
if ($this->anyProjectPHIDs || $this->anyUserProjectPHIDs) {
$joins[] = qsprintf(
$conn_r,
'JOIN %T anyproject ON anyproject.taskPHID = task.phid',
$project_dao->getTableName());
}
if ($this->xprojectPHIDs) {
$joins[] = qsprintf(
$conn_r,
'LEFT JOIN %T xproject ON xproject.taskPHID = task.phid
AND xproject.projectPHID IN (%Ls)',
$project_dao->getTableName(),
$this->xprojectPHIDs);
}
if ($this->subscriberPHIDs) {
$subscriber_dao = new ManiphestTaskSubscriber();
$joins[] = qsprintf(
$conn_r,
'JOIN %T subscriber ON subscriber.taskPHID = task.phid',
$subscriber_dao->getTableName());
}
switch ($this->groupBy) {
case self::GROUP_PROJECT:
$ignore_group_phids = $this->getIgnoreGroupedProjectPHIDs();
if ($ignore_group_phids) {
$joins[] = qsprintf(
$conn_r,
'LEFT JOIN %T projectGroup ON task.phid = projectGroup.taskPHID
AND projectGroup.projectPHID NOT IN (%Ls)',
$project_dao->getTableName(),
$ignore_group_phids);
} else {
$joins[] = qsprintf(
$conn_r,
'LEFT JOIN %T projectGroup ON task.phid = projectGroup.taskPHID',
$project_dao->getTableName());
}
$joins[] = qsprintf(
$conn_r,
'LEFT JOIN %T projectGroupName
ON projectGroup.projectPHID = projectGroupName.indexedObjectPHID',
id(new ManiphestNameIndex())->getTableName());
break;
}
$joins[] = $this->buildApplicationSearchJoinClause($conn_r);
return implode(' ', $joins);
}
private function buildGroupClause(AphrontDatabaseConnection $conn_r) {
$joined_multiple_rows = (count($this->projectPHIDs) > 1) ||
(count($this->anyProjectPHIDs) > 1) ||
($this->getApplicationSearchMayJoinMultipleRows());
$joined_project_name = ($this->groupBy == self::GROUP_PROJECT);
// If we're joining multiple rows, we need to group the results by the
// task IDs.
if ($joined_multiple_rows) {
if ($joined_project_name) {
return 'GROUP BY task.phid, projectGroup.projectPHID';
} else {
return 'GROUP BY task.phid';
}
} else {
return '';
}
}
/**
* Return project PHIDs which we should ignore when grouping tasks by
* project. For example, if a user issues a query like:
*
* Tasks in all projects: Frontend, Bugs
*
* ...then we don't show "Frontend" or "Bugs" groups in the result set, since
* they're meaningless as all results are in both groups.
*
* Similarly, for queries like:
*
* Tasks in any projects: Public Relations
*
* ...we ignore the single project, as every result is in that project. (In
* the case that there are several "any" projects, we do not ignore them.)
*
* @return list<phid> Project PHIDs which should be ignored in query
* construction.
*/
private function getIgnoreGroupedProjectPHIDs() {
$phids = array();
if ($this->projectPHIDs) {
$phids[] = $this->projectPHIDs;
}
if (count($this->anyProjectPHIDs) == 1) {
$phids[] = $this->anyProjectPHIDs;
}
// Maybe we should also exclude the "excludeProjectPHIDs"? It won't
// impact the results, but we might end up with a better query plan.
// Investigate this on real data? This is likely very rare.
return array_mergev($phids);
}
private function loadCursorObject($id) {
$results = id(new ManiphestTaskQuery())
->setViewer($this->getPagingViewer())
->withIDs(array((int)$id))
->execute();
return head($results);
}
protected function getPagingValue($result) {
$id = $result->getID();
switch ($this->groupBy) {
case self::GROUP_NONE:
return $id;
case self::GROUP_PRIORITY:
return $id.'.'.$result->getPriority();
case self::GROUP_OWNER:
return rtrim($id.'.'.$result->getOwnerPHID(), '.');
case self::GROUP_STATUS:
return $id.'.'.$result->getStatus();
case self::GROUP_PROJECT:
return rtrim($id.'.'.$result->getGroupByProjectPHID(), '.');
default:
throw new Exception("Unknown group query '{$this->groupBy}'!");
}
}
protected function buildPagingClause(AphrontDatabaseConnection $conn_r) {
$default = parent::buildPagingClause($conn_r);
$before_id = $this->getBeforeID();
$after_id = $this->getAfterID();
if (!$before_id && !$after_id) {
return $default;
}
$cursor_id = nonempty($before_id, $after_id);
$cursor_parts = explode('.', $cursor_id, 2);
$task_id = $cursor_parts[0];
$group_id = idx($cursor_parts, 1);
$cursor = $this->loadCursorObject($task_id);
if (!$cursor) {
return null;
}
$columns = array();
switch ($this->groupBy) {
case self::GROUP_NONE:
break;
case self::GROUP_PRIORITY:
$columns[] = array(
'name' => 'task.priority',
'value' => (int)$group_id,
'type' => 'int',
);
break;
case self::GROUP_OWNER:
$columns[] = array(
'name' => '(task.ownerOrdering IS NULL)',
'value' => (int)(strlen($group_id) ? 0 : 1),
'type' => 'int',
);
if ($group_id) {
$paging_users = id(new PhabricatorPeopleQuery())
->setViewer($this->getViewer())
->withPHIDs(array($group_id))
->execute();
if (!$paging_users) {
return null;
}
$columns[] = array(
'name' => 'task.ownerOrdering',
'value' => head($paging_users)->getUsername(),
'type' => 'string',
'reverse' => true,
);
}
break;
case self::GROUP_STATUS:
$columns[] = array(
'name' => 'task.status',
'value' => (int)$group_id,
'type' => 'int',
);
break;
case self::GROUP_PROJECT:
$columns[] = array(
'name' => '(projectGroupName.indexedObjectName IS NULL)',
'value' => (int)(strlen($group_id) ? 0 : 1),
'type' => 'int',
);
if ($group_id) {
$paging_projects = id(new PhabricatorProjectQuery())
->setViewer($this->getViewer())
->withPHIDs(array($group_id))
->execute();
if (!$paging_projects) {
return null;
}
$columns[] = array(
'name' => 'projectGroupName.indexedObjectName',
'value' => head($paging_projects)->getName(),
'type' => 'string',
'reverse' => true,
);
}
break;
default:
throw new Exception("Unknown group query '{$this->groupBy}'!");
}
switch ($this->orderBy) {
case self::ORDER_PRIORITY:
if ($this->groupBy != self::GROUP_PRIORITY) {
$columns[] = array(
'name' => 'task.priority',
'value' => (int)$cursor->getPriority(),
'type' => 'int',
);
}
$columns[] = array(
'name' => 'task.subpriority',
'value' => (int)$cursor->getSubpriority(),
'type' => 'int',
'reverse' => true,
);
$columns[] = array(
'name' => 'task.dateModified',
'value' => (int)$cursor->getDateModified(),
'type' => 'int',
);
break;
case self::ORDER_CREATED:
$columns[] = array(
'name' => 'task.id',
'value' => (int)$cursor->getID(),
'type' => 'int',
);
break;
case self::ORDER_MODIFIED:
$columns[] = array(
'name' => 'task.dateModified',
'value' => (int)$cursor->getDateModified(),
'type' => 'int',
);
break;
case self::ORDER_TITLE:
$columns[] = array(
'name' => 'task.title',
'value' => $cursor->getTitle(),
'type' => 'string',
);
$columns[] = array(
'name' => 'task.id',
'value' => $cursor->getID(),
'type' => 'int',
);
break;
default:
throw new Exception("Unknown order query '{$this->orderBy}'!");
}
return $this->buildPagingClauseFromMultipleColumns(
$conn_r,
$columns,
array(
'reversed' => (bool)($before_id xor $this->getReversePaging()),
));
}
protected function getApplicationSearchObjectPHIDColumn() {
return 'task.phid';
}
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationManiphest';
+ }
+
}
diff --git a/src/applications/meta/phid/PhabricatorApplicationPHIDTypeApplication.php b/src/applications/meta/phid/PhabricatorApplicationPHIDTypeApplication.php
index 6554bd717c..718557266f 100644
--- a/src/applications/meta/phid/PhabricatorApplicationPHIDTypeApplication.php
+++ b/src/applications/meta/phid/PhabricatorApplicationPHIDTypeApplication.php
@@ -1,48 +1,45 @@
<?php
final class PhabricatorApplicationPHIDTypeApplication
extends PhabricatorPHIDType {
const TYPECONST = 'APPS';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Application');
}
public function newObject() {
return null;
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorApplicationQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$application = $objects[$phid];
$handle->setName($application->getName());
$handle->setURI($application->getApplicationURI());
}
}
public function canLoadNamedObject($name) {
return false;
}
}
diff --git a/src/applications/meta/query/PhabricatorApplicationQuery.php b/src/applications/meta/query/PhabricatorApplicationQuery.php
index 30106cd1f2..ca14d6954a 100644
--- a/src/applications/meta/query/PhabricatorApplicationQuery.php
+++ b/src/applications/meta/query/PhabricatorApplicationQuery.php
@@ -1,135 +1,144 @@
<?php
final class PhabricatorApplicationQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $installed;
private $beta;
private $firstParty;
private $nameContains;
private $unlisted;
private $classes;
private $phids;
const ORDER_APPLICATION = 'order:application';
const ORDER_NAME = 'order:name';
private $order = self::ORDER_APPLICATION;
public function withNameContains($name_contains) {
$this->nameContains = $name_contains;
return $this;
}
public function withInstalled($installed) {
$this->installed = $installed;
return $this;
}
public function withBeta($beta) {
$this->beta = $beta;
return $this;
}
public function withFirstParty($first_party) {
$this->firstParty = $first_party;
return $this;
}
public function withUnlisted($unlisted) {
$this->unlisted = $unlisted;
return $this;
}
public function withClasses(array $classes) {
$this->classes = $classes;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function setOrder($order) {
$this->order = $order;
return $this;
}
public function loadPage() {
$apps = PhabricatorApplication::getAllApplications();
if ($this->classes) {
$classes = array_fuse($this->classes);
foreach ($apps as $key => $app) {
if (empty($classes[get_class($app)])) {
unset($apps[$key]);
}
}
}
if ($this->phids) {
$phids = array_fuse($this->phids);
foreach ($apps as $key => $app) {
if (empty($phids[$app->getPHID()])) {
unset($apps[$key]);
}
}
}
if (strlen($this->nameContains)) {
foreach ($apps as $key => $app) {
if (stripos($app->getName(), $this->nameContains) === false) {
unset($apps[$key]);
}
}
}
if ($this->installed !== null) {
foreach ($apps as $key => $app) {
if ($app->isInstalled() != $this->installed) {
unset($apps[$key]);
}
}
}
if ($this->beta !== null) {
foreach ($apps as $key => $app) {
if ($app->isBeta() != $this->beta) {
unset($apps[$key]);
}
}
}
if ($this->firstParty !== null) {
foreach ($apps as $key => $app) {
if ($app->isFirstParty() != $this->firstParty) {
unset($apps[$key]);
}
}
}
if ($this->unlisted !== null) {
foreach ($apps as $key => $app) {
if ($app->isUnlisted() != $this->unlisted) {
unset($apps[$key]);
}
}
}
switch ($this->order) {
case self::ORDER_NAME:
$apps = msort($apps, 'getName');
break;
case self::ORDER_APPLICATION:
$apps = $apps;
break;
default:
throw new Exception(
pht('Unknown order "%s"!', $this->order));
}
return $apps;
}
+
+ public function getQueryApplicationClass() {
+ // NOTE: Although this belongs to the "Applications" application, trying
+ // to filter its results just leaves us recursing indefinitely. Users
+ // always have access to applications regardless of other policy settings
+ // anyway.
+ return null;
+ }
+
}
diff --git a/src/applications/notification/PhabricatorNotificationQuery.php b/src/applications/notification/PhabricatorNotificationQuery.php
index 736990e08e..731d9dca73 100644
--- a/src/applications/notification/PhabricatorNotificationQuery.php
+++ b/src/applications/notification/PhabricatorNotificationQuery.php
@@ -1,114 +1,120 @@
<?php
/**
* @task config Configuring the Query
* @task exec Query Execution
*/
final class PhabricatorNotificationQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $userPHID;
private $keys;
private $unread;
/* -( Configuring the Query )---------------------------------------------- */
public function setUserPHID($user_phid) {
$this->userPHID = $user_phid;
return $this;
}
public function withKeys(array $keys) {
$this->keys = $keys;
return $this;
}
/**
* Filter results by read/unread status. Note that `true` means to return
* only unread notifications, while `false` means to return only //read//
* notifications. The default is `null`, which returns both.
*
* @param mixed True or false to filter results by read status. Null to remove
* the filter.
* @return this
* @task config
*/
public function withUnread($unread) {
$this->unread = $unread;
return $this;
}
/* -( Query Execution )---------------------------------------------------- */
protected function loadPage() {
if (!$this->userPHID) {
throw new Exception("Call setUser() before executing the query");
}
$story_table = new PhabricatorFeedStoryData();
$notification_table = new PhabricatorFeedStoryNotification();
$conn = $story_table->establishConnection('r');
$data = queryfx_all(
$conn,
"SELECT story.*, notif.hasViewed FROM %T notif
JOIN %T story ON notif.chronologicalKey = story.chronologicalKey
%Q
ORDER BY notif.chronologicalKey DESC
%Q",
$notification_table->getTableName(),
$story_table->getTableName(),
$this->buildWhereClause($conn),
$this->buildLimitClause($conn));
$viewed_map = ipull($data, 'hasViewed', 'chronologicalKey');
$stories = PhabricatorFeedStory::loadAllFromRows(
$data,
$this->getViewer());
foreach ($stories as $key => $story) {
$story->setHasViewed($viewed_map[$key]);
}
return $stories;
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->userPHID) {
$where[] = qsprintf(
$conn_r,
'notif.userPHID = %s',
$this->userPHID);
}
if ($this->unread !== null) {
$where[] = qsprintf(
$conn_r,
'notif.hasViewed = %d',
(int)!$this->unread);
}
if ($this->keys) {
$where[] = qsprintf(
$conn_r,
'notif.chronologicalKey IN (%Ls)',
$this->keys);
}
return $this->formatWhereClause($where);
}
protected function getPagingValue($item) {
return $item->getChronologicalKey();
}
+
+ public function getQueryApplicationClass() {
+ // TODO: No actual "Notification" app yet, but there probably should be.
+ return null;
+ }
+
}
diff --git a/src/applications/owners/phid/PhabricatorOwnersPHIDTypePackage.php b/src/applications/owners/phid/PhabricatorOwnersPHIDTypePackage.php
index af7976aad0..b82b47a9ea 100644
--- a/src/applications/owners/phid/PhabricatorOwnersPHIDTypePackage.php
+++ b/src/applications/owners/phid/PhabricatorOwnersPHIDTypePackage.php
@@ -1,46 +1,43 @@
<?php
final class PhabricatorOwnersPHIDTypePackage extends PhabricatorPHIDType {
const TYPECONST = 'OPKG';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Owners Package');
}
public function newObject() {
return new PhabricatorOwnersPackage();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorOwnersPackageQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$package = $objects[$phid];
$name = $package->getName();
$id = $package->getID();
$handle->setName($name);
$handle->setURI("/owners/package/{$id}/");
}
}
}
diff --git a/src/applications/owners/query/PhabricatorOwnersPackageQuery.php b/src/applications/owners/query/PhabricatorOwnersPackageQuery.php
index 68b0dfe0ed..90996c9f04 100644
--- a/src/applications/owners/query/PhabricatorOwnersPackageQuery.php
+++ b/src/applications/owners/query/PhabricatorOwnersPackageQuery.php
@@ -1,82 +1,87 @@
<?php
final class PhabricatorOwnersPackageQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $phids;
private $ownerPHIDs;
/**
* Owners are direct owners, and members of owning projects.
*/
public function withOwnerPHIDs(array $phids) {
$this->ownerPHIDs = $phids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
protected function loadPage() {
$table = new PhabricatorOwnersPackage();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT p.* FROM %T p %Q %Q %Q %Q',
$table->getTableName(),
$this->buildJoinClause($conn_r),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
private function buildJoinClause(AphrontDatabaseConnection $conn_r) {
$joins = array();
if ($this->ownerPHIDs) {
$joins[] = qsprintf(
$conn_r,
'JOIN %T o ON o.packageID = p.id',
id(new PhabricatorOwnersOwner())->getTableName());
}
return implode(' ', $joins);
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'p.phid IN (%Ls)',
$this->phids);
}
if ($this->ownerPHIDs) {
$base_phids = $this->ownerPHIDs;
$query = new PhabricatorProjectQuery();
$query->setViewer($this->getViewer());
$query->withMemberPHIDs($base_phids);
$projects = $query->execute();
$project_phids = mpull($projects, 'getPHID');
$all_phids = array_merge($base_phids, $project_phids);
$where[] = qsprintf(
$conn_r,
'o.userPHID IN (%Ls)',
$all_phids);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationOwners';
+ }
+
}
diff --git a/src/applications/paste/phid/PhabricatorPastePHIDTypePaste.php b/src/applications/paste/phid/PhabricatorPastePHIDTypePaste.php
index 024cdd9f16..5e3ef4fd81 100644
--- a/src/applications/paste/phid/PhabricatorPastePHIDTypePaste.php
+++ b/src/applications/paste/phid/PhabricatorPastePHIDTypePaste.php
@@ -1,76 +1,73 @@
<?php
final class PhabricatorPastePHIDTypePaste extends PhabricatorPHIDType {
const TYPECONST = 'PSTE';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Paste');
}
public function newObject() {
return new PhabricatorPaste();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorPasteQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$paste = $objects[$phid];
$id = $paste->getID();
$name = $paste->getFullName();
$handle->setName("P{$id}");
$handle->setFullName($name);
$handle->setURI("/P{$id}");
}
}
public function canLoadNamedObject($name) {
return preg_match('/^P\d*[1-9]\d*$/i', $name);
}
public function loadNamedObjects(
PhabricatorObjectQuery $query,
array $names) {
$id_map = array();
foreach ($names as $name) {
$id = (int)substr($name, 1);
$id_map[$id][] = $name;
}
$objects = id(new PhabricatorPasteQuery())
->setViewer($query->getViewer())
->withIDs(array_keys($id_map))
->execute();
$results = array();
foreach ($objects as $id => $object) {
foreach (idx($id_map, $id, array()) as $name) {
$results[$name] = $object;
}
}
return $results;
}
}
diff --git a/src/applications/paste/query/PhabricatorPasteQuery.php b/src/applications/paste/query/PhabricatorPasteQuery.php
index aeea224205..c4db19e1ff 100644
--- a/src/applications/paste/query/PhabricatorPasteQuery.php
+++ b/src/applications/paste/query/PhabricatorPasteQuery.php
@@ -1,252 +1,257 @@
<?php
/**
* @group paste
*/
final class PhabricatorPasteQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $authorPHIDs;
private $parentPHIDs;
private $needContent;
private $needRawContent;
private $languages;
private $includeNoLanguage;
private $dateCreatedAfter;
private $dateCreatedBefore;
public function withIDs(array $ids) {
$this->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 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;
}
protected function loadPage() {
$table = new PhabricatorPaste();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT paste.* FROM %T paste %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
$pastes = $table->loadAllFromArray($data);
return $pastes;
}
protected function didFilterPage(array $pastes) {
if ($this->needRawContent) {
$pastes = $this->loadRawContent($pastes);
}
if ($this->needContent) {
$pastes = $this->loadContent($pastes);
}
return $pastes;
}
protected function buildWhereClause($conn_r) {
$where = array();
$where[] = $this->buildPagingClause($conn_r);
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
if ($this->authorPHIDs) {
$where[] = qsprintf(
$conn_r,
'authorPHID IN (%Ls)',
$this->authorPHIDs);
}
if ($this->parentPHIDs) {
$where[] = qsprintf(
$conn_r,
'parentPHID IN (%Ls)',
$this->parentPHIDs);
}
if ($this->languages) {
$where[] = qsprintf(
$conn_r,
'language IN (%Ls)',
$this->languages);
}
if ($this->dateCreatedAfter) {
$where[] = qsprintf(
$conn_r,
'dateCreated >= %d',
$this->dateCreatedAfter);
}
if ($this->dateCreatedBefore) {
$where[] = qsprintf(
$conn_r,
'dateCreated <= %d',
$this->dateCreatedBefore);
}
return $this->formatWhereClause($where);
}
private function getContentCacheKey(PhabricatorPaste $paste) {
return 'P'.$paste->getID().':content/'.$paste->getLanguage();
}
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);
$results = array();
$need_raw = array();
foreach ($pastes as $key => $paste) {
$key = $this->getContentCacheKey($paste);
if (isset($caches[$key])) {
$paste->attachContent(phutil_safe_html($caches[$key]));
$results[$paste->getID()] = $paste;
} else {
$need_raw[$key] = $paste;
}
}
if (!$need_raw) {
return $results;
}
$write_data = array();
$need_raw = $this->loadRawContent($need_raw);
foreach ($need_raw as $key => $paste) {
$content = $this->buildContent($paste);
$paste->attachContent($content);
$write_data[$this->getContentCacheKey($paste)] = (string)$content;
$results[$paste->getID()] = $paste;
}
$cache->setKeys($write_data);
return $results;
}
private function buildContent(PhabricatorPaste $paste) {
$language = $paste->getLanguage();
$source = $paste->getRawContent();
if (empty($language)) {
return PhabricatorSyntaxHighlighter::highlightWithFilename(
$paste->getTitle(),
$source);
} else {
return PhabricatorSyntaxHighlighter::highlightWithLanguage(
$language,
$source);
}
}
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationPaste';
+ }
+
}
diff --git a/src/applications/people/phid/PhabricatorPeoplePHIDTypeExternal.php b/src/applications/people/phid/PhabricatorPeoplePHIDTypeExternal.php
index b3c0044e60..d4e8241c19 100644
--- a/src/applications/people/phid/PhabricatorPeoplePHIDTypeExternal.php
+++ b/src/applications/people/phid/PhabricatorPeoplePHIDTypeExternal.php
@@ -1,47 +1,44 @@
<?php
final class PhabricatorPeoplePHIDTypeExternal extends PhabricatorPHIDType {
const TYPECONST = 'XUSR';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('External Account');
}
public function newObject() {
return new PhabricatorExternalAccount();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorExternalAccountQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$account = $objects[$phid];
$display_name = $account->getDisplayName();
$handle->setName($display_name);
}
}
public function canLoadNamedObject($name) {
return false;
}
}
diff --git a/src/applications/people/phid/PhabricatorPeoplePHIDTypeUser.php b/src/applications/people/phid/PhabricatorPeoplePHIDTypeUser.php
index 4cf6e21f30..a0bce6ea47 100644
--- a/src/applications/people/phid/PhabricatorPeoplePHIDTypeUser.php
+++ b/src/applications/people/phid/PhabricatorPeoplePHIDTypeUser.php
@@ -1,54 +1,51 @@
<?php
final class PhabricatorPeoplePHIDTypeUser extends PhabricatorPHIDType {
const TYPECONST = 'USER';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Phabricator User');
}
public function newObject() {
return new PhabricatorUser();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorPeopleQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
->withPHIDs($phids)
->needProfileImage(true)
- ->needStatus(true)
- ->execute();
+ ->needStatus(true);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$user = $objects[$phid];
$handle->setName($user->getUsername());
$handle->setURI('/p/'.$user->getUsername().'/');
$handle->setFullName(
$user->getUsername().' ('.$user->getRealName().')');
$handle->setImageURI($user->loadProfileImageURI());
$handle->setDisabled($user->getIsDisabled());
if ($user->hasStatus()) {
$status = $user->getStatus();
$handle->setStatus($status->getTextStatus());
$handle->setTitle($status->getTerseSummary($query->getViewer()));
}
}
}
}
diff --git a/src/applications/people/query/PhabricatorPeopleQuery.php b/src/applications/people/query/PhabricatorPeopleQuery.php
index 39613c5011..f2c0b312bd 100644
--- a/src/applications/people/query/PhabricatorPeopleQuery.php
+++ b/src/applications/people/query/PhabricatorPeopleQuery.php
@@ -1,285 +1,289 @@
<?php
final class PhabricatorPeopleQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $usernames;
private $realnames;
private $emails;
private $phids;
private $ids;
private $dateCreatedAfter;
private $dateCreatedBefore;
private $isAdmin;
private $isSystemAgent;
private $isDisabled;
private $nameLike;
private $needPrimaryEmail;
private $needProfile;
private $needProfileImage;
private $needStatus;
public function withIDs(array $ids) {
$this->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 withIsDisabled($disabled) {
$this->isDisabled = $disabled;
return $this;
}
public function withNameLike($like) {
$this->nameLike = $like;
return $this;
}
public function needPrimaryEmail($need) {
$this->needPrimaryEmail = $need;
return $this;
}
public function needProfile($need) {
$this->needProfile = $need;
return $this;
}
public function needProfileImage($need) {
$this->needProfileImage = $need;
return $this;
}
public function needStatus($need) {
$this->needStatus = $need;
return $this;
}
public function loadPage() {
$table = new PhabricatorUser();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T user %Q %Q %Q %Q %Q',
$table->getTableName(),
$this->buildJoinsClause($conn_r),
$this->buildWhereClause($conn_r),
$this->buildApplicationSearchGroupClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
if ($this->needPrimaryEmail) {
$table->putInSet(new LiskDAOSet());
}
return $table->loadAllFromArray($data);
}
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 = new PhabricatorUserProfile();
$profile->setUserPHID($user_phid);
}
$user->attachUserProfile($profile);
}
}
if ($this->needProfileImage) {
$user_profile_file_phids = mpull($users, 'getProfileImagePHID');
$user_profile_file_phids = array_filter($user_profile_file_phids);
if ($user_profile_file_phids) {
$files = id(new PhabricatorFileQuery())
->setParentQuery($this)
->setViewer($this->getViewer())
->withPHIDs($user_profile_file_phids)
->execute();
$files = mpull($files, null, 'getPHID');
} else {
$files = array();
}
foreach ($users as $user) {
$image_phid = $user->getProfileImagePHID();
if (isset($files[$image_phid])) {
$profile_image_uri = $files[$image_phid]->getBestURI();
} else {
$profile_image_uri = PhabricatorUser::getDefaultProfileImageURI();
}
$user->attachProfileImageURI($profile_image_uri);
}
}
if ($this->needStatus) {
$user_list = mpull($users, null, 'getPHID');
$statuses = id(new PhabricatorUserStatus())->loadCurrentStatuses(
array_keys($user_list));
foreach ($user_list as $phid => $user) {
$status = idx($statuses, $phid);
if ($status) {
$user->attachStatus($status);
}
}
}
return $users;
}
private function buildJoinsClause($conn_r) {
$joins = array();
if ($this->emails) {
$email_table = new PhabricatorUserEmail();
$joins[] = qsprintf(
$conn_r,
'JOIN %T email ON email.userPHID = user.PHID',
$email_table->getTableName());
}
$joins[] = $this->buildApplicationSearchJoinClause($conn_r);
$joins = implode(' ', $joins);
return $joins;
}
private function buildWhereClause($conn_r) {
$where = array();
if ($this->usernames) {
$where[] = qsprintf(
$conn_r,
'user.userName IN (%Ls)',
$this->usernames);
}
if ($this->emails) {
$where[] = qsprintf(
$conn_r,
'email.address IN (%Ls)',
$this->emails);
}
if ($this->realnames) {
$where[] = qsprintf(
$conn_r,
'user.realName IN (%Ls)',
$this->realnames);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'user.phid IN (%Ls)',
$this->phids);
}
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'user.id IN (%Ld)',
$this->ids);
}
if ($this->dateCreatedAfter) {
$where[] = qsprintf(
$conn_r,
'user.dateCreated >= %d',
$this->dateCreatedAfter);
}
if ($this->dateCreatedBefore) {
$where[] = qsprintf(
$conn_r,
'user.dateCreated <= %d',
$this->dateCreatedBefore);
}
if ($this->isAdmin) {
$where[] = qsprintf(
$conn_r,
'user.isAdmin = 1');
}
if ($this->isDisabled) {
$where[] = qsprintf(
$conn_r,
'user.isDisabled = 1');
}
if ($this->isSystemAgent) {
$where[] = qsprintf(
$conn_r,
'user.isSystemAgent = 1');
}
if (strlen($this->nameLike)) {
$where[] = qsprintf(
$conn_r,
'user.username LIKE %~ OR user.realname LIKE %~',
$this->nameLike,
$this->nameLike);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
protected function getPagingColumn() {
return 'user.id';
}
protected function getApplicationSearchObjectPHIDColumn() {
return 'user.phid';
}
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationPeople';
+ }
+
}
diff --git a/src/applications/phame/phid/PhabricatorPhamePHIDTypeBlog.php b/src/applications/phame/phid/PhabricatorPhamePHIDTypeBlog.php
index fe24e757b5..bf20c112e0 100644
--- a/src/applications/phame/phid/PhabricatorPhamePHIDTypeBlog.php
+++ b/src/applications/phame/phid/PhabricatorPhamePHIDTypeBlog.php
@@ -1,46 +1,43 @@
<?php
/**
* @group phame
*/
final class PhabricatorPhamePHIDTypeBlog extends PhabricatorPHIDType {
const TYPECONST = 'BLOG';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Phame Blog');
}
public function newObject() {
return new PhameBlog();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhameBlogQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$blog = $objects[$phid];
$handle->setName($blog->getName());
$handle->setFullName($blog->getName());
$handle->setURI('/phame/blog/view/'.$blog->getID().'/');
}
}
}
diff --git a/src/applications/phame/phid/PhabricatorPhamePHIDTypePost.php b/src/applications/phame/phid/PhabricatorPhamePHIDTypePost.php
index 790b1084f6..32ecbc231c 100644
--- a/src/applications/phame/phid/PhabricatorPhamePHIDTypePost.php
+++ b/src/applications/phame/phid/PhabricatorPhamePHIDTypePost.php
@@ -1,46 +1,43 @@
<?php
/**
* @group phame
*/
final class PhabricatorPhamePHIDTypePost extends PhabricatorPHIDType {
const TYPECONST = 'POST';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Phame Post');
}
public function newObject() {
return new PhamePost();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhamePostQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$post = $objects[$phid];
$handle->setName($post->getTitle());
$handle->setFullName($post->getTitle());
$handle->setURI('/phame/post/view/'.$post->getID().'/');
}
}
}
diff --git a/src/applications/phame/query/PhameBlogQuery.php b/src/applications/phame/query/PhameBlogQuery.php
index 022e694275..eb253de6f5 100644
--- a/src/applications/phame/query/PhameBlogQuery.php
+++ b/src/applications/phame/query/PhameBlogQuery.php
@@ -1,78 +1,83 @@
<?php
/**
* @group phame
*/
final class PhameBlogQuery extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $domain;
private $needBloggers;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withDomain($domain) {
$this->domain = $domain;
return $this;
}
protected function loadPage() {
$table = new PhameBlog();
$conn_r = $table->establishConnection('r');
$where_clause = $this->buildWhereClause($conn_r);
$order_clause = $this->buildOrderClause($conn_r);
$limit_clause = $this->buildLimitClause($conn_r);
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T b %Q %Q %Q',
$table->getTableName(),
$where_clause,
$order_clause,
$limit_clause);
$blogs = $table->loadAllFromArray($data);
return $blogs;
}
private function buildWhereClause($conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ls)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
if ($this->domain) {
$where[] = qsprintf(
$conn_r,
'domain = %s',
$this->domain);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
+ public function getQueryApplicationClass() {
+ // TODO: Can we set this without breaking public blogs?
+ return null;
+ }
+
}
diff --git a/src/applications/phame/query/PhamePostQuery.php b/src/applications/phame/query/PhamePostQuery.php
index 6356f54bec..94430990d4 100644
--- a/src/applications/phame/query/PhamePostQuery.php
+++ b/src/applications/phame/query/PhamePostQuery.php
@@ -1,144 +1,149 @@
<?php
/**
* @group phame
*/
final class PhamePostQuery extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $blogPHIDs;
private $bloggerPHIDs;
private $phameTitles;
private $visibility;
private $publishedAfter;
private $phids;
public function withIDs(array $ids) {
$this->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 withPhameTitles(array $phame_titles) {
$this->phameTitles = $phame_titles;
return $this;
}
public function withVisibility($visibility) {
$this->visibility = $visibility;
return $this;
}
public function withPublishedAfter($time) {
$this->publishedAfter = $time;
return $this;
}
protected function loadPage() {
$table = new PhamePost();
$conn_r = $table->establishConnection('r');
$where_clause = $this->buildWhereClause($conn_r);
$order_clause = $this->buildOrderClause($conn_r);
$limit_clause = $this->buildLimitClause($conn_r);
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T p %Q %Q %Q',
$table->getTableName(),
$where_clause,
$order_clause,
$limit_clause);
$posts = $table->loadAllFromArray($data);
if ($posts) {
// We require these to do visibility checks, so load them unconditionally.
$blog_phids = mpull($posts, 'getBlogPHID');
$blogs = id(new PhameBlogQuery())
->setViewer($this->getViewer())
->withPHIDs($blog_phids)
->execute();
$blogs = mpull($blogs, null, 'getPHID');
foreach ($posts as $post) {
if (isset($blogs[$post->getBlogPHID()])) {
$post->setBlog($blogs[$post->getBlogPHID()]);
}
}
}
return $posts;
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'p.id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'p.phid IN (%Ls)',
$this->phids);
}
if ($this->bloggerPHIDs) {
$where[] = qsprintf(
$conn_r,
'p.bloggerPHID IN (%Ls)',
$this->bloggerPHIDs);
}
if ($this->phameTitles) {
$where[] = qsprintf(
$conn_r,
'p.phameTitle IN (%Ls)',
$this->phameTitles);
}
if ($this->visibility !== null) {
$where[] = qsprintf(
$conn_r,
'p.visibility = %d',
$this->visibility);
}
if ($this->publishedAfter !== null) {
$where[] = qsprintf(
$conn_r,
'p.datePublished > %d',
$this->publishedAfter);
}
if ($this->blogPHIDs) {
$where[] = qsprintf(
$conn_r,
'p.blogPHID in (%Ls)',
$this->blogPHIDs);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
+ public function getQueryApplicationClass() {
+ // TODO: Does setting this break public blogs?
+ return null;
+ }
+
}
diff --git a/src/applications/phid/query/PhabricatorHandleQuery.php b/src/applications/phid/query/PhabricatorHandleQuery.php
index 5e089b1ed1..c3e923c0a7 100644
--- a/src/applications/phid/query/PhabricatorHandleQuery.php
+++ b/src/applications/phid/query/PhabricatorHandleQuery.php
@@ -1,70 +1,74 @@
<?php
final class PhabricatorHandleQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $phids = array();
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function loadPage() {
$types = PhabricatorPHIDType::getAllTypes();
$phids = array_unique($this->phids);
if (!$phids) {
return array();
}
$object_query = id(new PhabricatorObjectQuery())
->withPHIDs($phids)
->setViewer($this->getViewer());
$objects = $object_query->execute();
$filtered = $object_query->getPolicyFilteredPHIDs();
$groups = array();
foreach ($phids as $phid) {
$type = phid_get_type($phid);
$groups[$type][] = $phid;
}
$results = array();
foreach ($groups as $type => $phid_group) {
$handles = array();
foreach ($phid_group as $key => $phid) {
if (isset($handles[$phid])) {
unset($phid_group[$key]);
// The input had a duplicate PHID; just skip it.
continue;
}
$handles[$phid] = id(new PhabricatorObjectHandle())
->setType($type)
->setPHID($phid);
if (isset($objects[$phid])) {
$handles[$phid]->setComplete(true);
} else if (isset($filtered[$phid])) {
$handles[$phid]->setPolicyFiltered(true);
}
}
if (isset($types[$type])) {
$type_objects = array_select_keys($objects, $phid_group);
if ($type_objects) {
$have_object_phids = array_keys($type_objects);
$types[$type]->loadHandles(
$this,
array_select_keys($handles, $have_object_phids),
$type_objects);
}
}
$results += $handles;
}
return $results;
}
+ public function getQueryApplicationClass() {
+ return null;
+ }
+
}
diff --git a/src/applications/phid/query/PhabricatorObjectQuery.php b/src/applications/phid/query/PhabricatorObjectQuery.php
index a6ffa02e7e..a786d4f72d 100644
--- a/src/applications/phid/query/PhabricatorObjectQuery.php
+++ b/src/applications/phid/query/PhabricatorObjectQuery.php
@@ -1,155 +1,159 @@
<?php
final class PhabricatorObjectQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $phids = array();
private $names = array();
private $types;
private $namedResults;
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withNames(array $names) {
$this->names = $names;
return $this;
}
public function withTypes(array $types) {
$this->types = $types;
return $this;
}
public function loadPage() {
if ($this->namedResults === null) {
$this->namedResults = array();
}
$types = PhabricatorPHIDType::getAllTypes();
if ($this->types) {
$types = array_select_keys($types, $this->types);
}
$names = array_unique($this->names);
$phids = $this->phids;
// We allow objects to be named by their PHID in addition to their normal
// name so that, e.g., CLI tools which accept object names can also accept
// PHIDs and work as users expect.
$actually_phids = array();
if ($names) {
foreach ($names as $key => $name) {
if (!strncmp($name, 'PHID-', 5)) {
$actually_phids[] = $name;
$phids[] = $name;
unset($names[$key]);
}
}
}
$phids = array_unique($phids);
if ($names) {
$name_results = $this->loadObjectsByName($types, $names);
} else {
$name_results = array();
}
if ($phids) {
$phid_results = $this->loadObjectsByPHID($types, $phids);
} else {
$phid_results = array();
}
foreach ($actually_phids as $phid) {
if (isset($phid_results[$phid])) {
$name_results[$phid] = $phid_results[$phid];
}
}
$this->namedResults += $name_results;
return $phid_results + mpull($name_results, null, 'getPHID');
}
public function getNamedResults() {
if ($this->namedResults === null) {
throw new Exception("Call execute() before getNamedResults()!");
}
return $this->namedResults;
}
private function loadObjectsByName(array $types, array $names) {
$groups = array();
foreach ($names as $name) {
foreach ($types as $type => $type_impl) {
if (!$type_impl->canLoadNamedObject($name)) {
continue;
}
$groups[$type][] = $name;
break;
}
}
$results = array();
foreach ($groups as $type => $group) {
$results += $types[$type]->loadNamedObjects($this, $group);
}
return $results;
}
private function loadObjectsByPHID(array $types, array $phids) {
$results = array();
$workspace = $this->getObjectsFromWorkspace($phids);
foreach ($phids as $key => $phid) {
if (isset($workspace[$phid])) {
$results[$phid] = $workspace[$phid];
unset($phids[$key]);
}
}
if (!$phids) {
return $results;
}
$groups = array();
foreach ($phids as $phid) {
$type = phid_get_type($phid);
$groups[$type][] = $phid;
}
foreach ($groups as $type => $group) {
if (isset($types[$type])) {
$objects = $types[$type]->loadObjects($this, $group);
$results += mpull($objects, null, 'getPHID');
}
}
return $results;
}
protected function didFilterResults(array $filtered) {
foreach ($this->namedResults as $name => $result) {
if (isset($filtered[$result->getPHID()])) {
unset($this->namedResults[$name]);
}
}
}
/**
* This query disables policy filtering because it is performed in the
* subqueries which actually load objects. We don't need to re-filter
* results, since policies have already been applied.
*/
protected function shouldDisablePolicyFiltering() {
return true;
}
+ public function getQueryApplicationClass() {
+ return null;
+ }
+
}
diff --git a/src/applications/phid/type/PhabricatorPHIDType.php b/src/applications/phid/type/PhabricatorPHIDType.php
index 47e1351922..6ee4c4e8ec 100644
--- a/src/applications/phid/type/PhabricatorPHIDType.php
+++ b/src/applications/phid/type/PhabricatorPHIDType.php
@@ -1,89 +1,132 @@
<?php
abstract class PhabricatorPHIDType {
abstract public function getTypeConstant();
abstract public function getTypeName();
public function newObject() {
return null;
}
- abstract public function loadObjects(
+ /**
+ * Build a @{class:PhabricatorPolicyAwareQuery} to load objects of this type
+ * by PHID.
+ *
+ * If you can not build a single query which satisfies this requirement, you
+ * can provide a dummy implementation for this method and overload
+ * @{method:loadObjects} instead.
+ *
+ * @param PhabricatorObjectQuery Query being executed.
+ * @param list<phid> PHIDs to load.
+ * @return PhabricatorPolicyAwareQuery Query object which loads the
+ * specified PHIDs when executed.
+ */
+ abstract protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids);
+ /**
+ * Load objects of this type, by PHID. For most PHID types, it is only
+ * necessary to implement @{method:buildQueryForObjects} to get object
+ * loading to work.
+ *
+ * @param PhabricatorObjectQuery Query being executed.
+ * @param list<phid> PHIDs to load.
+ * @return list<wild> Corresponding objects.
+ */
+ public function loadObjects(
+ PhabricatorObjectQuery $query,
+ array $phids) {
+
+ $object_query = $this->buildQueryForObjects($query, $phids)
+ ->setViewer($query->getViewer())
+ ->setParentQuery($query);
+
+ // If the user doesn't have permission to use the application at all,
+ // just mark all the PHIDs as filtered. This primarily makes these
+ // objects show up as "Restricted" instead of "Unknown" when loaded as
+ // handles, which is technically true.
+ if (!$object_query->canViewerUseQueryApplication()) {
+ $object_query->addPolicyFilteredPHIDs(array_fuse($phids));
+ return array();
+ }
+
+ return $object_query->execute();
+ }
+
+
/**
* Populate provided handles with application-specific data, like titles and
* URIs.
*
* NOTE: The `$handles` and `$objects` lists are guaranteed to be nonempty
* and have the same keys: subclasses are expected to load information only
* for handles with visible objects.
*
* Because of this guarantee, a safe implementation will typically look like*
*
* foreach ($handles as $phid => $handle) {
* $object = $objects[$phid];
*
* $handle->setStuff($object->getStuff());
* // ...
* }
*
* In general, an implementation should call `setName()` and `setURI()` on
* each handle at a minimum. See @{class:PhabricatorObjectHandle} for other
* handle properties.
*
* @param PhabricatorHandleQuery Issuing query object.
* @param list<PhabricatorObjectHandle> Handles to populate with data.
* @param list<Object> Objects for these PHIDs loaded by
- * @{method:loadObjects()}.
+ * @{method:buildQueryForObjects()}.
* @return void
*/
abstract public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects);
public function canLoadNamedObject($name) {
return false;
}
public function loadNamedObjects(
PhabricatorObjectQuery $query,
array $names) {
throw new Exception("Not implemented!");
}
public static function getAllTypes() {
static $types;
if ($types === null) {
$objects = id(new PhutilSymbolLoader())
->setAncestorClass(__CLASS__)
->loadObjects();
$map = array();
$original = array();
foreach ($objects as $object) {
$type = $object->getTypeConstant();
if (isset($map[$type])) {
$that_class = $original[$type];
$this_class = get_class($object);
throw new Exception(
"Two PhabricatorPHIDType classes ({$that_class}, {$this_class}) ".
"both handle PHID type '{$type}'. A type may be handled by only ".
"one class.");
}
$original[$type] = get_class($object);
$map[$type] = $object;
}
$types = $map;
}
return $types;
}
}
diff --git a/src/applications/phlux/phid/PhluxPHIDTypeVariable.php b/src/applications/phlux/phid/PhluxPHIDTypeVariable.php
index e6dc1a5f66..5be0c23778 100644
--- a/src/applications/phlux/phid/PhluxPHIDTypeVariable.php
+++ b/src/applications/phlux/phid/PhluxPHIDTypeVariable.php
@@ -1,50 +1,47 @@
<?php
final class PhluxPHIDTypeVariable extends PhabricatorPHIDType {
const TYPECONST = 'PVAR';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Variable');
}
public function newObject() {
return new PhluxVariable();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhluxVariableQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$variable = $objects[$phid];
$key = $variable->getVariableKey();
$handle->setName($key);
$handle->setFullName(pht('Variable "%s"', $key));
$handle->setURI("/phlux/view/{$key}/");
}
}
public function canLoadNamedObject($name) {
return false;
}
}
diff --git a/src/applications/phlux/query/PhluxVariableQuery.php b/src/applications/phlux/query/PhluxVariableQuery.php
index 89e61b87a0..3b9dbeb17e 100644
--- a/src/applications/phlux/query/PhluxVariableQuery.php
+++ b/src/applications/phlux/query/PhluxVariableQuery.php
@@ -1,68 +1,72 @@
<?php
final class PhluxVariableQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $keys;
private $phids;
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withKeys(array $keys) {
$this->keys = $keys;
return $this;
}
protected function loadPage() {
$table = new PhluxVariable();
$conn_r = $table->establishConnection('r');
$rows = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($rows);
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
$where[] = $this->buildPagingClause($conn_r);
if ($this->keys) {
$where[] = qsprintf(
$conn_r,
'variableKey IN (%Ls)',
$this->keys);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
return $this->formatWhereClause($where);
}
protected function getPagingColumn() {
return 'variableKey';
}
protected function getPagingValue($result) {
return $result->getVariableKey();
}
protected function getReversePaging() {
return true;
}
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationPhlux';
+ }
+
}
diff --git a/src/applications/pholio/phid/PholioPHIDTypeImage.php b/src/applications/pholio/phid/PholioPHIDTypeImage.php
index 87c5dadf48..54b806cc21 100644
--- a/src/applications/pholio/phid/PholioPHIDTypeImage.php
+++ b/src/applications/pholio/phid/PholioPHIDTypeImage.php
@@ -1,48 +1,45 @@
<?php
final class PholioPHIDTypeImage extends PhabricatorPHIDType {
const TYPECONST = 'PIMG';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Image');
}
public function newObject() {
return new PholioImage();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PholioImageQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$image = $objects[$phid];
$id = $image->getID();
$mock_id = $image->getMockID();
$name = $image->getName();
$handle->setURI("/M{$mock_id}/{$id}/");
$handle->setName($name);
$handle->setFullName($name);
}
}
}
diff --git a/src/applications/pholio/phid/PholioPHIDTypeMock.php b/src/applications/pholio/phid/PholioPHIDTypeMock.php
index ced9f5e9cf..abfd36d2db 100644
--- a/src/applications/pholio/phid/PholioPHIDTypeMock.php
+++ b/src/applications/pholio/phid/PholioPHIDTypeMock.php
@@ -1,76 +1,73 @@
<?php
final class PholioPHIDTypeMock extends PhabricatorPHIDType {
const TYPECONST = 'MOCK';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Mock');
}
public function newObject() {
return new PholioMock();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PholioMockQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$mock = $objects[$phid];
$id = $mock->getID();
$name = $mock->getName();
$handle->setURI("/M{$id}");
$handle->setName("M{$id}");
$handle->setFullName("M{$id}: {$name}");
}
}
public function canLoadNamedObject($name) {
return preg_match('/^M\d*[1-9]\d*$/i', $name);
}
public function loadNamedObjects(
PhabricatorObjectQuery $query,
array $names) {
$id_map = array();
foreach ($names as $name) {
$id = (int)substr($name, 1);
$id_map[$id][] = $name;
}
$objects = id(new PholioMockQuery())
->setViewer($query->getViewer())
->withIDs(array_keys($id_map))
->execute();
$results = array();
foreach ($objects as $id => $object) {
foreach (idx($id_map, $id, array()) as $name) {
$results[$name] = $object;
}
}
return $results;
}
}
diff --git a/src/applications/pholio/query/PholioImageQuery.php b/src/applications/pholio/query/PholioImageQuery.php
index 71bdd3ab0e..4ade82e975 100644
--- a/src/applications/pholio/query/PholioImageQuery.php
+++ b/src/applications/pholio/query/PholioImageQuery.php
@@ -1,164 +1,168 @@
<?php
/**
* @group pholio
*/
final class PholioImageQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $mockIDs;
private $obsolete;
private $needInlineComments;
private $mockCache = array();
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withMockIDs(array $mock_ids) {
$this->mockIDs = $mock_ids;
return $this;
}
public function withObsolete($obsolete) {
$this->obsolete = $obsolete;
return $this;
}
public function needInlineComments($need_inline_comments) {
$this->needInlineComments = $need_inline_comments;
return $this;
}
public function setMockCache($mock_cache) {
$this->mockCache = $mock_cache;
return $this;
}
public function getMockCache() {
return $this->mockCache;
}
protected function loadPage() {
$table = new PholioImage();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
$images = $table->loadAllFromArray($data);
return $images;
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
$where[] = $this->buildPagingClause($conn_r);
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
if ($this->mockIDs) {
$where[] = qsprintf(
$conn_r,
'mockID IN (%Ld)',
$this->mockIDs);
}
if ($this->obsolete !== null) {
$where[] = qsprintf(
$conn_r,
'isObsolete = %d',
$this->obsolete);
}
return $this->formatWhereClause($where);
}
protected function willFilterPage(array $images) {
assert_instances_of($images, 'PholioImage');
if ($this->getMockCache()) {
$mocks = $this->getMockCache();
} else {
$mock_ids = mpull($images, 'getMockID');
// DO NOT set needImages to true; recursion results!
$mocks = id(new PholioMockQuery())
->setViewer($this->getViewer())
->withIDs($mock_ids)
->execute();
$mocks = mpull($mocks, null, 'getID');
}
foreach ($images as $index => $image) {
$mock = idx($mocks, $image->getMockID());
if ($mock) {
$image->attachMock($mock);
} else {
// mock is missing or we can't see it
unset($images[$index]);
}
}
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) {
$all_inline_comments = id(new PholioTransactionComment())
->loadAllWhere('imageid IN (%Ld)',
mpull($images, 'getID'));
$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 'PhabricatorApplicationPholio';
+ }
+
}
diff --git a/src/applications/pholio/query/PholioMockQuery.php b/src/applications/pholio/query/PholioMockQuery.php
index f08c9e4633..3b9a40fa9a 100644
--- a/src/applications/pholio/query/PholioMockQuery.php
+++ b/src/applications/pholio/query/PholioMockQuery.php
@@ -1,164 +1,168 @@
<?php
/**
* @group pholio
*/
final class PholioMockQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $authorPHIDs;
private $needCoverFiles;
private $needImages;
private $needInlineComments;
private $needTokenCounts;
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 needCoverFiles($need_cover_files) {
$this->needCoverFiles = $need_cover_files;
return $this;
}
public function needImages($need_images) {
$this->needImages = $need_images;
return $this;
}
public function needInlineComments($need_inline_comments) {
$this->needInlineComments = $need_inline_comments;
return $this;
}
public function needTokenCounts($need) {
$this->needTokenCounts = $need;
return $this;
}
protected function loadPage() {
$table = new PholioMock();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
$mocks = $table->loadAllFromArray($data);
if ($mocks && $this->needImages) {
$this->loadImages($mocks);
}
if ($mocks && $this->needCoverFiles) {
$this->loadCoverFiles($mocks);
}
if ($mocks && $this->needTokenCounts) {
$this->loadTokenCounts($mocks);
}
return $mocks;
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
$where[] = $this->buildPagingClause($conn_r);
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
if ($this->authorPHIDs) {
$where[] = qsprintf(
$conn_r,
'authorPHID in (%Ls)',
$this->authorPHIDs);
}
return $this->formatWhereClause($where);
}
private function loadImages(array $mocks) {
assert_instances_of($mocks, 'PholioMock');
$mock_map = mpull($mocks, null, 'getID');
$all_images = id(new PholioImageQuery())
->setViewer($this->getViewer())
->setMockCache($mock_map)
->withMockIDs(array_keys($mock_map))
->needInlineComments($this->needInlineComments)
->execute();
$image_groups = mgroup($all_images, 'getMockID');
foreach ($mocks as $mock) {
$mock_images = idx($image_groups, $mock->getID(), array());
$mock->attachAllImages($mock_images);
$active_images = mfilter($mock_images, 'getIsObsolete', true);
$mock->attachImages(msort($active_images, 'getSequence'));
}
}
private function loadCoverFiles(array $mocks) {
assert_instances_of($mocks, 'PholioMock');
$cover_file_phids = mpull($mocks, 'getCoverPHID');
$cover_files = id(new PhabricatorFileQuery())
->setViewer($this->getViewer())
->withPHIDs($cover_file_phids)
->execute();
$cover_files = mpull($cover_files, null, 'getPHID');
foreach ($mocks as $mock) {
$file = idx($cover_files, $mock->getCoverPHID());
if (!$file) {
$file = PhabricatorFile::loadBuiltin($this->getViewer(), 'missing.png');
}
$mock->attachCoverFile($file);
}
}
private function loadTokenCounts(array $mocks) {
assert_instances_of($mocks, 'PholioMock');
$phids = mpull($mocks, 'getPHID');
$counts = id(new PhabricatorTokenCountQuery())
->withObjectPHIDs($phids)
->execute();
foreach ($mocks as $mock) {
$mock->attachTokenCount(idx($counts, $mock->getPHID(), 0));
}
}
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationPholio';
+ }
+
}
diff --git a/src/applications/phortune/query/PhortuneAccountQuery.php b/src/applications/phortune/query/PhortuneAccountQuery.php
index ca48f78767..6feb7aace3 100644
--- a/src/applications/phortune/query/PhortuneAccountQuery.php
+++ b/src/applications/phortune/query/PhortuneAccountQuery.php
@@ -1,98 +1,103 @@
<?php
final class PhortuneAccountQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $memberPHIDs;
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;
}
protected function loadPage() {
$table = new PhortuneAccount();
$conn = $table->establishConnection('r');
$rows = queryfx_all(
$conn,
'SELECT a.* FROM %T a %Q %Q %Q %Q',
$table->getTableName(),
$this->buildJoinClause($conn),
$this->buildWhereClause($conn),
$this->buildOrderClause($conn),
$this->buildLimitClause($conn));
return $table->loadAllFromArray($rows);
}
protected function willFilterPage(array $accounts) {
$query = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(mpull($accounts, 'getPHID'))
->withEdgeTypes(array(PhabricatorEdgeConfig::TYPE_ACCOUNT_HAS_MEMBER));
$query->execute();
foreach ($accounts as $account) {
$member_phids = $query->getDestinationPHIDs(array($account->getPHID()));
$account->attachMemberPHIDs($member_phids);
}
return $accounts;
}
private function buildWhereClause(AphrontDatabaseConnection $conn) {
$where = array();
$where[] = $this->buildPagingClause($conn);
if ($this->ids) {
$where[] = qsprintf(
$conn,
'a.id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn,
'a.phid IN (%Ls)',
$this->phids);
}
if ($this->memberPHIDs) {
$where[] = qsprintf(
$conn,
'm.dst IN (%Ls)',
$this->memberPHIDs);
}
return $this->formatWhereClause($where);
}
private function buildJoinClause(AphrontDatabaseConnection $conn) {
$joins = array();
if ($this->memberPHIDs) {
$joins[] = qsprintf(
$conn,
'LEFT JOIN %T m ON a.phid = m.src AND m.type = %d',
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
PhabricatorEdgeConfig::TYPE_ACCOUNT_HAS_MEMBER);
}
return implode(' ', $joins);
}
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationPhortune';
+ }
+
}
diff --git a/src/applications/phortune/query/PhortunePaymentMethodQuery.php b/src/applications/phortune/query/PhortunePaymentMethodQuery.php
index 552845bfaa..7623af9e1a 100644
--- a/src/applications/phortune/query/PhortunePaymentMethodQuery.php
+++ b/src/applications/phortune/query/PhortunePaymentMethodQuery.php
@@ -1,113 +1,118 @@
<?php
final class PhortunePaymentMethodQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $accountPHIDs;
const STATUS_ANY = 'status-any';
const STATUS_OPEN = 'status-open';
private $status = self::STATUS_ANY;
public function withIDs(array $ids) {
$this->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 withStatus($status) {
$this->status = $status;
return $this;
}
protected function loadPage() {
$table = new PhortunePaymentMethod();
$conn = $table->establishConnection('r');
$rows = queryfx_all(
$conn,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn),
$this->buildOrderClause($conn),
$this->buildLimitClause($conn));
return $table->loadAllFromArray($rows);
}
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]);
continue;
}
$method->attachAccount($account);
}
return $methods;
}
private function buildWhereClause(AphrontDatabaseConnection $conn) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn,
'phid IN (%Ls)',
$this->phids);
}
if ($this->accountPHIDs) {
$where[] = qsprintf(
$conn,
'accountPHID IN (%Ls)',
$this->accountPHIDs);
}
switch ($this->status) {
case self::STATUS_ANY;
break;
case self::STATUS_OPEN:
$where[] = qsprintf(
$conn,
'status in (%Ls)',
array(
PhortunePaymentMethod::STATUS_ACTIVE,
PhortunePaymentMethod::STATUS_FAILED,
));
break;
default:
throw new Exception("Unknown status '{$this->status}'!");
}
$where[] = $this->buildPagingClause($conn);
return $this->formatWhereClause($where);
}
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationPhortune';
+ }
+
}
diff --git a/src/applications/phortune/query/PhortuneProductQuery.php b/src/applications/phortune/query/PhortuneProductQuery.php
index e87518c019..28c17ce2cf 100644
--- a/src/applications/phortune/query/PhortuneProductQuery.php
+++ b/src/applications/phortune/query/PhortuneProductQuery.php
@@ -1,56 +1,60 @@
<?php
final class PhortuneProductQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
protected function loadPage() {
$table = new PhortuneProduct();
$conn = $table->establishConnection('r');
$rows = queryfx_all(
$conn,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn),
$this->buildOrderClause($conn),
$this->buildLimitClause($conn));
return $table->loadAllFromArray($rows);
}
private function buildWhereClause(AphrontDatabaseConnection $conn) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn,
'phid IN (%Ls)',
$this->phids);
}
$where[] = $this->buildPagingClause($conn);
return $this->formatWhereClause($where);
}
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationPhortune';
+ }
+
}
diff --git a/src/applications/phrequent/query/PhrequentUserTimeQuery.php b/src/applications/phrequent/query/PhrequentUserTimeQuery.php
index dedcce86d0..190be7652b 100644
--- a/src/applications/phrequent/query/PhrequentUserTimeQuery.php
+++ b/src/applications/phrequent/query/PhrequentUserTimeQuery.php
@@ -1,305 +1,310 @@
<?php
final class PhrequentUserTimeQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
const ORDER_ID_ASC = 0;
const ORDER_ID_DESC = 1;
const ORDER_STARTED_ASC = 2;
const ORDER_STARTED_DESC = 3;
const ORDER_ENDED_ASC = 4;
const ORDER_ENDED_DESC = 5;
const ORDER_DURATION_ASC = 6;
const ORDER_DURATION_DESC = 7;
const ENDED_YES = 0;
const ENDED_NO = 1;
const ENDED_ALL = 2;
private $userPHIDs;
private $objectPHIDs;
private $order = self::ORDER_ID_ASC;
private $ended = self::ENDED_ALL;
private $needPreemptingEvents;
public function withUserPHIDs($user_phids) {
$this->userPHIDs = $user_phids;
return $this;
}
public function withObjectPHIDs($object_phids) {
$this->objectPHIDs = $object_phids;
return $this;
}
public function withEnded($ended) {
$this->ended = $ended;
return $this;
}
public function setOrder($order) {
$this->order = $order;
return $this;
}
public function needPreemptingEvents($need_events) {
$this->needPreemptingEvents = $need_events;
return $this;
}
private function buildWhereClause(AphrontDatabaseConnection $conn) {
$where = array();
if ($this->userPHIDs) {
$where[] = qsprintf(
$conn,
'userPHID IN (%Ls)',
$this->userPHIDs);
}
if ($this->objectPHIDs) {
$where[] = qsprintf(
$conn,
'objectPHID IN (%Ls)',
$this->objectPHIDs);
}
switch ($this->ended) {
case self::ENDED_ALL:
break;
case self::ENDED_YES:
$where[] = qsprintf(
$conn,
'dateEnded IS NOT NULL');
break;
case self::ENDED_NO:
$where[] = qsprintf(
$conn,
'dateEnded IS NULL');
break;
default:
throw new Exception("Unknown ended '{$this->ended}'!");
}
$where[] = $this->buildPagingClause($conn);
return $this->formatWhereClause($where);
}
protected function getPagingColumn() {
switch ($this->order) {
case self::ORDER_ID_ASC:
case self::ORDER_ID_DESC:
return 'id';
case self::ORDER_STARTED_ASC:
case self::ORDER_STARTED_DESC:
return 'dateStarted';
case self::ORDER_ENDED_ASC:
case self::ORDER_ENDED_DESC:
return 'dateEnded';
case self::ORDER_DURATION_ASC:
case self::ORDER_DURATION_DESC:
return 'COALESCE(dateEnded, UNIX_TIMESTAMP()) - dateStarted';
default:
throw new Exception("Unknown order '{$this->order}'!");
}
}
protected function getPagingValue($result) {
switch ($this->order) {
case self::ORDER_ID_ASC:
case self::ORDER_ID_DESC:
return $result->getID();
case self::ORDER_STARTED_ASC:
case self::ORDER_STARTED_DESC:
return $result->getDateStarted();
case self::ORDER_ENDED_ASC:
case self::ORDER_ENDED_DESC:
return $result->getDateEnded();
case self::ORDER_DURATION_ASC:
case self::ORDER_DURATION_DESC:
return ($result->getDateEnded() || time()) - $result->getDateStarted();
default:
throw new Exception("Unknown order '{$this->order}'!");
}
}
protected function getReversePaging() {
switch ($this->order) {
case self::ORDER_ID_ASC:
case self::ORDER_STARTED_ASC:
case self::ORDER_ENDED_ASC:
case self::ORDER_DURATION_ASC:
return true;
case self::ORDER_ID_DESC:
case self::ORDER_STARTED_DESC:
case self::ORDER_ENDED_DESC:
case self::ORDER_DURATION_DESC:
return false;
default:
throw new Exception("Unknown order '{$this->order}'!");
}
}
protected function loadPage() {
$usertime = new PhrequentUserTime();
$conn = $usertime->establishConnection('r');
$data = queryfx_all(
$conn,
'SELECT usertime.* FROM %T usertime %Q %Q %Q',
$usertime->getTableName(),
$this->buildWhereClause($conn),
$this->buildOrderClause($conn),
$this->buildLimitClause($conn));
return $usertime->loadAllFromArray($data);
}
protected function didFilterPage(array $page) {
if ($this->needPreemptingEvents) {
$usertime = new PhrequentUserTime();
$conn_r = $usertime->establishConnection('r');
$preempt = array();
foreach ($page as $event) {
$preempt[] = qsprintf(
$conn_r,
'(userPHID = %s AND
(dateStarted BETWEEN %d AND %d) AND
(dateEnded IS NULL OR dateEnded > %d))',
$event->getUserPHID(),
$event->getDateStarted(),
nonempty($event->getDateEnded(), PhabricatorTime::getNow()),
$event->getDateStarted());
}
$preempting_events = queryfx_all(
$conn_r,
'SELECT * FROM %T WHERE %Q ORDER BY dateStarted ASC, id ASC',
$usertime->getTableName(),
implode(' OR ', $preempt));
$preempting_events = $usertime->loadAllFromArray($preempting_events);
$preempting_events = mgroup($preempting_events, 'getUserPHID');
foreach ($page as $event) {
$e_start = $event->getDateStarted();
$e_end = $event->getDateEnded();
$select = array();
$user_events = idx($preempting_events, $event->getUserPHID(), array());
foreach ($user_events as $u_event) {
if ($u_event->getID() == $event->getID()) {
// Don't allow an event to preempt itself.
continue;
}
$u_start = $u_event->getDateStarted();
$u_end = $u_event->getDateEnded();
if (($u_start >= $e_start) && ($u_end <= $e_end) &&
($u_end === null || $u_end > $e_start)) {
$select[] = $u_event;
}
}
$event->attachPreemptingEvents($select);
}
}
return $page;
}
/* -( Helper Functions ) --------------------------------------------------- */
public static function getEndedSearchOptions() {
return array(
self::ENDED_ALL => pht('All'),
self::ENDED_NO => pht('No'),
self::ENDED_YES => pht('Yes'));
}
public static function getOrderSearchOptions() {
return array(
self::ORDER_STARTED_ASC => pht('by furthest start date'),
self::ORDER_STARTED_DESC => pht('by nearest start date'),
self::ORDER_ENDED_ASC => pht('by furthest end date'),
self::ORDER_ENDED_DESC => pht('by nearest end date'),
self::ORDER_DURATION_ASC => pht('by smallest duration'),
self::ORDER_DURATION_DESC => pht('by largest duration'));
}
public static function getUserTotalObjectsTracked(
PhabricatorUser $user) {
$usertime_dao = new PhrequentUserTime();
$conn = $usertime_dao->establishConnection('r');
$count = queryfx_one(
$conn,
'SELECT COUNT(usertime.id) N FROM %T usertime '.
'WHERE usertime.userPHID = %s '.
'AND usertime.dateEnded IS NULL',
$usertime_dao->getTableName(),
$user->getPHID());
return $count['N'];
}
public static function isUserTrackingObject(
PhabricatorUser $user,
$phid) {
$usertime_dao = new PhrequentUserTime();
$conn = $usertime_dao->establishConnection('r');
$count = queryfx_one(
$conn,
'SELECT COUNT(usertime.id) N FROM %T usertime '.
'WHERE usertime.userPHID = %s '.
'AND usertime.objectPHID = %s '.
'AND usertime.dateEnded IS NULL',
$usertime_dao->getTableName(),
$user->getPHID(),
$phid);
return $count['N'] > 0;
}
public static function getUserTimeSpentOnObject(
PhabricatorUser $user,
$phid) {
$usertime_dao = new PhrequentUserTime();
$conn = $usertime_dao->establishConnection('r');
// First calculate all the time spent where the
// usertime blocks have ended.
$sum_ended = queryfx_one(
$conn,
'SELECT SUM(usertime.dateEnded - usertime.dateStarted) N '.
'FROM %T usertime '.
'WHERE usertime.userPHID = %s '.
'AND usertime.objectPHID = %s '.
'AND usertime.dateEnded IS NOT NULL',
$usertime_dao->getTableName(),
$user->getPHID(),
$phid);
// Now calculate the time spent where the usertime
// blocks have not yet ended.
$sum_not_ended = queryfx_one(
$conn,
'SELECT SUM(UNIX_TIMESTAMP() - usertime.dateStarted) N '.
'FROM %T usertime '.
'WHERE usertime.userPHID = %s '.
'AND usertime.objectPHID = %s '.
'AND usertime.dateEnded IS NULL',
$usertime_dao->getTableName(),
$user->getPHID(),
$phid);
return $sum_ended['N'] + $sum_not_ended['N'];
}
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationPhrequent';
+ }
+
}
diff --git a/src/applications/phriction/phid/PhrictionPHIDTypeDocument.php b/src/applications/phriction/phid/PhrictionPHIDTypeDocument.php
index 554457bba0..e0ebcd92a2 100644
--- a/src/applications/phriction/phid/PhrictionPHIDTypeDocument.php
+++ b/src/applications/phriction/phid/PhrictionPHIDTypeDocument.php
@@ -1,56 +1,53 @@
<?php
final class PhrictionPHIDTypeDocument extends PhabricatorPHIDType {
const TYPECONST = 'WIKI';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Wiki Document');
}
public function newObject() {
return new PhrictionDocument();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhrictionDocumentQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$document = $objects[$phid];
$content = $document->getContent();
$title = $content->getTitle();
$slug = $document->getSlug();
$status = $document->getStatus();
$handle->setName($title);
$handle->setURI(PhrictionDocument::getSlugURI($slug));
if ($status != PhrictionDocumentStatus::STATUS_EXISTS) {
$handle->setStatus(PhabricatorObjectHandleStatus::STATUS_CLOSED);
}
}
}
public function canLoadNamedObject($name) {
return false;
}
}
diff --git a/src/applications/phriction/query/PhrictionDocumentQuery.php b/src/applications/phriction/query/PhrictionDocumentQuery.php
index a45d701ed5..5f2b3b1b6f 100644
--- a/src/applications/phriction/query/PhrictionDocumentQuery.php
+++ b/src/applications/phriction/query/PhrictionDocumentQuery.php
@@ -1,188 +1,193 @@
<?php
/**
* @group phriction
*/
final class PhrictionDocumentQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $slugs;
private $status = 'status-any';
const STATUS_ANY = 'status-any';
const STATUS_OPEN = 'status-open';
const STATUS_NONSTUB = 'status-nonstub';
private $order = 'order-created';
const ORDER_CREATED = 'order-created';
const ORDER_UPDATED = 'order-updated';
public function withIDs(array $ids) {
$this->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 withStatus($status) {
$this->status = $status;
return $this;
}
public function setOrder($order) {
$this->order = $order;
return $this;
}
protected function loadPage() {
$document = new PhrictionDocument();
$conn_r = $document->establishConnection('r');
$rows = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$document->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $document->loadAllFromArray($rows);
}
protected function willFilterPage(array $documents) {
$contents = id(new PhrictionContent())->loadAllWhere(
'id IN (%Ld)',
mpull($documents, 'getContentID'));
foreach ($documents as $key => $document) {
$content_id = $document->getContentID();
if (empty($contents[$content_id])) {
unset($documents[$key]);
continue;
}
$document->attachContent($contents[$content_id]);
}
foreach ($documents as $document) {
$document->attachProject(null);
}
$project_slugs = array();
foreach ($documents as $key => $document) {
$slug = $document->getSlug();
if (!PhrictionDocument::isProjectSlug($slug)) {
continue;
}
$project_slugs[$key] = PhrictionDocument::getProjectSlugIdentifier($slug);
}
if ($project_slugs) {
$projects = id(new PhabricatorProjectQuery())
->setViewer($this->getViewer())
->withPhrictionSlugs($project_slugs)
->execute();
$projects = mpull($projects, null, 'getPhrictionSlug');
foreach ($documents as $key => $document) {
$slug = idx($project_slugs, $key);
if ($slug) {
$project = idx($projects, $slug);
if (!$project) {
unset($documents[$key]);
continue;
}
$document->attachProject($project);
}
}
}
return $documents;
}
protected function buildWhereClause(AphrontDatabaseConnection $conn) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn,
'phid IN (%Ls)',
$this->phids);
}
if ($this->slugs) {
$where[] = qsprintf(
$conn,
'slug IN (%Ls)',
$this->slugs);
}
switch ($this->status) {
case self::STATUS_OPEN:
$where[] = qsprintf(
$conn,
'status NOT IN (%Ld)',
array(
PhrictionDocumentStatus::STATUS_DELETED,
PhrictionDocumentStatus::STATUS_MOVED,
PhrictionDocumentStatus::STATUS_STUB,
));
break;
case self::STATUS_NONSTUB:
$where[] = qsprintf(
$conn,
'status NOT IN (%Ld)',
array(
PhrictionDocumentStatus::STATUS_MOVED,
PhrictionDocumentStatus::STATUS_STUB,
));
break;
case self::STATUS_ANY:
break;
default:
throw new Exception("Unknown status '{$this->status}'!");
}
$where[] = $this->buildPagingClause($conn);
return $this->formatWhereClause($where);
}
protected function getPagingColumn() {
switch ($this->order) {
case self::ORDER_CREATED:
return 'id';
case self::ORDER_UPDATED:
return 'contentID';
default:
throw new Exception("Unknown order '{$this->order}'!");
}
}
protected function getPagingValue($result) {
switch ($this->order) {
case self::ORDER_CREATED:
return $result->getID();
case self::ORDER_UPDATED:
return $result->getContentID();
default:
throw new Exception("Unknown order '{$this->order}'!");
}
}
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationPhriction';
+ }
+
}
diff --git a/src/applications/policy/__tests__/PhabricatorPolicyAwareTestQuery.php b/src/applications/policy/__tests__/PhabricatorPolicyAwareTestQuery.php
index 9ff1e98ec6..dc6fc09f35 100644
--- a/src/applications/policy/__tests__/PhabricatorPolicyAwareTestQuery.php
+++ b/src/applications/policy/__tests__/PhabricatorPolicyAwareTestQuery.php
@@ -1,36 +1,40 @@
<?php
/**
* Configurable test query for implementing Policy unit tests.
*/
final class PhabricatorPolicyAwareTestQuery
extends PhabricatorPolicyAwareQuery {
private $results;
private $offset = 0;
public function setResults(array $results) {
$this->results = $results;
return $this;
}
protected function willExecute() {
$this->offset = 0;
}
protected function loadPage() {
if ($this->getRawResultLimit()) {
return array_slice(
$this->results,
$this->offset,
$this->getRawResultLimit());
} else {
return array_slice($this->results, $this->offset);
}
}
public function nextPage(array $page) {
$this->offset += count($page);
}
+ public function getQueryApplicationClass() {
+ return null;
+ }
+
}
diff --git a/src/applications/policy/__tests__/PhabricatorPolicyTestCase.php b/src/applications/policy/__tests__/PhabricatorPolicyTestCase.php
index b8b0236b52..6dcbc6a78d 100644
--- a/src/applications/policy/__tests__/PhabricatorPolicyTestCase.php
+++ b/src/applications/policy/__tests__/PhabricatorPolicyTestCase.php
@@ -1,292 +1,308 @@
<?php
final class PhabricatorPolicyTestCase extends PhabricatorTestCase {
/**
* Verify that any user can view an object with POLICY_PUBLIC.
*/
public function testPublicPolicyEnabled() {
$env = PhabricatorEnv::beginScopedEnv();
$env->overrideEnvConfig('policy.allow-public', true);
$this->expectVisibility(
$this->buildObject(PhabricatorPolicies::POLICY_PUBLIC),
array(
'public' => true,
'user' => true,
'admin' => true,
),
'Public Policy (Enabled in Config)');
}
/**
* Verify that POLICY_PUBLIC is interpreted as POLICY_USER when public
* policies are disallowed.
*/
public function testPublicPolicyDisabled() {
$env = PhabricatorEnv::beginScopedEnv();
$env->overrideEnvConfig('policy.allow-public', false);
$this->expectVisibility(
$this->buildObject(PhabricatorPolicies::POLICY_PUBLIC),
array(
'public' => false,
'user' => true,
'admin' => true,
),
'Public Policy (Disabled in Config)');
}
/**
* Verify that any logged-in user can view an object with POLICY_USER, but
* logged-out users can not.
*/
public function testUsersPolicy() {
$this->expectVisibility(
$this->buildObject(PhabricatorPolicies::POLICY_USER),
array(
'public' => false,
'user' => true,
'admin' => true,
),
'User Policy');
}
/**
* Verify that only administrators can view an object with POLICY_ADMIN.
*/
public function testAdminPolicy() {
$this->expectVisibility(
$this->buildObject(PhabricatorPolicies::POLICY_ADMIN),
array(
'public' => false,
'user' => false,
'admin' => true,
),
'Admin Policy');
}
/**
* Verify that no one can view an object with POLICY_NOONE.
*/
public function testNoOnePolicy() {
$this->expectVisibility(
$this->buildObject(PhabricatorPolicies::POLICY_NOONE),
array(
'public' => false,
'user' => false,
'admin' => false,
),
'No One Policy');
}
/**
* Test offset-based filtering.
*/
public function testOffsets() {
$results = array(
$this->buildObject(PhabricatorPolicies::POLICY_NOONE),
$this->buildObject(PhabricatorPolicies::POLICY_NOONE),
$this->buildObject(PhabricatorPolicies::POLICY_NOONE),
$this->buildObject(PhabricatorPolicies::POLICY_USER),
$this->buildObject(PhabricatorPolicies::POLICY_USER),
$this->buildObject(PhabricatorPolicies::POLICY_USER),
);
$query = new PhabricatorPolicyAwareTestQuery();
$query->setResults($results);
$query->setViewer($this->buildUser('user'));
$this->assertEqual(
3,
count($query->setLimit(3)->setOffset(0)->execute()),
'Invisible objects are ignored.');
$this->assertEqual(
0,
count($query->setLimit(3)->setOffset(3)->execute()),
'Offset pages through visible objects only.');
$this->assertEqual(
2,
count($query->setLimit(3)->setOffset(1)->execute()),
'Offsets work correctly.');
$this->assertEqual(
2,
count($query->setLimit(0)->setOffset(1)->execute()),
'Offset with no limit works.');
}
/**
* Test limits.
*/
public function testLimits() {
$results = array(
$this->buildObject(PhabricatorPolicies::POLICY_USER),
$this->buildObject(PhabricatorPolicies::POLICY_USER),
$this->buildObject(PhabricatorPolicies::POLICY_USER),
$this->buildObject(PhabricatorPolicies::POLICY_USER),
$this->buildObject(PhabricatorPolicies::POLICY_USER),
$this->buildObject(PhabricatorPolicies::POLICY_USER),
);
$query = new PhabricatorPolicyAwareTestQuery();
$query->setResults($results);
$query->setViewer($this->buildUser('user'));
$this->assertEqual(
3,
count($query->setLimit(3)->setOffset(0)->execute()),
'Limits work.');
$this->assertEqual(
2,
count($query->setLimit(3)->setOffset(4)->execute()),
'Limit + offset work.');
}
/**
* Test that omnipotent users bypass policies.
*/
public function testOmnipotence() {
$results = array(
$this->buildObject(PhabricatorPolicies::POLICY_NOONE),
);
$query = new PhabricatorPolicyAwareTestQuery();
$query->setResults($results);
$query->setViewer(PhabricatorUser::getOmnipotentUser());
$this->assertEqual(
1,
count($query->execute()));
}
/**
* Test that invalid policies reject viewers of all types.
*/
public function testRejectInvalidPolicy() {
$invalid_policy = "the duck goes quack";
$object = $this->buildObject($invalid_policy);
$this->expectVisibility(
$object = $this->buildObject($invalid_policy),
array(
'public' => false,
'user' => false,
'admin' => false,
),
'Invalid Policy');
}
/**
* An omnipotent user should be able to see even objects with invalid
* policies.
*/
public function testInvalidPolicyVisibleByOmnipotentUser() {
$invalid_policy = "the cow goes moo";
$object = $this->buildObject($invalid_policy);
$results = array(
$object,
);
$query = new PhabricatorPolicyAwareTestQuery();
$query->setResults($results);
$query->setViewer(PhabricatorUser::getOmnipotentUser());
$this->assertEqual(
1,
count($query->execute()));
}
+ public function testAllQueriesBelongToActualApplications() {
+ $queries = id(new PhutilSymbolLoader())
+ ->setAncestorClass('PhabricatorPolicyAwareQuery')
+ ->loadObjects();
+
+ foreach ($queries as $qclass => $query) {
+ $class = $query->getQueryApplicationClass();
+ if (!$class) {
+ continue;
+ }
+ $this->assertEqual(
+ true,
+ class_exists($class),
+ "Application class '{$class}' for query '{$qclass}'");
+ }
+ }
/**
* Test an object for visibility across multiple user specifications.
*/
private function expectVisibility(
PhabricatorPolicyTestObject $object,
array $map,
$description) {
foreach ($map as $spec => $expect) {
$viewer = $this->buildUser($spec);
$query = new PhabricatorPolicyAwareTestQuery();
$query->setResults(array($object));
$query->setViewer($viewer);
$caught = null;
try {
$result = $query->executeOne();
} catch (PhabricatorPolicyException $ex) {
$caught = $ex;
}
if ($expect) {
$this->assertEqual(
$object,
$result,
"{$description} with user {$spec} should succeed.");
} else {
$this->assertEqual(
true,
$caught instanceof PhabricatorPolicyException,
"{$description} with user {$spec} should fail.");
}
}
}
/**
* Build a test object to spec.
*/
private function buildObject($policy) {
$object = new PhabricatorPolicyTestObject();
$object->setCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
));
$object->setPolicies(
array(
PhabricatorPolicyCapability::CAN_VIEW => $policy,
));
return $object;
}
/**
* Build a test user to spec.
*/
private function buildUser($spec) {
$user = new PhabricatorUser();
switch ($spec) {
case 'public':
break;
case 'user':
$user->setPHID(1);
break;
case 'admin':
$user->setPHID(1);
$user->setIsAdmin(true);
break;
default:
throw new Exception("Unknown user spec '{$spec}'.");
}
return $user;
}
}
diff --git a/src/applications/policy/phid/PhabricatorPolicyPHIDTypePolicy.php b/src/applications/policy/phid/PhabricatorPolicyPHIDTypePolicy.php
index 27f693d8a8..8941de2e5b 100644
--- a/src/applications/policy/phid/PhabricatorPolicyPHIDTypePolicy.php
+++ b/src/applications/policy/phid/PhabricatorPolicyPHIDTypePolicy.php
@@ -1,48 +1,45 @@
<?php
final class PhabricatorPolicyPHIDTypePolicy
extends PhabricatorPHIDType {
const TYPECONST = 'PLCY';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Policy');
}
public function newObject() {
return new PhabricatorPolicy();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorPolicyQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$policy = $objects[$phid];
$handle->setName($policy->getName());
$handle->setURI($policy->getHref());
}
}
public function canLoadNamedObject($name) {
return false;
}
}
diff --git a/src/applications/policy/query/PhabricatorPolicyQuery.php b/src/applications/policy/query/PhabricatorPolicyQuery.php
index 9a078abf3e..246741786d 100644
--- a/src/applications/policy/query/PhabricatorPolicyQuery.php
+++ b/src/applications/policy/query/PhabricatorPolicyQuery.php
@@ -1,227 +1,232 @@
<?php
final class PhabricatorPolicyQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $object;
private $phids;
public function setObject(PhabricatorPolicyInterface $object) {
$this->object = $object;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public static function loadPolicies(
PhabricatorUser $viewer,
PhabricatorPolicyInterface $object) {
$results = array();
$map = array();
foreach ($object->getCapabilities() as $capability) {
$map[$capability] = $object->getPolicy($capability);
}
$policies = id(new PhabricatorPolicyQuery())
->setViewer($viewer)
->withPHIDs($map)
->execute();
foreach ($map as $capability => $phid) {
$results[$capability] = $policies[$phid];
}
return $results;
}
public static function renderPolicyDescriptions(
PhabricatorUser $viewer,
PhabricatorPolicyInterface $object,
$icon = false) {
$policies = self::loadPolicies($viewer, $object);
foreach ($policies as $capability => $policy) {
$policies[$capability] = $policy->renderDescription($icon);
}
return $policies;
}
public function loadPage() {
if ($this->object && $this->phids) {
throw new Exception(
"You can not issue a policy query with both setObject() and ".
"setPHIDs().");
} else if ($this->object) {
$phids = $this->loadObjectPolicyPHIDs();
} else {
$phids = $this->phids;
}
$phids = array_fuse($phids);
$results = array();
// First, load global policies.
foreach ($this->getGlobalPolicies() as $phid => $policy) {
if (isset($phids[$phid])) {
$results[$phid] = $policy;
unset($phids[$phid]);
}
}
// If we still need policies, we're going to have to fetch data. Bucket
// the remaining policies into rule-based policies and handle-based
// policies.
if ($phids) {
$rule_policies = array();
$handle_policies = array();
foreach ($phids as $phid) {
$phid_type = phid_get_type($phid);
if ($phid_type == PhabricatorPolicyPHIDTypePolicy::TYPECONST) {
$rule_policies[$phid] = $phid;
} else {
$handle_policies[$phid] = $phid;
}
}
if ($handle_policies) {
$handles = id(new PhabricatorHandleQuery())
->setViewer($this->getViewer())
->withPHIDs($handle_policies)
->execute();
foreach ($handle_policies as $phid) {
$results[$phid] = PhabricatorPolicy::newFromPolicyAndHandle(
$phid,
$handles[$phid]);
}
}
if ($rule_policies) {
$rules = id(new PhabricatorPolicy())->loadAllWhere(
'phid IN (%Ls)',
$rule_policies);
$results += mpull($rules, null, 'getPHID');
}
}
$results = msort($results, 'getSortKey');
return $results;
}
public static function isGlobalPolicy($policy) {
$globalPolicies = self::getGlobalPolicies();
if (isset($globalPolicies[$policy])) {
return true;
}
return false;
}
public static function getGlobalPolicy($policy) {
if (!self::isGlobalPolicy($policy)) {
throw new Exception("Policy '{$policy}' is not a global policy!");
}
return idx(self::getGlobalPolicies(), $policy);
}
private static function getGlobalPolicies() {
static $constants = array(
PhabricatorPolicies::POLICY_PUBLIC,
PhabricatorPolicies::POLICY_USER,
PhabricatorPolicies::POLICY_ADMIN,
PhabricatorPolicies::POLICY_NOONE,
);
$results = array();
foreach ($constants as $constant) {
$results[$constant] = id(new PhabricatorPolicy())
->setType(PhabricatorPolicyType::TYPE_GLOBAL)
->setPHID($constant)
->setName(self::getGlobalPolicyName($constant))
->setShortName(self::getGlobalPolicyShortName($constant))
->makeEphemeral();
}
return $results;
}
private static function getGlobalPolicyName($policy) {
switch ($policy) {
case PhabricatorPolicies::POLICY_PUBLIC:
return pht('Public (No Login Required)');
case PhabricatorPolicies::POLICY_USER:
return pht('All Users');
case PhabricatorPolicies::POLICY_ADMIN:
return pht('Administrators');
case PhabricatorPolicies::POLICY_NOONE:
return pht('No One');
default:
return pht('Unknown Policy');
}
}
private static function getGlobalPolicyShortName($policy) {
switch ($policy) {
case PhabricatorPolicies::POLICY_PUBLIC:
return pht('Public');
default:
return null;
}
}
private function loadObjectPolicyPHIDs() {
$phids = array();
$viewer = $this->getViewer();
if ($viewer->getPHID()) {
$projects = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->withMemberPHIDs(array($viewer->getPHID()))
->execute();
foreach ($projects as $project) {
$phids[] = $project->getPHID();
}
}
$capabilities = $this->object->getCapabilities();
foreach ($capabilities as $capability) {
$policy = $this->object->getPolicy($capability);
if (!$policy) {
continue;
}
$phids[] = $policy;
}
// If this install doesn't have "Public" enabled, don't include it as an
// option unless the object already has a "Public" policy. In this case we
// retain the policy but enforce it as though it was "All Users".
$show_public = PhabricatorEnv::getEnvConfig('policy.allow-public');
foreach ($this->getGlobalPolicies() as $phid => $policy) {
if ($phid == PhabricatorPolicies::POLICY_PUBLIC) {
if (!$show_public) {
continue;
}
}
$phids[] = $phid;
}
return $phids;
}
protected function shouldDisablePolicyFiltering() {
// Policy filtering of policies is currently perilous and not required by
// the application.
return true;
}
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationPolicy';
+ }
+
}
diff --git a/src/applications/ponder/phid/PonderPHIDTypeAnswer.php b/src/applications/ponder/phid/PonderPHIDTypeAnswer.php
index ebe9c40fb6..be6b4d6779 100644
--- a/src/applications/ponder/phid/PonderPHIDTypeAnswer.php
+++ b/src/applications/ponder/phid/PonderPHIDTypeAnswer.php
@@ -1,45 +1,42 @@
<?php
final class PonderPHIDTypeAnswer extends PhabricatorPHIDType {
const TYPECONST = 'ANSW';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Answer');
}
public function newObject() {
return new PonderAnswer();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PonderAnswerQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$answer = $objects[$phid];
$id = $answer->getID();
$handle->setName("Answer {$id}");
$handle->setURI($answer->getURI());
}
}
}
diff --git a/src/applications/ponder/phid/PonderPHIDTypeQuestion.php b/src/applications/ponder/phid/PonderPHIDTypeQuestion.php
index 23f59095a7..2f42e91371 100644
--- a/src/applications/ponder/phid/PonderPHIDTypeQuestion.php
+++ b/src/applications/ponder/phid/PonderPHIDTypeQuestion.php
@@ -1,75 +1,72 @@
<?php
final class PonderPHIDTypeQuestion extends PhabricatorPHIDType {
const TYPECONST = 'QUES';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Question');
}
public function newObject() {
return new PonderQuestion();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PonderQuestionQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$question = $objects[$phid];
$id = $question->getID();
$handle->setName("Q{$id}");
$handle->setURI("/Q{$id}");
$handle->setFullName($question->getFullTitle());
}
}
public function canLoadNamedObject($name) {
return preg_match('/^Q\d*[1-9]\d*$/i', $name);
}
public function loadNamedObjects(
PhabricatorObjectQuery $query,
array $names) {
$id_map = array();
foreach ($names as $name) {
$id = (int)substr($name, 1);
$id_map[$id][] = $name;
}
$objects = id(new PonderQuestionQuery())
->setViewer($query->getViewer())
->withIDs(array_keys($id_map))
->execute();
$results = array();
foreach ($objects as $id => $object) {
foreach (idx($id_map, $id, array()) as $name) {
$results[$name] = $object;
}
}
return $results;
}
}
diff --git a/src/applications/ponder/query/PonderAnswerQuery.php b/src/applications/ponder/query/PonderAnswerQuery.php
index 66e532c6b6..df6e679b5a 100644
--- a/src/applications/ponder/query/PonderAnswerQuery.php
+++ b/src/applications/ponder/query/PonderAnswerQuery.php
@@ -1,126 +1,131 @@
<?php
final class PonderAnswerQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $authorPHIDs;
private $questionIDs;
private $needViewerVotes;
public function withIDs(array $ids) {
$this->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;
}
public function needViewerVotes($need_viewer_votes) {
$this->needViewerVotes = $need_viewer_votes;
return $this;
}
private function buildWhereClause($conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
if ($this->authorPHIDs) {
$where[] = qsprintf(
$conn_r,
'authorPHID IN (%Ls)',
$this->authorPHIDs);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
public function loadPage() {
$answer = new PonderAnswer();
$conn_r = $answer->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT a.* FROM %T a %Q %Q %Q',
$answer->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $answer->loadAllFromArray($data);
}
public 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);
}
if ($this->needViewerVotes) {
$viewer_phid = $this->getViewer()->getPHID();
$etype = PhabricatorEdgeConfig::TYPE_ANSWER_HAS_VOTING_USER;
$edges = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(mpull($answers, 'getPHID'))
->withDestinationPHIDs(array($viewer_phid))
->withEdgeTypes(array($etype))
->needEdgeData(true)
->execute();
foreach ($answers as $answer) {
$user_edge = idx(
$edges[$answer->getPHID()][$etype],
$viewer_phid,
array());
$answer->attachUserVote($viewer_phid, idx($user_edge, 'data', 0));
}
}
return $answers;
}
protected function getReversePaging() {
return true;
}
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationPonder';
+ }
+
}
diff --git a/src/applications/ponder/query/PonderQuestionQuery.php b/src/applications/ponder/query/PonderQuestionQuery.php
index e297fe06bb..eeec6b8071 100644
--- a/src/applications/ponder/query/PonderQuestionQuery.php
+++ b/src/applications/ponder/query/PonderQuestionQuery.php
@@ -1,197 +1,202 @@
<?php
final class PonderQuestionQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
const ORDER_CREATED = 'order-created';
const ORDER_HOTTEST = 'order-hottest';
private $ids;
private $phids;
private $authorPHIDs;
private $answererPHIDs;
private $order = self::ORDER_CREATED;
private $status = 'status-any';
const STATUS_ANY = 'status-any';
const STATUS_OPEN = 'status-open';
const STATUS_CLOSED = 'status-closed';
private $needAnswers;
private $needViewerVotes;
public function withIDs(array $ids) {
$this->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 withStatus($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 needViewerVotes($need_viewer_votes) {
$this->needViewerVotes = $need_viewer_votes;
return $this;
}
public function setOrder($order) {
$this->order = $order;
return $this;
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'q.id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'q.phid IN (%Ls)',
$this->phids);
}
if ($this->authorPHIDs) {
$where[] = qsprintf(
$conn_r,
'q.authorPHID IN (%Ls)',
$this->authorPHIDs);
}
if ($this->status) {
switch ($this->status) {
case self::STATUS_ANY:
break;
case self::STATUS_OPEN:
$where[] = qsprintf(
$conn_r,
'q.status = %d',
PonderQuestionStatus::STATUS_OPEN);
break;
case self::STATUS_CLOSED:
$where[] = qsprintf(
$conn_r,
'q.status = %d',
PonderQuestionStatus::STATUS_CLOSED);
break;
default:
throw new Exception("Unknown status query '{$this->status}'!");
}
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
private function buildOrderByClause(AphrontDatabaseConnection $conn_r) {
switch ($this->order) {
case self::ORDER_HOTTEST:
return qsprintf($conn_r, 'ORDER BY q.heat DESC, q.id DESC');
case self::ORDER_CREATED:
return qsprintf($conn_r, 'ORDER BY q.id DESC');
default:
throw new Exception("Unknown order '{$this->order}'!");
}
}
protected function loadPage() {
$question = new PonderQuestion();
$conn_r = $question->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT q.* FROM %T q %Q %Q %Q %Q',
$question->getTableName(),
$this->buildJoinsClause($conn_r),
$this->buildWhereClause($conn_r),
$this->buildOrderByClause($conn_r),
$this->buildLimitClause($conn_r));
return $question->loadAllFromArray($data);
}
public function willFilterPage(array $questions) {
if ($this->needAnswers) {
$aquery = id(new PonderAnswerQuery())
->setViewer($this->getViewer())
->withQuestionIDs(mpull($questions, 'getID'));
if ($this->needViewerVotes) {
$aquery->needViewerVotes($this->needViewerVotes);
}
$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->needViewerVotes) {
$viewer_phid = $this->getViewer()->getPHID();
$etype = PhabricatorEdgeConfig::TYPE_QUESTION_HAS_VOTING_USER;
$edges = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(mpull($questions, 'getPHID'))
->withDestinationPHIDs(array($viewer_phid))
->withEdgeTypes(array($etype))
->needEdgeData(true)
->execute();
foreach ($questions as $question) {
$user_edge = idx(
$edges[$question->getPHID()][$etype],
$viewer_phid,
array());
$question->attachUserVote($viewer_phid, idx($user_edge, 'data', 0));
}
}
return $questions;
}
private function buildJoinsClause(AphrontDatabaseConnection $conn_r) {
$joins = array();
if ($this->answererPHIDs) {
$answer_table = new PonderAnswer();
$joins[] = qsprintf(
$conn_r,
'JOIN %T a ON a.questionID = q.id AND a.authorPHID IN (%Ls)',
$answer_table->getTableName(),
$this->answererPHIDs);
}
return implode(' ', $joins);
}
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationPonder';
+ }
+
}
diff --git a/src/applications/project/phid/PhabricatorProjectPHIDTypeProject.php b/src/applications/project/phid/PhabricatorProjectPHIDTypeProject.php
index 5b7bbbe936..97a81b1d0c 100644
--- a/src/applications/project/phid/PhabricatorProjectPHIDTypeProject.php
+++ b/src/applications/project/phid/PhabricatorProjectPHIDTypeProject.php
@@ -1,52 +1,49 @@
<?php
final class PhabricatorProjectPHIDTypeProject extends PhabricatorPHIDType {
const TYPECONST = 'PROJ';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Project');
}
public function newObject() {
return new PhabricatorProject();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorProjectQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$project = $objects[$phid];
$name = $project->getName();
$id = $project->getID();
$handle->setName($name);
$handle->setObjectName('#'.rtrim($project->getPhrictionSlug(), '/'));
$handle->setURI("/project/view/{$id}/");
}
}
public function canLoadNamedObject($name) {
// TODO: We should be able to load named projects by hashtag, e.g. "#yolo".
return false;
}
}
diff --git a/src/applications/project/query/PhabricatorProjectQuery.php b/src/applications/project/query/PhabricatorProjectQuery.php
index 70bfa9d573..00025668c0 100644
--- a/src/applications/project/query/PhabricatorProjectQuery.php
+++ b/src/applications/project/query/PhabricatorProjectQuery.php
@@ -1,263 +1,268 @@
<?php
final class PhabricatorProjectQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $memberPHIDs;
private $slugs;
private $status = 'status-any';
const STATUS_ANY = 'status-any';
const STATUS_OPEN = 'status-open';
const STATUS_CLOSED = 'status-closed';
const STATUS_ACTIVE = 'status-active';
const STATUS_ARCHIVED = 'status-archived';
private $needMembers;
private $needProfiles;
public function withIDs(array $ids) {
$this->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 withMemberPHIDs(array $member_phids) {
$this->memberPHIDs = $member_phids;
return $this;
}
public function withPhrictionSlugs(array $slugs) {
$this->slugs = $slugs;
return $this;
}
public function needMembers($need_members) {
$this->needMembers = $need_members;
return $this;
}
public function needProfiles($need_profiles) {
$this->needProfiles = $need_profiles;
return $this;
}
protected function getPagingColumn() {
return 'name';
}
protected function getPagingValue($result) {
return $result->getName();
}
protected function getReversePaging() {
return true;
}
protected function loadPage() {
$table = new PhabricatorProject();
$conn_r = $table->establishConnection('r');
// NOTE: Because visibility checks for projects depend on whether or not
// the user is a project member, we always load their membership. If we're
// loading all members anyway we can piggyback on that; otherwise we
// do an explicit join.
$select_clause = '';
if (!$this->needMembers) {
$select_clause = ', vm.dst viewerIsMember';
}
$data = queryfx_all(
$conn_r,
'SELECT p.* %Q FROM %T p %Q %Q %Q %Q %Q',
$select_clause,
$table->getTableName(),
$this->buildJoinClause($conn_r),
$this->buildWhereClause($conn_r),
$this->buildGroupClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
$projects = $table->loadAllFromArray($data);
if ($projects) {
$viewer_phid = $this->getViewer()->getPHID();
if ($this->needMembers) {
$etype = PhabricatorEdgeConfig::TYPE_PROJ_MEMBER;
$members = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(mpull($projects, 'getPHID'))
->withEdgeTypes(array($etype))
->execute();
foreach ($projects as $project) {
$phid = $project->getPHID();
$project->attachMemberPHIDs(array_keys($members[$phid][$etype]));
$project->setIsUserMember(
$viewer_phid,
isset($members[$phid][$etype][$viewer_phid]));
}
} else {
foreach ($data as $row) {
$projects[$row['id']]->setIsUserMember(
$viewer_phid,
($row['viewerIsMember'] !== null));
}
}
}
return $projects;
}
protected function didFilterPage(array $projects) {
if ($this->needProfiles) {
$profiles = id(new PhabricatorProjectProfile())->loadAllWhere(
'projectPHID IN (%Ls)',
mpull($projects, 'getPHID'));
$profiles = mpull($profiles, null, 'getProjectPHID');
$default = null;
if ($profiles) {
$file_phids = mpull($profiles, 'getProfileImagePHID');
$files = id(new PhabricatorFileQuery())
->setParentQuery($this)
->setViewer($this->getViewer())
->withPHIDs($file_phids)
->execute();
$files = mpull($files, null, 'getPHID');
foreach ($profiles as $profile) {
$file = idx($files, $profile->getProfileImagePHID());
if (!$file) {
if (!$default) {
$default = PhabricatorFile::loadBuiltin(
$this->getViewer(),
'project.png');
}
$file = $default;
}
$profile->attachProfileImageFile($file);
}
}
foreach ($projects as $project) {
$profile = idx($profiles, $project->getPHID());
if (!$profile) {
if (!$default) {
$default = PhabricatorFile::loadBuiltin(
$this->getViewer(),
'project.png');
}
$profile = id(new PhabricatorProjectProfile())
->setProjectPHID($project->getPHID())
->attachProfileImageFile($default);
}
$project->attachProfile($profile);
}
}
return $projects;
}
private function buildWhereClause($conn_r) {
$where = array();
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(
"Unknown project status '{$this->status}'!");
}
$where[] = qsprintf(
$conn_r,
'status IN (%Ld)',
$filter);
}
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
if ($this->memberPHIDs) {
$where[] = qsprintf(
$conn_r,
'e.dst IN (%Ls)',
$this->memberPHIDs);
}
if ($this->slugs) {
$where[] = qsprintf(
$conn_r,
'phrictionSlug IN (%Ls)',
$this->slugs);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
private function buildGroupClause($conn_r) {
if ($this->memberPHIDs) {
return 'GROUP BY p.id';
} else {
return '';
}
}
private function buildJoinClause($conn_r) {
$joins = array();
if (!$this->needMembers) {
$joins[] = qsprintf(
$conn_r,
'LEFT JOIN %T vm ON vm.src = p.phid AND vm.type = %d AND vm.dst = %s',
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
PhabricatorEdgeConfig::TYPE_PROJ_MEMBER,
$this->getViewer()->getPHID());
}
if ($this->memberPHIDs) {
$joins[] = qsprintf(
$conn_r,
'JOIN %T e ON e.src = p.phid AND e.type = %d',
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
PhabricatorEdgeConfig::TYPE_PROJ_MEMBER);
}
return implode(' ', $joins);
}
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationProject';
+ }
+
}
diff --git a/src/applications/releeph/phid/ReleephPHIDTypeBranch.php b/src/applications/releeph/phid/ReleephPHIDTypeBranch.php
index 7106dfc9a1..3511a67c69 100644
--- a/src/applications/releeph/phid/ReleephPHIDTypeBranch.php
+++ b/src/applications/releeph/phid/ReleephPHIDTypeBranch.php
@@ -1,48 +1,45 @@
<?php
final class ReleephPHIDTypeBranch extends PhabricatorPHIDType {
const TYPECONST = 'REBR';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Releeph Branch');
}
public function newObject() {
return new ReleephBranch();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new ReleephBranchQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$branch = $objects[$phid];
$handle->setURI($branch->getURI());
$handle->setName($branch->getBasename());
$handle->setFullName($branch->getName());
}
}
public function canLoadNamedObject($name) {
return false;
}
}
diff --git a/src/applications/releeph/phid/ReleephPHIDTypeProject.php b/src/applications/releeph/phid/ReleephPHIDTypeProject.php
index f127b75849..e15994bf83 100644
--- a/src/applications/releeph/phid/ReleephPHIDTypeProject.php
+++ b/src/applications/releeph/phid/ReleephPHIDTypeProject.php
@@ -1,47 +1,44 @@
<?php
final class ReleephPHIDTypeProject extends PhabricatorPHIDType {
const TYPECONST = 'REPR';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Releeph Project');
}
public function newObject() {
return new ReleephProject();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new ReleephProjectQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$project = $objects[$phid];
$handle->setName($project->getName());
$handle->setURI($project->getURI());
}
}
public function canLoadNamedObject($name) {
return false;
}
}
diff --git a/src/applications/releeph/phid/ReleephPHIDTypeRequest.php b/src/applications/releeph/phid/ReleephPHIDTypeRequest.php
index a3529a612c..506585fce0 100644
--- a/src/applications/releeph/phid/ReleephPHIDTypeRequest.php
+++ b/src/applications/releeph/phid/ReleephPHIDTypeRequest.php
@@ -1,51 +1,48 @@
<?php
final class ReleephPHIDTypeRequest extends PhabricatorPHIDType {
const TYPECONST = 'RERQ';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Releeph Request');
}
public function newObject() {
return new ReleephRequest();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new ReleephRequestQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$request = $objects[$phid];
$id = $request->getID();
$title = $request->getSummaryForDisplay();
$handle->setURI("/RQ{$id}");
$handle->setName($title);
$handle->setFullName("RQ{$id}: {$title}");
}
}
public function canLoadNamedObject($name) {
return false;
}
}
diff --git a/src/applications/releeph/query/ReleephBranchQuery.php b/src/applications/releeph/query/ReleephBranchQuery.php
index c1ab888fea..5a0bf40d06 100644
--- a/src/applications/releeph/query/ReleephBranchQuery.php
+++ b/src/applications/releeph/query/ReleephBranchQuery.php
@@ -1,132 +1,137 @@
<?php
final class ReleephBranchQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $projectIDs;
const STATUS_ALL = 'status-all';
const STATUS_OPEN = 'status-open';
private $status = self::STATUS_ALL;
private $needCutPointCommits;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function needCutPointCommits($need_commits) {
$this->needCutPointCommits = $need_commits;
return $this;
}
public function withStatus($status) {
$this->status = $status;
return $this;
}
public function withProjectIDs(array $ids) {
$this->projectIDs = $ids;
return $this;
}
public function loadPage() {
$table = new ReleephBranch();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
public function willFilterPage(array $branches) {
$project_ids = mpull($branches, 'getReleephProjectID');
$projects = id(new ReleephProjectQuery())
->withIDs($project_ids)
->setViewer($this->getViewer())
->execute();
foreach ($branches as $key => $branch) {
$project_id = $project_ids[$key];
if (isset($projects[$project_id])) {
$branch->attachProject($projects[$project_id]);
} else {
unset($branches[$key]);
}
}
if ($this->needCutPointCommits) {
$commit_phids = mpull($branches, 'getCutPointCommitPHID');
$commits = id(new DiffusionCommitQuery())
->setViewer($this->getViewer())
->withPHIDs($commit_phids)
->execute();
$commits = mpull($commits, null, 'getPHID');
foreach ($branches as $branch) {
$commit = idx($commits, $branch->getCutPointCommitPHID());
$branch->attachCutPointCommit($commit);
}
}
return $branches;
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
if ($this->projectIDs) {
$where[] = qsprintf(
$conn_r,
'releephProjectID IN (%Ld)',
$this->projectIDs);
}
$status = $this->status;
switch ($status) {
case self::STATUS_ALL:
break;
case self::STATUS_OPEN:
$where[] = qsprintf(
$conn_r,
'isActive = 1');
break;
default:
throw new Exception("Unknown status constant '{$status}'!");
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationReleeph';
+ }
+
}
diff --git a/src/applications/releeph/query/ReleephProjectQuery.php b/src/applications/releeph/query/ReleephProjectQuery.php
index 5a1b09bf4a..2823633499 100644
--- a/src/applications/releeph/query/ReleephProjectQuery.php
+++ b/src/applications/releeph/query/ReleephProjectQuery.php
@@ -1,130 +1,134 @@
<?php
final class ReleephProjectQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $active;
private $ids;
private $phids;
private $needRepositories;
private $order = 'order-id';
const ORDER_ID = 'order-id';
const ORDER_NAME = 'order-name';
public function withActive($active) {
$this->active = $active;
return $this;
}
public function setOrder($order) {
$this->order = $order;
return $this;
}
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function loadPage() {
$table = new ReleephProject();
$conn_r = $table->establishConnection('r');
$rows = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($rows);
}
public function willFilterPage(array $projects) {
assert_instances_of($projects, 'ReleephProject');
$repository_phids = mpull($projects, 'getRepositoryPHID');
$repositories = id(new PhabricatorRepositoryQuery())
->setViewer($this->getViewer())
->withPHIDs($repository_phids)
->execute();
$repositories = mpull($repositories, null, 'getPHID');
foreach ($projects as $key => $project) {
$repo = idx($repositories, $project->getRepositoryPHID());
if (!$repo) {
unset($projects[$key]);
continue;
}
$project->attachRepository($repo);
}
return $projects;
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->active !== null) {
$where[] = qsprintf(
$conn_r,
'isActive = %d',
(int)$this->active);
}
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ls)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
protected function getReversePaging() {
switch ($this->order) {
case self::ORDER_NAME:
return true;
}
return parent::getReversePaging();
}
protected function getPagingValue($result) {
switch ($this->order) {
case self::ORDER_NAME:
return $result->getName();
}
return parent::getPagingValue();
}
protected function getPagingColumn() {
switch ($this->order) {
case self::ORDER_NAME:
return 'name';
case self::ORDER_ID:
return parent::getPagingColumn();
default:
throw new Exception("Uknown order '{$this->order}'!");
}
}
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationReleeph';
+ }
+
}
diff --git a/src/applications/releeph/query/ReleephRequestQuery.php b/src/applications/releeph/query/ReleephRequestQuery.php
index 560f7898fc..b4a9985341 100644
--- a/src/applications/releeph/query/ReleephRequestQuery.php
+++ b/src/applications/releeph/query/ReleephRequestQuery.php
@@ -1,235 +1,240 @@
<?php
final class ReleephRequestQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $requestedCommitPHIDs;
private $commitToRevMap;
private $ids;
private $phids;
private $severities;
private $requestorPHIDs;
private $branchIDs;
private $revisionPHIDs;
const STATUS_ALL = 'status-all';
const STATUS_OPEN = 'status-open';
const STATUS_REQUESTED = 'status-requested';
const STATUS_NEEDS_PULL = 'status-needs-pull';
const STATUS_REJECTED = 'status-rejected';
const STATUS_ABANDONED = 'status-abandoned';
const STATUS_PULLED = 'status-pulled';
const STATUS_NEEDS_REVERT = 'status-needs-revert';
const STATUS_REVERTED = 'status-reverted';
private $status = self::STATUS_ALL;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withBranchIDs(array $branch_ids) {
$this->branchIDs = $branch_ids;
return $this;
}
public function getRevisionPHID($commit_phid) {
if ($this->commitToRevMap) {
return idx($this->commitToRevMap, $commit_phid, null);
}
return null;
}
public function withStatus($status) {
$this->status = $status;
return $this;
}
public function withRequestedCommitPHIDs(array $requested_commit_phids) {
$this->requestedCommitPHIDs = $requested_commit_phids;
return $this;
}
public function withRequestorPHIDs(array $phids) {
$this->requestorPHIDs = $phids;
return $this;
}
public function withSeverities(array $severities) {
$this->severities = $severities;
return $this;
}
public function withRevisionPHIDs(array $revision_phids) {
$this->revisionPHIDs = $revision_phids;
return $this;
}
public function loadPage() {
$table = new ReleephRequest();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
public function willFilterPage(array $requests) {
// TODO: These should be serviced by the query, but are not currently
// denormalized anywhere. For now, filter them here instead.
$keep_status = array_fuse($this->getKeepStatusConstants());
if ($keep_status) {
foreach ($requests as $key => $request) {
if (empty($keep_status[$request->getStatus()])) {
unset($requests[$key]);
}
}
}
if ($this->severities) {
$severities = array_fuse($this->severities);
foreach ($requests as $key => $request) {
// NOTE: Facebook uses a custom field here.
if (ReleephDefaultFieldSelector::isFacebook()) {
$severity = $request->getDetail('severity');
} else {
$severity = $request->getDetail('releeph:severity');
}
if (empty($severities[$severity])) {
unset($requests[$key]);
}
}
}
return $requests;
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
if ($this->branchIDs) {
$where[] = qsprintf(
$conn_r,
'branchID IN (%Ld)',
$this->branchIDs);
}
if ($this->requestedCommitPHIDs) {
$where[] = qsprintf(
$conn_r,
'requestCommitPHID IN (%Ls)',
$this->requestedCommitPHIDs);
}
if ($this->requestorPHIDs) {
$where[] = qsprintf(
$conn_r,
'requestUserPHID IN (%Ls)',
$this->requestorPHIDs);
}
if ($this->revisionPHIDs) {
$type = PhabricatorEdgeConfig::TYPE_DREV_HAS_COMMIT;
$edges = id(new PhabricatorEdgeQuery())
->withSourcePHIDs($this->revisionPHIDs)
->withEdgeTypes(array($type))
->execute();
$this->commitToRevMap = array();
foreach ($edges as $revision_phid => $edge) {
foreach ($edge[$type] as $commitPHID => $item) {
$this->commitToRevMap[$commitPHID] = $revision_phid;
}
}
if (!$this->commitToRevMap) {
throw new PhabricatorEmptyQueryException("Malformed Revision Phids");
}
$where[] = qsprintf(
$conn_r,
'requestCommitPHID IN (%Ls)',
array_keys($this->commitToRevMap));
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
private function getKeepStatusConstants() {
switch ($this->status) {
case self::STATUS_ALL:
return array();
case self::STATUS_OPEN:
return array(
ReleephRequestStatus::STATUS_REQUESTED,
ReleephRequestStatus::STATUS_NEEDS_PICK,
ReleephRequestStatus::STATUS_NEEDS_REVERT,
);
case self::STATUS_REQUESTED:
return array(
ReleephRequestStatus::STATUS_REQUESTED,
);
case self::STATUS_NEEDS_PULL:
return array(
ReleephRequestStatus::STATUS_NEEDS_PICK,
);
case self::STATUS_REJECTED:
return array(
ReleephRequestStatus::STATUS_REJECTED,
);
case self::STATUS_ABANDONED:
return array(
ReleephRequestStatus::STATUS_ABANDONED,
);
case self::STATUS_PULLED:
return array(
ReleephRequestStatus::STATUS_PICKED,
);
case self::STATUS_NEEDS_REVERT:
return array(
ReleephRequestStatus::NEEDS_REVERT,
);
case self::STATUS_REVERTED:
return array(
ReleephRequestStatus::REVERTED,
);
default:
throw new Exception("Unknown status '{$this->status}'!");
}
}
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationReleeph';
+ }
+
}
diff --git a/src/applications/repository/phid/PhabricatorRepositoryPHIDTypeArcanistProject.php b/src/applications/repository/phid/PhabricatorRepositoryPHIDTypeArcanistProject.php
index 49f24131a8..e66191fc96 100644
--- a/src/applications/repository/phid/PhabricatorRepositoryPHIDTypeArcanistProject.php
+++ b/src/applications/repository/phid/PhabricatorRepositoryPHIDTypeArcanistProject.php
@@ -1,45 +1,42 @@
<?php
/**
* @group repository
*/
final class PhabricatorRepositoryPHIDTypeArcanistProject
extends PhabricatorPHIDType {
const TYPECONST = 'APRJ';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Arcanist Project');
}
public function newObject() {
return new PhabricatorRepositoryArcanistProject();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorRepositoryArcanistProjectQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$project = $objects[$phid];
$handle->setName($project->getName());
}
}
}
diff --git a/src/applications/repository/phid/PhabricatorRepositoryPHIDTypeCommit.php b/src/applications/repository/phid/PhabricatorRepositoryPHIDTypeCommit.php
index 34f4c4be57..31aab86e95 100644
--- a/src/applications/repository/phid/PhabricatorRepositoryPHIDTypeCommit.php
+++ b/src/applications/repository/phid/PhabricatorRepositoryPHIDTypeCommit.php
@@ -1,87 +1,84 @@
<?php
final class PhabricatorRepositoryPHIDTypeCommit extends PhabricatorPHIDType {
const TYPECONST = 'CMIT';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Commit');
}
public function newObject() {
return new PhabricatorRepositoryCommit();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new DiffusionCommitQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$commit = $objects[$phid];
$repository = $commit->getRepository();
$callsign = $repository->getCallsign();
$commit_identifier = $commit->getCommitIdentifier();
$name = $repository->formatCommitName($commit_identifier);
$summary = $commit->getSummary();
if (strlen($summary)) {
$full_name = $name.': '.$summary;
} else {
$full_name = $name;
}
$handle->setName($name);
$handle->setFullName($full_name);
$handle->setURI('/r'.$callsign.$commit_identifier);
$handle->setTimestamp($commit->getEpoch());
}
}
public static function getCommitObjectNamePattern() {
$min_unqualified = PhabricatorRepository::MINIMUM_UNQUALIFIED_HASH;
$min_qualified = PhabricatorRepository::MINIMUM_QUALIFIED_HASH;
return
'r[A-Z]+[1-9]\d*'.
'|'.
'r[A-Z]+[a-f0-9]{'.$min_qualified.',40}'.
'|'.
'[a-f0-9]{'.$min_unqualified.',40}';
}
public function canLoadNamedObject($name) {
$pattern = self::getCommitObjectNamePattern();
return preg_match('(^'.$pattern.'$)', $name);
}
public function loadNamedObjects(
PhabricatorObjectQuery $query,
array $names) {
$query = id(new DiffusionCommitQuery())
->setViewer($query->getViewer())
->withIdentifiers($names);
$query->execute();
return $query->getIdentifierMap();
}
}
diff --git a/src/applications/repository/phid/PhabricatorRepositoryPHIDTypeRepository.php b/src/applications/repository/phid/PhabricatorRepositoryPHIDTypeRepository.php
index 70e4c8bf48..1e1cf8f888 100644
--- a/src/applications/repository/phid/PhabricatorRepositoryPHIDTypeRepository.php
+++ b/src/applications/repository/phid/PhabricatorRepositoryPHIDTypeRepository.php
@@ -1,78 +1,75 @@
<?php
final class PhabricatorRepositoryPHIDTypeRepository
extends PhabricatorPHIDType {
const TYPECONST = 'REPO';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Repository');
}
public function newObject() {
return new PhabricatorRepository();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorRepositoryQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$repository = $objects[$phid];
$callsign = $repository->getCallsign();
$name = $repository->getName();
$handle->setName("r{$callsign}");
$handle->setFullName("r{$callsign} ({$name})");
$handle->setURI("/diffusion/{$callsign}/");
}
}
public function canLoadNamedObject($name) {
return preg_match('/^r[A-Z]+$/', $name);
}
public function loadNamedObjects(
PhabricatorObjectQuery $query,
array $names) {
$id_map = array();
foreach ($names as $name) {
$id = substr($name, 1);
$id_map[$id][] = $name;
}
$objects = id(new PhabricatorRepositoryQuery())
->setViewer($query->getViewer())
->withCallsigns(array_keys($id_map))
->execute();
$results = array();
foreach ($objects as $object) {
$callsign = $object->getCallsign();
foreach (idx($id_map, $callsign, array()) as $name) {
$results[$name] = $object;
}
}
return $results;
}
}
diff --git a/src/applications/repository/query/PhabricatorRepositoryArcanistProjectQuery.php b/src/applications/repository/query/PhabricatorRepositoryArcanistProjectQuery.php
index 9e34bda100..13f4cf1820 100644
--- a/src/applications/repository/query/PhabricatorRepositoryArcanistProjectQuery.php
+++ b/src/applications/repository/query/PhabricatorRepositoryArcanistProjectQuery.php
@@ -1,84 +1,90 @@
<?php
/**
* @group repository
*/
final class PhabricatorRepositoryArcanistProjectQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $needRepositories;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function needRepositories($need_repositories) {
$this->needRepositories = $need_repositories;
return $this;
}
protected function loadPage() {
$table = new PhabricatorRepositoryArcanistProject();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
public function willFilterPage(array $projects) {
assert_instances_of($projects, 'PhabricatorRepositoryArcanistProject');
if ($this->needRepositories) {
$repository_ids = mpull($projects, 'getRepositoryID');
$repositories = id(new PhabricatorRepositoryQuery())
->setViewer($this->getViewer())
->withIDs($repository_ids)
->execute();
foreach ($projects as $project) {
$repo = idx($repositories, $project->getRepositoryID());
$project->attachRepository($repo);
}
}
return $projects;
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
+
+ public function getQueryApplicationClass() {
+ // TODO: Diffusion? Differential?
+ return null;
+ }
+
}
diff --git a/src/applications/repository/query/PhabricatorRepositoryQuery.php b/src/applications/repository/query/PhabricatorRepositoryQuery.php
index 92c374de42..92d8b95adf 100644
--- a/src/applications/repository/query/PhabricatorRepositoryQuery.php
+++ b/src/applications/repository/query/PhabricatorRepositoryQuery.php
@@ -1,320 +1,325 @@
<?php
final class PhabricatorRepositoryQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $callsigns;
private $types;
private $uuids;
const STATUS_OPEN = 'status-open';
const STATUS_CLOSED = 'status-closed';
const STATUS_ALL = 'status-all';
private $status = self::STATUS_ALL;
const ORDER_CREATED = 'order-created';
const ORDER_COMMITTED = 'order-committed';
const ORDER_CALLSIGN = 'order-callsign';
const ORDER_NAME = 'order-name';
private $order = self::ORDER_CREATED;
private $needMostRecentCommits;
private $needCommitCounts;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withCallsigns(array $callsigns) {
$this->callsigns = $callsigns;
return $this;
}
public function withStatus($status) {
$this->status = $status;
return $this;
}
public function withTypes(array $types) {
$this->types = $types;
return $this;
}
public function withUUIDs(array $uuids) {
$this->uuids = $uuids;
return $this;
}
public function needCommitCounts($need_counts) {
$this->needCommitCounts = $need_counts;
return $this;
}
public function needMostRecentCommits($need_commits) {
$this->needMostRecentCommits = $need_commits;
return $this;
}
public function setOrder($order) {
$this->order = $order;
return $this;
}
protected function loadPage() {
$table = new PhabricatorRepository();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T r %Q %Q %Q %Q',
$table->getTableName(),
$this->buildJoinsClause($conn_r),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
$repositories = $table->loadAllFromArray($data);
if ($this->needCommitCounts) {
$sizes = ipull($data, 'size', 'id');
foreach ($repositories as $id => $repository) {
$repository->attachCommitCount(nonempty($sizes[$id], 0));
}
}
if ($this->needMostRecentCommits) {
$commit_ids = ipull($data, 'lastCommitID', 'id');
$commit_ids = array_filter($commit_ids);
if ($commit_ids) {
$commits = id(new DiffusionCommitQuery())
->setViewer($this->getViewer())
->withIDs($commit_ids)
->execute();
} else {
$commits = array();
}
foreach ($repositories as $id => $repository) {
$commit = null;
if (idx($commit_ids, $id)) {
$commit = idx($commits, $commit_ids[$id]);
}
$repository->attachMostRecentCommit($commit);
}
}
return $repositories;
}
public function willFilterPage(array $repositories) {
assert_instances_of($repositories, 'PhabricatorRepository');
// TODO: Denormalize repository status into the PhabricatorRepository
// table so we can do this filtering in the database.
foreach ($repositories as $key => $repo) {
$status = $this->status;
switch ($status) {
case self::STATUS_OPEN:
if (!$repo->isTracked()) {
unset($repositories[$key]);
}
break;
case self::STATUS_CLOSED:
if ($repo->isTracked()) {
unset($repositories[$key]);
}
break;
case self::STATUS_ALL:
break;
default:
throw new Exception("Unknown status '{$status}'!");
}
}
return $repositories;
}
public function getReversePaging() {
switch ($this->order) {
case self::ORDER_CALLSIGN:
case self::ORDER_NAME:
return true;
}
return false;
}
protected function getPagingColumn() {
$order = $this->order;
switch ($order) {
case self::ORDER_CREATED:
return 'r.id';
case self::ORDER_COMMITTED:
return 's.epoch';
case self::ORDER_CALLSIGN:
return 'r.callsign';
case self::ORDER_NAME:
return 'r.name';
default:
throw new Exception("Unknown order '{$order}!'");
}
}
private function loadCursorObject($id) {
$query = id(new PhabricatorRepositoryQuery())
->setViewer($this->getPagingViewer())
->withIDs(array((int)$id));
if ($this->order == self::ORDER_COMMITTED) {
$query->needMostRecentCommits(true);
}
$results = $query->execute();
return head($results);
}
protected function buildPagingClause(AphrontDatabaseConnection $conn_r) {
$default = parent::buildPagingClause($conn_r);
$before_id = $this->getBeforeID();
$after_id = $this->getAfterID();
if (!$before_id && !$after_id) {
return $default;
}
$order = $this->order;
if ($order == self::ORDER_CREATED) {
return $default;
}
if ($before_id) {
$cursor = $this->loadCursorObject($before_id);
} else {
$cursor = $this->loadCursorObject($after_id);
}
if (!$cursor) {
return null;
}
$id_column = array(
'name' => 'r.id',
'type' => 'int',
'value' => $cursor->getID(),
);
$columns = array();
switch ($order) {
case self::ORDER_COMMITTED:
$commit = $cursor->getMostRecentCommit();
if (!$commit) {
return null;
}
$columns[] = array(
'name' => 's.epoch',
'type' => 'int',
'value' => $commit->getEpoch(),
);
$columns[] = $id_column;
break;
case self::ORDER_CALLSIGN:
$columns[] = array(
'name' => 'r.callsign',
'type' => 'string',
'value' => $cursor->getCallsign(),
'reverse' => true,
);
break;
case self::ORDER_NAME:
$columns[] = array(
'name' => 'r.name',
'type' => 'string',
'value' => $cursor->getName(),
'reverse' => true,
);
$columns[] = $id_column;
break;
default:
throw new Exception("Unknown order '{$order}'!");
}
return $this->buildPagingClauseFromMultipleColumns(
$conn_r,
$columns,
array(
// TODO: Clean up the column ordering stuff and then make this
// depend on getReversePaging().
'reversed' => (bool)($before_id),
));
}
private function buildJoinsClause(AphrontDatabaseConnection $conn_r) {
$joins = array();
$join_summary_table = $this->needCommitCounts ||
$this->needMostRecentCommits ||
($this->order == self::ORDER_COMMITTED);
if ($join_summary_table) {
$joins[] = qsprintf(
$conn_r,
'LEFT JOIN %T s ON r.id = s.repositoryID',
PhabricatorRepository::TABLE_SUMMARY);
}
return implode(' ', $joins);
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'r.id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'r.phid IN (%Ls)',
$this->phids);
}
if ($this->callsigns) {
$where[] = qsprintf(
$conn_r,
'r.callsign IN (%Ls)',
$this->callsigns);
}
if ($this->types) {
$where[] = qsprintf(
$conn_r,
'r.versionControlSystem IN (%Ls)',
$this->types);
}
if ($this->uuids) {
$where[] = qsprintf(
$conn_r,
'r.uuid IN (%Ls)',
$this->uuids);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationDiffusion';
+ }
+
}
diff --git a/src/applications/search/query/PhabricatorNamedQueryQuery.php b/src/applications/search/query/PhabricatorNamedQueryQuery.php
index 84f9a5eaa8..dccd228a32 100644
--- a/src/applications/search/query/PhabricatorNamedQueryQuery.php
+++ b/src/applications/search/query/PhabricatorNamedQueryQuery.php
@@ -1,84 +1,90 @@
<?php
/**
* @group search
*/
final class PhabricatorNamedQueryQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $engineClassNames;
private $userPHIDs;
private $queryKeys;
public function withIDs(array $ids) {
$this->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;
}
protected function loadPage() {
$table = new PhabricatorNamedQuery();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
private function buildWhereClause($conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->engineClassNames) {
$where[] = qsprintf(
$conn_r,
'engineClassName IN (%Ls)',
$this->engineClassNames);
}
if ($this->userPHIDs) {
$where[] = qsprintf(
$conn_r,
'userPHID IN (%Ls)',
$this->userPHIDs);
}
if ($this->queryKeys) {
$where[] = qsprintf(
$conn_r,
'queryKey IN (%Ls)',
$this->queryKeys);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
+
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationSearch';
+ }
+
}
diff --git a/src/applications/search/query/PhabricatorSavedQueryQuery.php b/src/applications/search/query/PhabricatorSavedQueryQuery.php
index 929da82a24..70773908a8 100644
--- a/src/applications/search/query/PhabricatorSavedQueryQuery.php
+++ b/src/applications/search/query/PhabricatorSavedQueryQuery.php
@@ -1,68 +1,74 @@
<?php
final class PhabricatorSavedQueryQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $engineClassNames;
private $queryKeys;
public function withIDs(array $ids) {
$this->ids = $ids;
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;
}
protected function loadPage() {
$table = new PhabricatorSavedQuery();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
private function buildWhereClause($conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->engineClassNames) {
$where[] = qsprintf(
$conn_r,
'engineClassName IN (%Ls)',
$this->engineClassNames);
}
if ($this->queryKeys) {
$where[] = qsprintf(
$conn_r,
'queryKey IN (%Ls)',
$this->queryKeys);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
+
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationSearch';
+ }
+
}
diff --git a/src/applications/slowvote/phid/PhabricatorSlowvotePHIDTypePoll.php b/src/applications/slowvote/phid/PhabricatorSlowvotePHIDTypePoll.php
index 7f40a8a7ac..36ad531375 100644
--- a/src/applications/slowvote/phid/PhabricatorSlowvotePHIDTypePoll.php
+++ b/src/applications/slowvote/phid/PhabricatorSlowvotePHIDTypePoll.php
@@ -1,73 +1,70 @@
<?php
final class PhabricatorSlowvotePHIDTypePoll extends PhabricatorPHIDType {
const TYPECONST = 'POLL';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Slowvote Poll');
}
public function newObject() {
return new PhabricatorSlowvotePoll();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorSlowvoteQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$poll = $objects[$phid];
$handle->setName('V'.$poll->getID());
$handle->setFullName('V'.$poll->getID().': '.$poll->getQuestion());
$handle->setURI('/V'.$poll->getID());
}
}
public function canLoadNamedObject($name) {
return preg_match('/^V\d*[1-9]\d*$/i', $name);
}
public function loadNamedObjects(
PhabricatorObjectQuery $query,
array $names) {
$id_map = array();
foreach ($names as $name) {
$id = (int)substr($name, 1);
$id_map[$id][] = $name;
}
$objects = id(new PhabricatorSlowvoteQuery())
->setViewer($query->getViewer())
->withIDs(array_keys($id_map))
->execute();
$results = array();
foreach ($objects as $id => $object) {
foreach (idx($id_map, $id, array()) as $name) {
$results[$name] = $object;
}
}
return $results;
}
}
diff --git a/src/applications/slowvote/query/PhabricatorSlowvoteQuery.php b/src/applications/slowvote/query/PhabricatorSlowvoteQuery.php
index 28963a44bc..8b4677c557 100644
--- a/src/applications/slowvote/query/PhabricatorSlowvoteQuery.php
+++ b/src/applications/slowvote/query/PhabricatorSlowvoteQuery.php
@@ -1,171 +1,176 @@
<?php
/**
* @group slowvote
*/
final class PhabricatorSlowvoteQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $authorPHIDs;
private $withVotesByViewer;
private $needOptions;
private $needChoices;
private $needViewerChoices;
public function withIDs($ids) {
$this->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 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 loadPage() {
$table = new PhabricatorSlowvotePoll();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT p.* FROM %T p %Q %Q %Q %Q',
$table->getTableName(),
$this->buildJoinsClause($conn_r),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
public 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;
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'p.id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'p.phid IN (%Ls)',
$this->phids);
}
if ($this->authorPHIDs) {
$where[] = qsprintf(
$conn_r,
'p.authorPHID IN (%Ls)',
$this->authorPHIDs);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
private function buildJoinsClause(AphrontDatabaseConnection $conn_r) {
$joins = array();
if ($this->withVotesByViewer) {
$joins[] = qsprintf(
$conn_r,
'JOIN %T vv ON vv.pollID = p.id AND vv.authorPHID = %s',
id(new PhabricatorSlowvoteChoice())->getTableName(),
$this->getViewer()->getPHID());
}
return implode(' ', $joins);
}
protected function getPagingColumn() {
return 'p.id';
}
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationSlowvote';
+ }
+
}
diff --git a/src/applications/tokens/phid/PhabricatorTokenPHIDTypeToken.php b/src/applications/tokens/phid/PhabricatorTokenPHIDTypeToken.php
index 7d0a1f69a6..c641a1efbd 100644
--- a/src/applications/tokens/phid/PhabricatorTokenPHIDTypeToken.php
+++ b/src/applications/tokens/phid/PhabricatorTokenPHIDTypeToken.php
@@ -1,44 +1,41 @@
<?php
final class PhabricatorTokenPHIDTypeToken extends PhabricatorPHIDType {
const TYPECONST = 'TOKN';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Token');
}
public function newObject() {
return new PhabricatorToken();
}
- public function loadObjects(
+ protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorTokenQuery())
- ->setViewer($query->getViewer())
- ->setParentQuery($query)
- ->withPHIDs($phids)
- ->execute();
+ ->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$token = $objects[$phid];
$name = $token->getName();
$handle->setName("{$name} Token");
}
}
}
diff --git a/src/applications/tokens/query/PhabricatorTokenGivenQuery.php b/src/applications/tokens/query/PhabricatorTokenGivenQuery.php
index 27a2c959dd..ea835f47cc 100644
--- a/src/applications/tokens/query/PhabricatorTokenGivenQuery.php
+++ b/src/applications/tokens/query/PhabricatorTokenGivenQuery.php
@@ -1,92 +1,96 @@
<?php
final class PhabricatorTokenGivenQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $authorPHIDs;
private $objectPHIDs;
private $tokenPHIDs;
public function withTokenPHIDs(array $token_phids) {
$this->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;
}
protected function loadPage() {
$table = new PhabricatorTokenGiven();
$conn_r = $table->establishConnection('r');
$rows = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($rows);
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->authorPHIDs) {
$where[] = qsprintf(
$conn_r,
'authorPHID IN (%Ls)',
$this->authorPHIDs);
}
if ($this->objectPHIDs) {
$where[] = qsprintf(
$conn_r,
'objectPHID IN (%Ls)',
$this->objectPHIDs);
}
if ($this->tokenPHIDs) {
$where[] = qsprintf(
$conn_r,
'tokenPHID IN (%Ls)',
$this->tokenPHIDs);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
public function willFilterPage(array $results) {
$object_phids = array_filter(mpull($results, 'getObjectPHID'));
if (!$object_phids) {
return array();
}
$objects = id(new PhabricatorObjectQuery())
->setViewer($this->getViewer())
->withPHIDs($object_phids)
->execute();
foreach ($results as $key => $result) {
$phid = $result->getObjectPHID();
if (empty($objects[$phid])) {
unset($results[$key]);
} else {
$result->attachObject($objects[$phid]);
}
}
return $results;
}
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationTokens';
+ }
+
}
diff --git a/src/applications/tokens/query/PhabricatorTokenQuery.php b/src/applications/tokens/query/PhabricatorTokenQuery.php
index c4bf4a975b..1fa5d347a8 100644
--- a/src/applications/tokens/query/PhabricatorTokenQuery.php
+++ b/src/applications/tokens/query/PhabricatorTokenQuery.php
@@ -1,64 +1,69 @@
<?php
final class PhabricatorTokenQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $phids;
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
protected function loadPage() {
$tokens = $this->getBuiltinTokens();
if ($this->phids) {
$map = array_fill_keys($this->phids, true);
foreach ($tokens as $key => $token) {
if (empty($map[$token->getPHID()])) {
unset($tokens[$key]);
}
}
}
return $tokens;
}
private function getBuiltinTokens() {
$specs = array(
array('like-1', pht('Like')),
array('like-2', pht('Dislike')),
array('heart-1', pht('Love')),
array('heart-2', pht('Heartbreak')),
array('medal-1', pht('Orange Medal')),
array('medal-2', pht('Grey Medal')),
array('medal-3', pht('Yellow Medal')),
array('medal-4', pht('Manufacturing Defect?')),
array('coin-1', pht('Haypence')),
array('coin-2', pht('Piece of Eight')),
array('coin-3', pht('Doubloon')),
array('coin-4', pht('Mountain of Wealth')),
array('misc-1', pht('Pterodactyl')),
array('misc-2', pht('Evil Spooky Haunted Tree')),
array('misc-3', pht('Baby Tequila')),
array('misc-4', pht('The World Burns')),
);
$type = PhabricatorTokenPHIDTypeToken::TYPECONST;
$tokens = array();
foreach ($specs as $id => $spec) {
list($image, $name) = $spec;
$token = id(new PhabricatorToken())
->setID($id)
->setName($name)
->setPHID('PHID-'.$type.'-'.$image);
$tokens[] = $token;
}
return $tokens;
}
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationTokens';
+ }
+
}
diff --git a/src/applications/tokens/query/PhabricatorTokenReceiverQuery.php b/src/applications/tokens/query/PhabricatorTokenReceiverQuery.php
index bfa784fa41..6485416eb2 100644
--- a/src/applications/tokens/query/PhabricatorTokenReceiverQuery.php
+++ b/src/applications/tokens/query/PhabricatorTokenReceiverQuery.php
@@ -1,37 +1,41 @@
<?php
final class PhabricatorTokenReceiverQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $tokenCounts;
protected function loadPage() {
$table = new PhabricatorTokenCount();
$conn_r = $table->establishConnection('r');
$rows = queryfx_all(
$conn_r,
'SELECT objectPHID, tokenCount FROM %T ORDER BY tokenCount DESC',
$table->getTableName());
$this->tokenCounts = ipull($rows, 'tokenCount', 'objectPHID');
return ipull($rows, 'objectPHID');
}
public function willFilterPage(array $phids) {
$objects = id(new PhabricatorObjectQuery())
->setViewer($this->getViewer())
->withPHIDs($phids)
->execute();
// Reorder the objects in the input order.
$objects = array_select_keys($objects, $phids);
return $objects;
}
public function getTokenCounts() {
return $this->tokenCounts;
}
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationTokens';
+ }
+
}
diff --git a/src/applications/transactions/phid/PhabricatorApplicationTransactionPHIDTypeTransaction.php b/src/applications/transactions/phid/PhabricatorApplicationTransactionPHIDTypeTransaction.php
index d818dad226..97841ba04f 100644
--- a/src/applications/transactions/phid/PhabricatorApplicationTransactionPHIDTypeTransaction.php
+++ b/src/applications/transactions/phid/PhabricatorApplicationTransactionPHIDTypeTransaction.php
@@ -1,80 +1,92 @@
<?php
final class PhabricatorApplicationTransactionPHIDTypeTransaction
extends PhabricatorPHIDType {
const TYPECONST = 'XACT';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Transaction');
}
public function newObject() {
// NOTE: We could produce an object here, but we'd need to take a PHID type
// and subtype to do so. Currently, we never write edges to transactions,
// so leave this unimplemented for the moment.
return null;
}
+ protected function buildQueryForObjects(
+ PhabricatorObjectQuery $object_query,
+ array $phids) {
+ throw new Exception();
+ }
+
public function loadObjects(
PhabricatorObjectQuery $object_query,
array $phids) {
static $queries;
if ($queries === null) {
$objects = id(new PhutilSymbolLoader())
->setAncestorClass('PhabricatorApplicationTransactionQuery')
->loadObjects();
$queries = array();
foreach ($objects as $object) {
$type = $object
->getTemplateApplicationTransaction()
->getApplicationTransactionType();
$queries[$type] = $object;
}
}
$phid_subtypes = array();
foreach ($phids as $phid) {
$subtype = phid_get_subtype($phid);
if ($subtype) {
$phid_subtypes[$subtype][] = $phid;
}
}
$results = array();
foreach ($phid_subtypes as $subtype => $subtype_phids) {
$query = idx($queries, $subtype);
if (!$query) {
continue;
}
- $xactions = id(clone $query)
+ $xaction_query = id(clone $query)
->setViewer($object_query->getViewer())
->setParentQuery($object_query)
- ->withPHIDs($subtype_phids)
- ->execute();
+ ->withPHIDs($subtype_phids);
+
+ if (!$xaction_query->canViewerUseQueryApplication()) {
+ $object_query->addPolicyFilteredPHIDs(array_fuse($subtype_phids));
+ continue;
+ }
+
+ $xactions = $xaction_query->execute();
$results += mpull($xactions, null, 'getPHID');
}
return $results;
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
// NOTE: We don't produce meaningful handles here because they're
// impractical to produce and no application uses them.
}
}
diff --git a/src/applications/transactions/query/PhabricatorApplicationTransactionCommentQuery.php b/src/applications/transactions/query/PhabricatorApplicationTransactionCommentQuery.php
index 6cb16c7e50..038fadb9cb 100644
--- a/src/applications/transactions/query/PhabricatorApplicationTransactionCommentQuery.php
+++ b/src/applications/transactions/query/PhabricatorApplicationTransactionCommentQuery.php
@@ -1,62 +1,68 @@
<?php
final class PhabricatorApplicationTransactionCommentQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $template;
private $phids;
private $transactionPHIDs;
public function setTemplate(
PhabricatorApplicationTransactionComment $template) {
$this->template = $template;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withTransactionPHIDs(array $transaction_phids) {
$this->transactionPHIDs = $transaction_phids;
return $this;
}
protected function loadPage() {
$table = $this->template;
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T xc %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
if ($this->transactionPHIDs) {
$where[] = qsprintf(
$conn_r,
'transactionPHID IN (%Ls)',
$this->transactionPHIDs);
}
return $this->formatWhereClause($where);
}
+ public function getQueryApplicationClass() {
+ // TODO: Figure out the app via the template?
+ return null;
+ }
+
+
}
diff --git a/src/applications/transactions/query/PhabricatorApplicationTransactionQuery.php b/src/applications/transactions/query/PhabricatorApplicationTransactionQuery.php
index 00523cffe4..1ef212d7e5 100644
--- a/src/applications/transactions/query/PhabricatorApplicationTransactionQuery.php
+++ b/src/applications/transactions/query/PhabricatorApplicationTransactionQuery.php
@@ -1,163 +1,169 @@
<?php
abstract class PhabricatorApplicationTransactionQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $phids;
private $objectPHIDs;
private $authorPHIDs;
private $transactionTypes;
private $needComments = true;
private $needHandles = true;
abstract public function getTemplateApplicationTransaction();
protected function buildMoreWhereClauses(AphrontDatabaseConnection $conn_r) {
return array();
}
protected function getReversePaging() {
return true;
}
public function withPHIDs(array $phids) {
$this->phids = $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 withTransactionTypes(array $transaction_types) {
$this->transactionTypes = $transaction_types;
return $this;
}
public function needComments($need) {
$this->needComments = $need;
return $this;
}
public function needHandles($need) {
$this->needHandles = $need;
return $this;
}
protected function loadPage() {
$table = $this->getTemplateApplicationTransaction();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T x %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
$xactions = $table->loadAllFromArray($data);
foreach ($xactions as $xaction) {
$xaction->attachViewer($this->getViewer());
}
if ($this->needComments) {
$comment_phids = array_filter(mpull($xactions, 'getCommentPHID'));
$comments = array();
if ($comment_phids) {
$comments = id(new PhabricatorApplicationTransactionCommentQuery())
->setTemplate($table->getApplicationTransactionCommentObject())
->setViewer($this->getViewer())
->withPHIDs($comment_phids)
->execute();
$comments = mpull($comments, null, 'getPHID');
}
foreach ($xactions as $xaction) {
if ($xaction->getCommentPHID()) {
$comment = idx($comments, $xaction->getCommentPHID());
if ($comment) {
$xaction->attachComment($comment);
}
}
}
} else {
foreach ($xactions as $xaction) {
$xaction->setCommentNotLoaded(true);
}
}
if ($this->needHandles) {
$phids = array();
foreach ($xactions as $xaction) {
$phids[$xaction->getPHID()] = $xaction->getRequiredHandlePHIDs();
}
$handles = array();
$merged = array_mergev($phids);
if ($merged) {
$handles = id(new PhabricatorHandleQuery())
->setViewer($this->getViewer())
->withPHIDs($merged)
->execute();
}
foreach ($xactions as $xaction) {
$xaction->setHandles(
array_select_keys(
$handles,
$phids[$xaction->getPHID()]));
}
}
return $xactions;
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
if ($this->objectPHIDs) {
$where[] = qsprintf(
$conn_r,
'objectPHID IN (%Ls)',
$this->objectPHIDs);
}
if ($this->authorPHIDs) {
$where[] = qsprintf(
$conn_r,
'authorPHID IN (%Ls)',
$this->authorPHIDs);
}
if ($this->transactionTypes) {
$where[] = qsprintf(
$conn_r,
'transactionType IN (%Ls)',
$this->transactionTypes);
}
foreach ($this->buildMoreWhereClauses($conn_r) as $clause) {
$where[] = $clause;
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
+
+ public function getQueryApplicationClass() {
+ // TODO: Sort this out?
+ return null;
+ }
+
}
diff --git a/src/infrastructure/query/policy/PhabricatorPolicyAwareQuery.php b/src/infrastructure/query/policy/PhabricatorPolicyAwareQuery.php
index 20b45df42d..55c6edc277 100644
--- a/src/infrastructure/query/policy/PhabricatorPolicyAwareQuery.php
+++ b/src/infrastructure/query/policy/PhabricatorPolicyAwareQuery.php
@@ -1,584 +1,628 @@
<?php
/**
* A @{class:PhabricatorQuery} which filters results according to visibility
* policies for the querying user. Broadly, this class allows you to implement
* a query that returns only objects the user is allowed to see.
*
* $results = id(new ExampleQuery())
* ->setViewer($user)
* ->withConstraint($example)
* ->execute();
*
* Normally, you should extend @{class:PhabricatorCursorPagedPolicyAwareQuery},
* not this class. @{class:PhabricatorCursorPagedPolicyAwareQuery} provides a
* more practical interface for building usable queries against most object
* types.
*
* NOTE: Although this class extends @{class:PhabricatorOffsetPagedQuery},
* offset paging with policy filtering is not efficient. All results must be
* loaded into the application and filtered here: skipping `N` rows via offset
* is an `O(N)` operation with a large constant. Prefer cursor-based paging
* with @{class:PhabricatorCursorPagedPolicyAwareQuery}, which can filter far
* more efficiently in MySQL.
*
* @task config Query Configuration
* @task exec Executing Queries
* @task policyimpl Policy Query Implementation
*/
abstract class PhabricatorPolicyAwareQuery extends PhabricatorOffsetPagedQuery {
private $viewer;
private $raisePolicyExceptions;
private $parentQuery;
private $rawResultLimit;
private $capabilities;
private $workspace = array();
private $policyFilteredPHIDs = array();
+ private $canUseApplication;
/* -( Query Configuration )------------------------------------------------ */
/**
* Set the viewer who is executing the query. Results will be filtered
* according to the viewer's capabilities. You must set a viewer to execute
* a policy query.
*
* @param PhabricatorUser The viewing user.
* @return this
* @task config
*/
final public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
/**
* Get the query's viewer.
*
* @return PhabricatorUser The viewing user.
* @task config
*/
final public function getViewer() {
return $this->viewer;
}
/**
* Set the parent query of this query. This is useful for nested queries so
* that configuration like whether or not to raise policy exceptions is
* seamlessly passed along to child queries.
*
* @return this
* @task config
*/
final public function setParentQuery(PhabricatorPolicyAwareQuery $query) {
$this->parentQuery = $query;
return $this;
}
/**
* Get the parent query. See @{method:setParentQuery} for discussion.
*
* @return PhabricatorPolicyAwareQuery The parent query.
* @task config
*/
final public function getParentQuery() {
return $this->parentQuery;
}
/**
* Hook to configure whether this query should raise policy exceptions.
*
* @return this
* @task config
*/
final public function setRaisePolicyExceptions($bool) {
$this->raisePolicyExceptions = $bool;
return $this;
}
/**
* @return bool
* @task config
*/
final public function shouldRaisePolicyExceptions() {
return (bool) $this->raisePolicyExceptions;
}
/**
* @task config
*/
final public function requireCapabilities(array $capabilities) {
$this->capabilities = $capabilities;
return $this;
}
/* -( Query Execution )---------------------------------------------------- */
/**
* Execute the query, expecting a single result. This method simplifies
* loading objects for detail pages or edit views.
*
* // Load one result by ID.
* $obj = id(new ExampleQuery())
* ->setViewer($user)
* ->withIDs(array($id))
* ->executeOne();
* if (!$obj) {
* return new Aphront404Response();
* }
*
* If zero results match the query, this method returns `null`.
* If one result matches the query, this method returns that result.
*
* If two or more results match the query, this method throws an exception.
* You should use this method only when the query constraints guarantee at
* most one match (e.g., selecting a specific ID or PHID).
*
* If one result matches the query but it is caught by the policy filter (for
* example, the user is trying to view or edit an object which exists but
* which they do not have permission to see) a policy exception is thrown.
*
* @return mixed Single result, or null.
* @task exec
*/
final public function executeOne() {
$this->setRaisePolicyExceptions(true);
try {
$results = $this->execute();
} catch (Exception $ex) {
$this->setRaisePolicyExceptions(false);
throw $ex;
}
if (count($results) > 1) {
throw new Exception("Expected a single result!");
}
if (!$results) {
return null;
}
return head($results);
}
/**
* Execute the query, loading all visible results.
*
* @return list<PhabricatorPolicyInterface> Result objects.
* @task exec
*/
final public function execute() {
if (!$this->viewer) {
throw new Exception("Call setViewer() before execute()!");
}
$parent_query = $this->getParentQuery();
if ($parent_query) {
$this->setRaisePolicyExceptions(
$parent_query->shouldRaisePolicyExceptions());
}
$results = array();
$filter = $this->getPolicyFilter();
$offset = (int)$this->getOffset();
$limit = (int)$this->getLimit();
$count = 0;
if ($limit) {
$need = $offset + $limit;
} else {
$need = 0;
}
$this->willExecute();
do {
if ($need) {
$this->rawResultLimit = min($need - $count, 1024);
} else {
$this->rawResultLimit = 0;
}
- try {
- $page = $this->loadPage();
- } catch (PhabricatorEmptyQueryException $ex) {
+ if ($this->canViewerUseQueryApplication()) {
+ try {
+ $page = $this->loadPage();
+ } catch (PhabricatorEmptyQueryException $ex) {
+ $page = array();
+ }
+ } else {
$page = array();
}
if ($page) {
$maybe_visible = $this->willFilterPage($page);
} else {
$maybe_visible = array();
}
if ($this->shouldDisablePolicyFiltering()) {
$visible = $maybe_visible;
} else {
$visible = $filter->apply($maybe_visible);
$policy_filtered = array();
foreach ($maybe_visible as $key => $object) {
if (empty($visible[$key])) {
$phid = $object->getPHID();
if ($phid) {
$policy_filtered[$phid] = $phid;
}
}
}
$this->addPolicyFilteredPHIDs($policy_filtered);
}
if ($visible) {
$this->putObjectsInWorkspace($this->getWorkspaceMapForPage($visible));
$visible = $this->didFilterPage($visible);
}
$removed = array();
foreach ($maybe_visible as $key => $object) {
if (empty($visible[$key])) {
$removed[$key] = $object;
}
}
$this->didFilterResults($removed);
foreach ($visible as $key => $result) {
++$count;
// If we have an offset, we just ignore that many results and start
// storing them only once we've hit the offset. This reduces memory
// requirements for large offsets, compared to storing them all and
// slicing them away later.
if ($count > $offset) {
$results[$key] = $result;
}
if ($need && ($count >= $need)) {
// If we have all the rows we need, break out of the paging query.
break 2;
}
}
if (!$this->rawResultLimit) {
// If we don't have a load count, we loaded all the results. We do
// not need to load another page.
break;
}
if (count($page) < $this->rawResultLimit) {
// If we have a load count but the unfiltered results contained fewer
// objects, we know this was the last page of objects; we do not need
// to load another page because we can deduce it would be empty.
break;
}
$this->nextPage($page);
} while (true);
$results = $this->didLoadResults($results);
return $results;
}
private function getPolicyFilter() {
$filter = new PhabricatorPolicyFilter();
$filter->setViewer($this->viewer);
if (!$this->capabilities) {
$capabilities = array(
PhabricatorPolicyCapability::CAN_VIEW,
);
} else {
$capabilities = $this->capabilities;
}
$filter->requireCapabilities($capabilities);
$filter->raisePolicyExceptions($this->shouldRaisePolicyExceptions());
return $filter;
}
protected function didRejectResult(PhabricatorPolicyInterface $object) {
$this->getPolicyFilter()->rejectObject(
$object,
$object->getPolicy(PhabricatorPolicyCapability::CAN_VIEW),
PhabricatorPolicyCapability::CAN_VIEW);
}
- protected function addPolicyFilteredPHIDs(array $phids) {
+ public function addPolicyFilteredPHIDs(array $phids) {
$this->policyFilteredPHIDs += $phids;
if ($this->getParentQuery()) {
$this->getParentQuery()->addPolicyFilteredPHIDs($phids);
}
return $this;
}
/**
* Return a map of all object PHIDs which were loaded in the query but
* filtered out by policy constraints. This allows a caller to distinguish
* between objects which do not exist (or, at least, were filtered at the
* content level) and objects which exist but aren't visible.
*
* @return map<phid, phid> Map of object PHIDs which were filtered
* by policies.
* @task exec
*/
public function getPolicyFilteredPHIDs() {
return $this->policyFilteredPHIDs;
}
/* -( Query Workspace )---------------------------------------------------- */
/**
* Put a map of objects into the query workspace. Many queries perform
* subqueries, which can eventually end up loading the same objects more than
* once (often to perform policy checks).
*
* For example, loading a user may load the user's profile image, which might
* load the user object again in order to verify that the viewer has
* permission to see the file.
*
* The "query workspace" allows queries to load objects from elsewhere in a
* query block instead of refetching them.
*
* When using the query workspace, it's important to obey two rules:
*
* **Never put objects into the workspace which the viewer may not be able
* to see**. You need to apply all policy filtering //before// putting
* objects in the workspace. Otherwise, subqueries may read the objects and
* use them to permit access to content the user shouldn't be able to view.
*
* **Fully enrich objects pulled from the workspace.** After pulling objects
* from the workspace, you still need to load and attach any additional
* content the query requests. Otherwise, a query might return objects without
* requested content.
*
* Generally, you do not need to update the workspace yourself: it is
* automatically populated as a side effect of objects surviving policy
* filtering.
*
* @param map<phid, PhabricatorPolicyInterface> Objects to add to the query
* workspace.
* @return this
* @task workspace
*/
public function putObjectsInWorkspace(array $objects) {
assert_instances_of($objects, 'PhabricatorPolicyInterface');
$viewer_phid = $this->getViewer()->getPHID();
// The workspace is scoped per viewer to prevent accidental contamination.
if (empty($this->workspace[$viewer_phid])) {
$this->workspace[$viewer_phid] = array();
}
$this->workspace[$viewer_phid] += $objects;
return $this;
}
/**
* Retrieve objects from the query workspace. For more discussion about the
* workspace mechanism, see @{method:putObjectsInWorkspace}. This method
* searches both the current query's workspace and the workspaces of parent
* queries.
*
* @param list<phid> List of PHIDs to retreive.
* @return this
* @task workspace
*/
public function getObjectsFromWorkspace(array $phids) {
$viewer_phid = $this->getViewer()->getPHID();
$results = array();
foreach ($phids as $key => $phid) {
if (isset($this->workspace[$viewer_phid][$phid])) {
$results[$phid] = $this->workspace[$viewer_phid][$phid];
unset($phids[$key]);
}
}
if ($phids && $this->getParentQuery()) {
$results += $this->getParentQuery()->getObjectsFromWorkspace($phids);
}
return $results;
}
/**
* Convert a result page to a `<phid, PhabricatorPolicyInterface>` map.
*
* @param list<PhabricatorPolicyInterface> Objects.
* @return map<phid, PhabricatorPolicyInterface> Map of objects which can
* be put into the workspace.
* @task workspace
*/
protected function getWorkspaceMapForPage(array $results) {
$map = array();
foreach ($results as $result) {
$phid = $result->getPHID();
if ($phid !== null) {
$map[$phid] = $result;
}
}
return $map;
}
/* -( Policy Query Implementation )---------------------------------------- */
/**
* Get the number of results @{method:loadPage} should load. If the value is
* 0, @{method:loadPage} should load all available results.
*
* @return int The number of results to load, or 0 for all results.
* @task policyimpl
*/
final protected function getRawResultLimit() {
return $this->rawResultLimit;
}
/**
* Hook invoked before query execution. Generally, implementations should
* reset any internal cursors.
*
* @return void
* @task policyimpl
*/
protected function willExecute() {
return;
}
/**
* Load a raw page of results. Generally, implementations should load objects
* from the database. They should attempt to return the number of results
* hinted by @{method:getRawResultLimit}.
*
* @return list<PhabricatorPolicyInterface> List of filterable policy objects.
* @task policyimpl
*/
abstract protected function loadPage();
/**
* Update internal state so that the next call to @{method:loadPage} will
* return new results. Generally, you should adjust a cursor position based
* on the provided result page.
*
* @param list<PhabricatorPolicyInterface> The current page of results.
* @return void
* @task policyimpl
*/
abstract protected function nextPage(array $page);
/**
* Hook for applying a page filter prior to the privacy filter. This allows
* you to drop some items from the result set without creating problems with
* pagination or cursor updates. You can also load and attach data which is
* required to perform policy filtering.
*
* Generally, you should load non-policy data and perform non-policy filtering
* later, in @{method:didFilterPage}. Strictly fewer objects will make it that
* far (so the program will load less data) and subqueries from that context
* can use the query workspace to further reduce query load.
*
* This method will only be called if data is available. Implementations
* do not need to handle the case of no results specially.
*
* @param list<wild> Results from `loadPage()`.
* @return list<PhabricatorPolicyInterface> Objects for policy filtering.
* @task policyimpl
*/
protected function willFilterPage(array $page) {
return $page;
}
/**
* Hook for performing additional non-policy loading or filtering after an
* object has satisfied all policy checks. Generally, this means loading and
* attaching related data.
*
* Subqueries executed during this phase can use the query workspace, which
* may improve performance or make circular policies resolvable. Data which
* is not necessary for policy filtering should generally be loaded here.
*
* This callback can still filter objects (for example, if attachable data
* is discovered to not exist), but should not do so for policy reasons.
*
* This method will only be called if data is available. Implementations do
* not need to handle the case of no results specially.
*
* @param list<wild> Results from @{method:willFilterPage()}.
* @return list<PhabricatorPolicyInterface> Objects after additional
* non-policy processing.
*/
protected function didFilterPage(array $page) {
return $page;
}
/**
* Hook for removing filtered results from alternate result sets. This
* hook will be called with any objects which were returned by the query but
* filtered for policy reasons. The query should remove them from any cached
* or partial result sets.
*
* @param list<wild> List of objects that should not be returned by alternate
* result mechanisms.
* @return void
* @task policyimpl
*/
protected function didFilterResults(array $results) {
return;
}
/**
* Hook for applying final adjustments before results are returned. This is
* used by @{class:PhabricatorCursorPagedPolicyAwareQuery} to reverse results
* that are queried during reverse paging.
*
* @param list<PhabricatorPolicyInterface> Query results.
* @return list<PhabricatorPolicyInterface> Final results.
* @task policyimpl
*/
protected function didLoadResults(array $results) {
return $results;
}
/**
* Allows a subclass to disable policy filtering. This method is dangerous.
* It should be used only if the query loads data which has already been
* filtered (for example, because it wraps some other query which uses
* normal policy filtering).
*
* @return bool True to disable all policy filtering.
* @task policyimpl
*/
protected function shouldDisablePolicyFiltering() {
return false;
}
+
+ /**
+ * If this query belongs to an application, return the application class name
+ * here. This will prevent the query from returning results if the viewer can
+ * not access the application.
+ *
+ * If this query does not belong to an application, return `null`.
+ *
+ * @return string|null Application class name.
+ */
+ abstract public function getQueryApplicationClass();
+
+
+ /**
+ * Determine if the viewer has permission to use this query's application.
+ * For queries which aren't part of an application, this method always returns
+ * true.
+ *
+ * @return bool True if the viewer has application-level permission to
+ * execute the query.
+ */
+ public function canViewerUseQueryApplication() {
+ if ($this->canUseApplication === null) {
+ $class = $this->getQueryApplicationClass();
+ if (!$class) {
+ $this->canUseApplication = true;
+ } else {
+ $result = id(new PhabricatorApplicationQuery())
+ ->setViewer($this->getViewer())
+ ->withClasses(array($class))
+ ->execute();
+
+ $this->canUseApplication = (bool)$result;
+ }
+ }
+
+ return $this->canUseApplication;
+ }
+
}

File Metadata

Mime Type
text/x-diff
Expires
Fri, Nov 14, 2:14 AM (18 h, 40 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
336714
Default Alt Text
(383 KB)

Event Timeline