diff --git a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php index c6aacc4146..d4181c0635 100644 --- a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php +++ b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php @@ -1,618 +1,620 @@ getResourceURIMapRules() + array( - '/(?:(?P(?:feed|jump))/)?$' => + '/(?:(?Pjump)/)?$' => + 'PhabricatorDirectoryMainController', + '/(?:(?Pfeed)/)(?:(?P[^/]+)/)?$' => 'PhabricatorDirectoryMainController', '/directory/' => array( '(?P\d+)/$' => 'PhabricatorDirectoryCategoryViewController', 'edit/$' => 'PhabricatorDirectoryEditController', 'item/edit/(?:(?P\d+)/)?$' => 'PhabricatorDirectoryItemEditController', 'item/delete/(?P\d+)/' => 'PhabricatorDirectoryItemDeleteController', 'category/edit/(?:(?P\d+)/)?$' => 'PhabricatorDirectoryCategoryEditController', 'category/delete/(?P\d+)/' => 'PhabricatorDirectoryCategoryDeleteController', ), '/file/' => array( '$' => 'PhabricatorFileListController', 'filter/(?P\w+)/$' => 'PhabricatorFileListController', 'upload/$' => 'PhabricatorFileUploadController', 'dropupload/$' => 'PhabricatorFileDropUploadController', 'delete/(?P\d+)/$' => 'PhabricatorFileDeleteController', 'info/(?P[^/]+)/' => 'PhabricatorFileInfoController', 'data/(?P[^/]+)/(?P[^/]+)/' => 'PhabricatorFileDataController', // TODO: This is a deprecated version of /data/. Remove it after // old links have had a chance to rot. 'alt/(?P[^/]+)/(?P[^/]+)/' => 'PhabricatorFileDataController', 'macro/' => array( '$' => 'PhabricatorFileMacroListController', 'edit/(?:(?P\d+)/)?$' => 'PhabricatorFileMacroEditController', 'delete/(?P\d+)/$' => 'PhabricatorFileMacroDeleteController', ), 'proxy/$' => 'PhabricatorFileProxyController', 'xform/(?P[^/]+)/(?P[^/]+)/' => 'PhabricatorFileTransformController', ), '/phid/' => array( '$' => 'PhabricatorPHIDLookupController', ), '/people/' => array( '$' => 'PhabricatorPeopleListController', 'logs/$' => 'PhabricatorPeopleLogsController', 'edit/(?:(?P\d+)/(?:(?P\w+)/)?)?$' => 'PhabricatorPeopleEditController', ), '/p/(?P\w+)/(?:(?P\w+)/)?$' => 'PhabricatorPeopleProfileController', '/conduit/' => array( '$' => 'PhabricatorConduitConsoleController', 'method/(?P[^/]+)/$' => 'PhabricatorConduitConsoleController', 'log/$' => 'PhabricatorConduitLogController', 'log/view/(?P[^/]+)/$' => 'PhabricatorConduitLogController', 'token/$' => 'PhabricatorConduitTokenController', ), '/api/(?P[^/]+)$' => 'PhabricatorConduitAPIController', '/D(?P\d+)' => 'DifferentialRevisionViewController', '/differential/' => array( '$' => 'DifferentialRevisionListController', 'filter/(?P\w+)/$' => 'DifferentialRevisionListController', 'stats/(?P\w+)/$' => 'DifferentialRevisionStatsController', 'diff/' => array( '(?P\d+)/$' => 'DifferentialDiffViewController', 'create/$' => 'DifferentialDiffCreateController', ), 'changeset/$' => 'DifferentialChangesetViewController', 'revision/edit/(?:(?P\d+)/)?$' => 'DifferentialRevisionEditController', 'comment/' => array( 'preview/(?P\d+)/$' => 'DifferentialCommentPreviewController', 'save/$' => 'DifferentialCommentSaveController', 'inline/' => array( 'preview/(?P\d+)/$' => 'DifferentialInlineCommentPreviewController', 'edit/(?P\d+)/$' => 'DifferentialInlineCommentEditController', ), ), 'subscribe/(?Padd|rem)/(?P\d+)/$' => 'DifferentialSubscribeController', ), '/typeahead/' => array( 'common/(?P\w+)/$' => 'PhabricatorTypeaheadCommonDatasourceController', ), '/mail/' => array( '$' => 'PhabricatorMetaMTAListController', 'send/$' => 'PhabricatorMetaMTASendController', 'view/(?P\d+)/$' => 'PhabricatorMetaMTAViewController', 'lists/$' => 'PhabricatorMetaMTAMailingListsController', 'lists/edit/(?:(?P\d+)/)?$' => 'PhabricatorMetaMTAMailingListEditController', 'receive/$' => 'PhabricatorMetaMTAReceiveController', 'received/$' => 'PhabricatorMetaMTAReceivedListController', 'sendgrid/$' => 'PhabricatorMetaMTASendGridReceiveController', ), '/login/' => array( '$' => 'PhabricatorLoginController', 'email/$' => 'PhabricatorEmailLoginController', 'etoken/(?P\w+)/$' => 'PhabricatorEmailTokenController', 'refresh/$' => 'PhabricatorRefreshCSRFController', 'validate/$' => 'PhabricatorLoginValidateController', ), '/logout/$' => 'PhabricatorLogoutController', '/oauth/' => array( '(?P\w+)/' => array( 'login/$' => 'PhabricatorOAuthLoginController', 'diagnose/$' => 'PhabricatorOAuthDiagnosticsController', 'unlink/$' => 'PhabricatorOAuthUnlinkController', ), ), '/oauthserver/' => array( 'auth/' => 'PhabricatorOAuthServerAuthController', 'token/' => 'PhabricatorOAuthServerTokenController', 'test/' => 'PhabricatorOAuthServerTestController', ), '/xhprof/' => array( 'profile/(?P[^/]+)/$' => 'PhabricatorXHProfProfileController', ), '/~/' => 'DarkConsoleController', '/settings/' => array( '(?:page/(?P[^/]+)/)?$' => 'PhabricatorUserSettingsController', ), '/maniphest/' => array( '$' => 'ManiphestTaskListController', 'view/(?P\w+)/$' => 'ManiphestTaskListController', 'report/(?:(?P\w+)/)?$' => 'ManiphestReportController', 'task/' => array( 'create/$' => 'ManiphestTaskEditController', 'edit/(?P\d+)/$' => 'ManiphestTaskEditController', 'descriptionchange/(?P\d+)/$' => 'ManiphestTaskDescriptionChangeController', 'descriptiondiff/$' => 'ManiphestTaskDescriptionDiffController', 'descriptionpreview/$' => 'ManiphestTaskDescriptionPreviewController', ), 'transaction/' => array( 'save/' => 'ManiphestTransactionSaveController', 'preview/(?P\d+)/$' => 'ManiphestTransactionPreviewController', ), ), '/T(?P\d+)$' => 'ManiphestTaskDetailController', '/repository/' => array( '$' => 'PhabricatorRepositoryListController', 'create/$' => 'PhabricatorRepositoryCreateController', 'edit/(?P\d+)/(?:(?P\w+)?/)?$' => 'PhabricatorRepositoryEditController', 'delete/(?P\d+)/$' => 'PhabricatorRepositoryDeleteController', 'project/(?P\d+)/' => 'PhabricatorRepositoryArcanistProjectEditController', ), '/search/' => array( '$' => 'PhabricatorSearchController', '(?P[^/]+)/$' => 'PhabricatorSearchController', 'attach/(?P[^/]+)/(?P\w+)/(?:(?P\w+)/)?$' => 'PhabricatorSearchAttachController', 'select/(?P\w+)/$' => 'PhabricatorSearchSelectController', 'index/(?P[^/]+)/$' => 'PhabricatorSearchIndexController', ), '/project/' => array( '$' => 'PhabricatorProjectListController', 'filter/(?P[^/]+)/$' => 'PhabricatorProjectListController', 'edit/(?P\d+)/$' => 'PhabricatorProjectProfileEditController', 'view/(?P\d+)/(?:(?P\w+)/)?$' => 'PhabricatorProjectProfileController', 'create/$' => 'PhabricatorProjectCreateController', 'update/(?P\d+)/(?P[^/]+)/$' => 'PhabricatorProjectUpdateController', ), '/r(?P[A-Z]+)(?P[a-z0-9]+)$' => 'DiffusionCommitController', '/diffusion/' => array( '$' => 'DiffusionHomeController', '(?P[A-Z]+)/' => array( '$' => 'DiffusionRepositoryController', 'repository/'. '(?P[^/]+)/'. '$' => 'DiffusionRepositoryController', 'change/'. '(?P.*?)'. '(?:[;](?P[a-z0-9]+))?'. '$' => 'DiffusionChangeController', 'history/'. '(?P.*?)'. '(?:[;](?P[a-z0-9]+))?'. '$' => 'DiffusionHistoryController', 'browse/'. '(?P.*?)'. '(?:[;](?P[a-z0-9]+))?'. '(?:[$](?P\d+))?'. '$' => 'DiffusionBrowseController', 'diff/'. '(?P.*?)'. '(?:[;](?P[a-z0-9]+))?'. '$' => 'DiffusionDiffController', 'lastmodified/'. '(?P.*?)'. '(?:[;](?P[a-z0-9]+))?'. '$' => 'DiffusionLastModifiedController', ), 'services/' => array( 'path/' => array( 'complete/$' => 'DiffusionPathCompleteController', 'validate/$' => 'DiffusionPathValidateController', ), ), 'author/' => array( '$' => 'DiffusionCommitListController', '(?P\w+)/$' => 'DiffusionCommitListController', ), 'symbol/(?P[^/]+)/$' => 'DiffusionSymbolController', ), '/daemon/' => array( 'task/(?P\d+)/$' => 'PhabricatorWorkerTaskDetailController', 'task/(?P\d+)/(?P[^/]+)/$' => 'PhabricatorWorkerTaskUpdateController', 'log/' => array( '$' => 'PhabricatorDaemonLogListController', 'combined/$' => 'PhabricatorDaemonCombinedLogController', '(?P\d+)/$' => 'PhabricatorDaemonLogViewController', ), 'timeline/$' => 'PhabricatorDaemonTimelineConsoleController', 'timeline/(?P\d+)/$' => 'PhabricatorDaemonTimelineEventController', '$' => 'PhabricatorDaemonConsoleController', ), '/herald/' => array( '$' => 'HeraldHomeController', 'view/(?P[^/]+)/' => array( '$' => 'HeraldHomeController', '(?Pglobal)/$' => 'HeraldHomeController' ), 'new/(?:(?P[^/]+)/)?$' => 'HeraldNewController', 'rule/(?:(?P\d+)/)?$' => 'HeraldRuleController', 'history/(?P\d+)/$' => 'HeraldRuleEditHistoryController', 'delete/(?P\d+)/$' => 'HeraldDeleteController', 'test/$' => 'HeraldTestConsoleController', 'all/' => array( '$' => 'HeraldAllRulesController', 'view/(?P[^/]+)/$' => 'HeraldAllRulesController', ), 'transcript/$' => 'HeraldTranscriptListController', 'transcript/(?P\d+)/(?:(?P\w+)/)?$' => 'HeraldTranscriptController', ), '/uiexample/' => array( '$' => 'PhabricatorUIExampleRenderController', 'view/(?P[^/]+)/$' => 'PhabricatorUIExampleRenderController', ), '/owners/' => array( '$' => 'PhabricatorOwnersListController', 'view/(?P[^/]+)/$' => 'PhabricatorOwnersListController', 'edit/(?P\d+)/$' => 'PhabricatorOwnersEditController', 'new/$' => 'PhabricatorOwnersEditController', 'package/(?P\d+)/$' => 'PhabricatorOwnersDetailController', 'delete/(?P\d+)/$' => 'PhabricatorOwnersDeleteController', '(?Prelated|attention)/' => array( '$' => 'PhabricatorOwnerRelatedListController', '(?Ppackage|owner)/$' => 'PhabricatorOwnerRelatedListController', ), ), '/audit/' => array( '$' => 'PhabricatorAuditEditController', 'edit/$' => 'PhabricatorAuditEditController', ), '/xhpast/' => array( '$' => 'PhabricatorXHPASTViewRunController', 'view/(?P\d+)/$' => 'PhabricatorXHPASTViewFrameController', 'frameset/(?P\d+)/$' => 'PhabricatorXHPASTViewFramesetController', 'input/(?P\d+)/$' => 'PhabricatorXHPASTViewInputController', 'tree/(?P\d+)/$' => 'PhabricatorXHPASTViewTreeController', 'stream/(?P\d+)/$' => 'PhabricatorXHPASTViewStreamController', ), '/status/$' => 'PhabricatorStatusController', '/paste/' => array( '$' => 'PhabricatorPasteListController', 'filter/(?P\w+)/$' => 'PhabricatorPasteListController', ), '/P(?P\d+)$' => 'PhabricatorPasteViewController', '/help/' => array( 'keyboardshortcut/$' => 'PhabricatorHelpKeyboardShortcutController', ), '/countdown/' => array( '$' => 'PhabricatorCountdownListController', '(?P\d+)/$' => 'PhabricatorCountdownViewController', 'edit/(?:(?P\d+)/)?$' => 'PhabricatorCountdownEditController', 'delete/(?P\d+)/$' => 'PhabricatorCountdownDeleteController' ), '/feed/public/$' => 'PhabricatorFeedPublicStreamController', '/V(?P\d+)$' => 'PhabricatorSlowvotePollController', '/vote/' => array( '(?:view/(?P\w+)/)?$' => 'PhabricatorSlowvoteListController', 'create/' => 'PhabricatorSlowvoteCreateController', ), // Match "/w/" with slug "/". '/w(?P/)$' => 'PhrictionDocumentController', // Match "/w/x/y/z/" with slug "x/y/z/". '/w/(?P.+/)$' => 'PhrictionDocumentController', '/phriction/' => array( '$' => 'PhrictionListController', 'list/(?P[^/]+)/$' => 'PhrictionListController', 'history(?P/)$' => 'PhrictionHistoryController', 'history/(?P.+/)$' => 'PhrictionHistoryController', 'edit/(?:(?P\d+)/)?$' => 'PhrictionEditController', 'delete/(?P\d+)/$' => 'PhrictionDeleteController', 'preview/$' => 'PhrictionDocumentPreviewController', 'diff/(?P\d+)/$' => 'PhrictionDiffController', ), '/calendar/' => array( '$' => 'PhabricatorCalendarBrowseController', ), '/drydock/' => array( '$' => 'DrydockResourceListController', 'resource/$' => 'DrydockResourceListController', 'resource/allocate/$' => 'DrydockResourceAllocateController', 'host/' => array( '$' => 'DrydockHostListController', 'edit/$' => 'DrydockHostEditController', 'edit/(?P\d+)/$' => 'DrydockhostEditController', ), 'lease/$' => 'DrydockLeaseListController', ), '/chatlog/' => array( '$' => 'PhabricatorChatLogChannelListController', 'channel/(?P[^/]+)/$' => 'PhabricatorChatLogChannelLogController', ), ); } protected function getResourceURIMapRules() { return array( '/res/' => array( '(?Ppkg/)?(?P[a-f0-9]{8})/(?P.+\.(?:css|js))$' => 'CelerityResourceController', ), ); } public function buildRequest() { $request = new AphrontRequest($this->getHost(), $this->getPath()); $request->setRequestData($_GET + $_POST); $request->setApplicationConfiguration($this); return $request; } public function handleException(Exception $ex) { // Always log the unhandled exception. phlog($ex); $class = phutil_escape_html(get_class($ex)); $message = phutil_escape_html($ex->getMessage()); if (PhabricatorEnv::getEnvConfig('phabricator.show-stack-traces')) { $trace = $this->renderStackTrace($ex->getTrace()); } else { $trace = null; } $content = '
'. '
'.$message.'
'. $trace. '
'; $user = $this->getRequest()->getUser(); if (!$user) { // If we hit an exception very early, we won't have a user. $user = new PhabricatorUser(); } $dialog = new AphrontDialogView(); $dialog ->setTitle('Unhandled Exception ("'.$class.'")') ->setClass('aphront-exception-dialog') ->setUser($user) ->appendChild($content); if ($this->getRequest()->isAjax()) { $dialog->addCancelButton('/', 'Close'); } $response = new AphrontDialogResponse(); $response->setDialog($dialog); return $response; } public function willSendResponse(AphrontResponse $response) { $request = $this->getRequest(); $response->setRequest($request); if ($response instanceof AphrontDialogResponse) { if (!$request->isAjax()) { $view = new PhabricatorStandardPageView(); $view->setRequest($request); $view->appendChild( '
'. $response->buildResponseString(). '
'); $response = new AphrontWebpageResponse(); $response->setContent($view->render()); return $response; } else { return id(new AphrontAjaxResponse()) ->setContent(array( 'dialog' => $response->buildResponseString(), )); } } else if ($response instanceof AphrontRedirectResponse) { if ($request->isAjax()) { return id(new AphrontAjaxResponse()) ->setContent( array( 'redirect' => $response->getURI(), )); } } return $response; } public function build404Controller() { return array(new Phabricator404Controller($this->getRequest()), array()); } public function buildRedirectController($uri) { return array( new PhabricatorRedirectController($this->getRequest()), array( 'uri' => $uri, )); } private function renderStackTrace($trace) { $libraries = PhutilBootloader::getInstance()->getAllLibraries(); // TODO: Make this configurable? $host = 'https://secure.phabricator.com'; $browse = array( 'arcanist' => $host.'/diffusion/ARC/browse/origin:master/src/', 'phutil' => $host.'/diffusion/PHU/browse/origin:master/src/', 'phabricator' => $host.'/diffusion/P/browse/origin:master/src/', ); $rows = array(); $depth = count($trace); foreach ($trace as $part) { $lib = null; $file = idx($part, 'file'); $relative = $file; foreach ($libraries as $library) { $root = phutil_get_library_root($library); if (Filesystem::isDescendant($file, $root)) { $lib = $library; $relative = Filesystem::readablePath($file, $root); break; } } $where = ''; if (isset($part['class'])) { $where .= $part['class'].'::'; } if (isset($part['function'])) { $where .= $part['function'].'()'; } if ($file) { if (isset($browse[$lib])) { $file_name = phutil_render_tag( 'a', array( 'href' => $browse[$lib].$relative.'$'.$part['line'], 'title' => $file, 'target' => '_blank', ), phutil_escape_html($relative)); } else { $file_name = phutil_render_tag( 'span', array( 'title' => $file, ), phutil_escape_html($relative)); } $file_name = $file_name.' : '.(int)$part['line']; } else { $file_name = '(Internal)'; } $rows[] = array( $depth--, phutil_escape_html($lib), $file_name, phutil_escape_html($where), ); } $table = new AphrontTableView($rows); $table->setHeaders( array( 'Depth', 'Library', 'File', 'Where', )); $table->setColumnClasses( array( 'n', '', '', 'wide', )); return '
'. '
Stack Trace
'. $table->render(). '
'; } } diff --git a/src/applications/directory/controller/main/PhabricatorDirectoryMainController.php b/src/applications/directory/controller/main/PhabricatorDirectoryMainController.php index 706872b427..50ab58ee74 100644 --- a/src/applications/directory/controller/main/PhabricatorDirectoryMainController.php +++ b/src/applications/directory/controller/main/PhabricatorDirectoryMainController.php @@ -1,553 +1,564 @@ filter = idx($data, 'filter'); + $this->subfilter = idx($data, 'subfilter'); } 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) { 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; } $jump_panel = $this->buildJumpPanel(); $revision_panel = $this->buildRevisionPanel(); - $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( '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/', '/^p$/i' => 'uri:/project/', '/^u$/i' => 'uri:/people/', '/r([A-Z]+)$/' => 'repository', '/r([A-Z]+)(\S+)$/' => 'commit', '/^d(\d+)$/i' => 'revision', '/^t(\d+)$/i' => 'task', '/^p\s+(.+)$/i' => 'project', '/^u\s+(\S+)$/i' => 'user', '/^task:\s*(.+)/i' => 'create-task', '/^(?: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]); case 'user': return id(new AphrontRedirectResponse()) ->setURI('/p/'.$matches[1].'/'); case 'project': $project = PhabricatorProjectQueryUtil ::findCloselyNamedProject($matches[1]); if ($project) { return id(new AphrontRedirectResponse()) ->setURI('/project/view/'.$project->getID().'/'); } else { $jump = $matches[1]; } break; case 'find-symbol': return id(new AphrontRedirectResponse()) ->setURI('/diffusion/symbol/'.$matches[1].'/?jump=true'); case 'create-task': return id(new AphrontRedirectResponse()) ->setURI('/maniphest/task/create/?title=' .phutil_escape_uri($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); + + $subnav = new AphrontSideNavFilterView(); + $subnav->setBaseURI(new PhutilURI('/feed/')); + + $subnav->addFilter('all', 'All Activity', '/feed/'); + $subnav->addFilter('projects', 'My Projects'); + + $filter = $subnav->selectFilter($this->subfilter, 'all'); + + switch ($filter) { + case 'all': + $phids = array(); + break; + case 'projects': + $phids = mpull($projects, 'getPHID'); + break; + } + + $view = $this->buildFeedView($phids); + $subnav->appendChild($view); + + $nav->appendChild($subnav); + 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( '

No "Unbreak Now!" Tasks: '. 'Nothing appears to be critically broken right now.

'); $panel = '
'.$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 '. 'projects you are a member of.'); $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( '

No "Needs Triage" Tasks: '. 'No tasks in projects you are a member of '. 'need triage.

'); } 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) { $fields = $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); } else { $panel->appendChild('

No revisions are waiting on you.

'); } 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('

You have no assigned tasks.

'); } 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) { + private function buildFeedView(array $phids) { $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'))); + if ($phids) { + $feed_query->setFilterPHIDs($phids); + } // 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; - } + $feed_query->setAfter($request->getStr('after')); + $feed_query->setBefore($request->getStr('before')); + $limit = 500; // 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_new = ($request->getStr('before')) || + ($request->getStr('after') && $extra_row); $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(), + 'href' => '?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(), + 'href' => '?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 '
'. '
'. '

Feed

'. '
'. $feed_view->render(). '
'. $new_link. $old_link. '
'. '
'; } 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('article/Jump_Nav_User_Guide.html'); $doc_link = phutil_render_tag( 'a', array( 'href' => $doc_href, ), - 'Jump Nav Use Guide'); + 'Jump Nav User 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 D123 to quickly jump to '. 'it. See '.$doc_link.' or type help.'); $panel = new AphrontPanelView(); $panel->appendChild( phabricator_render_form( $user, array( 'action' => '/jump/', 'method' => 'POST', ), $jump_input)); $nav_buttons = array(); if (PhabricatorEnv::getEnvConfig('maniphest.enabled')) { $nav_buttons['/maniphest/task/create/'] = 'Create a Task'; } $nav_buttons['/file/'] = 'Upload a File'; $nav_buttons['/paste/'] = 'Create Paste'; if (PhabricatorEnv::getEnvConfig('phriction.enabled')) { $nav_buttons['/w/'] = 'Browse Wiki'; } $nav_buttons['/diffusion/'] = 'Browse Code'; $panel->appendChild('
'); foreach ($nav_buttons as $uri => $name) { $panel->appendChild( phutil_render_tag( 'a', array( 'href' => $uri, 'class' => 'button grey', ), phutil_escape_html($name))); } $panel->appendChild('
'); return $panel; } } diff --git a/src/applications/directory/controller/main/__init__.php b/src/applications/directory/controller/main/__init__.php index 2f9726df2c..0a3850f4dd 100644 --- a/src/applications/directory/controller/main/__init__.php +++ b/src/applications/directory/controller/main/__init__.php @@ -1,34 +1,36 @@