diff --git a/src/applications/base/controller/PhabricatorController.php b/src/applications/base/controller/PhabricatorController.php index e103b87d43..45ba5dc069 100644 --- a/src/applications/base/controller/PhabricatorController.php +++ b/src/applications/base/controller/PhabricatorController.php @@ -1,313 +1,313 @@ shouldAllowPublic()) { return false; } return true; } public function shouldRequireAdmin() { return false; } public function shouldRequireEnabledUser() { return true; } public function shouldAllowPublic() { return false; } public function shouldRequireEmailVerification() { $need_verify = PhabricatorUserEmail::isEmailVerificationRequired(); $need_login = $this->shouldRequireLogin(); return ($need_login && $need_verify); } final public function willBeginExecution() { $request = $this->getRequest(); $user = new PhabricatorUser(); $phusr = $request->getCookie('phusr'); $phsid = $request->getCookie('phsid'); if (strlen($phusr) && $phsid) { $info = queryfx_one( $user->establishConnection('r'), 'SELECT u.* FROM %T u JOIN %T s ON u.phid = s.userPHID AND s.type LIKE %> AND s.sessionKey = %s', $user->getTableName(), 'phabricator_session', 'web-', $phsid); if ($info) { $user->loadFromArray($info); } } $translation = $user->getTranslation(); if ($translation && $translation != PhabricatorEnv::getEnvConfig('translation.provider')) { $translation = newv($translation, array()); PhutilTranslator::getInstance() ->setLanguage($translation->getLanguage()) ->addTranslations($translation->getTranslations()); } $request->setUser($user); if ($user->getIsDisabled() && $this->shouldRequireEnabledUser()) { $disabled_user_controller = new PhabricatorDisabledUserController( $request); return $this->delegateToController($disabled_user_controller); } $event = new PhabricatorEvent( PhabricatorEventType::TYPE_CONTROLLER_CHECKREQUEST, array( 'request' => $request, 'controller' => $this, )); $event->setUser($user); PhutilEventEngine::dispatchEvent($event); $checker_controller = $event->getValue('controller'); if ($checker_controller != $this) { return $this->delegateToController($checker_controller); } $preferences = $user->loadPreferences(); if (PhabricatorEnv::getEnvConfig('darkconsole.enabled')) { $dark_console = PhabricatorUserPreferences::PREFERENCE_DARK_CONSOLE; if ($preferences->getPreference($dark_console) || PhabricatorEnv::getEnvConfig('darkconsole.always-on')) { $console = new DarkConsoleCore(); $request->getApplicationConfiguration()->setConsole($console); } } if ($this->shouldRequireLogin() && !$user->getPHID()) { $login_controller = new PhabricatorLoginController($request); return $this->delegateToController($login_controller); } if ($this->shouldRequireEmailVerification()) { $email = $user->loadPrimaryEmail(); if (!$email) { throw new Exception( "No primary email address associated with this account!"); } if (!$email->getIsVerified()) { $verify_controller = new PhabricatorMustVerifyEmailController($request); return $this->delegateToController($verify_controller); } } if ($this->shouldRequireAdmin() && !$user->getIsAdmin()) { return new Aphront403Response(); } } public function buildStandardPageView() { $view = new PhabricatorStandardPageView(); $view->setRequest($this->getRequest()); $view->setController($this); return $view; } public function buildStandardPageResponse($view, array $data) { $page = $this->buildStandardPageView(); $page->appendChild($view); $response = new AphrontWebpageResponse(); $response->setContent($page->render()); return $response; } public function getApplicationURI($path = '') { if (!$this->getCurrentApplication()) { throw new Exception("No application!"); } return $this->getCurrentApplication()->getBaseURI().ltrim($path, '/'); } public function buildApplicationPage($view, array $options) { $page = $this->buildStandardPageView(); $application = $this->getCurrentApplication(); + $page->setTitle(idx($options, 'title')); if ($application) { $page->setApplicationName($application->getName()); - $page->setTitle(idx($options, 'title')); if ($application->getTitleGlyph()) { $page->setGlyph($application->getTitleGlyph()); } } if (!($view instanceof AphrontSideNavFilterView)) { $nav = new AphrontSideNavFilterView(); $nav->appendChild($view); $view = $nav; } $view->setUser($this->getRequest()->getUser()); $page->appendChild($view); if (idx($options, 'device')) { $page->setDeviceReady(true); } $page->setShowChrome(idx($options, 'chrome', true)); $page->setDust(idx($options, 'dust', false)); $application_menu = $this->buildApplicationMenu(); if ($application_menu) { $page->setApplicationMenu($application_menu); } $response = new AphrontWebpageResponse(); return $response->setContent($page->render()); } public function didProcessRequest($response) { $request = $this->getRequest(); $response->setRequest($request); $seen = array(); while ($response instanceof AphrontProxyResponse) { $hash = spl_object_hash($response); if (isset($seen[$hash])) { $seen[] = get_class($response); throw new Exception( "Cycle while reducing proxy responses: ". implode(' -> ', $seen)); } $seen[$hash] = get_class($response); $response = $response->reduceProxyResponse(); } if ($response instanceof AphrontDialogResponse) { if (!$request->isAjax()) { $view = new PhabricatorStandardPageView(); $view->setRequest($request); $view->setController($this); $view->appendChild(hsprintf( '
%s
', $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; } protected function getHandle($phid) { if (empty($this->handles[$phid])) { throw new Exception( "Attempting to access handle which wasn't loaded: {$phid}"); } return $this->handles[$phid]; } protected function loadHandles(array $phids) { $phids = array_filter($phids); $this->handles = $this->loadViewerHandles($phids); return $this; } protected function getLoadedHandles() { return $this->handles; } protected function loadViewerHandles(array $phids) { return id(new PhabricatorObjectHandleData($phids)) ->setViewer($this->getRequest()->getUser()) ->loadHandles(); } /** * Render a list of links to handles, identified by PHIDs. The handles must * already be loaded. * * @param list List of PHIDs to render links to. * @param string Style, one of "\n" (to put each item on its own line) * or "," (to list items inline, separated by commas). * @return string Rendered list of handle links. */ protected function renderHandlesForPHIDs(array $phids, $style = "\n") { $style_map = array( "\n" => phutil_tag('br'), ',' => ', ', ); if (empty($style_map[$style])) { throw new Exception("Unknown handle list style '{$style}'!"); } $items = array(); foreach ($phids as $phid) { $items[] = $this->getHandle($phid)->renderLink(); } return phutil_implode_html($style_map[$style], $items); } protected function buildApplicationMenu() { return null; } protected function buildApplicationCrumbs() { $crumbs = array(); $application = $this->getCurrentApplication(); if ($application) { $sprite = $application->getIconName(); if (!$sprite) { $sprite = 'application'; } $crumbs[] = id(new PhabricatorCrumbView()) ->setHref($this->getApplicationURI()) ->setIcon($sprite); } $view = new PhabricatorCrumbsView(); foreach ($crumbs as $crumb) { $view->addCrumb($crumb); } return $view; } } diff --git a/src/applications/directory/controller/PhabricatorDirectoryMainController.php b/src/applications/directory/controller/PhabricatorDirectoryMainController.php index 52c347f56c..63f70ccc0b 100644 --- a/src/applications/directory/controller/PhabricatorDirectoryMainController.php +++ b/src/applications/directory/controller/PhabricatorDirectoryMainController.php @@ -1,466 +1,470 @@ filter = idx($data, 'filter'); } public function processRequest() { $user = $this->getRequest()->getUser(); if ($this->filter == 'jump') { return $this->buildJumpResponse(); } $nav = $this->buildNav(); $project_query = new PhabricatorProjectQuery(); $project_query->setViewer($user); $project_query->withMemberPHIDs(array($user->getPHID())); $projects = $project_query->execute(); 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; } if (PhabricatorEnv::getEnvConfig('welcome.html') !== null) { $welcome_panel = $this->buildWelcomePanel(); } else { $welcome_panel = null; } $jump_panel = $this->buildJumpPanel(); $revision_panel = $this->buildRevisionPanel(); $audit_panel = $this->buildAuditPanel(); $commit_panel = $this->buildCommitPanel(); $content = array( $jump_panel, $welcome_panel, $unbreak_panel, $triage_panel, $revision_panel, $tasks_panel, $audit_panel, $commit_panel, $this->minipanels, ); $nav->appendChild($content); $nav->appendChild(new PhabricatorGlobalUploadTargetView()); + $title = PhabricatorEnv::getEnvConfig('phabricator.serious-business') ? + 'Phabricator' : + pht('Bacon Ice Cream for Breakfast'); + return $this->buildApplicationPage( $nav, array( - 'title' => 'Phabricator', + 'title' => $title, 'device' => true, )); } private function buildJumpResponse() { $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().'/'); } else { return id(new AphrontRedirectResponse())->setURI('/'); } } 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_tag( 'a', array( 'href' => '/maniphest/view/all/', 'class' => 'grey button', ), "View All Unbreak Now \xC2\xBB")); $panel->appendChild($this->buildTaskListView($tasks)); $panel->setNoBackground(); 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->withAnyProjects(mpull($projects, 'getPHID')); $task_query->setLimit(10); $tasks = $task_query->execute(); } else { $tasks = array(); } if (!$tasks) { return $this->renderMiniPanel( 'No "Needs Triage" Tasks', hsprintf( 'No tasks in projects you are a member of '. 'need triage.')); } $panel = new AphrontPanelView(); $panel->setHeader('Needs Triage'); $panel->setCaption(hsprintf( 'Open tasks with "Needs Triage" priority in '. 'projects you are a member of.')); $panel->addButton( phutil_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)); $panel->setNoBackground(); 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($blocking, $active, ) = DifferentialRevisionQuery::splitResponsible( $revisions, array($user_phid)); if (!$blocking && !$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_tag( 'a', array( 'href' => '/differential/', 'class' => 'button grey', ), "View Active Revisions \xC2\xBB")); $revision_view = id(new DifferentialRevisionListView()) ->setHighlightAge(true) ->setRevisions(array_merge($blocking, $active)) ->setFields(DifferentialRevisionListView::getDefaultFields($user)) ->setUser($user) ->loadAssets(); $phids = array_merge( array($user_phid), $revision_view->getRequiredHandlePHIDs()); $handles = $this->loadViewerHandles($phids); $revision_view->setHandles($handles); $panel->appendChild($revision_view); $panel->setNoBackground(); return $panel; } private function buildWelcomePanel() { $panel = new AphrontPanelView(); $panel->appendChild( phutil_safe_html( PhabricatorEnv::getEnvConfig('welcome.html'))); $panel->setNoBackground(); 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->addButton( phutil_tag( 'a', array( 'href' => '/maniphest/', 'class' => 'button grey', ), "View Active Tasks \xC2\xBB")); $panel->appendChild($this->buildTaskListView($tasks)); $panel->setNoBackground(); 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 = $this->loadViewerHandles($phids); $view = new ManiphestTaskListView(); $view->setTasks($tasks); $view->setUser($user); $view->setHandles($handles); return $view; } 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_tag( 'a', array( 'href' => $doc_href, ), 'Jump Nav User Guide'); $jump_input = phutil_tag( 'input', array( 'type' => 'text', 'class' => 'phabricator-jump-nav', 'name' => 'jump', 'id' => $uniq_id, 'value' => $query, )); $jump_caption = phutil_tag( 'p', array( 'class' => 'phabricator-jump-nav-caption', ), hsprintf( 'Enter the name of an object like D123 to quickly jump to '. 'it. See %s or type help.', $doc_link)); $panel = new AphrontPanelView(); $panel->setHeader('Jump Nav'); $panel->appendChild( phabricator_form( $user, array( 'action' => '/jump/', 'method' => 'POST', 'class' => 'phabricator-jump-nav-form', ), array( $jump_input, $jump_caption, ))); return $panel; } private function renderMiniPanel($title, $body) { $panel = new AphrontMiniPanelView(); $panel->appendChild( phutil_tag( 'p', array( ), array( phutil_tag('strong', array(), $title.': '), $body ))); $this->minipanels[] = $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 = $this->loadViewerHandles($phids); $view->setHandles($handles); $panel = new AphrontPanelView(); $panel->setHeader('Audits'); $panel->setCaption('Commits awaiting your audit.'); $panel->appendChild($view); $panel->addButton( phutil_tag( 'a', array( 'href' => '/audit/', 'class' => 'button grey', ), "View Active Audits \xC2\xBB")); $panel->setNoBackground(); return $panel; } public function buildCommitPanel() { $request = $this->getRequest(); $user = $request->getUser(); $phids = array($user->getPHID()); $query = new PhabricatorAuditCommitQuery(); $query->withAuthorPHIDs($phids); $query->withStatus(PhabricatorAuditCommitQuery::STATUS_CONCERN); $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 = $this->loadViewerHandles($phids); $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_tag( 'a', array( 'href' => '/audit/', 'class' => 'button grey', ), "View Problem Commits \xC2\xBB")); $panel->setNoBackground(); return $panel; } }