diff --git a/src/applications/directory/controller/main/PhabricatorDirectoryMainController.php b/src/applications/directory/controller/main/PhabricatorDirectoryMainController.php index 2b65d6da7..dd178ed48 100644 --- a/src/applications/directory/controller/main/PhabricatorDirectoryMainController.php +++ b/src/applications/directory/controller/main/PhabricatorDirectoryMainController.php @@ -1,521 +1,521 @@ <?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. */ class PhabricatorDirectoryMainController extends PhabricatorDirectoryController { private $filter; public function willProcessRequest(array $data) { $this->filter = idx($data, 'filter'); } public function shouldRequireAdmin() { // These controllers are admin-only by default, but this one is public, // so allow non-admin users to view it. return false; } public function processRequest() { $user = $this->getRequest()->getUser(); $nav = $this->buildNav(); $this->filter = $nav->selectFilter($this->filter, 'home'); switch ($this->filter) { case 'jump': break; case 'home': case 'feed': $project_query = new PhabricatorProjectQuery(); $project_query->setMembers(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); default: return $this->buildMainResponse($nav, $projects); } } private function buildMainResponse($nav, $projects) { $unbreak_panel = $this->buildUnbreakNowPanel(); $triage_panel = $this->buildNeedsTriagePanel($projects); $jump_panel = $this->buildJumpPanel(); $revision_panel = $this->buildRevisionPanel(); $tasks_panel = $this->buildTasksPanel(); $feed_view = $this->buildFeedView($projects, $is_full = false); $content = array( $unbreak_panel, $triage_panel, $jump_panel, $revision_panel, $tasks_panel, $feed_view, ); $nav->appendChild($content); return $this->buildStandardPageResponse( $nav, array( 'title' => 'Phabricator', )); } private function buildJumpResponse($nav) { $request = $this->getRequest(); if ($request->isFormPost()) { $jump = $request->getStr('jump'); $jump = trim($jump); $help_href = PhabricatorEnv::getDocLink( - 'articles/Jump_Nav_User_Guide.html'); + 'article/Jump_Nav_User_Guide.html'); $patterns = array( '/^help/i' => 'uri:'.$help_href, '/^d$/i' => 'uri:/differential/', '/^r$/i' => 'uri:/diffusion/', '/^t$/i' => 'uri:/maniphest/', '/r([A-Z]+)$/' => 'repository', '/r([A-Z]+)(\S+)$/' => 'commit', '/^d(\d+)$/i' => 'revision', '/^t(\d+)$/i' => 'task', // TODO: '/^p$/i' => 'uri:/projects/', // TODO: '/^u$/i' => 'uri:/people/', // TODO: '/^p\s+(\S+)$/i' => 'project', // TODO: '/^u\s+(\S+)$/i' => 'user', // TODO: '/^task:\s+(\S+)/i' => 'create-task', // TODO: '/^(?:s|symbol)\s+(\S+)/i' => 'find-symbol', ); foreach ($patterns as $pattern => $effect) { $matches = null; if (preg_match($pattern, $jump, $matches)) { if (!strncmp($effect, 'uri:', 4)) { return id(new AphrontRedirectResponse()) ->setURI(substr($effect, 4)); } else { switch ($effect) { case 'repository': return id(new AphrontRedirectResponse()) ->setURI('/diffusion/'.$matches[1].'/'); case 'commit': return id(new AphrontRedirectResponse()) ->setURI('/'.$matches[0]); case 'revision': return id(new AphrontRedirectResponse()) ->setURI('/D'.$matches[1]); case 'task': return id(new AphrontRedirectResponse()) ->setURI('/T'.$matches[1]); default: throw new Exception("Unknown jump effect '{$effect}'!"); } } } } $query = new PhabricatorSearchQuery(); $query->setQuery($jump); $query->save(); return id(new AphrontRedirectResponse()) ->setURI('/search/'.$query->getQueryKey().'/'); } $nav->appendChild($this->buildJumpPanel()); return $this->buildStandardPageResponse( $nav, array( 'title' => 'Jump Nav', )); } private function buildFeedResponse($nav, $projects) { $view = $this->buildFeedView($projects, $is_full = true); $nav->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); $task_query->setCalculateRows(true); $tasks = $task_query->execute(); if ($tasks) { $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 ('.$task_query->getRowCount().") \xC2\xBB")); $panel->appendChild($this->buildTaskListView($tasks)); } else { $panel = new AphrontMiniPanelView(); $panel->appendChild( '<p><strong>No "Unbreak Now!" Tasks:</strong> '. 'Nothing appears to be critically broken right now.</p>'); $panel = '<br />'.$panel->render(); } return $panel; } private function buildNeedsTriagePanel(array $projects) { $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->setCalculateRows(true); $task_query->setLimit(10); $tasks = $task_query->execute(); } else { $tasks = array(); } if ($tasks) { $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/alltriage/', 'class' => 'grey button', ), 'View All Triage ('.$task_query->getRowCount().") \xC2\xBB")); $panel->appendChild($this->buildTaskListView($tasks)); } else { $panel = new AphrontMiniPanelView(); $panel->appendChild( '<p><strong>No "Needs Triage" Tasks:</strong> '. 'No tasks in <a href="/project/">projects you are a member of</a> '. 'need triage.</p>'); } 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); $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")); if ($active) { $revision_view = id(new DifferentialRevisionListView()) ->setRevisions($active) ->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); } else { $panel->appendChild('<p>No revisions are waiting on you.</p>'); } 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->setCalculateRows(true); $task_query->setLimit(10); $tasks = $task_query->execute(); $panel = new AphrontPanelView(); $panel->setHeader('Assigned Tasks'); $panel->setCaption('Tasks assigned to you.'); if ($tasks) { $panel->addButton( phutil_render_tag( 'a', array( 'href' => '/maniphest/', 'class' => 'button grey', ), "View All Assigned Tasks (".$task_query->getRowCount().") \xC2\xBB")); $panel->appendChild($this->buildTaskListView($tasks)); } else { $panel->addButton( phutil_render_tag( 'a', array( 'href' => '/maniphest/?users='. ManiphestTaskOwner::OWNER_UP_FOR_GRABS, 'class' => 'button grey', ), "View Unassigned Tasks \xC2\xBB")); $panel->appendChild('<p>You have no assigned tasks.</p>'); } return $panel; } private function buildTaskListView(array $tasks) { $user = $this->getRequest()->getUser(); $phids = array_filter(mpull($tasks, 'getOwnerPHID')); $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles(); $view = new ManiphestTaskListView(); $view->setTasks($tasks); $view->setUser($user); $view->setHandles($handles); return $view; } private function buildFeedView(array $projects, $is_full) { $request = $this->getRequest(); $user = $request->getUser(); $user_phid = $user->getPHID(); $feed_query = new PhabricatorFeedQuery(); $feed_query->setFilterPHIDs( array_merge( array($user_phid), mpull($projects, 'getPHID'))); // TODO: All this limit stuff should probably be consolidated into the // feed query? $old_link = null; $new_link = null; if ($is_full) { $feed_query->setAfter($request->getStr('after')); $feed_query->setBefore($request->getStr('before')); $limit = 500; } else { $limit = 100; } // Grab one more story than we intend to display so we can figure out // if we need to render an "Older Posts" link or not (with reasonable // accuracy, at least). $feed_query->setLimit($limit + 1); $feed = $feed_query->execute(); $extra_row = (count($feed) == $limit + 1); if ($is_full) { $have_new = ($request->getStr('before')) || ($request->getStr('after') && $extra_row); } else { $have_new = false; } $have_old = ($request->getStr('after')) || ($request->getStr('before') && $extra_row) || (!$request->getStr('before') && !$request->getStr('after') && $extra_row); $feed = array_slice($feed, 0, $limit, $preserve_keys = true); if ($have_old) { $old_link = phutil_render_tag( 'a', array( 'href' => '/feed/?before='.end($feed)->getChronologicalKey(), 'class' => 'phabricator-feed-older-link', ), "Older Stories \xC2\xBB"); } if ($have_new) { $new_link = phutil_render_tag( 'a', array( 'href' => '/feed/?after='.reset($feed)->getChronologicalKey(), 'class' => 'phabricator-feed-newer-link', ), "\xC2\xAB Newer Stories"); } $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">'. $new_link. $old_link. '</div>'. '</div>'; } private function buildJumpPanel() { $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('articles/Jump_Nav_User_Guide.html'); + $doc_href = PhabricatorEnv::getDocLink('article/Jump_Nav_User_Guide.html'); $doc_link = phutil_render_tag( 'a', array( 'href' => $doc_href, ), 'Jump Nav Use Guide'); $jump_input = phutil_render_tag( 'input', array( 'type' => 'text', 'class' => 'phabricator-jump-nav', 'name' => 'jump', 'id' => $uniq_id, )). 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->appendChild( phabricator_render_form( $user, array( 'action' => '/jump/', 'method' => 'POST', ), $jump_input)); $nav_buttons = array( '/maniphest/task/create/' => 'Create a Task', '/file/' => 'Upload a File', '/paste/' => 'Create Paste', '/w/' => 'Browse Wiki', '/diffusion/' => 'Browse Code', ); $panel->appendChild('<div class="phabricator-jump-nav-buttons">'); foreach ($nav_buttons as $uri => $name) { $panel->appendChild( phutil_render_tag( 'a', array( 'href' => $uri, 'class' => 'button grey', ), phutil_escape_html($name))); } $panel->appendChild('</div>'); return $panel; } }