2011-12-16 17:08:18 -08:00
|
|
|
<?php
|
|
|
|
|
2012-09-13 10:15:08 -07:00
|
|
|
final class PhabricatorProjectQuery
|
|
|
|
extends PhabricatorCursorPagedPolicyAwareQuery {
|
2011-12-16 17:08:18 -08:00
|
|
|
|
2012-01-17 16:29:35 -08:00
|
|
|
private $ids;
|
|
|
|
private $phids;
|
2012-08-07 11:54:24 -07:00
|
|
|
private $memberPHIDs;
|
2013-03-08 07:12:24 -08:00
|
|
|
private $slugs;
|
2011-12-16 17:08:18 -08:00
|
|
|
|
2012-02-07 14:59:38 -08:00
|
|
|
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';
|
|
|
|
|
2012-01-17 16:29:35 -08:00
|
|
|
private $needMembers;
|
2013-10-06 17:07:20 -07:00
|
|
|
private $needProfiles;
|
2012-01-17 16:29:35 -08:00
|
|
|
|
|
|
|
public function withIDs(array $ids) {
|
|
|
|
$this->ids = $ids;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function withPHIDs(array $phids) {
|
|
|
|
$this->phids = $phids;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-02-07 14:59:38 -08:00
|
|
|
public function withStatus($status) {
|
|
|
|
$this->status = $status;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-08-07 11:54:24 -07:00
|
|
|
public function withMemberPHIDs(array $member_phids) {
|
|
|
|
$this->memberPHIDs = $member_phids;
|
2011-12-16 17:08:18 -08:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2013-03-08 07:12:24 -08:00
|
|
|
public function withPhrictionSlugs(array $slugs) {
|
|
|
|
$this->slugs = $slugs;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-01-17 16:29:35 -08:00
|
|
|
public function needMembers($need_members) {
|
|
|
|
$this->needMembers = $need_members;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2013-10-06 17:07:20 -07:00
|
|
|
public function needProfiles($need_profiles) {
|
|
|
|
$this->needProfiles = $need_profiles;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-08-08 17:10:10 -07:00
|
|
|
protected function getPagingColumn() {
|
|
|
|
return 'name';
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function getPagingValue($result) {
|
|
|
|
return $result->getName();
|
|
|
|
}
|
|
|
|
|
2012-08-11 07:05:45 -07:00
|
|
|
protected function getReversePaging() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-03-01 11:28:02 -08:00
|
|
|
protected function loadPage() {
|
2012-08-07 11:54:39 -07:00
|
|
|
$table = new PhabricatorProject();
|
2011-12-16 17:08:18 -08:00
|
|
|
$conn_r = $table->establishConnection('r');
|
|
|
|
|
2012-08-11 07:05:01 -07:00
|
|
|
// 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';
|
|
|
|
}
|
|
|
|
|
2011-12-16 17:08:18 -08:00
|
|
|
$data = queryfx_all(
|
|
|
|
$conn_r,
|
2012-08-11 07:05:01 -07:00
|
|
|
'SELECT p.* %Q FROM %T p %Q %Q %Q %Q %Q',
|
|
|
|
$select_clause,
|
2011-12-16 17:08:18 -08:00
|
|
|
$table->getTableName(),
|
2012-08-07 18:02:05 -07:00
|
|
|
$this->buildJoinClause($conn_r),
|
|
|
|
$this->buildWhereClause($conn_r),
|
|
|
|
$this->buildGroupClause($conn_r),
|
2012-08-11 07:05:01 -07:00
|
|
|
$this->buildOrderClause($conn_r),
|
2012-08-07 11:54:39 -07:00
|
|
|
$this->buildLimitClause($conn_r));
|
2011-12-16 17:08:18 -08:00
|
|
|
|
2012-01-17 16:29:35 -08:00
|
|
|
$projects = $table->loadAllFromArray($data);
|
|
|
|
|
2012-08-11 07:05:01 -07:00
|
|
|
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));
|
|
|
|
}
|
2012-01-17 16:29:35 -08:00
|
|
|
}
|
Fix some file policy issues and add a "Query Workspace"
Summary:
Ref T603. Several issues here:
1. Currently, `FileQuery` does not actually respect object attachment edges when doing policy checks. Everything else works fine, but this was missing an `array_keys()`.
2. Once that's fixed, we hit a bunch of recursion issues. For example, when loading a User we load the profile picture, and then that loads the User, and that loads the profile picture, etc.
3. Introduce a "Query Workspace", which holds objects we know we've loaded and know we can see but haven't finished filtering and/or attaching data to. This allows subqueries to look up objects instead of querying for them.
- We can probably generalize this a bit to make a few other queries more efficient. Pholio currently has a similar (but less general) "mock cache". However, it's keyed by ID instead of PHID so it's not easy to reuse this right now.
This is a bit complex for the problem being solved, but I think it's the cleanest approach and I believe the primitive will be useful in the future.
Test Plan: Looked at pastes, macros, mocks and projects as a logged-in and logged-out user.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T603
Differential Revision: https://secure.phabricator.com/D7309
2013-10-14 14:36:06 -07:00
|
|
|
}
|
2013-10-06 17:07:20 -07:00
|
|
|
|
Fix some file policy issues and add a "Query Workspace"
Summary:
Ref T603. Several issues here:
1. Currently, `FileQuery` does not actually respect object attachment edges when doing policy checks. Everything else works fine, but this was missing an `array_keys()`.
2. Once that's fixed, we hit a bunch of recursion issues. For example, when loading a User we load the profile picture, and then that loads the User, and that loads the profile picture, etc.
3. Introduce a "Query Workspace", which holds objects we know we've loaded and know we can see but haven't finished filtering and/or attaching data to. This allows subqueries to look up objects instead of querying for them.
- We can probably generalize this a bit to make a few other queries more efficient. Pholio currently has a similar (but less general) "mock cache". However, it's keyed by ID instead of PHID so it's not easy to reuse this right now.
This is a bit complex for the problem being solved, but I think it's the cleanest approach and I believe the primitive will be useful in the future.
Test Plan: Looked at pastes, macros, mocks and projects as a logged-in and logged-out user.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T603
Differential Revision: https://secure.phabricator.com/D7309
2013-10-14 14:36:06 -07:00
|
|
|
return $projects;
|
|
|
|
}
|
2013-10-06 17:07:43 -07:00
|
|
|
|
Fix some file policy issues and add a "Query Workspace"
Summary:
Ref T603. Several issues here:
1. Currently, `FileQuery` does not actually respect object attachment edges when doing policy checks. Everything else works fine, but this was missing an `array_keys()`.
2. Once that's fixed, we hit a bunch of recursion issues. For example, when loading a User we load the profile picture, and then that loads the User, and that loads the profile picture, etc.
3. Introduce a "Query Workspace", which holds objects we know we've loaded and know we can see but haven't finished filtering and/or attaching data to. This allows subqueries to look up objects instead of querying for them.
- We can probably generalize this a bit to make a few other queries more efficient. Pholio currently has a similar (but less general) "mock cache". However, it's keyed by ID instead of PHID so it's not easy to reuse this right now.
This is a bit complex for the problem being solved, but I think it's the cleanest approach and I believe the primitive will be useful in the future.
Test Plan: Looked at pastes, macros, mocks and projects as a logged-in and logged-out user.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T603
Differential Revision: https://secure.phabricator.com/D7309
2013-10-14 14:36:06 -07:00
|
|
|
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) {
|
2013-10-06 17:07:43 -07:00
|
|
|
if (!$default) {
|
|
|
|
$default = PhabricatorFile::loadBuiltin(
|
|
|
|
$this->getViewer(),
|
|
|
|
'profile.png');
|
|
|
|
}
|
Fix some file policy issues and add a "Query Workspace"
Summary:
Ref T603. Several issues here:
1. Currently, `FileQuery` does not actually respect object attachment edges when doing policy checks. Everything else works fine, but this was missing an `array_keys()`.
2. Once that's fixed, we hit a bunch of recursion issues. For example, when loading a User we load the profile picture, and then that loads the User, and that loads the profile picture, etc.
3. Introduce a "Query Workspace", which holds objects we know we've loaded and know we can see but haven't finished filtering and/or attaching data to. This allows subqueries to look up objects instead of querying for them.
- We can probably generalize this a bit to make a few other queries more efficient. Pholio currently has a similar (but less general) "mock cache". However, it's keyed by ID instead of PHID so it's not easy to reuse this right now.
This is a bit complex for the problem being solved, but I think it's the cleanest approach and I believe the primitive will be useful in the future.
Test Plan: Looked at pastes, macros, mocks and projects as a logged-in and logged-out user.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T603
Differential Revision: https://secure.phabricator.com/D7309
2013-10-14 14:36:06 -07:00
|
|
|
$file = $default;
|
|
|
|
}
|
|
|
|
$profile->attachProfileImageFile($file);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($projects as $project) {
|
|
|
|
$profile = idx($profiles, $project->getPHID());
|
|
|
|
if (!$profile) {
|
|
|
|
if (!$default) {
|
|
|
|
$default = PhabricatorFile::loadBuiltin(
|
|
|
|
$this->getViewer(),
|
|
|
|
'profile.png');
|
2013-10-06 17:07:20 -07:00
|
|
|
}
|
Fix some file policy issues and add a "Query Workspace"
Summary:
Ref T603. Several issues here:
1. Currently, `FileQuery` does not actually respect object attachment edges when doing policy checks. Everything else works fine, but this was missing an `array_keys()`.
2. Once that's fixed, we hit a bunch of recursion issues. For example, when loading a User we load the profile picture, and then that loads the User, and that loads the profile picture, etc.
3. Introduce a "Query Workspace", which holds objects we know we've loaded and know we can see but haven't finished filtering and/or attaching data to. This allows subqueries to look up objects instead of querying for them.
- We can probably generalize this a bit to make a few other queries more efficient. Pholio currently has a similar (but less general) "mock cache". However, it's keyed by ID instead of PHID so it's not easy to reuse this right now.
This is a bit complex for the problem being solved, but I think it's the cleanest approach and I believe the primitive will be useful in the future.
Test Plan: Looked at pastes, macros, mocks and projects as a logged-in and logged-out user.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T603
Differential Revision: https://secure.phabricator.com/D7309
2013-10-14 14:36:06 -07:00
|
|
|
$profile = id(new PhabricatorProjectProfile())
|
|
|
|
->setProjectPHID($project->getPHID())
|
|
|
|
->attachProfileImageFile($default);
|
2013-10-06 17:07:20 -07:00
|
|
|
}
|
Fix some file policy issues and add a "Query Workspace"
Summary:
Ref T603. Several issues here:
1. Currently, `FileQuery` does not actually respect object attachment edges when doing policy checks. Everything else works fine, but this was missing an `array_keys()`.
2. Once that's fixed, we hit a bunch of recursion issues. For example, when loading a User we load the profile picture, and then that loads the User, and that loads the profile picture, etc.
3. Introduce a "Query Workspace", which holds objects we know we've loaded and know we can see but haven't finished filtering and/or attaching data to. This allows subqueries to look up objects instead of querying for them.
- We can probably generalize this a bit to make a few other queries more efficient. Pholio currently has a similar (but less general) "mock cache". However, it's keyed by ID instead of PHID so it's not easy to reuse this right now.
This is a bit complex for the problem being solved, but I think it's the cleanest approach and I believe the primitive will be useful in the future.
Test Plan: Looked at pastes, macros, mocks and projects as a logged-in and logged-out user.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T603
Differential Revision: https://secure.phabricator.com/D7309
2013-10-14 14:36:06 -07:00
|
|
|
$project->attachProfile($profile);
|
2013-10-06 17:07:20 -07:00
|
|
|
}
|
2012-01-17 16:29:35 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
return $projects;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function buildWhereClause($conn_r) {
|
|
|
|
$where = array();
|
|
|
|
|
2012-02-07 14:59:38 -08:00
|
|
|
if ($this->status != self::STATUS_ANY) {
|
|
|
|
switch ($this->status) {
|
|
|
|
case self::STATUS_OPEN:
|
|
|
|
case self::STATUS_ACTIVE:
|
2012-08-07 18:02:05 -07:00
|
|
|
$filter = array(
|
|
|
|
PhabricatorProjectStatus::STATUS_ACTIVE,
|
|
|
|
);
|
2012-02-07 14:59:38 -08:00
|
|
|
break;
|
2012-08-07 18:02:05 -07:00
|
|
|
case self::STATUS_CLOSED:
|
2012-02-07 14:59:38 -08:00
|
|
|
case self::STATUS_ARCHIVED:
|
2012-08-07 18:02:05 -07:00
|
|
|
$filter = array(
|
|
|
|
PhabricatorProjectStatus::STATUS_ARCHIVED,
|
|
|
|
);
|
2012-02-07 14:59:38 -08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new Exception(
|
|
|
|
"Unknown project status '{$this->status}'!");
|
|
|
|
}
|
2012-08-07 18:02:05 -07:00
|
|
|
$where[] = qsprintf(
|
|
|
|
$conn_r,
|
|
|
|
'status IN (%Ld)',
|
|
|
|
$filter);
|
2012-02-07 14:59:38 -08:00
|
|
|
}
|
|
|
|
|
2012-01-17 16:29:35 -08:00
|
|
|
if ($this->ids) {
|
|
|
|
$where[] = qsprintf(
|
|
|
|
$conn_r,
|
|
|
|
'id IN (%Ld)',
|
|
|
|
$this->ids);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->phids) {
|
|
|
|
$where[] = qsprintf(
|
|
|
|
$conn_r,
|
|
|
|
'phid IN (%Ls)',
|
|
|
|
$this->phids);
|
|
|
|
}
|
|
|
|
|
2012-08-07 18:02:05 -07:00
|
|
|
if ($this->memberPHIDs) {
|
|
|
|
$where[] = qsprintf(
|
|
|
|
$conn_r,
|
2012-08-11 07:05:01 -07:00
|
|
|
'e.dst IN (%Ls)',
|
2012-08-07 18:02:05 -07:00
|
|
|
$this->memberPHIDs);
|
|
|
|
}
|
|
|
|
|
2013-03-08 07:12:24 -08:00
|
|
|
if ($this->slugs) {
|
|
|
|
$where[] = qsprintf(
|
|
|
|
$conn_r,
|
|
|
|
'phrictionSlug IN (%Ls)',
|
|
|
|
$this->slugs);
|
|
|
|
}
|
|
|
|
|
2012-08-08 17:10:10 -07:00
|
|
|
$where[] = $this->buildPagingClause($conn_r);
|
|
|
|
|
2012-08-07 11:54:39 -07:00
|
|
|
return $this->formatWhereClause($where);
|
2011-12-16 17:08:18 -08:00
|
|
|
}
|
|
|
|
|
2012-08-07 18:02:05 -07:00
|
|
|
private function buildGroupClause($conn_r) {
|
|
|
|
if ($this->memberPHIDs) {
|
|
|
|
return 'GROUP BY p.id';
|
|
|
|
} else {
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
}
|
2011-12-16 17:08:18 -08:00
|
|
|
|
2012-08-07 18:02:05 -07:00
|
|
|
private function buildJoinClause($conn_r) {
|
2011-12-16 17:08:18 -08:00
|
|
|
$joins = array();
|
2012-08-07 18:02:05 -07:00
|
|
|
|
2012-08-11 07:05:01 -07:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
2012-08-07 11:54:24 -07:00
|
|
|
if ($this->memberPHIDs) {
|
2011-12-16 17:08:18 -08:00
|
|
|
$joins[] = qsprintf(
|
|
|
|
$conn_r,
|
2012-08-11 07:05:01 -07:00
|
|
|
'JOIN %T e ON e.src = p.phid AND e.type = %d',
|
|
|
|
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
|
|
|
|
PhabricatorEdgeConfig::TYPE_PROJ_MEMBER);
|
2011-12-16 17:08:18 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
return implode(' ', $joins);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|