Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2891578
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
85 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/applications/audit/editor/PhabricatorAuditCommentEditor.php b/src/applications/audit/editor/PhabricatorAuditCommentEditor.php
index 8a352a3a72..956f8e24f4 100644
--- a/src/applications/audit/editor/PhabricatorAuditCommentEditor.php
+++ b/src/applications/audit/editor/PhabricatorAuditCommentEditor.php
@@ -1,530 +1,530 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorAuditCommentEditor {
private $commit;
private $user;
private $attachInlineComments;
private $auditors = array();
private $ccs = array();
public function __construct(PhabricatorRepositoryCommit $commit) {
$this->commit = $commit;
return $this;
}
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
public function addAuditors(array $auditor_phids) {
$this->auditors = array_merge($this->auditors, $auditor_phids);
return $this;
}
public function addCCs(array $cc_phids) {
$this->ccs = array_merge($this->ccs, $cc_phids);
return $this;
}
public function setAttachInlineComments($attach_inline_comments) {
$this->attachInlineComments = $attach_inline_comments;
return $this;
}
public function addComment(PhabricatorAuditComment $comment) {
$commit = $this->commit;
$user = $this->user;
$other_comments = id(new PhabricatorAuditComment())->loadAllWhere(
'targetPHID = %s',
$commit->getPHID());
$inline_comments = array();
if ($this->attachInlineComments) {
$inline_comments = id(new PhabricatorAuditInlineComment())->loadAllWhere(
'authorPHID = %s AND commitPHID = %s
AND auditCommentID IS NULL',
$user->getPHID(),
$commit->getPHID());
}
$comment
->setActorPHID($user->getPHID())
->setTargetPHID($commit->getPHID())
->save();
$content_blocks = array($comment->getContent());
if ($inline_comments) {
foreach ($inline_comments as $inline) {
$inline->setAuditCommentID($comment->getID());
$inline->save();
$content_blocks[] = $inline->getContent();
}
}
$ccs = $this->ccs;
$auditors = $this->auditors;
$metadata = $comment->getMetadata();
$metacc = array();
// Find any "@mentions" in the content blocks.
$mention_ccs = PhabricatorMarkupEngine::extractPHIDsFromMentions(
$content_blocks);
if ($mention_ccs) {
$metacc = idx(
$metadata,
PhabricatorAuditComment::METADATA_ADDED_CCS,
array());
foreach ($mention_ccs as $cc_phid) {
$metacc[] = $cc_phid;
}
}
if ($metacc) {
$ccs = array_merge($ccs, $metacc);
}
// When a user submits an audit comment, we update all the audit requests
// they have authority over to reflect the most recent status. The general
// idea here is that if audit has triggered for, e.g., several packages, but
// a user owns all of them, they can clear the audit requirement in one go
// without auditing the commit for each trigger.
$audit_phids = self::loadAuditPHIDsForUser($this->user);
$audit_phids = array_fill_keys($audit_phids, true);
$requests = id(new PhabricatorRepositoryAuditRequest())
->loadAllWhere(
'commitPHID = %s',
$commit->getPHID());
$action = $comment->getAction();
// TODO: We should validate the action, currently we allow anyone to, e.g.,
// close an audit if they muck with form parameters. I'll followup with this
// and handle the no-effect cases (e.g., closing and already-closed audit).
$user_is_author = ($user->getPHID() == $commit->getAuthorPHID());
if ($action == PhabricatorAuditActionConstants::CLOSE) {
// "Close" means wipe out all the concerns.
$concerned_status = PhabricatorAuditStatusConstants::CONCERNED;
foreach ($requests as $request) {
if ($request->getAuditStatus() == $concerned_status) {
$request->setAuditStatus(PhabricatorAuditStatusConstants::CLOSED);
$request->save();
}
}
} else if ($action == PhabricatorAuditActionConstants::RESIGN) {
// "Resign" has unusual rules for writing user rows, only affects the
// user row (never package/project rows), and always affects the user
// row (other actions don't, if they were able to affect a package/project
// row).
$user_request = null;
foreach ($requests as $request) {
if ($request->getAuditorPHID() == $user->getPHID()) {
$user_request = $request;
break;
}
}
if (!$user_request) {
$user_request = id(new PhabricatorRepositoryAuditRequest())
->setCommitPHID($commit->getPHID())
->setAuditorPHID($user->getPHID())
->setAuditReasons(array("Resigned"));
}
$user_request
->setAuditStatus(PhabricatorAuditStatusConstants::RESIGNED)
->save();
$requests[] = $user_request;
} else {
$have_any_requests = false;
foreach ($requests as $request) {
if (empty($audit_phids[$request->getAuditorPHID()])) {
continue;
}
$request_is_for_user = ($request->getAuditorPHID() == $user->getPHID());
$have_any_requests = true;
$new_status = null;
switch ($action) {
case PhabricatorAuditActionConstants::COMMENT:
case PhabricatorAuditActionConstants::ADD_CCS:
case PhabricatorAuditActionConstants::ADD_AUDITORS:
// Commenting or adding cc's/auditors doesn't change status.
break;
case PhabricatorAuditActionConstants::ACCEPT:
if (!$user_is_author || $request_is_for_user) {
// When modifying your own commits, you act only on behalf of
// yourself, not your packages/projects -- the idea being that
// you can't accept your own commits.
$new_status = PhabricatorAuditStatusConstants::ACCEPTED;
}
break;
case PhabricatorAuditActionConstants::CONCERN:
if (!$user_is_author || $request_is_for_user) {
// See above.
$new_status = PhabricatorAuditStatusConstants::CONCERNED;
}
break;
default:
throw new Exception("Unknown action '{$action}'!");
}
if ($new_status !== null) {
$request->setAuditStatus($new_status);
$request->save();
}
}
// If the user has no current authority over any audit trigger, make a
// new one to represent their audit state.
if (!$have_any_requests) {
$new_status = null;
switch ($action) {
case PhabricatorAuditActionConstants::COMMENT:
case PhabricatorAuditActionConstants::ADD_CCS:
case PhabricatorAuditActionConstants::ADD_AUDITORS:
$new_status = PhabricatorAuditStatusConstants::AUDIT_NOT_REQUIRED;
break;
case PhabricatorAuditActionConstants::ACCEPT:
$new_status = PhabricatorAuditStatusConstants::ACCEPTED;
break;
case PhabricatorAuditActionConstants::CONCERN:
$new_status = PhabricatorAuditStatusConstants::CONCERNED;
break;
case PhabricatorAuditActionConstants::CLOSE:
// Impossible to reach this block with 'close'.
default:
throw new Exception("Unknown or invalid action '{$action}'!");
}
$request = id(new PhabricatorRepositoryAuditRequest())
->setCommitPHID($commit->getPHID())
->setAuditorPHID($user->getPHID())
->setAuditStatus($new_status)
->setAuditReasons(array("Voluntary Participant"))
->save();
$requests[] = $request;
}
}
$requests_by_auditor = mpull($requests, null, 'getAuditorPHID');
$requests_phids = array_keys($requests_by_auditor);
$ccs = array_diff($ccs, $requests_phids);
$auditors = array_diff($auditors, $requests_phids);
if ($action == PhabricatorAuditActionConstants::ADD_CCS) {
if ($ccs) {
$metadata[PhabricatorAuditComment::METADATA_ADDED_CCS] = $ccs;
$comment->setMetaData($metadata);
} else {
$comment->setAction(PhabricatorAuditActionConstants::COMMENT);
}
}
if ($action == PhabricatorAuditActionConstants::ADD_AUDITORS) {
if ($auditors) {
$metadata[PhabricatorAuditComment::METADATA_ADDED_AUDITORS]
= $auditors;
$comment->setMetaData($metadata);
} else {
$comment->setAction(PhabricatorAuditActionConstants::COMMENT);
}
}
$comment->save();
if ($auditors) {
foreach ($auditors as $auditor_phid) {
$audit_requested = PhabricatorAuditStatusConstants::AUDIT_REQUESTED;
$requests[] = id (new PhabricatorRepositoryAuditRequest())
->setCommitPHID($commit->getPHID())
->setAuditorPHID($auditor_phid)
->setAuditStatus($audit_requested)
->setAuditReasons(
array('Added by ' . $user->getUsername()))
->save();
}
}
if ($ccs) {
foreach ($ccs as $cc_phid) {
$audit_cc = PhabricatorAuditStatusConstants::CC;
$requests[] = id (new PhabricatorRepositoryAuditRequest())
->setCommitPHID($commit->getPHID())
->setAuditorPHID($cc_phid)
->setAuditStatus($audit_cc)
->setAuditReasons(
array('Added by ' . $user->getUsername()))
->save();
}
}
$commit->updateAuditStatus($requests);
$commit->save();
$this->publishFeedStory($comment, array_keys($audit_phids));
PhabricatorSearchCommitIndexer::indexCommit($commit);
$this->sendMail($comment, $other_comments, $inline_comments, $requests);
}
/**
* Load the PHIDs for all objects the user has the authority to act as an
* audit for. This includes themselves, and any packages they are an owner
* of.
*/
public static function loadAuditPHIDsForUser(PhabricatorUser $user) {
$phids = array();
// The user can audit on their own behalf.
$phids[$user->getPHID()] = true;
// The user can audit on behalf of all packages they own.
$owned_packages = PhabricatorOwnersOwner::loadAffiliatedPackages(
$user->getPHID());
if ($owned_packages) {
$packages = id(new PhabricatorOwnersPackage())->loadAllWhere(
'id IN (%Ld)',
mpull($owned_packages, 'getPackageID'));
foreach (mpull($packages, 'getPHID') as $phid) {
$phids[$phid] = true;
}
}
// The user can audit on behalf of all projects they are a member of.
$query = new PhabricatorProjectQuery();
- $query->setMembers(array($user->getPHID()));
+ $query->withMemberPHIDs(array($user->getPHID()));
$projects = $query->execute();
foreach ($projects as $project) {
$phids[$project->getPHID()] = true;
}
return array_keys($phids);
}
private function publishFeedStory(
PhabricatorAuditComment $comment,
array $more_phids) {
$commit = $this->commit;
$user = $this->user;
$related_phids = array_merge(
array(
$user->getPHID(),
$commit->getPHID(),
),
$more_phids);
id(new PhabricatorFeedStoryPublisher())
->setRelatedPHIDs($related_phids)
->setStoryAuthorPHID($user->getPHID())
->setStoryTime(time())
->setStoryType(PhabricatorFeedStoryTypeConstants::STORY_AUDIT)
->setStoryData(
array(
'commitPHID' => $commit->getPHID(),
'action' => $comment->getAction(),
'content' => $comment->getContent(),
))
->publish();
}
private function sendMail(
PhabricatorAuditComment $comment,
array $other_comments,
array $inline_comments,
array $requests) {
assert_instances_of($other_comments, 'PhabricatorAuditComment');
assert_instances_of($inline_comments, 'PhabricatorInlineCommentInterface');
$commit = $this->commit;
$data = $commit->loadCommitData();
$summary = $data->getSummary();
$commit_phid = $commit->getPHID();
$phids = array($commit_phid);
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
$handle = $handles[$commit_phid];
$name = $handle->getName();
$map = array(
PhabricatorAuditActionConstants::CONCERN => 'Raised Concern',
PhabricatorAuditActionConstants::ACCEPT => 'Accepted',
PhabricatorAuditActionConstants::RESIGN => 'Resigned',
PhabricatorAuditActionConstants::CLOSE => 'Closed',
PhabricatorAuditActionConstants::ADD_CCS => 'Added CCs',
PhabricatorAuditActionConstants::ADD_AUDITORS => 'Added Auditors',
);
$verb = idx($map, $comment->getAction(), 'Commented On');
$reply_handler = self::newReplyHandlerForCommit($commit);
$prefix = PhabricatorEnv::getEnvConfig('metamta.diffusion.subject-prefix');
$repository = id(new PhabricatorRepository())
->load($commit->getRepositoryID());
$threading = self::getMailThreading($repository, $commit);
list($thread_id, $thread_topic) = $threading;
$body = $this->renderMailBody(
$comment,
"{$name}: {$summary}",
$handle,
$reply_handler,
$inline_comments);
$email_to = array();
$email_cc = array();
$author_phid = $data->getCommitDetail('authorPHID');
if ($author_phid) {
$email_to[] = $author_phid;
}
$email_cc = array();
foreach ($other_comments as $other_comment) {
$email_cc[] = $other_comment->getActorPHID();
}
foreach ($requests as $request) {
if ($request->getAuditStatus() == PhabricatorAuditStatusConstants::CC) {
$email_cc[] = $request->getAuditorPHID();
}
}
$phids = array_merge($email_to, $email_cc);
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
// NOTE: Always set $is_new to false, because the "first" mail in the
// thread is the Herald notification of the commit.
$is_new = false;
$template = id(new PhabricatorMetaMTAMail())
->setSubject("{$name}: {$summary}")
->setSubjectPrefix($prefix)
->setVarySubjectPrefix("[{$verb}]")
->setFrom($comment->getActorPHID())
->setThreadID($thread_id, $is_new)
->addHeader('Thread-Topic', $thread_topic)
->setRelatedPHID($commit->getPHID())
->setIsBulk(true)
->setBody($body);
$mails = $reply_handler->multiplexMail(
$template,
array_select_keys($handles, $email_to),
array_select_keys($handles, $email_cc));
foreach ($mails as $mail) {
$mail->saveAndSend();
}
}
public static function getMailThreading(
PhabricatorRepository $repository,
PhabricatorRepositoryCommit $commit) {
return array(
'diffusion-audit-'.$commit->getPHID(),
'Commit r'.$repository->getCallsign().$commit->getCommitIdentifier(),
);
}
public static function newReplyHandlerForCommit($commit) {
$reply_handler = PhabricatorEnv::newObjectFromConfig(
'metamta.diffusion.reply-handler');
$reply_handler->setMailReceiver($commit);
return $reply_handler;
}
private function renderMailBody(
PhabricatorAuditComment $comment,
$cname,
PhabricatorObjectHandle $handle,
PhabricatorMailReplyHandler $reply_handler,
array $inline_comments) {
assert_instances_of($inline_comments, 'PhabricatorInlineCommentInterface');
$commit = $this->commit;
$user = $this->user;
$name = $user->getUsername();
$verb = PhabricatorAuditActionConstants::getActionPastTenseVerb(
$comment->getAction());
$body = new PhabricatorMetaMTAMailBody();
$body->addRawSection("{$name} {$verb} commit {$cname}.");
$body->addRawSection($comment->getContent());
if ($inline_comments) {
$block = array();
$path_map = id(new DiffusionPathQuery())
->withPathIDs(mpull($inline_comments, 'getPathID'))
->execute();
$path_map = ipull($path_map, 'path', 'id');
foreach ($inline_comments as $inline) {
$path = idx($path_map, $inline->getPathID());
if ($path === null) {
continue;
}
$start = $inline->getLineNumber();
$len = $inline->getLineLength();
if ($len) {
$range = $start.'-'.($start + $len);
} else {
$range = $start;
}
$content = $inline->getContent();
$block[] = "{$path}:{$range} {$content}";
}
$body->addTextSection(pht('INLINE COMMENTS'), implode("\n", $block));
}
$body->addTextSection(
pht('COMMIT'),
PhabricatorEnv::getProductionURI($handle->getURI()));
$body->addReplySection($reply_handler->getReplyHandlerInstructions());
return $body->render();
}
}
diff --git a/src/applications/conduit/method/project/ConduitAPI_project_query_Method.php b/src/applications/conduit/method/project/ConduitAPI_project_query_Method.php
index 28b312739e..e8404dcb5d 100644
--- a/src/applications/conduit/method/project/ConduitAPI_project_query_Method.php
+++ b/src/applications/conduit/method/project/ConduitAPI_project_query_Method.php
@@ -1,97 +1,97 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group conduit
*/
final class ConduitAPI_project_query_Method extends ConduitAPI_project_Method {
public function getMethodDescription() {
return "Execute searches for Projects.";
}
public function defineParamTypes() {
$statuses = array(
PhabricatorProjectQuery::STATUS_ANY,
PhabricatorProjectQuery::STATUS_OPEN,
PhabricatorProjectQuery::STATUS_CLOSED,
PhabricatorProjectQuery::STATUS_ACTIVE,
PhabricatorProjectQuery::STATUS_ARCHIVED,
);
return array(
'ids' => 'optional list<int>',
'phids' => 'optional list<phid>',
'status' => 'optional enum<'.implode(', ', $statuses).'>',
'members' => 'optional list<phid>',
'limit' => 'optional int',
'offset' => 'optional int',
);
}
public function defineReturnType() {
return 'list';
}
public function defineErrorTypes() {
return array(
);
}
protected function execute(ConduitAPIRequest $request) {
$query = new PhabricatorProjectQuery();
$query->needMembers(true);
$ids = $request->getValue('ids');
if ($ids) {
$query->withIDs($ids);
}
$status = $request->getValue('status');
if ($status) {
$query->withStatus($status);
}
$phids = $request->getValue('phids');
if ($phids) {
$query->withPHIDs($phids);
}
$members = $request->getValue('members');
if ($members) {
- $query->setMembers($members);
+ $query->withMemberPHIDs($members);
}
$limit = $request->getValue('limit');
if ($limit) {
$query->setLimit($limit);
}
$offset = $request->getValue('offset');
if ($offset) {
$query->setOffset($offset);
}
$results = $query->execute();
return $this->buildProjectInfoDictionaries($results);
}
}
diff --git a/src/applications/directory/controller/PhabricatorDirectoryMainController.php b/src/applications/directory/controller/PhabricatorDirectoryMainController.php
index 5fd117f911..7cd7424b1e 100644
--- a/src/applications/directory/controller/PhabricatorDirectoryMainController.php
+++ b/src/applications/directory/controller/PhabricatorDirectoryMainController.php
@@ -1,847 +1,847 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorDirectoryMainController
extends PhabricatorDirectoryController {
private $filter;
private $subfilter;
public function willProcessRequest(array $data) {
$this->filter = idx($data, 'filter');
$this->subfilter = idx($data, 'subfilter');
}
public function processRequest() {
$user = $this->getRequest()->getUser();
$nav = $this->buildNav();
$this->filter = $nav->selectFilter($this->filter, 'home');
switch ($this->filter) {
case 'jump':
case 'apps':
break;
case 'home':
case 'feed':
$project_query = new PhabricatorProjectQuery();
- $project_query->setMembers(array($user->getPHID()));
+ $project_query->withMemberPHIDs(array($user->getPHID()));
$projects = $project_query->execute();
break;
default:
throw new Exception("Unknown filter '{$this->filter}'!");
}
switch ($this->filter) {
case 'feed':
return $this->buildFeedResponse($nav, $projects);
case 'jump':
return $this->buildJumpResponse($nav);
case 'apps':
return $this->buildAppsResponse($nav);
default:
return $this->buildMainResponse($nav, $projects);
}
}
private function buildMainResponse($nav, array $projects) {
assert_instances_of($projects, 'PhabricatorProject');
if (PhabricatorEnv::getEnvConfig('maniphest.enabled')) {
$unbreak_panel = $this->buildUnbreakNowPanel();
$triage_panel = $this->buildNeedsTriagePanel($projects);
$tasks_panel = $this->buildTasksPanel();
} else {
$unbreak_panel = null;
$triage_panel = null;
$tasks_panel = null;
}
$flagged_panel = $this->buildFlaggedPanel();
$jump_panel = $this->buildJumpPanel();
$revision_panel = $this->buildRevisionPanel();
$app_panel = $this->buildAppPanel();
$audit_panel = $this->buildAuditPanel();
$commit_panel = $this->buildCommitPanel();
$content = array(
$app_panel,
$jump_panel,
$unbreak_panel,
$triage_panel,
$revision_panel,
$tasks_panel,
$flagged_panel,
$audit_panel,
$commit_panel,
);
$nav->appendChild($content);
return $this->buildStandardPageResponse(
$nav,
array(
'title' => 'Phabricator',
));
}
private function buildJumpResponse($nav) {
$request = $this->getRequest();
$jump = $request->getStr('jump');
$response = PhabricatorJumpNavHandler::jumpPostResponse($jump);
if ($response) {
return $response;
} else if ($request->isFormPost()) {
$query = new PhabricatorSearchQuery();
$query->setQuery($jump);
$query->save();
return id(new AphrontRedirectResponse())
->setURI('/search/'.$query->getQueryKey().'/');
}
$nav->appendChild($this->buildJumpPanel($jump));
return $this->buildStandardPageResponse(
$nav,
array(
'title' => 'Jump Nav',
));
}
private function buildFeedResponse($nav, array $projects) {
assert_instances_of($projects, 'PhabricatorProject');
$subnav = new AphrontSideNavFilterView();
$subnav->setBaseURI(new PhutilURI('/feed/'));
$subnav->addFilter('all', 'All Activity', '/feed/');
$subnav->addFilter('projects', 'My Projects');
$nav->appendChild($subnav);
$filter = $subnav->selectFilter($this->subfilter, 'all');
switch ($filter) {
case 'all':
$view = $this->buildFeedView(array());
break;
case 'projects':
if ($projects) {
$phids = mpull($projects, 'getPHID');
$view = $this->buildFeedView($phids);
} else {
$view = new AphrontErrorView();
$view->setSeverity(AphrontErrorView::SEVERITY_NODATA);
$view->setTitle('No Projects');
$view->appendChild('You have not joined any projects.');
}
break;
}
$subnav->appendChild($view);
return $this->buildStandardPageResponse(
$nav,
array(
'title' => 'Feed',
));
}
private function buildUnbreakNowPanel() {
$user = $this->getRequest()->getUser();
$user_phid = $user->getPHID();
$task_query = new ManiphestTaskQuery();
$task_query->withStatus(ManiphestTaskQuery::STATUS_OPEN);
$task_query->withPriority(ManiphestTaskPriority::PRIORITY_UNBREAK_NOW);
$task_query->setLimit(10);
$tasks = $task_query->execute();
if (!$tasks) {
return $this->renderMiniPanel(
'No "Unbreak Now!" Tasks',
'Nothing appears to be critically broken right now.');
}
$panel = new AphrontPanelView();
$panel->setHeader('Unbreak Now!');
$panel->setCaption('Open tasks with "Unbreak Now!" priority.');
$panel->addButton(
phutil_render_tag(
'a',
array(
'href' => '/maniphest/view/all/',
'class' => 'grey button',
),
"View All Unbreak Now \xC2\xBB"));
$panel->appendChild($this->buildTaskListView($tasks));
return $panel;
}
private function buildFlaggedPanel() {
$user = $this->getRequest()->getUser();
$flag_query = id(new PhabricatorFlagQuery())
->withOwnerPHIDs(array($user->getPHID()))
->needHandles(true)
->setLimit(10);
$flags = $flag_query->execute();
if (!$flags) {
return $this->renderMiniPanel(
'No Flags',
"You haven't flagged anything.");
}
$panel = new AphrontPanelView();
$panel->setHeader('Flagged Objects');
$panel->setCaption("Objects you've flagged.");
$flag_view = new PhabricatorFlagListView();
$flag_view->setFlags($flags);
$flag_view->setUser($user);
$panel->appendChild($flag_view);
$panel->addButton(
phutil_render_tag(
'a',
array(
'href' => '/flag/',
'class' => 'grey button',
),
"View All Flags \xC2\xBB"));
return $panel;
}
private function buildNeedsTriagePanel(array $projects) {
assert_instances_of($projects, 'PhabricatorProject');
$user = $this->getRequest()->getUser();
$user_phid = $user->getPHID();
if ($projects) {
$task_query = new ManiphestTaskQuery();
$task_query->withStatus(ManiphestTaskQuery::STATUS_OPEN);
$task_query->withPriority(ManiphestTaskPriority::PRIORITY_TRIAGE);
$task_query->withProjects(mpull($projects, 'getPHID'));
$task_query->withAnyProject(true);
$task_query->setLimit(10);
$tasks = $task_query->execute();
} else {
$tasks = array();
}
if (!$tasks) {
return $this->renderMiniPanel(
'No "Needs Triage" Tasks',
'No tasks in <a href="/project/">projects you are a member of</a> '.
'need triage.');
}
$panel = new AphrontPanelView();
$panel->setHeader('Needs Triage');
$panel->setCaption(
'Open tasks with "Needs Triage" priority in '.
'<a href="/project/">projects you are a member of</a>.');
$panel->addButton(
phutil_render_tag(
'a',
array(
// TODO: This should filter to just your projects' need-triage
// tasks?
'href' => '/maniphest/view/projecttriage/',
'class' => 'grey button',
),
"View All Triage \xC2\xBB"));
$panel->appendChild($this->buildTaskListView($tasks));
return $panel;
}
private function buildRevisionPanel() {
$user = $this->getRequest()->getUser();
$user_phid = $user->getPHID();
$revision_query = new DifferentialRevisionQuery();
$revision_query->withStatus(DifferentialRevisionQuery::STATUS_OPEN);
$revision_query->withResponsibleUsers(array($user_phid));
$revision_query->needRelationships(true);
// NOTE: We need to unlimit this query to hit the responsible user
// fast-path.
$revision_query->setLimit(null);
$revisions = $revision_query->execute();
list($active, $waiting) = DifferentialRevisionQuery::splitResponsible(
$revisions,
$user_phid);
if (!$active) {
return $this->renderMiniPanel(
'No Waiting Revisions',
'No revisions are waiting on you.');
}
$panel = new AphrontPanelView();
$panel->setHeader('Revisions Waiting on You');
$panel->setCaption('Revisions waiting for you for review or commit.');
$panel->addButton(
phutil_render_tag(
'a',
array(
'href' => '/differential/',
'class' => 'button grey',
),
"View Active Revisions \xC2\xBB"));
$revision_view = id(new DifferentialRevisionListView())
->setRevisions($active)
->setFields(DifferentialRevisionListView::getDefaultFields())
->setUser($user);
$phids = array_merge(
array($user_phid),
$revision_view->getRequiredHandlePHIDs());
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
$revision_view->setHandles($handles);
$panel->appendChild($revision_view);
return $panel;
}
private function buildTasksPanel() {
$user = $this->getRequest()->getUser();
$user_phid = $user->getPHID();
$task_query = new ManiphestTaskQuery();
$task_query->withStatus(ManiphestTaskQuery::STATUS_OPEN);
$task_query->setGroupBy(ManiphestTaskQuery::GROUP_PRIORITY);
$task_query->withOwners(array($user_phid));
$task_query->setLimit(10);
$tasks = $task_query->execute();
if (!$tasks) {
return $this->renderMiniPanel(
'No Assigned Tasks',
'You have no assigned tasks.');
}
$panel = new AphrontPanelView();
$panel->setHeader('Assigned Tasks');
$panel->setCaption('Tasks assigned to you.');
$panel->addButton(
phutil_render_tag(
'a',
array(
'href' => '/maniphest/',
'class' => 'button grey',
),
"View Active Tasks \xC2\xBB"));
$panel->appendChild($this->buildTaskListView($tasks));
return $panel;
}
private function buildTaskListView(array $tasks) {
assert_instances_of($tasks, 'ManiphestTask');
$user = $this->getRequest()->getUser();
$phids = array_merge(
array_filter(mpull($tasks, 'getOwnerPHID')),
array_mergev(mpull($tasks, 'getProjectPHIDs')));
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
$view = new ManiphestTaskListView();
$view->setTasks($tasks);
$view->setUser($user);
$view->setHandles($handles);
return $view;
}
private function buildFeedView(array $phids) {
$request = $this->getRequest();
$user = $request->getUser();
$user_phid = $user->getPHID();
$feed_query = new PhabricatorFeedQuery();
$feed_query->setViewer($user);
if ($phids) {
$feed_query->setFilterPHIDs($phids);
}
$pager = new AphrontCursorPagerView();
$pager->readFromRequest($request);
$pager->setPageSize(200);
$feed = $feed_query->executeWithCursorPager($pager);
$builder = new PhabricatorFeedBuilder($feed);
$builder->setUser($user);
$feed_view = $builder->buildView();
return
'<div style="padding: 1em 3em;">'.
'<div style="margin: 0 1em;">'.
'<h1 style="font-size: 18px; '.
'border-bottom: 1px solid #aaaaaa; '.
'padding: 0;">Feed</h1>'.
'</div>'.
$feed_view->render().
'<div class="phabricator-feed-frame">'.
$pager->render().
'</div>'.
'</div>';
}
private function buildJumpPanel($query=null) {
$request = $this->getRequest();
$user = $request->getUser();
$uniq_id = celerity_generate_unique_node_id();
Javelin::initBehavior(
'phabricator-autofocus',
array(
'id' => $uniq_id,
));
require_celerity_resource('phabricator-jump-nav');
$doc_href = PhabricatorEnv::getDocLink('article/Jump_Nav_User_Guide.html');
$doc_link = phutil_render_tag(
'a',
array(
'href' => $doc_href,
),
'Jump Nav User Guide');
$jump_input = phutil_render_tag(
'input',
array(
'type' => 'text',
'class' => 'phabricator-jump-nav',
'name' => 'jump',
'id' => $uniq_id,
'value' => $query,
));
$jump_caption = phutil_render_tag(
'p',
array(
'class' => 'phabricator-jump-nav-caption',
),
'Enter the name of an object like <tt>D123</tt> to quickly jump to '.
'it. See '.$doc_link.' or type <tt>help</tt>.');
$panel = new AphrontPanelView();
$panel->addClass('aphront-unpadded-panel-view');
$panel->appendChild(
phabricator_render_form(
$user,
array(
'action' => '/jump/',
'method' => 'POST',
'class' => 'phabricator-jump-nav-form',
),
$jump_input.
$jump_caption));
return $panel;
}
private function buildAppPanel() {
require_celerity_resource('phabricator-app-buttons-css');
$nav_buttons = array();
$nav_buttons[] = array(
'Differential',
'/differential/',
'differential',
'Code Reviews');
if (PhabricatorEnv::getEnvConfig('maniphest.enabled')) {
$nav_buttons[] = array(
'Maniphest',
'/maniphest/',
'maniphest',
'Tasks');
$nav_buttons[] = array(
'Create Task',
'/maniphest/task/create/',
'create-task');
}
$nav_buttons[] = array(
'Upload File',
'/file/',
'upload-file',
'Share Files');
$nav_buttons[] = array(
'Create Paste',
'/paste/',
'create-paste',
'Share Text');
if (PhabricatorEnv::getEnvConfig('phriction.enabled')) {
$nav_buttons[] = array(
'Phriction',
'/w/',
'phriction',
'Browse Wiki');
}
$nav_buttons[] = array(
'Diffusion',
'/diffusion/',
'diffusion',
'Browse Code');
$nav_buttons[] = array(
'Audit',
'/audit/',
'audit',
'Audit Code');
$view = new AphrontNullView();
$view->appendChild('<div class="phabricator-app-buttons">');
foreach ($nav_buttons as $info) {
// Subtitle is optional.
list($name, $uri, $icon, $subtitle) = array_merge($info, array(null));
if ($subtitle) {
$subtitle =
'<div class="phabricator-app-subtitle">'.
phutil_escape_html($subtitle).
'</div>';
}
$button = phutil_render_tag(
'a',
array(
'href' => $uri,
'class' => 'app-button icon-'.$icon,
),
phutil_render_tag(
'div',
array(
'class' => 'app-icon icon-'.$icon,
),
''));
$caption = phutil_render_tag(
'a',
array(
'href' => $uri,
'class' => 'phabricator-button-caption',
),
phutil_escape_html($name).$subtitle);
$view->appendChild(
'<div class="phabricator-app-button">'.
$button.
$caption.
'</div>');
}
$view->appendChild('<div style="clear: both;"></div></div>');
return $view;
}
private function renderMiniPanel($title, $body) {
$panel = new AphrontMiniPanelView();
$panel->appendChild(
phutil_render_tag(
'p',
array(
),
'<strong>'.$title.':</strong> '.$body));
return $panel;
}
public function buildAuditPanel() {
$request = $this->getRequest();
$user = $request->getUser();
$phids = PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($user);
$query = new PhabricatorAuditQuery();
$query->withAuditorPHIDs($phids);
$query->withStatus(PhabricatorAuditQuery::STATUS_OPEN);
$query->withAwaitingUser($user);
$query->needCommitData(true);
$query->setLimit(10);
$audits = $query->execute();
$commits = $query->getCommits();
if (!$audits) {
return $this->renderMinipanel(
'No Audits',
'No commits are waiting for you to audit them.');
}
$view = new PhabricatorAuditListView();
$view->setAudits($audits);
$view->setCommits($commits);
$view->setUser($user);
$phids = $view->getRequiredHandlePHIDs();
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
$view->setHandles($handles);
$panel = new AphrontPanelView();
$panel->setHeader('Audits');
$panel->setCaption('Commits awaiting your audit.');
$panel->appendChild($view);
$panel->addButton(
phutil_render_tag(
'a',
array(
'href' => '/audit/',
'class' => 'button grey',
),
"View Active Audits \xC2\xBB"));
return $panel;
}
public function buildCommitPanel() {
$request = $this->getRequest();
$user = $request->getUser();
$phids = array($user->getPHID());
$query = new PhabricatorAuditCommitQuery();
$query->withAuthorPHIDs($phids);
$query->withStatus(PhabricatorAuditQuery::STATUS_OPEN);
$query->needCommitData(true);
$query->setLimit(10);
$commits = $query->execute();
if (!$commits) {
return $this->renderMinipanel(
'No Problem Commits',
'No one has raised concerns with your commits.');
}
$view = new PhabricatorAuditCommitListView();
$view->setCommits($commits);
$view->setUser($user);
$phids = $view->getRequiredHandlePHIDs();
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
$view->setHandles($handles);
$panel = new AphrontPanelView();
$panel->setHeader('Problem Commits');
$panel->setCaption('Commits which auditors have raised concerns about.');
$panel->appendChild($view);
$panel->addButton(
phutil_render_tag(
'a',
array(
'href' => '/audit/',
'class' => 'button grey',
),
"View Problem Commits \xC2\xBB"));
return $panel;
}
public function buildAppsResponse(AphrontSideNavFilterView $nav) {
$user = $this->getRequest()->getUser();
$apps = array(
array(
'/repository/',
'Repositories',
'Configure tracked source code repositories.',
),
array(
'/herald/',
'Herald',
'Create notification rules. Watch for danger!',
),
array(
'/file/',
'Files',
'Upload and download files. Blob store for Pokemon pictures.',
),
array(
'/project/',
'Projects',
'Group stuff into big piles.',
),
array(
'/vote/',
'Slowvote',
'Create polls. Design by committee.',
),
array(
'/countdown/',
'Countdown',
'Count down to events. Utilize the full capabilities of your ALU.',
),
array(
'/people/',
'People',
'User directory. Sort of a social utility.',
),
array(
'/owners/',
'Owners',
'Keep track of who owns code. Adopt today!',
),
array(
'/conduit/',
'Conduit API Console',
'Web console for Conduit API.',
),
array(
'/daemon/',
'Daemon Console',
'Offline process management.',
),
array(
'/mail/',
'MetaMTA',
'Manage mail delivery. Yo dawg, we heard you like MTAs.',
array(
'admin' => true,
),
),
array(
'/phid/',
'PHID Manager',
'Debugging tool for PHIDs.',
),
array(
'/xhpast/',
'XHPAST',
'Web interface to PHP AST tool. Lex XHP AST & CTS FYI, LOL.',
),
array(
'http://www.phabricator.com/docs/phabricator/',
'Phabricator Ducks',
'Oops, that should say "Docs".',
array(
'new' => true,
),
),
array(
'http://www.phabricator.com/docs/arcanist/',
'Arcanist Docs',
'Words have never been so finely crafted.',
array(
'new' => true,
),
),
array(
'http://www.phabricator.com/docs/libphutil/',
'libphutil Docs',
'Soothing prose; seductive poetry.',
array(
'new' => true,
),
),
array(
'http://www.phabricator.com/docs/javelin/',
'Javelin Docs',
'O, what noble scribe hath penned these words?',
array(
'new' => true,
),
),
array(
'/uiexample/',
'UI Examples',
'Phabricator UI elements. A gallery of modern art.',
array(
'new' => true,
),
),
);
$out = array();
foreach ($apps as $app) {
if (empty($app[3])) {
$app[3] = array();
}
$app[3] += array(
'admin' => false,
'new' => false,
);
list($href, $name, $desc, $options) = $app;
if ($options['admin'] && !$user->getIsAdmin()) {
continue;
}
$link = phutil_render_tag(
'a',
array(
'href' => $href,
'target' => $options['new'] ? '_blank' : null,
),
phutil_escape_html($name));
$out[] =
'<div class="aphront-directory-item">'.
'<h1>'.$link.'</h1>'.
'<p>'.phutil_escape_html($desc).'</p>'.
'</div>';
}
require_celerity_resource('phabricator-directory-css');
$out =
'<div class="aphront-directory-list">'.
implode("\n", $out).
'</div>';
$nav->appendChild($out);
return $this->buildStandardPageResponse(
$nav,
array(
'title' => 'More Stuff',
));
}
}
diff --git a/src/applications/maniphest/controller/ManiphestTaskListController.php b/src/applications/maniphest/controller/ManiphestTaskListController.php
index 69fdb0ce6f..7a0d88b74b 100644
--- a/src/applications/maniphest/controller/ManiphestTaskListController.php
+++ b/src/applications/maniphest/controller/ManiphestTaskListController.php
@@ -1,910 +1,910 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group maniphest
*/
final class ManiphestTaskListController extends ManiphestController {
const DEFAULT_PAGE_SIZE = 1000;
private $view;
public function willProcessRequest(array $data) {
$this->view = idx($data, 'view');
}
private function getArrToStrList($key) {
$arr = $this->getRequest()->getArr($key);
$arr = implode(',', $arr);
return nonempty($arr, null);
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
if ($request->isFormPost()) {
// Redirect to GET so URIs can be copy/pasted.
$task_ids = $request->getStr('set_tasks');
$task_ids = nonempty($task_ids, null);
$search_text = $request->getStr('set_search');
$search_text = nonempty($search_text, null);
$min_priority = $request->getInt('set_lpriority');
$min_priority = nonempty($min_priority, null);
$max_priority = $request->getInt('set_hpriority');
$max_priority = nonempty($max_priority, null);
$uri = $request->getRequestURI()
->alter('users', $this->getArrToStrList('set_users'))
->alter('projects', $this->getArrToStrList('set_projects'))
->alter('xprojects', $this->getArrToStrList('set_xprojects'))
->alter('owners', $this->getArrToStrList('set_owners'))
->alter('authors', $this->getArrToStrList('set_authors'))
->alter('lpriority', $min_priority)
->alter('hpriority', $max_priority)
->alter('tasks', $task_ids)
->alter('search', $search_text);
return id(new AphrontRedirectResponse())->setURI($uri);
}
$nav = $this->buildBaseSideNav();
$has_filter = array(
'action' => true,
'created' => true,
'subscribed' => true,
'triage' => true,
'projecttriage' => true,
'projectall' => true,
);
$query = null;
$key = $request->getStr('key');
if (!$key && !$this->view) {
if ($this->getDefaultQuery()) {
$key = $this->getDefaultQuery()->getQueryKey();
}
}
if ($key) {
$query = id(new PhabricatorSearchQuery())->loadOneWhere(
'queryKey = %s',
$key);
}
// If the user is running a saved query, load query parameters from that
// query. Otherwise, build a new query object from the HTTP request.
if ($query) {
$nav->selectFilter('Q:'.$query->getQueryKey(), 'custom');
$this->view = 'custom';
} else {
$this->view = $nav->selectFilter($this->view, 'action');
$query = $this->buildQueryFromRequest();
}
// Execute the query.
list($tasks, $handles, $total_count) = self::loadTasks($query);
// Extract information we need to render the filters from the query.
$search_text = $query->getParameter('fullTextSearch');
$user_phids = $query->getParameter('userPHIDs', array());
$task_ids = $query->getParameter('taskIDs', array());
$owner_phids = $query->getParameter('ownerPHIDs', array());
$author_phids = $query->getParameter('authorPHIDs', array());
$project_phids = $query->getParameter('projectPHIDs', array());
$exclude_project_phids = $query->getParameter(
'excludeProjectPHIDs',
array());
$low_priority = $query->getParameter('lowPriority');
$high_priority = $query->getParameter('highPriority');
$page_size = $query->getParameter('limit');
$page = $query->getParameter('offset');
$q_status = $query->getParameter('status');
$q_group = $query->getParameter('group');
$q_order = $query->getParameter('order');
$form = id(new AphrontFormView())
->setUser($user)
->setAction(
$request->getRequestURI()
->alter('key', null)
->alter(
$this->getStatusRequestKey(),
$this->getStatusRequestValue($q_status))
->alter(
$this->getOrderRequestKey(),
$this->getOrderRequestValue($q_order))
->alter(
$this->getGroupRequestKey(),
$this->getGroupRequestValue($q_group)));
if (isset($has_filter[$this->view])) {
$tokens = array();
foreach ($user_phids as $phid) {
$tokens[$phid] = $handles[$phid]->getFullName();
}
$form->appendChild(
id(new AphrontFormTokenizerControl())
->setDatasource('/typeahead/common/searchowner/')
->setName('set_users')
->setLabel('Users')
->setValue($tokens));
}
if ($this->view == 'custom') {
$form->appendChild(
id(new AphrontFormTextControl())
->setName('set_search')
->setLabel('Search')
->setValue($search_text)
);
$form->appendChild(
id(new AphrontFormTextControl())
->setName('set_tasks')
->setLabel('Task IDs')
->setValue(join(',', $task_ids))
);
$tokens = array();
foreach ($owner_phids as $phid) {
$tokens[$phid] = $handles[$phid]->getFullName();
}
$form->appendChild(
id(new AphrontFormTokenizerControl())
->setDatasource('/typeahead/common/searchowner/')
->setName('set_owners')
->setLabel('Owners')
->setValue($tokens));
$tokens = array();
foreach ($author_phids as $phid) {
$tokens[$phid] = $handles[$phid]->getFullName();
}
$form->appendChild(
id(new AphrontFormTokenizerControl())
->setDatasource('/typeahead/common/users/')
->setName('set_authors')
->setLabel('Authors')
->setValue($tokens));
}
$tokens = array();
foreach ($project_phids as $phid) {
$tokens[$phid] = $handles[$phid]->getFullName();
}
if ($this->view != 'projectall' && $this->view != 'projecttriage') {
$form->appendChild(
id(new AphrontFormTokenizerControl())
->setDatasource('/typeahead/common/searchproject/')
->setName('set_projects')
->setLabel('Projects')
->setValue($tokens));
}
if ($this->view == 'custom') {
$tokens = array();
foreach ($exclude_project_phids as $phid) {
$tokens[$phid] = $handles[$phid]->getFullName();
}
$form->appendChild(
id(new AphrontFormTokenizerControl())
->setDatasource('/typeahead/common/projects/')
->setName('set_xprojects')
->setLabel('Exclude Projects')
->setValue($tokens));
$priority = ManiphestTaskPriority::getLowestPriority();
if ($low_priority) {
$priority = $low_priority;
}
$form->appendChild(
id(new AphrontFormSelectControl())
->setLabel('Min Priority')
->setName('set_lpriority')
->setValue($priority)
->setOptions(array_reverse(
ManiphestTaskPriority::getTaskPriorityMap(), true)));
$priority = ManiphestTaskPriority::getHighestPriority();
if ($high_priority) {
$priority = $high_priority;
}
$form->appendChild(
id(new AphrontFormSelectControl())
->setLabel('Max Priority')
->setName('set_hpriority')
->setValue($priority)
->setOptions(ManiphestTaskPriority::getTaskPriorityMap()));
}
$form
->appendChild($this->renderStatusControl($q_status))
->appendChild($this->renderGroupControl($q_group))
->appendChild($this->renderOrderControl($q_order));
$submit = id(new AphrontFormSubmitControl())
->setValue('Filter Tasks');
// Only show "Save..." for novel queries which have some kind of query
// parameters set.
if ($this->view === 'custom'
&& empty($key)
&& $request->getRequestURI()->getQueryParams()) {
$submit->addCancelButton(
'/maniphest/custom/edit/?key='.$query->getQueryKey(),
'Save Custom Query...');
}
$form->appendChild($submit);
$create_uri = new PhutilURI('/maniphest/task/create/');
if ($project_phids) {
// If we have project filters selected, use them as defaults for task
// creation.
$create_uri->setQueryParam('projects', implode(';', $project_phids));
}
$filter = new AphrontListFilterView();
$filter->addButton(
phutil_render_tag(
'a',
array(
'href' => (string)$create_uri,
'class' => 'green button',
),
'Create New Task'));
if (empty($key)) {
$filter->appendChild($form);
}
$nav->appendChild($filter);
$have_tasks = false;
foreach ($tasks as $group => $list) {
if (count($list)) {
$have_tasks = true;
break;
}
}
require_celerity_resource('maniphest-task-summary-css');
$list_container = new AphrontNullView();
$list_container->appendChild('<div class="maniphest-list-container">');
if (!$have_tasks) {
$list_container->appendChild(
'<h1 class="maniphest-task-group-header">'.
'No matching tasks.'.
'</h1>');
} else {
$pager = new AphrontPagerView();
$pager->setURI($request->getRequestURI(), 'offset');
$pager->setPageSize($page_size);
$pager->setOffset($page);
$pager->setCount($total_count);
$cur = ($pager->getOffset() + 1);
$max = min($pager->getOffset() + $page_size, $total_count);
$tot = $total_count;
$cur = number_format($cur);
$max = number_format($max);
$tot = number_format($tot);
$list_container->appendChild(
'<div class="maniphest-total-result-count">'.
"Displaying tasks {$cur} - {$max} of {$tot}.".
'</div>');
$selector = new AphrontNullView();
$group = $query->getParameter('group');
$order = $query->getParameter('order');
$is_draggable =
($group == 'priority') ||
($group == 'none' && $order == 'priority');
$lists = new AphrontNullView();
$lists->appendChild('<div class="maniphest-group-container">');
foreach ($tasks as $group => $list) {
$task_list = new ManiphestTaskListView();
$task_list->setShowBatchControls(true);
if ($is_draggable) {
$task_list->setShowSubpriorityControls(true);
}
$task_list->setUser($user);
$task_list->setTasks($list);
$task_list->setHandles($handles);
$count = number_format(count($list));
$lists->appendChild(
javelin_render_tag(
'h1',
array(
'class' => 'maniphest-task-group-header',
'sigil' => 'task-group',
'meta' => array(
'priority' => head($list)->getPriority(),
),
),
phutil_escape_html($group).' ('.$count.')'));
$lists->appendChild($task_list);
}
$lists->appendChild('</div>');
$selector->appendChild($lists);
$selector->appendChild($this->renderBatchEditor($query));
$form_id = celerity_generate_unique_node_id();
$selector = phabricator_render_form(
$user,
array(
'method' => 'POST',
'action' => '/maniphest/batch/',
'id' => $form_id,
),
$selector->render());
$list_container->appendChild($selector);
$list_container->appendChild($pager);
Javelin::initBehavior(
'maniphest-subpriority-editor',
array(
'root' => $form_id,
'uri' => '/maniphest/subpriority/',
));
}
$list_container->appendChild('</div>');
$nav->appendChild($list_container);
return $this->buildStandardPageResponse(
$nav,
array(
'title' => 'Task List',
));
}
public static function loadTasks(PhabricatorSearchQuery $search_query) {
$any_project = false;
$search_text = $search_query->getParameter('fullTextSearch');
$user_phids = $search_query->getParameter('userPHIDs', array());
$project_phids = $search_query->getParameter('projectPHIDs', array());
$task_ids = $search_query->getParameter('taskIDs', array());
$xproject_phids = $search_query->getParameter(
'excludeProjectPHIDs',
array());
$owner_phids = $search_query->getParameter('ownerPHIDs', array());
$author_phids = $search_query->getParameter('authorPHIDs', array());
$low_priority = $search_query->getParameter('lowPriority');
$low_priority = nonempty($low_priority,
ManiphestTaskPriority::getLowestPriority());
$high_priority = $search_query->getParameter('highPriority');
$high_priority = nonempty($high_priority,
ManiphestTaskPriority::getHighestPriority());
$query = new ManiphestTaskQuery();
$query->withProjects($project_phids);
$query->withTaskIDs($task_ids);
if ($xproject_phids) {
$query->withoutProjects($xproject_phids);
}
if ($owner_phids) {
$query->withOwners($owner_phids);
}
if ($author_phids) {
$query->withAuthors($author_phids);
}
$status = $search_query->getParameter('status', 'all');
if (!empty($status['open']) && !empty($status['closed'])) {
$query->withStatus(ManiphestTaskQuery::STATUS_ANY);
} else if (!empty($status['open'])) {
$query->withStatus(ManiphestTaskQuery::STATUS_OPEN);
} else {
$query->withStatus(ManiphestTaskQuery::STATUS_CLOSED);
}
switch ($search_query->getParameter('view')) {
case 'action':
$query->withOwners($user_phids);
break;
case 'created':
$query->withAuthors($user_phids);
break;
case 'subscribed':
$query->withSubscribers($user_phids);
break;
case 'triage':
$query->withOwners($user_phids);
$query->withPriority(ManiphestTaskPriority::PRIORITY_TRIAGE);
break;
case 'alltriage':
$query->withPriority(ManiphestTaskPriority::PRIORITY_TRIAGE);
break;
case 'all':
break;
case 'projecttriage':
$query->withPriority(ManiphestTaskPriority::PRIORITY_TRIAGE);
$any_project = true;
break;
case 'projectall':
$any_project = true;
break;
case 'custom':
$query->withPrioritiesBetween($low_priority, $high_priority);
break;
}
$query->withAnyProject($any_project);
$query->withFullTextSearch($search_text);
$order_map = array(
'priority' => ManiphestTaskQuery::ORDER_PRIORITY,
'created' => ManiphestTaskQuery::ORDER_CREATED,
'title' => ManiphestTaskQuery::ORDER_TITLE,
);
$query->setOrderBy(
idx(
$order_map,
$search_query->getParameter('order'),
ManiphestTaskQuery::ORDER_MODIFIED));
$group_map = array(
'priority' => ManiphestTaskQuery::GROUP_PRIORITY,
'owner' => ManiphestTaskQuery::GROUP_OWNER,
'status' => ManiphestTaskQuery::GROUP_STATUS,
'project' => ManiphestTaskQuery::GROUP_PROJECT,
);
$query->setGroupBy(
idx(
$group_map,
$search_query->getParameter('group'),
ManiphestTaskQuery::GROUP_NONE));
$query->setCalculateRows(true);
$query->setLimit($search_query->getParameter('limit'));
$query->setOffset($search_query->getParameter('offset'));
$data = $query->execute();
$total_row_count = $query->getRowCount();
$project_group_phids = array();
if ($search_query->getParameter('group') == 'project') {
foreach ($data as $task) {
foreach ($task->getProjectPHIDs() as $phid) {
$project_group_phids[] = $phid;
}
}
}
$handle_phids = mpull($data, 'getOwnerPHID');
$handle_phids = array_merge(
$handle_phids,
$project_phids,
$user_phids,
$xproject_phids,
$owner_phids,
$author_phids,
$project_group_phids,
array_mergev(mpull($data, 'getProjectPHIDs')));
$handles = id(new PhabricatorObjectHandleData($handle_phids))
->loadHandles();
switch ($search_query->getParameter('group')) {
case 'priority':
$data = mgroup($data, 'getPriority');
// If we have invalid priorities, they'll all map to "???". Merge
// arrays to prevent them from overwriting each other.
$out = array();
foreach ($data as $pri => $tasks) {
$out[ManiphestTaskPriority::getTaskPriorityName($pri)][] = $tasks;
}
foreach ($out as $pri => $tasks) {
$out[$pri] = array_mergev($tasks);
}
$data = $out;
break;
case 'status':
$data = mgroup($data, 'getStatus');
$out = array();
foreach ($data as $status => $tasks) {
$out[ManiphestTaskStatus::getTaskStatusFullName($status)] = $tasks;
}
$data = $out;
break;
case 'owner':
$data = mgroup($data, 'getOwnerPHID');
$out = array();
foreach ($data as $phid => $tasks) {
if ($phid) {
$out[$handles[$phid]->getFullName()] = $tasks;
} else {
$out['Unassigned'] = $tasks;
}
}
$data = $out;
ksort($data);
// Move "Unassigned" to the top of the list.
if (isset($data['Unassigned'])) {
$data = array('Unassigned' => $out['Unassigned']) + $out;
}
break;
case 'project':
$grouped = array();
foreach ($data as $task) {
$phids = $task->getProjectPHIDs();
if ($project_phids && $any_project !== true) {
// If the user is filtering on "Bugs", don't show a "Bugs" group
// with every result since that's silly (the query also does this
// on the backend).
$phids = array_diff($phids, $project_phids);
}
if ($phids) {
foreach ($phids as $phid) {
$grouped[$handles[$phid]->getName()][$task->getID()] = $task;
}
} else {
$grouped['No Project'][$task->getID()] = $task;
}
}
$data = $grouped;
ksort($data);
// Move "No Project" to the end of the list.
if (isset($data['No Project'])) {
$noproject = $data['No Project'];
unset($data['No Project']);
$data += array('No Project' => $noproject);
}
break;
default:
$data = array(
'Tasks' => $data,
);
break;
}
return array($data, $handles, $total_row_count);
}
private function renderBatchEditor(PhabricatorSearchQuery $search_query) {
Javelin::initBehavior(
'maniphest-batch-selector',
array(
'selectAll' => 'batch-select-all',
'selectNone' => 'batch-select-none',
'submit' => 'batch-select-submit',
'status' => 'batch-select-status-cell',
));
$select_all = javelin_render_tag(
'a',
array(
'href' => '#',
'mustcapture' => true,
'class' => 'grey button',
'id' => 'batch-select-all',
),
'Select All');
$select_none = javelin_render_tag(
'a',
array(
'href' => '#',
'mustcapture' => true,
'class' => 'grey button',
'id' => 'batch-select-none',
),
'Clear Selection');
$submit = phutil_render_tag(
'button',
array(
'id' => 'batch-select-submit',
'disabled' => 'disabled',
'class' => 'disabled',
),
'Batch Edit Selected Tasks »');
$export = javelin_render_tag(
'a',
array(
'href' => '/maniphest/export/'.$search_query->getQueryKey().'/',
'class' => 'grey button',
),
'Export Tasks to Excel...');
return
'<div class="maniphest-batch-editor">'.
'<div class="batch-editor-header">Batch Task Editor</div>'.
'<table class="maniphest-batch-editor-layout">'.
'<tr>'.
'<td>'.
$select_all.
$select_none.
'</td>'.
'<td>'.
$export.
'</td>'.
'<td id="batch-select-status-cell">'.
'0 Selected Tasks'.
'</td>'.
'<td class="batch-select-submit-cell">'.$submit.'</td>'.
'</tr>'.
'</table>'.
'</table>';
}
private function buildQueryFromRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$status = $this->getStatusValueFromRequest();
$group = $this->getGroupValueFromRequest();
$order = $this->getOrderValueFromRequest();
$user_phids = $request->getStrList(
'users',
array($user->getPHID()));
if ($this->view == 'projecttriage' || $this->view == 'projectall') {
$project_query = new PhabricatorProjectQuery();
- $project_query->setMembers($user_phids);
+ $project_query->withMemberPHIDs($user_phids);
$projects = $project_query->execute();
$project_phids = mpull($projects, 'getPHID');
} else {
$project_phids = $request->getStrList('projects');
}
$exclude_project_phids = $request->getStrList('xprojects');
$task_ids = $request->getStrList('tasks');
if ($task_ids) {
// We only need the integer portion of each task ID, so get rid of any
// non-numeric elements
$numeric_task_ids = array();
foreach ($task_ids as $task_id) {
$task_id = preg_replace('/[a-zA-Z]+/', '', $task_id);
if (!empty($task_id)) {
$numeric_task_ids[] = $task_id;
}
}
if (empty($numeric_task_ids)) {
$numeric_task_ids = array(null);
}
$task_ids = $numeric_task_ids;
}
$owner_phids = $request->getStrList('owners');
$author_phids = $request->getStrList('authors');
$search_string = $request->getStr('search');
$low_priority = $request->getInt('lpriority');
$high_priority = $request->getInt('hpriority');
$page = $request->getInt('offset');
$page_size = self::DEFAULT_PAGE_SIZE;
$query = new PhabricatorSearchQuery();
$query->setQuery('<<maniphest>>');
$query->setParameters(
array(
'fullTextSearch' => $search_string,
'view' => $this->view,
'userPHIDs' => $user_phids,
'projectPHIDs' => $project_phids,
'excludeProjectPHIDs' => $exclude_project_phids,
'ownerPHIDs' => $owner_phids,
'authorPHIDs' => $author_phids,
'taskIDs' => $task_ids,
'lowPriority' => $low_priority,
'highPriority' => $high_priority,
'group' => $group,
'order' => $order,
'offset' => $page,
'limit' => $page_size,
'status' => $status,
));
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$query->save();
unset($unguarded);
return $query;
}
/* -( Toggle Button Controls )---------------------------------------------
These are a giant mess since we have several different values: the request
key (GET param used in requests), the request value (short names used in
requests to keep URIs readable), and the query value (complex value stored in
the query).
*/
private function getStatusValueFromRequest() {
$map = $this->getStatusMap();
$val = $this->getRequest()->getStr($this->getStatusRequestKey());
return idx($map, $val, head($map));
}
private function getGroupValueFromRequest() {
$map = $this->getGroupMap();
$val = $this->getRequest()->getStr($this->getGroupRequestKey());
return idx($map, $val, head($map));
}
private function getOrderValueFromRequest() {
$map = $this->getOrderMap();
$val = $this->getRequest()->getStr($this->getOrderRequestKey());
return idx($map, $val, head($map));
}
private function getStatusRequestKey() {
return 's';
}
private function getGroupRequestKey() {
return 'g';
}
private function getOrderRequestKey() {
return 'o';
}
private function getStatusRequestValue($value) {
return array_search($value, $this->getStatusMap());
}
private function getGroupRequestValue($value) {
return array_search($value, $this->getGroupMap());
}
private function getOrderRequestValue($value) {
return array_search($value, $this->getOrderMap());
}
private function getStatusMap() {
return array(
'o' => array(
'open' => true,
),
'c' => array(
'closed' => true,
),
'oc' => array(
'open' => true,
'closed' => true,
),
);
}
private function getGroupMap() {
return array(
'p' => 'priority',
'o' => 'owner',
's' => 'status',
'j' => 'project',
'n' => 'none',
);
}
private function getOrderMap() {
return array(
'p' => 'priority',
'u' => 'updated',
'c' => 'created',
't' => 'title',
);
}
private function getStatusButtonMap() {
return array(
'o' => 'Open',
'c' => 'Closed',
'oc' => 'All',
);
}
private function getGroupButtonMap() {
return array(
'p' => 'Priority',
'o' => 'Owner',
's' => 'Status',
'j' => 'Project',
'n' => 'None',
);
}
private function getOrderButtonMap() {
return array(
'p' => 'Priority',
'u' => 'Updated',
'c' => 'Created',
't' => 'Title',
);
}
public function renderStatusControl($value) {
$request = $this->getRequest();
return id(new AphrontFormToggleButtonsControl())
->setLabel('Status')
->setValue($this->getStatusRequestValue($value))
->setBaseURI($request->getRequestURI(), $this->getStatusRequestKey())
->setButtons($this->getStatusButtonMap());
}
public function renderOrderControl($value) {
$request = $this->getRequest();
return id(new AphrontFormToggleButtonsControl())
->setLabel('Order')
->setValue($this->getOrderRequestValue($value))
->setBaseURI($request->getRequestURI(), $this->getOrderRequestKey())
->setButtons($this->getOrderButtonMap());
}
public function renderGroupControl($value) {
$request = $this->getRequest();
return id(new AphrontFormToggleButtonsControl())
->setLabel('Group')
->setValue($this->getGroupRequestValue($value))
->setBaseURI($request->getRequestURI(), $this->getGroupRequestKey())
->setButtons($this->getGroupButtonMap());
}
}
diff --git a/src/applications/owners/storage/PhabricatorOwnersOwner.php b/src/applications/owners/storage/PhabricatorOwnersOwner.php
index 8d9a8663be..72f35a2b06 100644
--- a/src/applications/owners/storage/PhabricatorOwnersOwner.php
+++ b/src/applications/owners/storage/PhabricatorOwnersOwner.php
@@ -1,86 +1,85 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorOwnersOwner extends PhabricatorOwnersDAO {
protected $packageID;
// this can be a project or a user. We assume that all members of a project
// owner also own the package; use the loadAffiliatedUserPHIDs method if
// you want to recursively grab all user ids that own a package
protected $userPHID;
public function getConfiguration() {
return array(
self::CONFIG_TIMESTAMPS => false,
) + parent::getConfiguration();
}
public static function loadAllForPackages(array $packages) {
assert_instances_of($packages, 'PhabricatorOwnersPackage');
if (!$packages) {
return array();
}
return id(new PhabricatorOwnersOwner())->loadAllWhere(
'packageID IN (%Ls)',
mpull($packages, 'getID'));
}
// Loads all user phids affiliated with a set of packages. This includes both
// user owners and all members of any project owners
public static function loadAffiliatedUserPHIDs(array $package_ids) {
if (!$package_ids) {
return array();
}
$owners = id(new PhabricatorOwnersOwner())->loadAllWhere(
'packageID IN (%Ls)',
$package_ids);
$all_phids = phid_group_by_type(mpull($owners, 'getUserPHID'));
$user_phids = idx($all_phids,
PhabricatorPHIDConstants::PHID_TYPE_USER,
array());
$users_in_project_phids = array();
if (idx($all_phids, PhabricatorPHIDConstants::PHID_TYPE_PROJ)) {
$users_in_project_phids = mpull(
id(new PhabricatorProjectAffiliation())->loadAllWhere(
'projectPHID IN (%Ls)',
idx($all_phids, PhabricatorPHIDConstants::PHID_TYPE_PROJ, array())),
'getUserPHID');
}
return array_unique(array_merge($users_in_project_phids, $user_phids));
}
// Loads all affiliated packages for a user. This includes packages owned by
// any project the user is a member of.
public static function loadAffiliatedPackages($user_phid) {
$query = new PhabricatorProjectQuery();
- $query->setMembers(array($user_phid));
+ $query->withMemberPHIDs(array($user_phid));
$query->withStatus(PhabricatorProjectQuery::STATUS_ACTIVE);
$projects = $query->execute();
$phids = mpull($projects, 'getPHID') + array($user_phid);
- return
- id(new PhabricatorOwnersOwner())->loadAllWhere(
- 'userPHID in (%Ls)',
- $phids);
+ return id(new PhabricatorOwnersOwner())->loadAllWhere(
+ 'userPHID in (%Ls)',
+ $phids);
}
}
diff --git a/src/applications/project/controller/PhabricatorProjectListController.php b/src/applications/project/controller/PhabricatorProjectListController.php
index 4dab336181..78536e9c33 100644
--- a/src/applications/project/controller/PhabricatorProjectListController.php
+++ b/src/applications/project/controller/PhabricatorProjectListController.php
@@ -1,180 +1,180 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorProjectListController
extends PhabricatorProjectController {
private $filter;
public function willProcessRequest(array $data) {
$this->filter = idx($data, 'filter');
}
public function processRequest() {
$request = $this->getRequest();
$nav = new AphrontSideNavFilterView();
$nav
->setBaseURI(new PhutilURI('/project/filter/'))
->addLabel('User')
->addFilter('active', 'Active')
->addSpacer()
->addLabel('All')
->addFilter('all', 'All Projects')
->addFilter('allactive','Active Projects');
$this->filter = $nav->selectFilter($this->filter, 'active');
$pager = new AphrontPagerView();
$pager->setPageSize(250);
$pager->setURI($request->getRequestURI(), 'page');
$pager->setOffset($request->getInt('page'));
$query = new PhabricatorProjectQuery();
$query->setOffset($pager->getOffset());
$query->setLimit($pager->getPageSize() + 1);
$view_phid = $request->getUser()->getPHID();
$status_filter = PhabricatorProjectQuery::STATUS_ANY;
switch ($this->filter) {
case 'active':
$table_header = 'Your Projects';
- $query->setMembers(array($view_phid));
+ $query->withMemberPHIDs(array($view_phid));
$query->withStatus(PhabricatorProjectQuery::STATUS_ACTIVE);
break;
case 'allactive':
$status_filter = PhabricatorProjectQuery::STATUS_ACTIVE;
$table_header = 'Active Projects';
// fallthrough
case 'all':
$table_header = 'All Projects';
$query->withStatus($status_filter);
break;
}
$projects = $query->execute();
$projects = $pager->sliceResults($projects);
$project_phids = mpull($projects, 'getPHID');
$profiles = array();
if ($projects) {
$profiles = id(new PhabricatorProjectProfile())->loadAllWhere(
'projectPHID in (%Ls)',
$project_phids);
$profiles = mpull($profiles, null, 'getProjectPHID');
}
$affil_groups = array();
if ($projects) {
$affil_groups = PhabricatorProjectAffiliation::loadAllForProjectPHIDs(
$project_phids);
}
$tasks = array();
$groups = array();
if ($project_phids) {
$query = id(new ManiphestTaskQuery())
->withProjects($project_phids)
->withAnyProject(true)
->withStatus(ManiphestTaskQuery::STATUS_OPEN)
->setLimit(PHP_INT_MAX);
$tasks = $query->execute();
foreach ($tasks as $task) {
foreach ($task->getProjectPHIDs() as $phid) {
$groups[$phid][] = $task;
}
}
}
$rows = array();
foreach ($projects as $project) {
$phid = $project->getPHID();
$profile = idx($profiles, $phid);
$affiliations = $affil_groups[$phid];
$group = idx($groups, $phid, array());
$task_count = count($group);
$population = count($affiliations);
if ($profile) {
$blurb = $profile->getBlurb();
$blurb = phutil_utf8_shorten($blurb, 64);
} else {
$blurb = null;
}
$rows[] = array(
phutil_render_tag(
'a',
array(
'href' => '/project/view/'.$project->getID().'/',
),
phutil_escape_html($project->getName())),
phutil_escape_html(
PhabricatorProjectStatus::getNameForStatus($project->getStatus())),
phutil_escape_html($blurb),
phutil_escape_html($population),
phutil_render_tag(
'a',
array(
'href' => '/maniphest/view/all/?projects='.$phid,
),
phutil_escape_html($task_count)),
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'Project',
'Status',
'Description',
'Population',
'Open Tasks',
));
$table->setColumnClasses(
array(
'pri',
'',
'wide',
'',
''
));
$panel = new AphrontPanelView();
$panel->setHeader($table_header);
$panel->setCreateButton('Create New Project', '/project/create/');
$panel->appendChild($table);
$panel->appendChild($pager);
$nav->appendChild($panel);
return $this->buildStandardPageResponse(
$nav,
array(
'title' => 'Projects',
));
}
}
diff --git a/src/applications/project/query/PhabricatorProjectQuery.php b/src/applications/project/query/PhabricatorProjectQuery.php
index b1d7e65f45..cb28027046 100644
--- a/src/applications/project/query/PhabricatorProjectQuery.php
+++ b/src/applications/project/query/PhabricatorProjectQuery.php
@@ -1,197 +1,197 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorProjectQuery {
private $ids;
private $phids;
- private $members;
+ private $memberPHIDs;
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 $limit;
private $offset;
private $needMembers;
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 setLimit($limit) {
$this->limit = $limit;
return $this;
}
public function setOffset($offset) {
$this->offset = $offset;
return $this;
}
- public function setMembers(array $members) {
- $this->members = $members;
+ public function withMemberPHIDs(array $member_phids) {
+ $this->memberPHIDs = $member_phids;
return $this;
}
public function needMembers($need_members) {
$this->needMembers = $need_members;
return $this;
}
public function execute() {
$table = id(new PhabricatorProject());
$conn_r = $table->establishConnection('r');
$where = $this->buildWhereClause($conn_r);
$joins = $this->buildJoinsClause($conn_r);
$limit = '';
if ($this->limit) {
$limit = qsprintf(
$conn_r,
'LIMIT %d, %d',
$this->offset,
$this->limit);
} else if ($this->offset) {
$limit = qsprintf(
$conn_r,
'LIMIT %d, %d',
$this->offset,
PHP_INT_MAX);
}
$order = 'ORDER BY name';
$data = queryfx_all(
$conn_r,
'SELECT p.* FROM %T p %Q %Q %Q %Q',
$table->getTableName(),
$joins,
$where,
$order,
$limit);
$projects = $table->loadAllFromArray($data);
if ($projects && $this->needMembers) {
$members = PhabricatorProjectAffiliation::loadAllForProjectPHIDs(
mpull($projects, 'getPHID'));
foreach ($projects as $project) {
$project->attachAffiliations(
array_values(idx($members, $project->getPHID(), array())));
}
}
return $projects;
}
private function buildWhereClause($conn_r) {
$where = array();
if ($this->status != self::STATUS_ANY) {
switch ($this->status) {
case self::STATUS_OPEN:
$where[] = qsprintf(
$conn_r,
'status IN (%Ld)',
array(
PhabricatorProjectStatus::STATUS_ACTIVE,
));
break;
case self::STATUS_CLOSED:
$where[] = qsprintf(
$conn_r,
'status IN (%Ld)',
array(
PhabricatorProjectStatus::STATUS_ARCHIVED,
));
break;
case self::STATUS_ACTIVE:
$where[] = qsprintf(
$conn_r,
'status = %d',
PhabricatorProjectStatus::STATUS_ACTIVE);
break;
case self::STATUS_ARCHIVED:
$where[] = qsprintf(
$conn_r,
'status = %d',
PhabricatorProjectStatus::STATUS_ARCHIVED);
break;
default:
throw new Exception(
"Unknown project status '{$this->status}'!");
}
}
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 ($where) {
$where = 'WHERE ('.implode(') AND (', $where).')';
} else {
$where = '';
}
return $where;
}
private function buildJoinsClause($conn_r) {
$affil_table = new PhabricatorProjectAffiliation();
$joins = array();
- if ($this->members) {
+ if ($this->memberPHIDs) {
$joins[] = qsprintf(
$conn_r,
'JOIN %T member ON member.projectPHID = p.phid
AND member.userPHID in (%Ls)',
$affil_table->getTableName(),
- $this->members);
+ $this->memberPHIDs);
}
return implode(' ', $joins);
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jan 19, 15:25 (3 w, 23 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1125964
Default Alt Text
(85 KB)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment