diff --git a/src/applications/diffusion/view/DiffusionCloneURIView.php b/src/applications/diffusion/view/DiffusionCloneURIView.php index fc861ed18..c9dae5756 100644 --- a/src/applications/diffusion/view/DiffusionCloneURIView.php +++ b/src/applications/diffusion/view/DiffusionCloneURIView.php @@ -1,131 +1,139 @@ repository = $repository; return $this; } public function getRepository() { return $this->repository; } public function setRepositoryURI(PhabricatorRepositoryURI $repository_uri) { $this->repositoryURI = $repository_uri; return $this; } public function getRepositoryURI() { return $this->repositoryURI; } public function setDisplayURI($display_uri) { $this->displayURI = $display_uri; return $this; } public function getDisplayURI() { return $this->displayURI; } public function render() { + $viewer = $this->getViewer(); + require_celerity_resource('diffusion-icons-css'); Javelin::initBehavior('select-content'); $uri_id = celerity_generate_unique_node_id(); $display = $this->getDisplayURI(); $input = javelin_tag( 'input', array( 'id' => $uri_id, 'type' => 'text', 'value' => $display, 'class' => 'diffusion-clone-uri', 'readonly' => 'true', )); $uri = $this->getRepositoryURI(); switch ($uri->getEffectiveIOType()) { case PhabricatorRepositoryURI::IO_READ: $io_icon = 'fa-eye'; $io_tip = pht('Read-Only'); break; case PhabricatorRepositoryURI::IO_READWRITE: $io_icon = 'fa-download'; $io_tip = pht('Read / Write'); break; default: $io_icon = 'fa-cloud'; $io_tip = pht('External'); break; } $io = id(new PHUIButtonView()) ->setTag('a') ->setColor(PHUIButtonView::GREY) ->setIcon($io_icon) ->setHref('#') ->addSigil('select-content') ->addSigil('has-tooltip') ->setMetadata( array( 'tip' => $io_tip, 'selectID' => $uri_id, )); switch ($uri->getEffectiveIOType()) { case PhabricatorRepositoryURI::IO_READ: case PhabricatorRepositoryURI::IO_READWRITE: switch ($uri->getBuiltinProtocol()) { case PhabricatorRepositoryURI::BUILTIN_PROTOCOL_SSH: - $auth_uri = '/settings/panel/ssh/'; + $auth_uri = id(new PhabricatorSSHKeysSettingsPanel()) + ->setViewer($viewer) + ->setUser($viewer) + ->getPanelURI(); $auth_tip = pht('Manage SSH Keys'); $auth_disabled = false; break; default: - $auth_uri = '/settings/panel/vcspassword'; + $auth_uri = id(new DiffusionSetPasswordSettingsPanel()) + ->setViewer($viewer) + ->setUser($viewer) + ->getPanelURI(); $auth_tip = pht('Manage Password'); $auth_disabled = false; break; } break; default: $auth_disabled = true; $auth_tip = pht('External'); $auth_uri = '#'; break; } $credentials = id(new PHUIButtonView()) ->setTag('a') ->setColor(PHUIButtonView::GREY) ->setIcon('fa-key') ->setTooltip($auth_tip) ->setHref($auth_uri) ->setDisabled($auth_disabled); $cells = array(); $cells[] = phutil_tag('td', array(), $input); $cells[] = phutil_tag('th', array(), $io); $cells[] = phutil_tag('th', array(), $credentials); $row = phutil_tag('tr', array(), $cells); return phutil_tag( 'table', array( 'class' => 'diffusion-clone-uri-table', ), $row); } } diff --git a/src/applications/settings/controller/PhabricatorSettingsMainController.php b/src/applications/settings/controller/PhabricatorSettingsMainController.php index fada4a093..841529a05 100644 --- a/src/applications/settings/controller/PhabricatorSettingsMainController.php +++ b/src/applications/settings/controller/PhabricatorSettingsMainController.php @@ -1,225 +1,225 @@ user; } private function isSelf() { $user = $this->getUser(); if (!$user) { return false; } $user_phid = $user->getPHID(); $viewer_phid = $this->getViewer()->getPHID(); return ($viewer_phid == $user_phid); } private function isTemplate() { return ($this->builtinKey !== null); } public function handleRequest(AphrontRequest $request) { $viewer = $this->getViewer(); // Redirect "/panel/XYZ/" to the viewer's personal settings panel. This // was the primary URI before global settings were introduced and allows - // generation of viewer-agnostic URIs for email. + // generation of viewer-agnostic URIs for email and logged-out users. $panel = $request->getURIData('panel'); if ($panel) { $panel = phutil_escape_uri($panel); $username = $viewer->getUsername(); $panel_uri = "/user/{$username}/page/{$panel}/"; $panel_uri = $this->getApplicationURI($panel_uri); return id(new AphrontRedirectResponse())->setURI($panel_uri); } $username = $request->getURIData('username'); $builtin = $request->getURIData('builtin'); $key = $request->getURIData('pageKey'); if ($builtin) { $this->builtinKey = $builtin; $preferences = id(new PhabricatorUserPreferencesQuery()) ->setViewer($viewer) ->withBuiltinKeys(array($builtin)) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->executeOne(); if (!$preferences) { $preferences = id(new PhabricatorUserPreferences()) ->attachUser(null) ->setBuiltinKey($builtin); } } else { $user = id(new PhabricatorPeopleQuery()) ->setViewer($viewer) ->withUsernames(array($username)) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->executeOne(); if (!$user) { return new Aphront404Response(); } $preferences = PhabricatorUserPreferences::loadUserPreferences($user); $this->user = $user; } if (!$preferences) { return new Aphront404Response(); } PhabricatorPolicyFilter::requireCapability( $viewer, $preferences, PhabricatorPolicyCapability::CAN_EDIT); $this->preferences = $preferences; $panels = $this->buildPanels($preferences); $nav = $this->renderSideNav($panels); $key = $nav->selectFilter($key, head($panels)->getPanelKey()); $panel = $panels[$key] ->setController($this) ->setNavigation($nav); $response = $panel->processRequest($request); if (($response instanceof AphrontResponse) || ($response instanceof AphrontResponseProducerInterface)) { return $response; } $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb($panel->getPanelName()); $title = $panel->getPanelName(); $view = id(new PHUITwoColumnView()) ->setNavigation($nav) ->setMainColumn($response); return $this->newPage() ->setTitle($title) ->setCrumbs($crumbs) ->appendChild($view); } private function buildPanels(PhabricatorUserPreferences $preferences) { $viewer = $this->getViewer(); $panels = PhabricatorSettingsPanel::getAllDisplayPanels(); $result = array(); foreach ($panels as $key => $panel) { $panel ->setPreferences($preferences) ->setViewer($viewer); if ($this->user) { $panel->setUser($this->user); } if (!$panel->isEnabled()) { continue; } if ($this->isTemplate()) { if (!$panel->isTemplatePanel()) { continue; } } else { if (!$this->isSelf() && !$panel->isManagementPanel()) { continue; } if ($this->isSelf() && !$panel->isUserPanel()) { continue; } } if (!empty($result[$key])) { throw new Exception(pht( "Two settings panels share the same panel key ('%s'): %s, %s.", $key, get_class($panel), get_class($result[$key]))); } $result[$key] = $panel; } if (!$result) { throw new Exception(pht('No settings panels are available.')); } return $result; } private function renderSideNav(array $panels) { $nav = new AphrontSideNavFilterView(); if ($this->isTemplate()) { $base_uri = 'builtin/'.$this->builtinKey.'/page/'; } else { $user = $this->getUser(); $base_uri = 'user/'.$user->getUsername().'/page/'; } $nav->setBaseURI(new PhutilURI($this->getApplicationURI($base_uri))); $group_key = null; foreach ($panels as $panel) { if ($panel->getPanelGroupKey() != $group_key) { $group_key = $panel->getPanelGroupKey(); $group = $panel->getPanelGroup(); $nav->addLabel($group->getPanelGroupName()); } $nav->addFilter($panel->getPanelKey(), $panel->getPanelName()); } return $nav; } public function buildApplicationMenu() { if ($this->preferences) { $panels = $this->buildPanels($this->preferences); return $this->renderSideNav($panels)->getMenu(); } return parent::buildApplicationMenu(); } protected function buildApplicationCrumbs() { $crumbs = parent::buildApplicationCrumbs(); $user = $this->getUser(); if (!$this->isSelf() && $user) { $username = $user->getUsername(); $crumbs->addTextCrumb($username, "/p/{$username}/"); } return $crumbs; } } diff --git a/src/applications/settings/panel/PhabricatorSettingsPanel.php b/src/applications/settings/panel/PhabricatorSettingsPanel.php index 7d86eaf24..eea48e540 100644 --- a/src/applications/settings/panel/PhabricatorSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorSettingsPanel.php @@ -1,277 +1,284 @@ user = $user; return $this; } public function getUser() { return $this->user; } public function setViewer(PhabricatorUser $viewer) { $this->viewer = $viewer; return $this; } public function getViewer() { return $this->viewer; } public function setOverrideURI($override_uri) { $this->overrideURI = $override_uri; return $this; } final public function setController(PhabricatorController $controller) { $this->controller = $controller; return $this; } final public function getController() { return $this->controller; } final public function setNavigation(AphrontSideNavFilterView $navigation) { $this->navigation = $navigation; return $this; } final public function getNavigation() { return $this->navigation; } public function setPreferences(PhabricatorUserPreferences $preferences) { $this->preferences = $preferences; return $this; } public function getPreferences() { return $this->preferences; } final public static function getAllPanels() { $panels = id(new PhutilClassMapQuery()) ->setAncestorClass(__CLASS__) ->setUniqueMethod('getPanelKey') ->execute(); return msortv($panels, 'getPanelOrderVector'); } final public static function getAllDisplayPanels() { $panels = array(); $groups = PhabricatorSettingsPanelGroup::getAllPanelGroupsWithPanels(); foreach ($groups as $group) { foreach ($group->getPanels() as $key => $panel) { $panels[$key] = $panel; } } return $panels; } final public function getPanelGroup() { $group_key = $this->getPanelGroupKey(); $groups = PhabricatorSettingsPanelGroup::getAllPanelGroupsWithPanels(); $group = idx($groups, $group_key); if (!$group) { throw new Exception( pht( 'No settings panel group with key "%s" exists!', $group_key)); } return $group; } /* -( Panel Configuration )------------------------------------------------ */ /** * Return a unique string used in the URI to identify this panel, like * "example". * * @return string Unique panel identifier (used in URIs). * @task config */ public function getPanelKey() { return $this->getPhobjectClassConstant('PANELKEY'); } /** * Return a human-readable description of the panel's contents, like * "Example Settings". * * @return string Human-readable panel name. * @task config */ abstract public function getPanelName(); /** * Return a panel group key constant for this panel. * * @return const Panel group key. * @task config */ abstract public function getPanelGroupKey(); /** * Return false to prevent this panel from being displayed or used. You can * do, e.g., configuration checks here, to determine if the feature your * panel controls is unavailble in this install. By default, all panels are * enabled. * * @return bool True if the panel should be shown. * @task config */ public function isEnabled() { return true; } /** * Return true if this panel is available to users while editing their own * settings. * * @return bool True to enable management on behalf of a user. * @task config */ public function isUserPanel() { return true; } /** * Return true if this panel is available to administrators while managing * bot and mailing list accounts. * * @return bool True to enable management on behalf of accounts. * @task config */ public function isManagementPanel() { return false; } /** * Return true if this panel is available while editing settings templates. * * @return bool True to allow editing in templates. * @task config */ public function isTemplatePanel() { return false; } /* -( Panel Implementation )----------------------------------------------- */ /** * Process a user request for this settings panel. Implement this method like * a lightweight controller. If you return an @{class:AphrontResponse}, the * response will be used in whole. If you return anything else, it will be * treated as a view and composed into a normal settings page. * * Generally, render your settings panel by returning a form, then return * a redirect when the user saves settings. * * @param AphrontRequest Incoming request. * @return wild Response to request, either as an * @{class:AphrontResponse} or something which can * be composed into a @{class:AphrontView}. * @task panel */ abstract public function processRequest(AphrontRequest $request); /** * Get the URI for this panel. * * @param string? Optional path to append. * @return string Relative URI for the panel. * @task panel */ final public function getPanelURI($path = '') { $path = ltrim($path, '/'); if ($this->overrideURI) { return rtrim($this->overrideURI, '/').'/'.$path; } $key = $this->getPanelKey(); $key = phutil_escape_uri($key); $user = $this->getUser(); if ($user) { - $username = $user->getUsername(); - return "/settings/user/{$username}/page/{$key}/{$path}"; + if ($user->isLoggedIn()) { + $username = $user->getUsername(); + return "/settings/user/{$username}/page/{$key}/{$path}"; + } else { + // For logged-out users, we can't put their username in the URI. This + // page will prompt them to login, then redirect them to the correct + // location. + return "/settings/panel/{$key}/"; + } } else { $builtin = $this->getPreferences()->getBuiltinKey(); return "/settings/builtin/{$builtin}/page/{$key}/{$path}"; } } /* -( Internals )---------------------------------------------------------- */ /** * Generates a key to sort the list of panels. * * @return string Sortable key. * @task internal */ final public function getPanelOrderVector() { return id(new PhutilSortVector()) ->addString($this->getPanelName()); } protected function newDialog() { return $this->getController()->newDialog(); } protected function writeSetting( PhabricatorUserPreferences $preferences, $key, $value) { $viewer = $this->getViewer(); $request = $this->getController()->getRequest(); $editor = id(new PhabricatorUserPreferencesEditor()) ->setActor($viewer) ->setContentSourceFromRequest($request) ->setContinueOnNoEffect(true) ->setContinueOnMissingFields(true); $xactions = array(); $xactions[] = $preferences->newTransaction($key, $value); $editor->applyTransactions($preferences, $xactions); } }