diff --git a/src/applications/diffusion/controller/DiffusionController.php b/src/applications/diffusion/controller/DiffusionController.php index dc916c7e9e..63f3897b7f 100644 --- a/src/applications/diffusion/controller/DiffusionController.php +++ b/src/applications/diffusion/controller/DiffusionController.php @@ -1,288 +1,299 @@ diffusionRequest = $request; return $this; } protected function getDiffusionRequest() { if (!$this->diffusionRequest) { throw new Exception(pht('No Diffusion request object!')); } return $this->diffusionRequest; } public function willBeginExecution() { $request = $this->getRequest(); // Check if this is a VCS request, e.g. from "git clone", "hg clone", or // "svn checkout". If it is, we jump off into repository serving code to // process the request. if (DiffusionServeController::isVCSRequest($request)) { $serve_controller = id(new DiffusionServeController()) ->setCurrentApplication($this->getCurrentApplication()); return $this->delegateToController($serve_controller); } return parent::willBeginExecution(); } protected function shouldLoadDiffusionRequest() { return true; } public function handleRequest(AphrontRequest $request) { if ($request->getURIData('callsign') && $this->shouldLoadDiffusionRequest()) { try { $drequest = DiffusionRequest::newFromAphrontRequestDictionary( $request->getURIMap(), $request); } catch (Exception $ex) { return id(new Aphront404Response()) ->setRequest($request); } $this->setDiffusionRequest($drequest); } return $this->processDiffusionRequest($request); } + protected function loadDiffusionContextForEdit() { + return $this->loadContext( + array( + 'edit' => true, + )); + } + protected function loadDiffusionContext() { + return $this->loadContext(array()); + } + + private function loadContext(array $options) { $request = $this->getRequest(); $viewer = $this->getViewer(); $identifier = $request->getURIData('repositoryCallsign'); if (!strlen($identifier)) { $identifier = (int)$request->getURIData('repositoryID'); } - $params = array( + $params = $options + array( 'repository' => $identifier, 'user' => $viewer, 'blob' => $request->getURIData('dblob'), 'commit' => $request->getURIData('commit'), 'path' => $request->getURIData('path'), 'line' => $request->getURIData('line'), 'branch' => $request->getURIData('branch'), 'lint' => $request->getStr('lint'), ); $drequest = DiffusionRequest::newFromDictionary($params); if (!$drequest) { return new Aphront404Response(); } $this->diffusionRequest = $drequest; return null; } protected function processDiffusionRequest(AphrontRequest $request) { throw new PhutilMethodNotImplementedException(); } public function buildCrumbs(array $spec = array()) { $crumbs = $this->buildApplicationCrumbs(); $crumb_list = $this->buildCrumbList($spec); foreach ($crumb_list as $crumb) { $crumbs->addCrumb($crumb); } return $crumbs; } private function buildCrumbList(array $spec = array()) { $spec = $spec + array( 'commit' => null, 'tags' => null, 'branches' => null, 'view' => null, ); $crumb_list = array(); // On the home page, we don't have a DiffusionRequest. if ($this->diffusionRequest) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); } else { $drequest = null; $repository = null; } if (!$repository) { return $crumb_list; } $repository_name = $repository->getName(); if (!$spec['commit'] && !$spec['tags'] && !$spec['branches']) { $branch_name = $drequest->getBranch(); if ($branch_name) { $repository_name .= ' ('.$branch_name.')'; } } $crumb = id(new PHUICrumbView()) ->setName($repository_name); if (!$spec['view'] && !$spec['commit'] && !$spec['tags'] && !$spec['branches']) { $crumb_list[] = $crumb; return $crumb_list; } $crumb->setHref( $drequest->generateURI( array( 'action' => 'branch', 'path' => '/', ))); $crumb_list[] = $crumb; $stable_commit = $drequest->getStableCommit(); $commit_name = $repository->formatCommitName($stable_commit); $commit_uri = $repository->getCommitURI($stable_commit); if ($spec['tags']) { $crumb = new PHUICrumbView(); if ($spec['commit']) { $crumb->setName(pht('Tags for %s', $commit_name)); $crumb->setHref($commit_uri); } else { $crumb->setName(pht('Tags')); } $crumb_list[] = $crumb; return $crumb_list; } if ($spec['branches']) { $crumb = id(new PHUICrumbView()) ->setName(pht('Branches')); $crumb_list[] = $crumb; return $crumb_list; } if ($spec['commit']) { $crumb = id(new PHUICrumbView()) ->setName($commit_name) ->setHref($commit_uri); $crumb_list[] = $crumb; return $crumb_list; } $crumb = new PHUICrumbView(); $view = $spec['view']; switch ($view) { case 'history': $view_name = pht('History'); break; case 'browse': $view_name = pht('Browse'); break; case 'lint': $view_name = pht('Lint'); break; case 'change': $view_name = pht('Change'); break; } $crumb = id(new PHUICrumbView()) ->setName($view_name); $crumb_list[] = $crumb; return $crumb_list; } protected function callConduitWithDiffusionRequest( $method, array $params = array()) { $user = $this->getRequest()->getUser(); $drequest = $this->getDiffusionRequest(); return DiffusionQuery::callConduitWithDiffusionRequest( $user, $drequest, $method, $params); } protected function getRepositoryControllerURI( PhabricatorRepository $repository, $path) { return $repository->getPathURI($path); } protected function renderPathLinks(DiffusionRequest $drequest, $action) { $path = $drequest->getPath(); $path_parts = array_filter(explode('/', trim($path, '/'))); $divider = phutil_tag( 'span', array( 'class' => 'phui-header-divider', ), '/'); $links = array(); if ($path_parts) { $links[] = phutil_tag( 'a', array( 'href' => $drequest->generateURI( array( 'action' => $action, 'path' => '', )), ), $drequest->getRepository()->getDisplayName()); $links[] = $divider; $accum = ''; $last_key = last_key($path_parts); foreach ($path_parts as $key => $part) { $accum .= '/'.$part; if ($key === $last_key) { $links[] = $part; } else { $links[] = phutil_tag( 'a', array( 'href' => $drequest->generateURI( array( 'action' => $action, 'path' => $accum.'/', )), ), $part); $links[] = $divider; } } } else { $links[] = $drequest->getRepository()->getDisplayName(); $links[] = $divider; } return $links; } protected function renderStatusMessage($title, $body) { return id(new PHUIInfoView()) ->setSeverity(PHUIInfoView::SEVERITY_WARNING) ->setTitle($title) ->appendChild($body); } protected function renderTablePagerBox(PHUIPagerView $pager) { return id(new PHUIBoxView()) ->addMargin(PHUI::MARGIN_LARGE) ->appendChild($pager); } } diff --git a/src/applications/diffusion/controller/DiffusionMirrorDeleteController.php b/src/applications/diffusion/controller/DiffusionMirrorDeleteController.php index c49cdb7517..a049146cb5 100644 --- a/src/applications/diffusion/controller/DiffusionMirrorDeleteController.php +++ b/src/applications/diffusion/controller/DiffusionMirrorDeleteController.php @@ -1,49 +1,45 @@ loadDiffusionContext(); if ($response) { return $response; } $viewer = $this->getViewer(); $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $mirror = id(new PhabricatorRepositoryMirrorQuery()) ->setViewer($viewer) ->withIDs(array($request->getURIData('id'))) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->executeOne(); if (!$mirror) { return new Aphront404Response(); } $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/#mirrors'); if ($request->isFormPost()) { $mirror->delete(); return id(new AphrontReloadResponse())->setURI($edit_uri); } - $dialog = id(new AphrontDialogView()) - ->setUser($viewer) + return $this->newDialog() ->setTitle(pht('Really delete mirror?')) ->appendChild( pht('Phabricator will stop pushing updates to this mirror.')) ->addSubmitButton(pht('Delete Mirror')) ->addCancelButton($edit_uri); - - return id(new AphrontDialogResponse()) - ->setDialog($dialog); } } diff --git a/src/applications/diffusion/controller/DiffusionMirrorEditController.php b/src/applications/diffusion/controller/DiffusionMirrorEditController.php index f5793f2afe..d438a9e6d2 100644 --- a/src/applications/diffusion/controller/DiffusionMirrorEditController.php +++ b/src/applications/diffusion/controller/DiffusionMirrorEditController.php @@ -1,134 +1,130 @@ loadDiffusionContext(); if ($response) { return $response; } $viewer = $this->getViewer(); $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); PhabricatorPolicyFilter::requireCapability( $viewer, $repository, PhabricatorPolicyCapability::CAN_EDIT); if ($request->getURIData('id')) { $mirror = id(new PhabricatorRepositoryMirrorQuery()) ->setViewer($viewer) ->withIDs(array($request->getURIData('id'))) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->executeOne(); if (!$mirror) { return new Aphront404Response(); } $is_new = false; } else { $mirror = PhabricatorRepositoryMirror::initializeNewMirror($viewer) ->setRepositoryPHID($repository->getPHID()) ->attachRepository($repository); $is_new = true; } $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/#mirrors'); $v_remote = $mirror->getRemoteURI(); $e_remote = true; $v_credentials = $mirror->getCredentialPHID(); $e_credentials = null; $credentials = id(new PassphraseCredentialQuery()) ->setViewer($viewer) ->withIsDestroyed(false) ->execute(); $errors = array(); if ($request->isFormPost()) { $v_remote = $request->getStr('remoteURI'); if (strlen($v_remote)) { try { PhabricatorRepository::assertValidRemoteURI($v_remote); $e_remote = null; } catch (Exception $ex) { $e_remote = pht('Invalid'); $errors[] = $ex->getMessage(); } } else { $e_remote = pht('Required'); $errors[] = pht('You must provide a remote URI.'); } $v_credentials = $request->getStr('credential'); if ($v_credentials) { $phids = mpull($credentials, null, 'getPHID'); if (empty($phids[$v_credentials])) { $e_credentials = pht('Invalid'); $errors[] = pht( 'You do not have permission to use those credentials.'); } } if (!$errors) { $mirror ->setRemoteURI($v_remote) ->setCredentialPHID($v_credentials) ->save(); return id(new AphrontReloadResponse())->setURI($edit_uri); } } $form_errors = null; if ($errors) { $form_errors = id(new PHUIInfoView()) ->setErrors($errors); } if ($is_new) { $title = pht('Create Mirror'); $submit = pht('Create Mirror'); } else { $title = pht('Edit Mirror'); $submit = pht('Save Changes'); } $form = id(new PHUIFormLayoutView()) ->appendChild( id(new AphrontFormTextControl()) ->setLabel(pht('Remote URI')) ->setName('remoteURI') ->setValue($v_remote) ->setError($e_remote)) ->appendChild( id(new PassphraseCredentialControl()) ->setLabel(pht('Credentials')) ->setName('credential') ->setAllowNull(true) ->setValue($v_credentials) ->setError($e_credentials) ->setOptions($credentials)); - $dialog = id(new AphrontDialogView()) - ->setUser($viewer) + return $this->newDialog() ->setTitle($title) ->setWidth(AphrontDialogView::WIDTH_FORM) ->appendChild($form_errors) ->appendChild($form) ->addSubmitButton($submit) ->addCancelButton($edit_uri); - - return id(new AphrontDialogResponse()) - ->setDialog($dialog); } } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php b/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php index 79c617f220..c6e2796fc1 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php @@ -1,890 +1,891 @@ getUser(); $this->edit = $request->getURIData('edit'); // NOTE: We can end up here via either "Create Repository", or via // "Import Repository", or via "Edit Remote", or via "Edit Policies". In // the latter two cases, we show only a few of the pages. $repository = null; $service = null; switch ($this->edit) { case 'remote': case 'policy': + $response = $this->loadDiffusionContextForEdit(); + if ($response) { + return $response; + } + $repository = $this->getDiffusionRequest()->getRepository(); // Make sure we have CAN_EDIT. PhabricatorPolicyFilter::requireCapability( $viewer, $repository, PhabricatorPolicyCapability::CAN_EDIT); $this->setRepository($repository); $cancel_uri = $this->getRepositoryControllerURI($repository, 'edit/'); break; case 'import': case 'create': $this->requireApplicationCapability( DiffusionCreateRepositoriesCapability::CAPABILITY); // Pick a random open service to allocate this repository on, if any // exist. If there are no services, we aren't in cluster mode and // will allocate locally. If there are services but none permit // allocations, we fail. $services = id(new AlmanacServiceQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withServiceClasses( array( 'AlmanacClusterRepositoryServiceType', )) ->execute(); if ($services) { // Filter out services which do not permit new allocations. foreach ($services as $key => $possible_service) { if ($possible_service->getAlmanacPropertyValue('closed')) { unset($services[$key]); } } if (!$services) { throw new Exception( pht( 'This install is configured in cluster mode, but all '. 'available repository cluster services are closed to new '. 'allocations. At least one service must be open to allow '. 'new allocations to take place.')); } shuffle($services); $service = head($services); } $cancel_uri = $this->getApplicationURI('new/'); break; default: throw new Exception(pht('Invalid edit operation!')); } $form = id(new PHUIPagedFormView()) ->setUser($viewer) ->setCancelURI($cancel_uri); switch ($this->edit) { case 'remote': $title = pht('Edit Remote'); $form ->addPage('remote-uri', $this->buildRemoteURIPage()) ->addPage('auth', $this->buildAuthPage()); break; case 'policy': $title = pht('Edit Policies'); $form ->addPage('policy', $this->buildPolicyPage()); break; case 'create': $title = pht('Create Repository'); $form ->addPage('vcs', $this->buildVCSPage()) ->addPage('name', $this->buildNamePage()) ->addPage('policy', $this->buildPolicyPage()) ->addPage('done', $this->buildDonePage()); break; case 'import': $title = pht('Import Repository'); $form ->addPage('vcs', $this->buildVCSPage()) ->addPage('name', $this->buildNamePage()) ->addPage('remote-uri', $this->buildRemoteURIPage()) ->addPage('auth', $this->buildAuthPage()) ->addPage('policy', $this->buildPolicyPage()) ->addPage('done', $this->buildDonePage()); break; } if ($request->isFormPost()) { $form->readFromRequest($request); if ($form->isComplete()) { $is_create = ($this->edit === 'import' || $this->edit === 'create'); $is_auth = ($this->edit == 'import' || $this->edit == 'remote'); $is_policy = ($this->edit != 'remote'); $is_init = ($this->edit == 'create'); if ($is_create) { $repository = PhabricatorRepository::initializeNewRepository( $viewer); } $template = id(new PhabricatorRepositoryTransaction()); $type_name = PhabricatorRepositoryTransaction::TYPE_NAME; $type_vcs = PhabricatorRepositoryTransaction::TYPE_VCS; $type_activate = PhabricatorRepositoryTransaction::TYPE_ACTIVATE; $type_local_path = PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH; $type_remote_uri = PhabricatorRepositoryTransaction::TYPE_REMOTE_URI; $type_hosting = PhabricatorRepositoryTransaction::TYPE_HOSTING; $type_http = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP; $type_ssh = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH; $type_credential = PhabricatorRepositoryTransaction::TYPE_CREDENTIAL; $type_view = PhabricatorTransactions::TYPE_VIEW_POLICY; $type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY; $type_space = PhabricatorTransactions::TYPE_SPACE; $type_push = PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY; $type_service = PhabricatorRepositoryTransaction::TYPE_SERVICE; $xactions = array(); // If we're creating a new repository, set all this core stuff. if ($is_create) { $callsign = $form->getPage('name') ->getControl('callsign')->getValue(); // We must set this to a unique value to save the repository // initially, and it's immutable, so we don't bother using // transactions to apply this change. $repository->setCallsign($callsign); $xactions[] = id(clone $template) ->setTransactionType($type_name) ->setNewValue( $form->getPage('name')->getControl('name')->getValue()); $xactions[] = id(clone $template) ->setTransactionType($type_vcs) ->setNewValue( $form->getPage('vcs')->getControl('vcs')->getValue()); $activate = $form->getPage('done') ->getControl('activate')->getValue(); $xactions[] = id(clone $template) ->setTransactionType($type_activate) ->setNewValue(($activate == 'start')); if ($service) { $xactions[] = id(clone $template) ->setTransactionType($type_service) ->setNewValue($service->getPHID()); } $default_local_path = PhabricatorEnv::getEnvConfig( 'repository.default-local-path'); $default_local_path = rtrim($default_local_path, '/'); $default_local_path = $default_local_path.'/'.$callsign.'/'; $xactions[] = id(clone $template) ->setTransactionType($type_local_path) ->setNewValue($default_local_path); } if ($is_init) { $xactions[] = id(clone $template) ->setTransactionType($type_hosting) ->setNewValue(true); $vcs = $form->getPage('vcs')->getControl('vcs')->getValue(); if ($vcs != PhabricatorRepositoryType::REPOSITORY_TYPE_SVN) { if (PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth')) { $v_http_mode = PhabricatorRepository::SERVE_READWRITE; } else { $v_http_mode = PhabricatorRepository::SERVE_OFF; } $xactions[] = id(clone $template) ->setTransactionType($type_http) ->setNewValue($v_http_mode); } if (PhabricatorEnv::getEnvConfig('diffusion.ssh-user')) { $v_ssh_mode = PhabricatorRepository::SERVE_READWRITE; } else { $v_ssh_mode = PhabricatorRepository::SERVE_OFF; } $xactions[] = id(clone $template) ->setTransactionType($type_ssh) ->setNewValue($v_ssh_mode); } if ($is_auth) { $xactions[] = id(clone $template) ->setTransactionType($type_remote_uri) ->setNewValue( $form->getPage('remote-uri')->getControl('remoteURI') ->getValue()); $xactions[] = id(clone $template) ->setTransactionType($type_credential) ->setNewValue( $form->getPage('auth')->getControl('credential')->getValue()); } if ($is_policy) { $policy_page = $form->getPage('policy'); $xactions[] = id(clone $template) ->setTransactionType($type_view) ->setNewValue($policy_page->getControl('viewPolicy')->getValue()); $xactions[] = id(clone $template) ->setTransactionType($type_edit) ->setNewValue($policy_page->getControl('editPolicy')->getValue()); if ($is_init || $repository->isHosted()) { $xactions[] = id(clone $template) ->setTransactionType($type_push) ->setNewValue($policy_page->getControl('pushPolicy')->getValue()); } $xactions[] = id(clone $template) ->setTransactionType($type_space) ->setNewValue( $policy_page->getControl('viewPolicy')->getSpacePHID()); } id(new PhabricatorRepositoryEditor()) ->setContinueOnNoEffect(true) ->setContentSourceFromRequest($request) ->setActor($viewer) ->applyTransactions($repository, $xactions); $repo_uri = $this->getRepositoryControllerURI($repository, 'edit/'); return id(new AphrontRedirectResponse())->setURI($repo_uri); } } else { $dict = array(); if ($repository) { $dict = array( 'remoteURI' => $repository->getRemoteURI(), 'credential' => $repository->getCredentialPHID(), 'viewPolicy' => $repository->getViewPolicy(), 'editPolicy' => $repository->getEditPolicy(), 'pushPolicy' => $repository->getPushPolicy(), 'spacePHID' => $repository->getSpacePHID(), ); } $form->readFromObject($dict); } $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb($title); - return $this->buildApplicationPage( - array( - $crumbs, - $form, - ), - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($form); } /* -( Page: VCS Type )----------------------------------------------------- */ private function buildVCSPage() { $is_import = ($this->edit == 'import'); if ($is_import) { $git_str = pht( 'Import a Git repository (for example, a repository hosted '. 'on GitHub).'); $hg_str = pht( 'Import a Mercurial repository (for example, a repository '. 'hosted on Bitbucket).'); $svn_str = pht('Import a Subversion repository.'); } else { $git_str = pht('Create a new, empty Git repository.'); $hg_str = pht('Create a new, empty Mercurial repository.'); $svn_str = pht('Create a new, empty Subversion repository.'); } $control = id(new AphrontFormRadioButtonControl()) ->setName('vcs') ->setLabel(pht('Type')) ->addButton( PhabricatorRepositoryType::REPOSITORY_TYPE_GIT, pht('Git'), $git_str) ->addButton( PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL, pht('Mercurial'), $hg_str) ->addButton( PhabricatorRepositoryType::REPOSITORY_TYPE_SVN, pht('Subversion'), $svn_str); return id(new PHUIFormPageView()) ->setPageName(pht('Repository Type')) ->setUser($this->getRequest()->getUser()) ->setValidateFormPageCallback(array($this, 'validateVCSPage')) ->addControl($control); } public function validateVCSPage(PHUIFormPageView $page) { $valid = array( PhabricatorRepositoryType::REPOSITORY_TYPE_GIT => true, PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL => true, PhabricatorRepositoryType::REPOSITORY_TYPE_SVN => true, ); $c_vcs = $page->getControl('vcs'); $v_vcs = $c_vcs->getValue(); if (!$v_vcs) { $c_vcs->setError(pht('Required')); $page->addPageError( pht('You must select a version control system.')); } else if (empty($valid[$v_vcs])) { $c_vcs->setError(pht('Invalid')); $page->addPageError( pht('You must select a valid version control system.')); } return $c_vcs->isValid(); } /* -( Page: Name and Callsign )-------------------------------------------- */ private function buildNamePage() { return id(new PHUIFormPageView()) ->setUser($this->getRequest()->getUser()) ->setPageName(pht('Repository Name and Location')) ->setValidateFormPageCallback(array($this, 'validateNamePage')) ->addRemarkupInstructions( pht( '**Choose a human-readable name for this repository**, like '. '"CompanyName Mobile App" or "CompanyName Backend Server". You '. 'can change this later.')) ->addControl( id(new AphrontFormTextControl()) ->setName('name') ->setLabel(pht('Name')) ->setCaption(pht('Human-readable repository name.'))) ->addRemarkupInstructions( pht( '**Choose a "Callsign" for the repository.** This is a short, '. 'unique string which identifies commits elsewhere in Phabricator. '. 'For example, you might use `M` for your mobile app repository '. 'and `B` for your backend repository.'. "\n\n". '**Callsigns must be UPPERCASE**, and can not be edited after the '. 'repository is created. Generally, you should choose short '. 'callsigns.')) ->addControl( id(new AphrontFormTextControl()) ->setName('callsign') ->setLabel(pht('Callsign')) ->setCaption(pht('Short UPPERCASE identifier.'))); } public function validateNamePage(PHUIFormPageView $page) { $c_name = $page->getControl('name'); $v_name = $c_name->getValue(); if (!strlen($v_name)) { $c_name->setError(pht('Required')); $page->addPageError( pht('You must choose a name for this repository.')); } $c_call = $page->getControl('callsign'); $v_call = $c_call->getValue(); if (!strlen($v_call)) { $c_call->setError(pht('Required')); $page->addPageError( pht('You must choose a callsign for this repository.')); } else if (!preg_match('/^[A-Z]+\z/', $v_call)) { $c_call->setError(pht('Invalid')); $page->addPageError( pht('The callsign must contain only UPPERCASE letters.')); } else { $exists = false; try { $repo = id(new PhabricatorRepositoryQuery()) ->setViewer($this->getRequest()->getUser()) ->withCallsigns(array($v_call)) ->executeOne(); $exists = (bool)$repo; } catch (PhabricatorPolicyException $ex) { $exists = true; } if ($exists) { $c_call->setError(pht('Not Unique')); $page->addPageError( pht( 'Another repository already uses that callsign. You must choose '. 'a unique callsign.')); } } return $c_name->isValid() && $c_call->isValid(); } /* -( Page: Remote URI )--------------------------------------------------- */ private function buildRemoteURIPage() { return id(new PHUIFormPageView()) ->setUser($this->getRequest()->getUser()) ->setPageName(pht('Repository Remote URI')) ->setValidateFormPageCallback(array($this, 'validateRemoteURIPage')) ->setAdjustFormPageCallback(array($this, 'adjustRemoteURIPage')) ->addControl( id(new AphrontFormTextControl()) ->setName('remoteURI')); } public function adjustRemoteURIPage(PHUIFormPageView $page) { $form = $page->getForm(); $is_git = false; $is_svn = false; $is_mercurial = false; if ($this->getRepository()) { $vcs = $this->getRepository()->getVersionControlSystem(); } else { $vcs = $form->getPage('vcs')->getControl('vcs')->getValue(); } switch ($vcs) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $is_git = true; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $is_svn = true; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $is_mercurial = true; break; default: throw new Exception(pht('Unsupported VCS!')); } $has_local = ($is_git || $is_mercurial); if ($is_git) { $uri_label = pht('Remote URI'); $instructions = pht( 'Enter the URI to clone this Git repository from. It should usually '. 'look like one of these examples:'. "\n\n". "| Example Git Remote URIs |\n". "| ----------------------- |\n". "| `git@github.com:example/example.git` |\n". "| `ssh://user@host.com/git/example.git` |\n". "| `https://example.com/repository.git` |\n"); } else if ($is_mercurial) { $uri_label = pht('Remote URI'); $instructions = pht( 'Enter the URI to clone this Mercurial repository from. It should '. 'usually look like one of these examples:'. "\n\n". "| Example Mercurial Remote URIs |\n". "| ----------------------- |\n". "| `ssh://hg@bitbucket.org/example/repository` |\n". "| `https://bitbucket.org/example/repository` |\n"); } else if ($is_svn) { $uri_label = pht('Repository Root'); $instructions = pht( 'Enter the **Repository Root** for this Subversion repository. '. 'You can figure this out by running `svn info` in a working copy '. 'and looking at the value in the `Repository Root` field. It '. 'should be a URI and will usually look like these:'. "\n\n". "| Example Subversion Repository Root URIs |\n". "| ------------------------------ |\n". "| `http://svn.example.org/svnroot/` |\n". "| `svn+ssh://svn.example.com/svnroot/` |\n". "| `svn://svn.example.net/svnroot/` |\n". "\n\n". "You **MUST** specify the root of the repository, not a ". "subdirectory. (If you want to import only part of a Subversion ". "repository, use the //Import Only// option at the end of this ". "workflow.)"); } else { throw new Exception(pht('Unsupported VCS!')); } $page->addRemarkupInstructions($instructions, 'remoteURI'); $page->getControl('remoteURI')->setLabel($uri_label); } public function validateRemoteURIPage(PHUIFormPageView $page) { $c_remote = $page->getControl('remoteURI'); $v_remote = $c_remote->getValue(); if (!strlen($v_remote)) { $c_remote->setError(pht('Required')); $page->addPageError( pht('You must specify a URI.')); } else { try { PhabricatorRepository::assertValidRemoteURI($v_remote); } catch (Exception $ex) { $c_remote->setError(pht('Invalid')); $page->addPageError($ex->getMessage()); } } return $c_remote->isValid(); } /* -( Page: Authentication )----------------------------------------------- */ public function buildAuthPage() { return id(new PHUIFormPageView()) ->setPageName(pht('Authentication')) ->setUser($this->getRequest()->getUser()) ->setAdjustFormPageCallback(array($this, 'adjustAuthPage')) ->addControl( id(new PassphraseCredentialControl()) ->setName('credential')); } public function adjustAuthPage($page) { $form = $page->getForm(); if ($this->getRepository()) { $vcs = $this->getRepository()->getVersionControlSystem(); } else { $vcs = $form->getPage('vcs')->getControl('vcs')->getValue(); } $remote_uri = $form->getPage('remote-uri') ->getControl('remoteURI') ->getValue(); $proto = PhabricatorRepository::getRemoteURIProtocol($remote_uri); $remote_user = $this->getRemoteURIUser($remote_uri); $c_credential = $page->getControl('credential'); $c_credential->setDefaultUsername($remote_user); if ($this->isSSHProtocol($proto)) { $c_credential->setLabel(pht('SSH Key')); $c_credential->setCredentialType( PassphraseSSHPrivateKeyTextCredentialType::CREDENTIAL_TYPE); $provides_type = PassphraseSSHPrivateKeyCredentialType::PROVIDES_TYPE; $page->addRemarkupInstructions( pht( 'Choose or add the SSH credentials to use to connect to the '. 'repository hosted at:'. "\n\n". " lang=text\n". " %s", $remote_uri), 'credential'); } else if ($this->isUsernamePasswordProtocol($proto)) { $c_credential->setLabel(pht('Password')); $c_credential->setAllowNull(true); $c_credential->setCredentialType( PassphrasePasswordCredentialType::CREDENTIAL_TYPE); $provides_type = PassphrasePasswordCredentialType::PROVIDES_TYPE; $page->addRemarkupInstructions( pht( 'Choose the username and password used to connect to the '. 'repository hosted at:'. "\n\n". " lang=text\n". " %s". "\n\n". "If this repository does not require a username or password, ". "you can continue to the next step.", $remote_uri), 'credential'); } else { throw new Exception(pht('Unknown URI protocol!')); } if ($provides_type) { $viewer = $this->getRequest()->getUser(); $options = id(new PassphraseCredentialQuery()) ->setViewer($viewer) ->withIsDestroyed(false) ->withProvidesTypes(array($provides_type)) ->execute(); $c_credential->setOptions($options); } } public function validateAuthPage(PHUIFormPageView $page) { $form = $page->getForm(); $remote_uri = $form->getPage('remote')->getControl('remoteURI')->getValue(); $proto = $this->getRemoteURIProtocol($remote_uri); $c_credential = $page->getControl('credential'); $v_credential = $c_credential->getValue(); // NOTE: We're using the omnipotent user here because the viewer might be // editing a repository they're allowed to edit which uses a credential they // are not allowed to see. This is fine, as long as they don't change it. $credential = id(new PassphraseCredentialQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withPHIDs(array($v_credential)) ->executeOne(); if ($this->isSSHProtocol($proto)) { if (!$credential) { $c_credential->setError(pht('Required')); $page->addPageError( pht('You must choose an SSH credential to connect over SSH.')); } $ssh_type = PassphraseSSHPrivateKeyCredentialType::PROVIDES_TYPE; if ($credential->getProvidesType() !== $ssh_type) { $c_credential->setError(pht('Invalid')); $page->addPageError( pht( 'You must choose an SSH credential, not some other type '. 'of credential.')); } } else if ($this->isUsernamePasswordProtocol($proto)) { if ($credential) { $password_type = PassphrasePasswordCredentialType::PROVIDES_TYPE; if ($credential->getProvidesType() !== $password_type) { $c_credential->setError(pht('Invalid')); $page->addPageError( pht( 'You must choose a username/password credential, not some other '. 'type of credential.')); } } return $c_credential->isValid(); } else { return true; } } /* -( Page: Policy )------------------------------------------------------- */ private function buildPolicyPage() { $viewer = $this->getRequest()->getUser(); if ($this->getRepository()) { $repository = $this->getRepository(); } else { $repository = PhabricatorRepository::initializeNewRepository($viewer); } $policies = id(new PhabricatorPolicyQuery()) ->setViewer($viewer) ->setObject($repository) ->execute(); $view_policy = id(new AphrontFormPolicyControl()) ->setUser($viewer) ->setCapability(PhabricatorPolicyCapability::CAN_VIEW) ->setPolicyObject($repository) ->setPolicies($policies) ->setName('viewPolicy'); $edit_policy = id(new AphrontFormPolicyControl()) ->setUser($viewer) ->setCapability(PhabricatorPolicyCapability::CAN_EDIT) ->setPolicyObject($repository) ->setPolicies($policies) ->setName('editPolicy'); $push_policy = id(new AphrontFormPolicyControl()) ->setUser($viewer) ->setCapability(DiffusionPushCapability::CAPABILITY) ->setPolicyObject($repository) ->setPolicies($policies) ->setName('pushPolicy'); return id(new PHUIFormPageView()) ->setPageName(pht('Policies')) ->setValidateFormPageCallback(array($this, 'validatePolicyPage')) ->setAdjustFormPageCallback(array($this, 'adjustPolicyPage')) ->setUser($viewer) ->addRemarkupInstructions( pht('Select access policies for this repository.')) ->addControl($view_policy) ->addControl($edit_policy) ->addControl($push_policy); } public function adjustPolicyPage(PHUIFormPageView $page) { if ($this->getRepository()) { $repository = $this->getRepository(); $show_push = $repository->isHosted(); } else { $show_push = ($this->edit == 'create'); } if (!$show_push) { $c_push = $page->getControl('pushPolicy'); $c_push->setHidden(true); } } public function validatePolicyPage(PHUIFormPageView $page) { $form = $page->getForm(); $viewer = $this->getRequest()->getUser(); $c_view = $page->getControl('viewPolicy'); $c_edit = $page->getControl('editPolicy'); $c_push = $page->getControl('pushPolicy'); $v_view = $c_view->getValue(); $v_edit = $c_edit->getValue(); $v_push = $c_push->getValue(); if ($this->getRepository()) { $repository = $this->getRepository(); } else { $repository = PhabricatorRepository::initializeNewRepository($viewer); } $proxy = clone $repository; $proxy->setViewPolicy($v_view); $proxy->setEditPolicy($v_edit); $can_view = PhabricatorPolicyFilter::hasCapability( $viewer, $proxy, PhabricatorPolicyCapability::CAN_VIEW); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $proxy, PhabricatorPolicyCapability::CAN_EDIT); if (!$can_view) { $c_view->setError(pht('Invalid')); $page->addPageError( pht( 'You can not use the selected policy, because you would be unable '. 'to see the repository.')); } if (!$can_edit) { $c_edit->setError(pht('Invalid')); $page->addPageError( pht( 'You can not use the selected edit policy, because you would be '. 'unable to edit the repository.')); } return $c_view->isValid() && $c_edit->isValid(); } /* -( Page: Done )--------------------------------------------------------- */ private function buildDonePage() { $is_create = ($this->edit == 'create'); if ($is_create) { $now_label = pht('Create Repository Now'); $now_caption = pht( 'Create the repository right away. This will create the repository '. 'using default settings.'); $wait_label = pht('Configure More Options First'); $wait_caption = pht( 'Configure more options before creating the repository. '. 'This will let you fine-tune settings. You can create the repository '. 'whenever you are ready.'); } else { $now_label = pht('Start Import Now'); $now_caption = pht( 'Start importing the repository right away. This will import '. 'the entire repository using default settings.'); $wait_label = pht('Configure More Options First'); $wait_caption = pht( 'Configure more options before beginning the repository '. 'import. This will let you fine-tune settings. You can '. 'start the import whenever you are ready.'); } return id(new PHUIFormPageView()) ->setPageName(pht('Repository Ready!')) ->setValidateFormPageCallback(array($this, 'validateDonePage')) ->setUser($this->getRequest()->getUser()) ->addControl( id(new AphrontFormRadioButtonControl()) ->setName('activate') ->setLabel(pht('Start Now')) ->addButton( 'start', $now_label, $now_caption) ->addButton( 'wait', $wait_label, $wait_caption)); } public function validateDonePage(PHUIFormPageView $page) { $c_activate = $page->getControl('activate'); $v_activate = $c_activate->getValue(); if ($v_activate != 'start' && $v_activate != 'wait') { $c_activate->setError(pht('Required')); $page->addPageError( pht('Make a choice about repository activation.')); } return $c_activate->isValid(); } /* -( Internal )----------------------------------------------------------- */ private function getRemoteURIUser($raw_uri) { $uri = new PhutilURI($raw_uri); if ($uri->getUser()) { return $uri->getUser(); } $git_uri = new PhutilGitURI($raw_uri); if (strlen($git_uri->getDomain()) && strlen($git_uri->getPath())) { return $git_uri->getUser(); } return null; } private function isSSHProtocol($proto) { return ($proto == 'git' || $proto == 'ssh' || $proto == 'svn+ssh'); } private function isUsernamePasswordProtocol($proto) { return ($proto == 'http' || $proto == 'https' || $proto == 'svn'); } private function setRepository(PhabricatorRepository $repository) { $this->repository = $repository; return $this; } private function getRepository() { return $this->repository; } } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryDefaultController.php b/src/applications/diffusion/controller/DiffusionRepositoryDefaultController.php index 9b72c66eea..efebd12c6f 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryDefaultController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryDefaultController.php @@ -1,17 +1,25 @@ loadDiffusionContext(); + if ($response) { + return $response; + } + // NOTE: This controller is just here to make sure we call // willBeginExecution() on any /diffusion/X/ URI, so we can intercept // `git`, `hg` and `svn` HTTP protocol requests. // If we made it here, it's probably because the user copy-pasted a // clone URI with "/anything.git" at the end into their web browser. // Send them to the canonical repository URI. + $drequest = $this->getDiffusionRequest(); + $repository = $drequest->getRepository(); + return id(new AphrontRedirectResponse()) - ->setURI($this->getDiffusionRequest()->getRepository()->getURI()); + ->setURI($repository->getURI()); } } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditActionsController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditActionsController.php index 2b0bc674be..8459498374 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditActionsController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditActionsController.php @@ -1,122 +1,109 @@ getUser(); - $drequest = $this->diffusionRequest; - $repository = $drequest->getRepository(); - - $repository = id(new PhabricatorRepositoryQuery()) - ->setViewer($viewer) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->withIDs(array($repository->getID())) - ->executeOne(); - - if (!$repository) { - return new Aphront404Response(); + public function handleRequest(AphrontRequest $request) { + $response = $this->loadDiffusionContextForEdit(); + if ($response) { + return $response; } + $viewer = $this->getViewer(); + $drequest = $this->getDiffusionRequest(); + $repository = $drequest->getRepository(); + $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/'); // NOTE: We're inverting these here, because the storage is silly. $v_notify = !$repository->getHumanReadableDetail('herald-disabled'); $v_autoclose = !$repository->getHumanReadableDetail('disable-autoclose'); if ($request->isFormPost()) { $v_notify = $request->getBool('notify'); $v_autoclose = $request->getBool('autoclose'); $xactions = array(); $template = id(new PhabricatorRepositoryTransaction()); $type_notify = PhabricatorRepositoryTransaction::TYPE_NOTIFY; $type_autoclose = PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE; $xactions[] = id(clone $template) ->setTransactionType($type_notify) ->setNewValue($v_notify); $xactions[] = id(clone $template) ->setTransactionType($type_autoclose) ->setNewValue($v_autoclose); id(new PhabricatorRepositoryEditor()) ->setContinueOnNoEffect(true) ->setContentSourceFromRequest($request) ->setActor($viewer) ->applyTransactions($repository, $xactions); return id(new AphrontRedirectResponse())->setURI($edit_uri); } $content = array(); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Edit Actions')); $title = pht('Edit Actions (%s)', $repository->getName()); $policies = id(new PhabricatorPolicyQuery()) ->setViewer($viewer) ->setObject($repository) ->execute(); $form = id(new AphrontFormView()) ->setUser($viewer) ->appendRemarkupInstructions( pht( "Normally, Phabricator publishes notifications when it discovers ". "new commits. You can disable publishing for this repository by ". "turning off **Notify/Publish**. This will disable notifications, ". "feed, and Herald (including audits and build plans) for this ". "repository.\n\n". "When Phabricator discovers a new commit, it can automatically ". "close associated revisions and tasks. If you don't want ". "Phabricator to close objects when it discovers new commits in ". "this repository, you can disable **Autoclose**.")) ->appendChild( id(new AphrontFormSelectControl()) ->setName('notify') ->setLabel(pht('Notify/Publish')) ->setValue((int)$v_notify) ->setOptions( array( 1 => pht('Enable Notifications, Feed and Herald'), 0 => pht('Disable Notifications, Feed and Herald'), ))) ->appendChild( id(new AphrontFormSelectControl()) ->setName('autoclose') ->setLabel(pht('Autoclose')) ->setValue((int)$v_autoclose) ->setOptions( array( 1 => pht('Enable Autoclose'), 0 => pht('Disable Autoclose'), ))) ->appendChild( id(new AphrontFormSubmitControl()) ->setValue(pht('Save Actions')) ->addCancelButton($edit_uri)); $form_box = id(new PHUIObjectBoxView()) ->setHeaderText($title) ->setForm($form); - return $this->buildApplicationPage( - array( - $crumbs, - $form_box, - ), - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($form_box); } } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditActivateController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditActivateController.php index 2342530195..c8428833ee 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditActivateController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditActivateController.php @@ -1,65 +1,49 @@ getUser(); - $drequest = $this->diffusionRequest; - $repository = $drequest->getRepository(); - - $repository = id(new PhabricatorRepositoryQuery()) - ->setViewer($viewer) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->withIDs(array($repository->getID())) - ->executeOne(); - - if (!$repository) { - return new Aphront404Response(); + public function handleRequest(AphrontRequest $request) { + $response = $this->loadDiffusionContextForEdit(); + if ($response) { + return $response; } + $viewer = $this->getViewer(); + $drequest = $this->getDiffusionRequest(); + $repository = $drequest->getRepository(); + $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/'); if ($request->isFormPost()) { $xaction = id(new PhabricatorRepositoryTransaction()) ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_ACTIVATE) ->setNewValue(!$repository->isTracked()); $editor = id(new PhabricatorRepositoryEditor()) ->setContinueOnNoEffect(true) ->setContentSourceFromRequest($request) ->setActor($viewer) ->applyTransactions($repository, array($xaction)); return id(new AphrontReloadResponse())->setURI($edit_uri); } - $dialog = id(new AphrontDialogView()) - ->setUser($viewer); - if ($repository->isTracked()) { - $dialog + return $this->newDialog() ->setTitle(pht('Deactivate Repository?')) ->appendChild( pht('Deactivate this repository?')) ->addSubmitButton(pht('Deactivate Repository')) ->addCancelButton($edit_uri); } else { - $dialog + return $this->newDialog() ->setTitle(pht('Activate Repository?')) ->appendChild( pht('Activate this repository?')) ->addSubmitButton(pht('Activate Repository')) ->addCancelButton($edit_uri); } - - return id(new AphrontDialogResponse()) - ->setDialog($dialog); } - } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditAutomationController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditAutomationController.php index 8fe5b70f97..e47bde3902 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditAutomationController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditAutomationController.php @@ -1,94 +1,82 @@ loadDiffusionContextForEdit(); + if ($response) { + return $response; + } + $viewer = $this->getViewer(); - $drequest = $this->diffusionRequest; + $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); - $repository = id(new PhabricatorRepositoryQuery()) - ->setViewer($viewer) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->withIDs(array($repository->getID())) - ->executeOne(); - if (!$repository) { - return new Aphront404Response(); - } - if (!$repository->supportsAutomation()) { return new Aphront404Response(); } $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/'); $v_blueprints = $repository->getHumanReadableDetail( 'automation.blueprintPHIDs'); if ($request->isFormPost()) { $v_blueprints = $request->getArr('blueprintPHIDs'); $xactions = array(); $template = id(new PhabricatorRepositoryTransaction()); $type_blueprints = PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS; $xactions[] = id(clone $template) ->setTransactionType($type_blueprints) ->setNewValue($v_blueprints); id(new PhabricatorRepositoryEditor()) ->setContinueOnNoEffect(true) ->setContentSourceFromRequest($request) ->setActor($viewer) ->applyTransactions($repository, $xactions); return id(new AphrontRedirectResponse())->setURI($edit_uri); } $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Edit Automation')); $title = pht('Edit %s', $repository->getName()); $form = id(new AphrontFormView()) ->setUser($viewer) ->appendRemarkupInstructions( pht( "Configure **Repository Automation** to allow Phabricator to ". "write to this repository.". "\n\n". "IMPORTANT: This feature is new, experimental, and not supported. ". "Use it at your own risk.")) ->appendControl( id(new AphrontFormTokenizerControl()) ->setLabel(pht('Use Blueprints')) ->setName('blueprintPHIDs') ->setValue($v_blueprints) ->setDatasource(new DrydockBlueprintDatasource())) ->appendChild( id(new AphrontFormSubmitControl()) ->setValue(pht('Save')) ->addCancelButton($edit_uri)); $object_box = id(new PHUIObjectBoxView()) ->setHeaderText($title) ->setForm($form); - return $this->buildApplicationPage( - array( - $crumbs, - $object_box, - ), - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($object_box); } } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php index 80771f65c4..5b4429b61a 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditBasicController.php @@ -1,176 +1,165 @@ getUser(); - $drequest = $this->diffusionRequest; - $repository = $drequest->getRepository(); - - $repository = id(new PhabricatorRepositoryQuery()) - ->setViewer($user) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->needProjectPHIDs(true) - ->withIDs(array($repository->getID())) - ->executeOne(); - - if (!$repository) { - return new Aphront404Response(); + public function handleRequest(AphrontRequest $request) { + $response = $this->loadDiffusionContextForEdit(); + if ($response) { + return $response; } + $viewer = $request->getUser(); + $drequest = $this->getDiffusionRequest(); + $repository = $drequest->getRepository(); + $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/'); $v_name = $repository->getName(); $v_desc = $repository->getDetail('description'); $v_clone_name = $repository->getDetail('clone-name'); + $v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs( + $repository->getPHID(), + PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); $e_name = true; $errors = array(); if ($request->isFormPost()) { $v_name = $request->getStr('name'); $v_desc = $request->getStr('description'); $v_projects = $request->getArr('projectPHIDs'); if ($repository->isHosted()) { $v_clone_name = $request->getStr('cloneName'); } if (!strlen($v_name)) { $e_name = pht('Required'); $errors[] = pht('Repository name is required.'); } else { $e_name = null; } if (!$errors) { $xactions = array(); $template = id(new PhabricatorRepositoryTransaction()); $type_name = PhabricatorRepositoryTransaction::TYPE_NAME; $type_desc = PhabricatorRepositoryTransaction::TYPE_DESCRIPTION; $type_edge = PhabricatorTransactions::TYPE_EDGE; $type_clone_name = PhabricatorRepositoryTransaction::TYPE_CLONE_NAME; $xactions[] = id(clone $template) ->setTransactionType($type_name) ->setNewValue($v_name); $xactions[] = id(clone $template) ->setTransactionType($type_desc) ->setNewValue($v_desc); $xactions[] = id(clone $template) ->setTransactionType($type_clone_name) ->setNewValue($v_clone_name); $xactions[] = id(clone $template) ->setTransactionType($type_edge) ->setMetadataValue( 'edge:type', PhabricatorProjectObjectHasProjectEdgeType::EDGECONST) ->setNewValue( array( '=' => array_fuse($v_projects), )); id(new PhabricatorRepositoryEditor()) ->setContinueOnNoEffect(true) ->setContentSourceFromRequest($request) - ->setActor($user) + ->setActor($viewer) ->applyTransactions($repository, $xactions); return id(new AphrontRedirectResponse())->setURI($edit_uri); } } $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Edit Basics')); $title = pht('Edit %s', $repository->getName()); $form = id(new AphrontFormView()) - ->setUser($user) + ->setUser($viewer) ->appendChild( id(new AphrontFormTextControl()) ->setName('name') ->setLabel(pht('Name')) ->setValue($v_name) ->setError($e_name)); if ($repository->isHosted()) { $form ->appendChild( id(new AphrontFormTextControl()) ->setName('cloneName') ->setLabel(pht('Clone/Checkout As')) ->setValue($v_clone_name) ->setCaption( pht( 'Optional directory name to use when cloning or checking out '. 'this repository.'))); } $form ->appendChild( id(new PhabricatorRemarkupControl()) - ->setUser($user) + ->setUser($viewer) ->setName('description') ->setLabel(pht('Description')) ->setValue($v_desc)) ->appendControl( id(new AphrontFormTokenizerControl()) ->setDatasource(new PhabricatorProjectDatasource()) ->setName('projectPHIDs') ->setLabel(pht('Projects')) - ->setValue($repository->getProjectPHIDs())) + ->setValue($v_projects)) ->appendChild( id(new AphrontFormSubmitControl()) ->setValue(pht('Save')) ->addCancelButton($edit_uri)) ->appendChild(id(new PHUIFormDividerControl())) ->appendRemarkupInstructions($this->getReadmeInstructions()); $object_box = id(new PHUIObjectBoxView()) ->setHeaderText($title) ->setForm($form) ->setFormErrors($errors); - return $this->buildApplicationPage( - array( - $crumbs, - $object_box, - ), - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($object_box); } private function getReadmeInstructions() { return pht(<<getRequest(); - $viewer = $request->getUser(); - $drequest = $this->diffusionRequest; - $repository = $drequest->getRepository(); - - $repository = id(new PhabricatorRepositoryQuery()) - ->setViewer($viewer) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->withIDs(array($repository->getID())) - ->executeOne(); - if (!$repository) { - return new Aphront404Response(); + public function handleRequest(AphrontRequest $request) { + $response = $this->loadDiffusionContextForEdit(); + if ($response) { + return $response; } + $viewer = $this->getViewer(); + $drequest = $this->getDiffusionRequest(); + $repository = $drequest->getRepository(); + $is_git = false; $is_hg = false; switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $is_git = true; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $is_hg = true; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: throw new Exception( pht('Subversion does not support branches!')); default: throw new Exception( pht('Repository has unknown version control system!')); } $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/'); $v_default = $repository->getHumanReadableDetail('default-branch'); $v_track = $repository->getDetail( 'branch-filter', array()); $v_track = array_keys($v_track); $v_autoclose = $repository->getDetail( 'close-commits-filter', array()); $v_autoclose = array_keys($v_autoclose); $e_track = null; $e_autoclose = null; $validation_exception = null; if ($request->isFormPost()) { $v_default = $request->getStr('default'); $v_track = $this->processBranches($request->getStr('track')); if (!$is_hg) { $v_autoclose = $this->processBranches($request->getStr('autoclose')); } $xactions = array(); $template = id(new PhabricatorRepositoryTransaction()); $type_default = PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH; $type_track = PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY; $type_autoclose = PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY; $xactions[] = id(clone $template) ->setTransactionType($type_default) ->setNewValue($v_default); $xactions[] = id(clone $template) ->setTransactionType($type_track) ->setNewValue($v_track); if (!$is_hg) { $xactions[] = id(clone $template) ->setTransactionType($type_autoclose) ->setNewValue($v_autoclose); } $editor = id(new PhabricatorRepositoryEditor()) ->setContinueOnNoEffect(true) ->setContentSourceFromRequest($request) ->setActor($viewer); try { $editor->applyTransactions($repository, $xactions); return id(new AphrontRedirectResponse())->setURI($edit_uri); } catch (PhabricatorApplicationTransactionValidationException $ex) { $validation_exception = $ex; $e_track = $validation_exception->getShortMessage($type_track); $e_autoclose = $validation_exception->getShortMessage($type_autoclose); } } $content = array(); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Edit Branches')); $title = pht('Edit Branches (%s)', $repository->getName()); $policies = id(new PhabricatorPolicyQuery()) ->setViewer($viewer) ->setObject($repository) ->execute(); $rows = array(); $rows[] = array( array( 'master', ), pht('Select only master.'), ); $rows[] = array( array( 'master', 'develop', 'release', ), pht('Select %s, %s, and %s.', 'master', 'develop', 'release'), ); $rows[] = array( array( 'master', 'regexp(/^release-/)', ), pht('Select master, and all branches which start with "%s".', 'release-'), ); $rows[] = array( array( 'regexp(/^(?!temp-)/)', ), pht('Select all branches which do not start with "%s".', 'temp-'), ); foreach ($rows as $k => $row) { $rows[$k][0] = phutil_tag( 'pre', array(), implode("\n", $row[0])); } $example_table = id(new AphrontTableView($rows)) ->setHeaders( array( pht('Example'), pht('Effect'), )) ->setColumnClasses( array( '', 'wide', )); $v_track = implode("\n", $v_track); $v_autoclose = implode("\n", $v_autoclose); $form = id(new AphrontFormView()) ->setUser($viewer) ->appendRemarkupInstructions( pht('You can choose a **Default Branch** for viewing this repository.')) ->appendChild( id(new AphrontFormTextControl()) ->setName('default') ->setLabel(pht('Default Branch')) ->setValue($v_default)) ->appendRemarkupInstructions( pht( 'If you want to import only some branches into Diffusion, you can '. 'list them in **Track Only**. Other branches will be ignored. If '. 'you do not specify any branches, all branches are tracked.')); if (!$is_hg) { $form->appendRemarkupInstructions( pht( 'If you have **Autoclose** enabled for this repository, Phabricator '. 'can close tasks and revisions when corresponding commits are '. 'pushed to the repository. If you want to autoclose objects only '. 'when commits appear on specific branches, you can list those '. 'branches in **Autoclose Only**. By default, all tracked branches '. 'will autoclose objects.')); } $form ->appendRemarkupInstructions( pht( 'When specifying branches, you should enter one branch name per '. 'line. You can use regular expressions to match branches by '. 'wrapping an expression in `%s`. For example:', 'regexp(...)')) ->appendChild( id(new AphrontFormMarkupControl()) ->setValue($example_table)) ->appendChild( id(new AphrontFormTextAreaControl()) ->setName('track') ->setLabel(pht('Track Only')) ->setError($e_track) ->setValue($v_track)); if (!$is_hg) { $form->appendChild( id(new AphrontFormTextAreaControl()) ->setName('autoclose') ->setLabel(pht('Autoclose Only')) ->setError($e_autoclose) ->setValue($v_autoclose)); } $form->appendChild( id(new AphrontFormSubmitControl()) ->setValue(pht('Save Branches')) ->addCancelButton($edit_uri)); $form_box = id(new PHUIObjectBoxView()) ->setValidationException($validation_exception) ->setHeaderText($title) ->setForm($form); - return $this->buildApplicationPage( - array( - $crumbs, - $form_box, - ), - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($form_box); } private function processBranches($string) { $lines = phutil_split_lines($string, $retain_endings = false); foreach ($lines as $key => $line) { $lines[$key] = trim($line); if (!strlen($lines[$key])) { unset($lines[$key]); } } return array_values($lines); } } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditDangerousController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditDangerousController.php index 076c2580e5..26d8b57f33 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditDangerousController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditDangerousController.php @@ -1,77 +1,62 @@ getUser(); - $drequest = $this->diffusionRequest; - $repository = $drequest->getRepository(); - - $repository = id(new PhabricatorRepositoryQuery()) - ->setViewer($viewer) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->withIDs(array($repository->getID())) - ->executeOne(); - - if (!$repository) { - return new Aphront404Response(); + public function handleRequest(AphrontRequest $request) { + $response = $this->loadDiffusionContextForEdit(); + if ($response) { + return $response; } + $viewer = $this->getViewer(); + $drequest = $this->getDiffusionRequest(); + $repository = $drequest->getRepository(); + if (!$repository->canAllowDangerousChanges()) { return new Aphront400Response(); } $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/'); if ($request->isFormPost()) { $xaction = id(new PhabricatorRepositoryTransaction()) ->setTransactionType(PhabricatorRepositoryTransaction::TYPE_DANGEROUS) ->setNewValue(!$repository->shouldAllowDangerousChanges()); $editor = id(new PhabricatorRepositoryEditor()) ->setContinueOnNoEffect(true) ->setContentSourceFromRequest($request) ->setActor($viewer) ->applyTransactions($repository, array($xaction)); return id(new AphrontReloadResponse())->setURI($edit_uri); } - $dialog = id(new AphrontDialogView()) - ->setUser($viewer); - $force = phutil_tag('tt', array(), '--force'); if ($repository->shouldAllowDangerousChanges()) { - $dialog + return $this->newDialog() ->setTitle(pht('Prevent Dangerous changes?')) ->appendChild( pht( 'It will no longer be possible to delete branches from this '. 'repository, or %s push to this repository.', $force)) ->addSubmitButton(pht('Prevent Dangerous Changes')) ->addCancelButton($edit_uri); } else { - $dialog + return $this->newDialog() ->setTitle(pht('Allow Dangerous Changes?')) ->appendChild( pht( 'If you allow dangerous changes, it will be possible to delete '. 'branches and %s push this repository. These operations can '. 'alter a repository in a way that is difficult to recover from.', $force)) ->addSubmitButton(pht('Allow Dangerous Changes')) ->addCancelButton($edit_uri); } - - return id(new AphrontDialogResponse()) - ->setDialog($dialog); } } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditDeleteController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditDeleteController.php index f39708fe1b..074e79445b 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditDeleteController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditDeleteController.php @@ -1,58 +1,46 @@ getUser(); - $drequest = $this->diffusionRequest; - $repository = $drequest->getRepository(); - - $repository = id(new PhabricatorRepositoryQuery()) - ->setViewer($viewer) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->withIDs(array($repository->getID())) - ->executeOne(); - if (!$repository) { - return new Aphront404Response(); + public function handleRequest(AphrontRequest $request) { + $response = $this->loadDiffusionContextForEdit(); + if ($response) { + return $response; } + $viewer = $this->getViewer(); + $drequest = $this->getDiffusionRequest(); + $repository = $drequest->getRepository(); + $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/'); $dialog = new AphrontDialogView(); $text_1 = pht( 'If you really want to delete the repository, run this command from '. 'the command line:'); $command = csprintf( 'phabricator/ $ ./bin/remove destroy %R', $repository->getMonogram()); $text_2 = pht( 'Repositories touch many objects and as such deletes are '. 'prohibitively expensive to run from the web UI.'); $body = phutil_tag( 'div', array( 'class' => 'phabricator-remarkup', ), array( phutil_tag('p', array(), $text_1), phutil_tag('p', array(), phutil_tag('tt', array(), $command)), phutil_tag('p', array(), $text_2), )); - $dialog = id(new AphrontDialogView()) - ->setUser($request->getUser()) + return $this->newDialog() ->setTitle(pht('Really want to delete the repository?')) ->appendChild($body) ->addCancelButton($edit_uri, pht('Okay')); - - return id(new AphrontDialogResponse())->setDialog($dialog); } - } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditEncodingController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditEncodingController.php index f71516e6ca..0059c9d6c0 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditEncodingController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditEncodingController.php @@ -1,110 +1,97 @@ getUser(); - $drequest = $this->diffusionRequest; - $repository = $drequest->getRepository(); - - $repository = id(new PhabricatorRepositoryQuery()) - ->setViewer($user) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->withIDs(array($repository->getID())) - ->executeOne(); - - if (!$repository) { - return new Aphront404Response(); + public function handleRequest(AphrontRequest $request) { + $response = $this->loadDiffusionContextForEdit(); + if ($response) { + return $response; } + $user = $this->getViewer(); + $drequest = $this->getDiffusionRequest(); + $repository = $drequest->getRepository(); + $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/'); $v_encoding = $repository->getDetail('encoding'); $e_encoding = null; $errors = array(); if ($request->isFormPost()) { $v_encoding = $request->getStr('encoding'); if (!$errors) { $xactions = array(); $template = id(new PhabricatorRepositoryTransaction()); $type_encoding = PhabricatorRepositoryTransaction::TYPE_ENCODING; $xactions[] = id(clone $template) ->setTransactionType($type_encoding) ->setNewValue($v_encoding); try { id(new PhabricatorRepositoryEditor()) ->setContinueOnNoEffect(true) ->setContentSourceFromRequest($request) ->setActor($user) ->applyTransactions($repository, $xactions); return id(new AphrontRedirectResponse())->setURI($edit_uri); } catch (Exception $ex) { $errors[] = $ex->getMessage(); } } } $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Edit Encoding')); $title = pht('Edit %s', $repository->getName()); $form = id(new AphrontFormView()) ->setUser($user) ->appendRemarkupInstructions($this->getEncodingInstructions()) ->appendChild( id(new AphrontFormTextControl()) ->setName('encoding') ->setLabel(pht('Text Encoding')) ->setValue($v_encoding) ->setError($e_encoding)) ->appendChild( id(new AphrontFormSubmitControl()) ->setValue(pht('Save Encoding')) ->addCancelButton($edit_uri)); $object_box = id(new PHUIObjectBoxView()) ->setHeaderText($title) ->setForm($form) ->setFormErrors($errors); - return $this->buildApplicationPage( - array( - $crumbs, - $object_box, - ), - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($object_box); } private function getEncodingInstructions() { return pht(<<getUser(); - $drequest = $this->diffusionRequest; + public function handleRequest(AphrontRequest $request) { + $response = $this->loadDiffusionContextForEdit(); + if ($response) { + return $response; + } + + $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); - $this->serve = $request->getURIData('serve'); - $repository = id(new PhabricatorRepositoryQuery()) - ->setViewer($user) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->withIDs(array($repository->getID())) - ->executeOne(); - if (!$repository) { - return new Aphront404Response(); - } + $this->serve = $request->getURIData('serve'); if (!$this->serve) { return $this->handleHosting($repository); } else { return $this->handleProtocols($repository); } } public function handleHosting(PhabricatorRepository $repository) { $request = $this->getRequest(); $user = $request->getUser(); $v_hosting = $repository->isHosted(); $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/'); $next_uri = $this->getRepositoryControllerURI($repository, 'edit/serve/'); if ($request->isFormPost()) { $v_hosting = $request->getBool('hosting'); $xactions = array(); $template = id(new PhabricatorRepositoryTransaction()); $type_hosting = PhabricatorRepositoryTransaction::TYPE_HOSTING; $xactions[] = id(clone $template) ->setTransactionType($type_hosting) ->setNewValue($v_hosting); id(new PhabricatorRepositoryEditor()) ->setContinueOnNoEffect(true) ->setContentSourceFromRequest($request) ->setActor($user) ->applyTransactions($repository, $xactions); return id(new AphrontRedirectResponse())->setURI($next_uri); } $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Edit Hosting')); $title = pht('Edit Hosting (%s)', $repository->getName()); $hosted_control = id(new AphrontFormRadioButtonControl()) ->setName('hosting') ->setLabel(pht('Hosting')) ->addButton( true, pht('Host Repository on Phabricator'), pht( 'Phabricator will host this repository. Users will be able to '. 'push commits to Phabricator. Phabricator will not pull '. 'changes from elsewhere.')) ->addButton( false, pht('Host Repository Elsewhere'), pht( 'Phabricator will pull updates to this repository from a master '. 'repository elsewhere (for example, on GitHub or Bitbucket). '. 'Users will not be able to push commits to this repository.')) ->setValue($v_hosting); $doc_href = PhabricatorEnv::getDoclink( 'Diffusion User Guide: Repository Hosting'); $form = id(new AphrontFormView()) ->setUser($user) ->appendRemarkupInstructions( pht( 'Phabricator can host repositories, or it can track repositories '. 'hosted elsewhere (like on GitHub or Bitbucket). For information '. 'on configuring hosting, see [[ %s | Diffusion User Guide: '. 'Repository Hosting]]', $doc_href)) ->appendChild($hosted_control) ->appendChild( id(new AphrontFormSubmitControl()) ->setValue(pht('Save and Continue')) ->addCancelButton($edit_uri)); $object_box = id(new PHUIObjectBoxView()) ->setHeaderText($title) ->setForm($form); - return $this->buildApplicationPage( - array( - $crumbs, - $object_box, - ), - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($object_box); } public function handleProtocols(PhabricatorRepository $repository) { $request = $this->getRequest(); $user = $request->getUser(); $type = $repository->getVersionControlSystem(); $is_svn = ($type == PhabricatorRepositoryType::REPOSITORY_TYPE_SVN); $v_http_mode = $repository->getDetail( 'serve-over-http', PhabricatorRepository::SERVE_OFF); $v_ssh_mode = $repository->getDetail( 'serve-over-ssh', PhabricatorRepository::SERVE_OFF); $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/'); $prev_uri = $this->getRepositoryControllerURI($repository, 'edit/hosting/'); if ($request->isFormPost()) { $v_http_mode = $request->getStr('http'); $v_ssh_mode = $request->getStr('ssh'); $xactions = array(); $template = id(new PhabricatorRepositoryTransaction()); $type_http = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP; $type_ssh = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH; if (!$is_svn) { $xactions[] = id(clone $template) ->setTransactionType($type_http) ->setNewValue($v_http_mode); } $xactions[] = id(clone $template) ->setTransactionType($type_ssh) ->setNewValue($v_ssh_mode); id(new PhabricatorRepositoryEditor()) ->setContinueOnNoEffect(true) ->setContentSourceFromRequest($request) ->setActor($user) ->applyTransactions($repository, $xactions); return id(new AphrontRedirectResponse())->setURI($edit_uri); } $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Edit Protocols')); $title = pht('Edit Protocols (%s)', $repository->getName()); $rw_message = pht( 'Phabricator will serve a read-write copy of this repository.'); if (!$repository->isHosted()) { $rw_message = array( $rw_message, phutil_tag('br'), phutil_tag('br'), pht( '%s: This repository is hosted elsewhere, so Phabricator can not '. 'perform writes. This mode will act like "Read Only" for '. 'repositories hosted elsewhere.', phutil_tag('strong', array(), pht('WARNING'))), ); } $ssh_control = id(new AphrontFormRadioButtonControl()) ->setName('ssh') ->setLabel(pht('SSH')) ->setValue($v_ssh_mode) ->addButton( PhabricatorRepository::SERVE_OFF, PhabricatorRepository::getProtocolAvailabilityName( PhabricatorRepository::SERVE_OFF), pht('Phabricator will not serve this repository over SSH.')) ->addButton( PhabricatorRepository::SERVE_READONLY, PhabricatorRepository::getProtocolAvailabilityName( PhabricatorRepository::SERVE_READONLY), pht( 'Phabricator will serve a read-only copy of this repository '. 'over SSH.')) ->addButton( PhabricatorRepository::SERVE_READWRITE, PhabricatorRepository::getProtocolAvailabilityName( PhabricatorRepository::SERVE_READWRITE), $rw_message); $http_control = id(new AphrontFormRadioButtonControl()) ->setName('http') ->setLabel(pht('HTTP')) ->setValue($v_http_mode) ->addButton( PhabricatorRepository::SERVE_OFF, PhabricatorRepository::getProtocolAvailabilityName( PhabricatorRepository::SERVE_OFF), pht('Phabricator will not serve this repository over HTTP.')) ->addButton( PhabricatorRepository::SERVE_READONLY, PhabricatorRepository::getProtocolAvailabilityName( PhabricatorRepository::SERVE_READONLY), pht( 'Phabricator will serve a read-only copy of this repository '. 'over HTTP.')) ->addButton( PhabricatorRepository::SERVE_READWRITE, PhabricatorRepository::getProtocolAvailabilityName( PhabricatorRepository::SERVE_READWRITE), $rw_message); if ($is_svn) { $http_control = id(new AphrontFormMarkupControl()) ->setLabel(pht('HTTP')) ->setValue( phutil_tag( 'em', array(), pht( 'Phabricator does not currently support HTTP access to '. 'Subversion repositories.'))); } $form = id(new AphrontFormView()) ->setUser($user) ->appendRemarkupInstructions( pht( 'Phabricator can serve repositories over various protocols. You can '. 'configure server protocols here.')) ->appendChild($ssh_control); if (!PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth')) { $form->appendRemarkupInstructions( pht( 'NOTE: The configuration setting [[ %s | %s ]] is currently '. 'disabled. You must enable it to activate authenticated access '. 'to repositories over HTTP.', '/config/edit/diffusion.allow-http-auth/', 'diffusion.allow-http-auth')); } $form ->appendChild($http_control) ->appendChild( id(new AphrontFormSubmitControl()) ->setValue(pht('Save Changes')) ->addCancelButton($prev_uri, pht('Back'))); $object_box = id(new PHUIObjectBoxView()) ->setHeaderText($title) ->setForm($form); - return $this->buildApplicationPage( - array( - $crumbs, - $object_box, - ), - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($object_box); } } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php index 780e83d089..1e19d45e7c 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php @@ -1,1362 +1,1362 @@ getUser(); - $drequest = $this->diffusionRequest; - $repository = $drequest->getRepository(); + public function handleRequest(AphrontRequest $request) { + $response = $this->loadDiffusionContextForEdit(); + if ($response) { + return $response; + } - PhabricatorPolicyFilter::requireCapability( - $viewer, - $repository, - PhabricatorPolicyCapability::CAN_EDIT); + $viewer = $this->getViewer(); + $drequest = $this->getDiffusionRequest(); + $repository = $drequest->getRepository(); $is_svn = false; $is_git = false; $is_hg = false; switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $is_git = true; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $is_svn = true; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $is_hg = true; break; } $has_branches = ($is_git || $is_hg); $has_local = $repository->usesLocalWorkingCopy(); $supports_staging = $repository->supportsStaging(); $supports_automation = $repository->supportsAutomation(); $crumbs = $this->buildApplicationCrumbs($is_main = true); $title = pht('Edit %s', $repository->getName()); $header = id(new PHUIHeaderView()) ->setHeader($title); if ($repository->isTracked()) { $header->setStatus('fa-check', 'bluegrey', pht('Active')); } else { $header->setStatus('fa-ban', 'dark', pht('Inactive')); } $basic_actions = $this->buildBasicActions($repository); $basic_properties = $this->buildBasicProperties($repository, $basic_actions); $policy_actions = $this->buildPolicyActions($repository); $policy_properties = $this->buildPolicyProperties($repository, $policy_actions); $remote_properties = null; if (!$repository->isHosted()) { $remote_properties = $this->buildRemoteProperties( $repository, $this->buildRemoteActions($repository)); } $encoding_actions = $this->buildEncodingActions($repository); $encoding_properties = $this->buildEncodingProperties($repository, $encoding_actions); $symbols_actions = $this->buildSymbolsActions($repository); $symbols_properties = $this->buildSymbolsProperties($repository, $symbols_actions); $hosting_properties = $this->buildHostingProperties( $repository, $this->buildHostingActions($repository)); $branches_properties = null; if ($has_branches) { $branches_properties = $this->buildBranchesProperties( $repository, $this->buildBranchesActions($repository)); } $subversion_properties = null; if ($is_svn) { $subversion_properties = $this->buildSubversionProperties( $repository, $this->buildSubversionActions($repository)); } $storage_properties = null; if ($has_local) { $storage_properties = $this->buildStorageProperties( $repository, $this->buildStorageActions($repository)); } $staging_properties = null; if ($supports_staging) { $staging_properties = $this->buildStagingProperties( $repository, $this->buildStagingActions($repository)); } $automation_properties = null; if ($supports_automation) { $automation_properties = $this->buildAutomationProperties( $repository, $this->buildAutomationActions($repository)); } $actions_properties = $this->buildActionsProperties( $repository, $this->buildActionsActions($repository)); $timeline = $this->buildTransactionTimeline( $repository, new PhabricatorRepositoryTransactionQuery()); $timeline->setShouldTerminate(true); $boxes = array(); $boxes[] = id(new PHUIObjectBoxView()) ->setHeader($header) ->addPropertyList($basic_properties); $boxes[] = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Policies')) ->addPropertyList($policy_properties); $boxes[] = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Hosting')) ->addPropertyList($hosting_properties); if ($repository->canMirror()) { $mirror_actions = $this->buildMirrorActions($repository); $mirror_properties = $this->buildMirrorProperties( $repository, $mirror_actions); $mirrors = id(new PhabricatorRepositoryMirrorQuery()) ->setViewer($viewer) ->withRepositoryPHIDs(array($repository->getPHID())) ->execute(); $mirror_list = $this->buildMirrorList($repository, $mirrors); $boxes[] = id(new PhabricatorAnchorView())->setAnchorName('mirrors'); $mirror_info = array(); if (PhabricatorEnv::getEnvConfig('phabricator.silent')) { $mirror_info[] = pht( 'Phabricator is running in silent mode, so changes will not '. 'be pushed to mirrors.'); } $boxes[] = id(new PHUIObjectBoxView()) ->setFormErrors($mirror_info) ->setHeaderText(pht('Mirrors')) ->addPropertyList($mirror_properties); $boxes[] = $mirror_list; } if ($remote_properties) { $boxes[] = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Remote')) ->addPropertyList($remote_properties); } if ($storage_properties) { $boxes[] = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Storage')) ->addPropertyList($storage_properties); } if ($staging_properties) { $boxes[] = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Staging')) ->addPropertyList($staging_properties); } if ($automation_properties) { $boxes[] = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Automation')) ->addPropertyList($automation_properties); } $boxes[] = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Text Encoding')) ->addPropertyList($encoding_properties); $boxes[] = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Symbols')) ->addPropertyList($symbols_properties); if ($branches_properties) { $boxes[] = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Branches')) ->addPropertyList($branches_properties); } if ($subversion_properties) { $boxes[] = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Subversion')) ->addPropertyList($subversion_properties); } $boxes[] = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Actions')) ->addPropertyList($actions_properties); return $this->buildApplicationPage( array( $crumbs, $boxes, $timeline, ), array( 'title' => $title, )); } private function buildBasicActions(PhabricatorRepository $repository) { $viewer = $this->getRequest()->getUser(); $view = id(new PhabricatorActionListView()) ->setUser($viewer); $edit = id(new PhabricatorActionView()) ->setIcon('fa-pencil') ->setName(pht('Edit Basic Information')) ->setHref($this->getRepositoryControllerURI($repository, 'edit/basic/')); $view->addAction($edit); $edit = id(new PhabricatorActionView()) ->setIcon('fa-refresh') ->setName(pht('Update Now')) ->setWorkflow(true) ->setHref( $this->getRepositoryControllerURI($repository, 'edit/update/')); $view->addAction($edit); $activate = id(new PhabricatorActionView()) ->setHref( $this->getRepositoryControllerURI($repository, 'edit/activate/')) ->setWorkflow(true); if ($repository->isTracked()) { $activate ->setIcon('fa-pause') ->setName(pht('Deactivate Repository')); } else { $activate ->setIcon('fa-play') ->setName(pht('Activate Repository')); } $view->addAction($activate); $view->addAction( id(new PhabricatorActionView()) ->setName(pht('Delete Repository')) ->setIcon('fa-times') ->setHref( $this->getRepositoryControllerURI($repository, 'edit/delete/')) ->setDisabled(true) ->setWorkflow(true)); return $view; } private function buildBasicProperties( PhabricatorRepository $repository, PhabricatorActionListView $actions) { $viewer = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setObject($repository) ->setActionList($actions); $type = PhabricatorRepositoryType::getNameForRepositoryType( $repository->getVersionControlSystem()); $view->addProperty(pht('Type'), $type); $view->addProperty(pht('Callsign'), $repository->getCallsign()); $clone_name = $repository->getDetail('clone-name'); if ($repository->isHosted()) { $view->addProperty( pht('Clone/Checkout As'), $clone_name ? $clone_name.'/' : phutil_tag('em', array(), $repository->getCloneName().'/')); } $view->invokeWillRenderEvent(); $view->addProperty( pht('Status'), $this->buildRepositoryStatus($repository)); $view->addProperty( pht('Update Frequency'), $this->buildRepositoryUpdateInterval($repository)); $description = $repository->getDetail('description'); $view->addSectionHeader( pht('Description'), PHUIPropertyListView::ICON_SUMMARY); if (!strlen($description)) { $description = phutil_tag('em', array(), pht('No description provided.')); } else { $description = PhabricatorMarkupEngine::renderOneObject( $repository, 'description', $viewer); } $view->addTextContent($description); return $view; } private function buildEncodingActions(PhabricatorRepository $repository) { $viewer = $this->getRequest()->getUser(); $view = id(new PhabricatorActionListView()) ->setUser($viewer); $edit = id(new PhabricatorActionView()) ->setIcon('fa-pencil') ->setName(pht('Edit Text Encoding')) ->setHref( $this->getRepositoryControllerURI($repository, 'edit/encoding/')); $view->addAction($edit); return $view; } private function buildEncodingProperties( PhabricatorRepository $repository, PhabricatorActionListView $actions) { $viewer = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setActionList($actions); $encoding = $repository->getDetail('encoding'); if (!$encoding) { $encoding = phutil_tag('em', array(), pht('Use Default (UTF-8)')); } $view->addProperty(pht('Encoding'), $encoding); return $view; } private function buildPolicyActions(PhabricatorRepository $repository) { $viewer = $this->getRequest()->getUser(); $view = id(new PhabricatorActionListView()) ->setUser($viewer); $edit = id(new PhabricatorActionView()) ->setIcon('fa-pencil') ->setName(pht('Edit Policies')) ->setHref( $this->getRepositoryControllerURI($repository, 'edit/policy/')); $view->addAction($edit); return $view; } private function buildPolicyProperties( PhabricatorRepository $repository, PhabricatorActionListView $actions) { $viewer = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setActionList($actions); $descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions( $viewer, $repository); $view_parts = array(); if (PhabricatorSpacesNamespaceQuery::getViewerSpacesExist($viewer)) { $space_phid = PhabricatorSpacesNamespaceQuery::getObjectSpacePHID( $repository); $view_parts[] = $viewer->renderHandle($space_phid); } $view_parts[] = $descriptions[PhabricatorPolicyCapability::CAN_VIEW]; $view->addProperty( pht('Visible To'), phutil_implode_html(" \xC2\xB7 ", $view_parts)); $view->addProperty( pht('Editable By'), $descriptions[PhabricatorPolicyCapability::CAN_EDIT]); $pushable = $repository->isHosted() ? $descriptions[DiffusionPushCapability::CAPABILITY] : phutil_tag('em', array(), pht('Not a Hosted Repository')); $view->addProperty(pht('Pushable By'), $pushable); return $view; } private function buildBranchesActions(PhabricatorRepository $repository) { $viewer = $this->getRequest()->getUser(); $view = id(new PhabricatorActionListView()) ->setUser($viewer); $edit = id(new PhabricatorActionView()) ->setIcon('fa-pencil') ->setName(pht('Edit Branches')) ->setHref( $this->getRepositoryControllerURI($repository, 'edit/branches/')); $view->addAction($edit); return $view; } private function buildBranchesProperties( PhabricatorRepository $repository, PhabricatorActionListView $actions) { $viewer = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setActionList($actions); $default_branch = nonempty( $repository->getHumanReadableDetail('default-branch'), phutil_tag('em', array(), $repository->getDefaultBranch())); $view->addProperty(pht('Default Branch'), $default_branch); $track_only = nonempty( $repository->getHumanReadableDetail('branch-filter', array()), phutil_tag('em', array(), pht('Track All Branches'))); $view->addProperty(pht('Track Only'), $track_only); $autoclose_only = nonempty( $repository->getHumanReadableDetail('close-commits-filter', array()), phutil_tag('em', array(), pht('Autoclose On All Branches'))); if ($repository->getDetail('disable-autoclose')) { $autoclose_only = phutil_tag('em', array(), pht('Disabled')); } $view->addProperty(pht('Autoclose Only'), $autoclose_only); return $view; } private function buildSubversionActions(PhabricatorRepository $repository) { $viewer = $this->getRequest()->getUser(); $view = id(new PhabricatorActionListView()) ->setUser($viewer); $edit = id(new PhabricatorActionView()) ->setIcon('fa-pencil') ->setName(pht('Edit Subversion Info')) ->setHref( $this->getRepositoryControllerURI($repository, 'edit/subversion/')); $view->addAction($edit); return $view; } private function buildSubversionProperties( PhabricatorRepository $repository, PhabricatorActionListView $actions) { $viewer = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setActionList($actions); $svn_uuid = nonempty( $repository->getUUID(), phutil_tag('em', array(), pht('Not Configured'))); $view->addProperty(pht('Subversion UUID'), $svn_uuid); $svn_subpath = nonempty( $repository->getHumanReadableDetail('svn-subpath'), phutil_tag('em', array(), pht('Import Entire Repository'))); $view->addProperty(pht('Import Only'), $svn_subpath); return $view; } private function buildActionsActions(PhabricatorRepository $repository) { $viewer = $this->getRequest()->getUser(); $view = id(new PhabricatorActionListView()) ->setUser($viewer); $edit = id(new PhabricatorActionView()) ->setIcon('fa-pencil') ->setName(pht('Edit Actions')) ->setHref( $this->getRepositoryControllerURI($repository, 'edit/actions/')); $view->addAction($edit); return $view; } private function buildActionsProperties( PhabricatorRepository $repository, PhabricatorActionListView $actions) { $viewer = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setActionList($actions); $notify = $repository->getDetail('herald-disabled') ? pht('Off') : pht('On'); $notify = phutil_tag('em', array(), $notify); $view->addProperty(pht('Publish/Notify'), $notify); $autoclose = $repository->getDetail('disable-autoclose') ? pht('Off') : pht('On'); $autoclose = phutil_tag('em', array(), $autoclose); $view->addProperty(pht('Autoclose'), $autoclose); return $view; } private function buildRemoteActions(PhabricatorRepository $repository) { $viewer = $this->getRequest()->getUser(); $view = id(new PhabricatorActionListView()) ->setUser($viewer); $edit = id(new PhabricatorActionView()) ->setIcon('fa-pencil') ->setName(pht('Edit Remote')) ->setHref( $this->getRepositoryControllerURI($repository, 'edit/remote/')); $view->addAction($edit); return $view; } private function buildRemoteProperties( PhabricatorRepository $repository, PhabricatorActionListView $actions) { $viewer = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setActionList($actions); $view->addProperty( pht('Remote URI'), $repository->getHumanReadableDetail('remote-uri')); $credential_phid = $repository->getCredentialPHID(); if ($credential_phid) { $view->addProperty( pht('Credential'), $viewer->renderHandle($credential_phid)); } return $view; } private function buildStorageActions(PhabricatorRepository $repository) { $viewer = $this->getRequest()->getUser(); $view = id(new PhabricatorActionListView()) ->setUser($viewer); $edit = id(new PhabricatorActionView()) ->setIcon('fa-pencil') ->setName(pht('Edit Storage')) ->setHref( $this->getRepositoryControllerURI($repository, 'edit/storage/')); $view->addAction($edit); return $view; } private function buildStorageProperties( PhabricatorRepository $repository, PhabricatorActionListView $actions) { $viewer = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setActionList($actions); $service_phid = $repository->getAlmanacServicePHID(); if ($service_phid) { $v_service = $viewer->renderHandle($service_phid); } else { $v_service = phutil_tag( 'em', array(), pht('Local')); } $view->addProperty( pht('Storage Service'), $v_service); $view->addProperty( pht('Storage Path'), $repository->getHumanReadableDetail('local-path')); return $view; } private function buildStagingActions(PhabricatorRepository $repository) { $viewer = $this->getViewer(); $view = id(new PhabricatorActionListView()) ->setUser($viewer); $edit = id(new PhabricatorActionView()) ->setIcon('fa-pencil') ->setName(pht('Edit Staging')) ->setHref( $this->getRepositoryControllerURI($repository, 'edit/staging/')); $view->addAction($edit); return $view; } private function buildStagingProperties( PhabricatorRepository $repository, PhabricatorActionListView $actions) { $viewer = $this->getViewer(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setActionList($actions); $staging_uri = $repository->getStagingURI(); if (!$staging_uri) { $staging_uri = phutil_tag('em', array(), pht('No Staging Area')); } $view->addProperty( pht('Staging Area'), $staging_uri); return $view; } private function buildAutomationActions(PhabricatorRepository $repository) { $viewer = $this->getViewer(); $view = id(new PhabricatorActionListView()) ->setUser($viewer); $edit = id(new PhabricatorActionView()) ->setIcon('fa-pencil') ->setName(pht('Edit Automation')) ->setHref( $this->getRepositoryControllerURI($repository, 'edit/automation/')); $view->addAction($edit); $can_test = $repository->canPerformAutomation(); $test = id(new PhabricatorActionView()) ->setIcon('fa-gamepad') ->setName(pht('Test Configuration')) ->setWorkflow(true) ->setDisabled(!$can_test) ->setHref( $this->getRepositoryControllerURI( $repository, 'edit/testautomation/')); $view->addAction($test); return $view; } private function buildAutomationProperties( PhabricatorRepository $repository, PhabricatorActionListView $actions) { $viewer = $this->getViewer(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setActionList($actions); $blueprint_phids = $repository->getAutomationBlueprintPHIDs(); if (!$blueprint_phids) { $blueprint_view = phutil_tag('em', array(), pht('Not Configured')); } else { $blueprint_view = id(new DrydockObjectAuthorizationView()) ->setUser($viewer) ->setObjectPHID($repository->getPHID()) ->setBlueprintPHIDs($blueprint_phids); } $view->addProperty(pht('Automation'), $blueprint_view); return $view; } private function buildHostingActions(PhabricatorRepository $repository) { $user = $this->getRequest()->getUser(); $view = id(new PhabricatorActionListView()) ->setUser($user); $edit = id(new PhabricatorActionView()) ->setIcon('fa-pencil') ->setName(pht('Edit Hosting')) ->setHref( $this->getRepositoryControllerURI($repository, 'edit/hosting/')); $view->addAction($edit); if ($repository->canAllowDangerousChanges()) { if ($repository->shouldAllowDangerousChanges()) { $changes = id(new PhabricatorActionView()) ->setIcon('fa-shield') ->setName(pht('Prevent Dangerous Changes')) ->setHref( $this->getRepositoryControllerURI($repository, 'edit/dangerous/')) ->setWorkflow(true); } else { $changes = id(new PhabricatorActionView()) ->setIcon('fa-bullseye') ->setName(pht('Allow Dangerous Changes')) ->setHref( $this->getRepositoryControllerURI($repository, 'edit/dangerous/')) ->setWorkflow(true); } $view->addAction($changes); } return $view; } private function buildHostingProperties( PhabricatorRepository $repository, PhabricatorActionListView $actions) { $user = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($user) ->setActionList($actions); $hosting = $repository->isHosted() ? pht('Hosted on Phabricator') : pht('Hosted Elsewhere'); $view->addProperty(pht('Hosting'), phutil_tag('em', array(), $hosting)); $view->addProperty( pht('Serve over HTTP'), phutil_tag( 'em', array(), PhabricatorRepository::getProtocolAvailabilityName( $repository->getServeOverHTTP()))); $view->addProperty( pht('Serve over SSH'), phutil_tag( 'em', array(), PhabricatorRepository::getProtocolAvailabilityName( $repository->getServeOverSSH()))); if ($repository->canAllowDangerousChanges()) { if ($repository->shouldAllowDangerousChanges()) { $description = pht('Allowed'); } else { $description = pht('Not Allowed'); } $view->addProperty( pht('Dangerous Changes'), $description); } return $view; } private function buildRepositoryStatus( PhabricatorRepository $repository) { $viewer = $this->getRequest()->getUser(); $is_cluster = $repository->getAlmanacServicePHID(); $view = new PHUIStatusListView(); $messages = id(new PhabricatorRepositoryStatusMessage()) ->loadAllWhere('repositoryID = %d', $repository->getID()); $messages = mpull($messages, null, 'getStatusType'); if ($repository->isTracked()) { $view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green') ->setTarget(pht('Repository Active'))); } else { $view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_WARNING, 'bluegrey') ->setTarget(pht('Repository Inactive')) ->setNote( pht('Activate this repository to begin or resume import.'))); return $view; } $binaries = array(); $svnlook_check = false; switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $binaries[] = 'git'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $binaries[] = 'svn'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $binaries[] = 'hg'; break; } if ($repository->isHosted()) { if ($repository->getServeOverHTTP() != PhabricatorRepository::SERVE_OFF) { switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $binaries[] = 'git-http-backend'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $binaries[] = 'svnserve'; $binaries[] = 'svnadmin'; $binaries[] = 'svnlook'; $svnlook_check = true; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $binaries[] = 'hg'; break; } } if ($repository->getServeOverSSH() != PhabricatorRepository::SERVE_OFF) { switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $binaries[] = 'git-receive-pack'; $binaries[] = 'git-upload-pack'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $binaries[] = 'svnserve'; $binaries[] = 'svnadmin'; $binaries[] = 'svnlook'; $svnlook_check = true; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $binaries[] = 'hg'; break; } } } $binaries = array_unique($binaries); if (!$is_cluster) { // We're only checking for binaries if we aren't running with a cluster // configuration. In theory, we could check for binaries on the // repository host machine, but we'd need to make this more complicated // to do that. foreach ($binaries as $binary) { $where = Filesystem::resolveBinary($binary); if (!$where) { $view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red') ->setTarget( pht('Missing Binary %s', phutil_tag('tt', array(), $binary))) ->setNote(pht( "Unable to find this binary in the webserver's PATH. You may ". "need to configure %s.", $this->getEnvConfigLink()))); } else { $view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green') ->setTarget( pht('Found Binary %s', phutil_tag('tt', array(), $binary))) ->setNote(phutil_tag('tt', array(), $where))); } } // This gets checked generically above. However, for svn commit hooks, we // need this to be in environment.append-paths because subversion strips // PATH. if ($svnlook_check) { $where = Filesystem::resolveBinary('svnlook'); if ($where) { $path = substr($where, 0, strlen($where) - strlen('svnlook')); $dirs = PhabricatorEnv::getEnvConfig('environment.append-paths'); $in_path = false; foreach ($dirs as $dir) { if (Filesystem::isDescendant($path, $dir)) { $in_path = true; break; } } if (!$in_path) { $view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red') ->setTarget( pht('Missing Binary %s', phutil_tag('tt', array(), $binary))) ->setNote(pht( 'Unable to find this binary in `%s`. '. 'You need to configure %s and include %s.', 'environment.append-paths', $this->getEnvConfigLink(), $path))); } } } } $doc_href = PhabricatorEnv::getDocLink('Managing Daemons with phd'); $daemon_instructions = pht( 'Use %s to start daemons. See %s.', phutil_tag('tt', array(), 'bin/phd start'), phutil_tag( 'a', array( 'href' => $doc_href, ), pht('Managing Daemons with phd'))); $pull_daemon = id(new PhabricatorDaemonLogQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE) ->withDaemonClasses(array('PhabricatorRepositoryPullLocalDaemon')) ->setLimit(1) ->execute(); if ($pull_daemon) { // TODO: In a cluster environment, we need a daemon on this repository's // host, specifically, and we aren't checking for that right now. This // is a reasonable proxy for things being more-or-less correctly set up, // though. $view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green') ->setTarget(pht('Pull Daemon Running'))); } else { $view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red') ->setTarget(pht('Pull Daemon Not Running')) ->setNote($daemon_instructions)); } $task_daemon = id(new PhabricatorDaemonLogQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE) ->withDaemonClasses(array('PhabricatorTaskmasterDaemon')) ->setLimit(1) ->execute(); if ($task_daemon) { $view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green') ->setTarget(pht('Task Daemon Running'))); } else { $view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red') ->setTarget(pht('Task Daemon Not Running')) ->setNote($daemon_instructions)); } if ($is_cluster) { // Just omit this status check for now in cluster environments. We // could make a service call and pull it from the repository host // eventually. } else if ($repository->usesLocalWorkingCopy()) { $local_parent = dirname($repository->getLocalPath()); if (Filesystem::pathExists($local_parent)) { $view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green') ->setTarget(pht('Storage Directory OK')) ->setNote(phutil_tag('tt', array(), $local_parent))); } else { $view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red') ->setTarget(pht('No Storage Directory')) ->setNote( pht( 'Storage directory %s does not exist, or is not readable by '. 'the webserver. Create this directory or make it readable.', phutil_tag('tt', array(), $local_parent)))); return $view; } $local_path = $repository->getLocalPath(); $message = idx($messages, PhabricatorRepositoryStatusMessage::TYPE_INIT); if ($message) { switch ($message->getStatusCode()) { case PhabricatorRepositoryStatusMessage::CODE_ERROR: $view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red') ->setTarget(pht('Initialization Error')) ->setNote($message->getParameter('message'))); return $view; case PhabricatorRepositoryStatusMessage::CODE_OKAY: if (Filesystem::pathExists($local_path)) { $view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green') ->setTarget(pht('Working Copy OK')) ->setNote(phutil_tag('tt', array(), $local_path))); } else { $view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red') ->setTarget(pht('Working Copy Error')) ->setNote( pht( 'Working copy %s has been deleted, or is not '. 'readable by the webserver. Make this directory '. 'readable. If it has been deleted, the daemons should '. 'restore it automatically.', phutil_tag('tt', array(), $local_path)))); return $view; } break; case PhabricatorRepositoryStatusMessage::CODE_WORKING: $view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_CLOCK, 'green') ->setTarget(pht('Initializing Working Copy')) ->setNote(pht('Daemons are initializing the working copy.'))); return $view; default: $view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red') ->setTarget(pht('Unknown Init Status')) ->setNote($message->getStatusCode())); return $view; } } else { $view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_CLOCK, 'orange') ->setTarget(pht('No Working Copy Yet')) ->setNote( pht('Waiting for daemons to build a working copy.'))); return $view; } } $message = idx($messages, PhabricatorRepositoryStatusMessage::TYPE_FETCH); if ($message) { switch ($message->getStatusCode()) { case PhabricatorRepositoryStatusMessage::CODE_ERROR: $message = $message->getParameter('message'); $suggestion = null; if (preg_match('/Permission denied \(publickey\)./', $message)) { $suggestion = pht( 'Public Key Error: This error usually indicates that the '. 'keypair you have configured does not have permission to '. 'access the repository.'); } $message = phutil_escape_html_newlines($message); if ($suggestion !== null) { $message = array( phutil_tag('strong', array(), $suggestion), phutil_tag('br'), phutil_tag('br'), phutil_tag('em', array(), pht('Raw Error')), phutil_tag('br'), $message, ); } $view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_WARNING, 'red') ->setTarget(pht('Update Error')) ->setNote($message)); return $view; case PhabricatorRepositoryStatusMessage::CODE_OKAY: $ago = (PhabricatorTime::getNow() - $message->getEpoch()); $view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green') ->setTarget(pht('Updates OK')) ->setNote( pht( 'Last updated %s (%s ago).', phabricator_datetime($message->getEpoch(), $viewer), phutil_format_relative_time_detailed($ago)))); break; } } else { $view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_CLOCK, 'orange') ->setTarget(pht('Waiting For Update')) ->setNote( pht('Waiting for daemons to read updates.'))); } if ($repository->isImporting()) { $progress = queryfx_all( $repository->establishConnection('r'), 'SELECT importStatus, count(*) N FROM %T WHERE repositoryID = %d GROUP BY importStatus', id(new PhabricatorRepositoryCommit())->getTableName(), $repository->getID()); $done = 0; $total = 0; foreach ($progress as $row) { $total += $row['N'] * 4; $status = $row['importStatus']; if ($status & PhabricatorRepositoryCommit::IMPORTED_MESSAGE) { $done += $row['N']; } if ($status & PhabricatorRepositoryCommit::IMPORTED_CHANGE) { $done += $row['N']; } if ($status & PhabricatorRepositoryCommit::IMPORTED_OWNERS) { $done += $row['N']; } if ($status & PhabricatorRepositoryCommit::IMPORTED_HERALD) { $done += $row['N']; } } if ($total) { $percentage = 100 * ($done / $total); } else { $percentage = 0; } // Cap this at "99.99%", because it's confusing to users when the actual // fraction is "99.996%" and it rounds up to "100.00%". if ($percentage > 99.99) { $percentage = 99.99; } $percentage = sprintf('%.2f%%', $percentage); $view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_CLOCK, 'green') ->setTarget(pht('Importing')) ->setNote( pht('%s Complete', $percentage))); } else { $view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green') ->setTarget(pht('Fully Imported'))); } if (idx($messages, PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE)) { $view->addItem( id(new PHUIStatusItemView()) ->setIcon(PHUIStatusItemView::ICON_UP, 'indigo') ->setTarget(pht('Prioritized')) ->setNote(pht('This repository will be updated soon!'))); } return $view; } private function buildRepositoryUpdateInterval( PhabricatorRepository $repository) { $smart_wait = $repository->loadUpdateInterval(); $doc_href = PhabricatorEnv::getDoclink( 'Diffusion User Guide: Repository Updates'); return array( phutil_format_relative_time_detailed($smart_wait), " \xC2\xB7 ", phutil_tag( 'a', array( 'href' => $doc_href, 'target' => '_blank', ), pht('Learn More')), ); } private function buildMirrorActions( PhabricatorRepository $repository) { $viewer = $this->getRequest()->getUser(); $mirror_actions = id(new PhabricatorActionListView()) ->setUser($viewer); $new_mirror_uri = $this->getRepositoryControllerURI( $repository, 'mirror/edit/'); $mirror_actions->addAction( id(new PhabricatorActionView()) ->setName(pht('Add Mirror')) ->setIcon('fa-plus') ->setHref($new_mirror_uri) ->setWorkflow(true)); return $mirror_actions; } private function buildMirrorProperties( PhabricatorRepository $repository, PhabricatorActionListView $actions) { $viewer = $this->getRequest()->getUser(); $mirror_properties = id(new PHUIPropertyListView()) ->setUser($viewer) ->setActionList($actions); $mirror_properties->addProperty( '', phutil_tag( 'em', array(), pht('Automatically push changes into other remotes.'))); return $mirror_properties; } private function buildMirrorList( PhabricatorRepository $repository, array $mirrors) { assert_instances_of($mirrors, 'PhabricatorRepositoryMirror'); $mirror_list = id(new PHUIObjectItemListView()) ->setNoDataString(pht('This repository has no configured mirrors.')); foreach ($mirrors as $mirror) { $item = id(new PHUIObjectItemView()) ->setHeader($mirror->getRemoteURI()); $edit_uri = $this->getRepositoryControllerURI( $repository, 'mirror/edit/'.$mirror->getID().'/'); $delete_uri = $this->getRepositoryControllerURI( $repository, 'mirror/delete/'.$mirror->getID().'/'); $item->addAction( id(new PHUIListItemView()) ->setIcon('fa-pencil') ->setHref($edit_uri) ->setWorkflow(true)); $item->addAction( id(new PHUIListItemView()) ->setIcon('fa-times') ->setHref($delete_uri) ->setWorkflow(true)); $mirror_list->addItem($item); } return $mirror_list; } private function buildSymbolsActions(PhabricatorRepository $repository) { $viewer = $this->getRequest()->getUser(); $view = id(new PhabricatorActionListView()) ->setUser($viewer); $edit = id(new PhabricatorActionView()) ->setIcon('fa-pencil') ->setName(pht('Edit Symbols')) ->setHref( $this->getRepositoryControllerURI($repository, 'edit/symbol/')); $view->addAction($edit); return $view; } private function buildSymbolsProperties( PhabricatorRepository $repository, PhabricatorActionListView $actions) { $viewer = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setActionList($actions); $languages = $repository->getSymbolLanguages(); if ($languages) { $languages = implode(', ', $languages); } else { $languages = phutil_tag('em', array(), pht('Any')); } $view->addProperty(pht('Languages'), $languages); $sources = $repository->getSymbolSources(); if ($sources) { $handles = $viewer->loadHandles($sources); $sources = $handles->renderList(); } else { $sources = phutil_tag('em', array(), pht('This Repository Only')); } $view->addProperty(pht('Use Symbols From'), $sources); return $view; } private function getEnvConfigLink() { $config_href = '/config/edit/environment.append-paths/'; return phutil_tag( 'a', array( 'href' => $config_href, ), 'environment.append-paths'); } } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditStagingController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditStagingController.php index 9fcfd767c0..deb8fad669 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditStagingController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditStagingController.php @@ -1,92 +1,81 @@ getUser(); - $drequest = $this->diffusionRequest; + public function handleRequest(AphrontRequest $request) { + $response = $this->loadDiffusionContextForEdit(); + if ($response) { + return $response; + } + + $viewer = $this->getViewer(); + $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); - $repository = id(new PhabricatorRepositoryQuery()) - ->setViewer($user) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->withIDs(array($repository->getID())) - ->executeOne(); - if (!$repository) { - return new Aphront404Response(); - } if (!$repository->supportsStaging()) { return new Aphront404Response(); } $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/'); $v_area = $repository->getHumanReadableDetail('staging-uri'); if ($request->isFormPost()) { $v_area = $request->getStr('area'); $xactions = array(); $template = id(new PhabricatorRepositoryTransaction()); $type_encoding = PhabricatorRepositoryTransaction::TYPE_STAGING_URI; $xactions[] = id(clone $template) ->setTransactionType($type_encoding) ->setNewValue($v_area); id(new PhabricatorRepositoryEditor()) ->setContinueOnNoEffect(true) ->setContentSourceFromRequest($request) - ->setActor($user) + ->setActor($viewer) ->applyTransactions($repository, $xactions); return id(new AphrontRedirectResponse())->setURI($edit_uri); } $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Edit Staging')); $title = pht('Edit %s', $repository->getName()); $form = id(new AphrontFormView()) - ->setUser($user) + ->setUser($viewer) ->appendRemarkupInstructions( pht( "To make it easier to run integration tests and builds on code ". "under review, you can configure a **Staging Area**. When `arc` ". "creates a diff, it will push a copy of the changes to the ". "configured staging area with a corresponding tag.". "\n\n". "IMPORTANT: This feature is new, experimental, and not supported. ". "Use it at your own risk.")) ->appendChild( id(new AphrontFormTextControl()) ->setLabel(pht('Staging Area URI')) ->setName('area') ->setValue($v_area)) ->appendChild( id(new AphrontFormSubmitControl()) ->setValue(pht('Save')) ->addCancelButton($edit_uri)); $object_box = id(new PHUIObjectBoxView()) ->setHeaderText($title) ->setForm($form); - return $this->buildApplicationPage( - array( - $crumbs, - $object_box, - ), - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($object_box); } } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditStorageController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditStorageController.php index 4afa594ed9..711844188a 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditStorageController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditStorageController.php @@ -1,84 +1,71 @@ getUser(); - $drequest = $this->diffusionRequest; - $repository = $drequest->getRepository(); - - $repository = id(new PhabricatorRepositoryQuery()) - ->setViewer($user) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->withIDs(array($repository->getID())) - ->executeOne(); - - if (!$repository) { - return new Aphront404Response(); + public function handleRequest(AphrontRequest $request) { + $response = $this->loadDiffusionContextForEdit(); + if ($response) { + return $response; } + $viewer = $this->getViewer(); + $drequest = $this->getDiffusionRequest(); + $repository = $drequest->getRepository(); + $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/'); $v_local = $repository->getHumanReadableDetail('local-path'); $errors = array(); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Edit Storage')); $title = pht('Edit %s', $repository->getName()); $service_phid = $repository->getAlmanacServicePHID(); if ($service_phid) { $handles = $this->loadViewerHandles(array($service_phid)); $v_service = $handles[$service_phid]->renderLink(); } else { $v_service = phutil_tag( 'em', array(), pht('Local')); } $form = id(new AphrontFormView()) - ->setUser($user) + ->setUser($viewer) ->appendChild( id(new AphrontFormMarkupControl()) ->setLabel(pht('Storage Service')) ->setValue($v_service)) ->appendChild( id(new AphrontFormMarkupControl()) ->setName('local') ->setLabel(pht('Storage Path')) ->setValue($v_local)) ->appendRemarkupInstructions( pht( "You can not adjust the local path for this repository from the ". "web interface. To edit it, run this command:\n\n %s", sprintf( 'phabricator/ $ ./bin/repository edit %s --as %s --local-path ...', $repository->getMonogram(), - $user->getUsername()))) + $viewer->getUsername()))) ->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton($edit_uri, pht('Done'))); $object_box = id(new PHUIObjectBoxView()) ->setHeaderText($title) ->setForm($form) ->setFormErrors($errors); - return $this->buildApplicationPage( - array( - $crumbs, - $object_box, - ), - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($object_box); } } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditSubversionController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditSubversionController.php index b125f26cae..93b9193f14 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditSubversionController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditSubversionController.php @@ -1,121 +1,108 @@ getUser(); - $drequest = $this->diffusionRequest; - $repository = $drequest->getRepository(); - - $repository = id(new PhabricatorRepositoryQuery()) - ->setViewer($viewer) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->withIDs(array($repository->getID())) - ->executeOne(); - - if (!$repository) { - return new Aphront404Response(); + public function handleRequest(AphrontRequest $request) { + $response = $this->loadDiffusionContextForEdit(); + if ($response) { + return $response; } + $viewer = $this->getViewer(); + $drequest = $this->getDiffusionRequest(); + $repository = $drequest->getRepository(); + switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: throw new Exception( pht('Git and Mercurial do not support editing SVN properties!')); case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: break; default: throw new Exception( pht('Repository has unknown version control system!')); } $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/'); $v_subpath = $repository->getHumanReadableDetail('svn-subpath'); $v_uuid = $repository->getUUID(); if ($request->isFormPost()) { $v_subpath = $request->getStr('subpath'); $v_uuid = $request->getStr('uuid'); $xactions = array(); $template = id(new PhabricatorRepositoryTransaction()); $type_subpath = PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH; $type_uuid = PhabricatorRepositoryTransaction::TYPE_UUID; $xactions[] = id(clone $template) ->setTransactionType($type_subpath) ->setNewValue($v_subpath); $xactions[] = id(clone $template) ->setTransactionType($type_uuid) ->setNewValue($v_uuid); id(new PhabricatorRepositoryEditor()) ->setContinueOnNoEffect(true) ->setContentSourceFromRequest($request) ->setActor($viewer) ->applyTransactions($repository, $xactions); return id(new AphrontRedirectResponse())->setURI($edit_uri); } $content = array(); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Edit Subversion Info')); $title = pht('Edit Subversion Info (%s)', $repository->getName()); $policies = id(new PhabricatorPolicyQuery()) ->setViewer($viewer) ->setObject($repository) ->execute(); $form = id(new AphrontFormView()) ->setUser($viewer) ->appendRemarkupInstructions( pht( "You can set the **Repository UUID**, which will help Phabriactor ". "provide better context in some cases. You can find the UUID of a ". "repository by running `%s`.\n\n". "If you want to import only part of a repository, like `trunk/`, ". "you can set a path in **Import Only**. Phabricator will ignore ". "commits which do not affect this path.", 'svn info')) ->appendChild( id(new AphrontFormTextControl()) ->setName('uuid') ->setLabel(pht('Repository UUID')) ->setValue($v_uuid)) ->appendChild( id(new AphrontFormTextControl()) ->setName('subpath') ->setLabel(pht('Import Only')) ->setValue($v_subpath)) ->appendChild( id(new AphrontFormSubmitControl()) ->setValue(pht('Save Subversion Info')) ->addCancelButton($edit_uri)); $form_box = id(new PHUIObjectBoxView()) ->setHeaderText($title) ->setForm($form); - return $this->buildApplicationPage( - array( - $crumbs, - $form_box, - ), - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($form_box); } } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditUpdateController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditUpdateController.php index 74df9c488b..303959d098 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditUpdateController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditUpdateController.php @@ -1,74 +1,66 @@ getUser(); - $drequest = $this->diffusionRequest; - $repository = $drequest->getRepository(); - - $repository = id(new PhabricatorRepositoryQuery()) - ->setViewer($viewer) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->withIDs(array($repository->getID())) - ->executeOne(); - if (!$repository) { - return new Aphront404Response(); + public function handleRequest(AphrontRequest $request) { + $response = $this->loadDiffusionContextForEdit(); + if ($response) { + return $response; } + $viewer = $this->getViewer(); + $drequest = $this->getDiffusionRequest(); + $repository = $drequest->getRepository(); + $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/'); if ($request->isFormPost()) { $params = array( 'repositories' => array( $repository->getPHID(), ), ); id(new ConduitCall('diffusion.looksoon', $params)) ->setUser($viewer) ->execute(); return id(new AphrontRedirectResponse())->setURI($edit_uri); } $doc_name = 'Diffusion User Guide: Repository Updates'; $doc_href = PhabricatorEnv::getDoclink($doc_name); $doc_link = phutil_tag( 'a', array( 'href' => $doc_href, 'target' => '_blank', ), $doc_name); return $this->newDialog() ->setTitle(pht('Update Repository Now')) ->appendParagraph( pht( 'Normally, Phabricator automatically updates repositories '. 'based on how much time has elapsed since the last commit. '. 'This helps reduce load if you have a large number of mostly '. 'inactive repositories, which is common.')) ->appendParagraph( pht( 'You can manually schedule an update for this repository. The '. 'daemons will perform the update as soon as possible. This may '. 'be helpful if you have just made a commit to a rarely used '. 'repository.')) ->appendParagraph( pht( 'To learn more about how Phabricator updates repositories, '. 'read %s in the documentation.', $doc_link)) ->addCancelButton($edit_uri) ->addSubmitButton(pht('Schedule Update')); } } diff --git a/src/applications/diffusion/controller/DiffusionRepositorySymbolsController.php b/src/applications/diffusion/controller/DiffusionRepositorySymbolsController.php index 133ee38813..6076d2df65 100644 --- a/src/applications/diffusion/controller/DiffusionRepositorySymbolsController.php +++ b/src/applications/diffusion/controller/DiffusionRepositorySymbolsController.php @@ -1,123 +1,110 @@ getUser(); - $drequest = $this->diffusionRequest; - $repository = $drequest->getRepository(); - - $repository = id(new PhabricatorRepositoryQuery()) - ->setViewer($user) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->withIDs(array($repository->getID())) - ->executeOne(); - - if (!$repository) { - return new Aphront404Response(); + public function handleRequest(AphrontRequest $request) { + $response = $this->loadDiffusionContextForEdit(); + if ($response) { + return $response; } + $viewer = $this->getViewer(); + $drequest = $this->getDiffusionRequest(); + $repository = $drequest->getRepository(); + $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/'); $v_sources = $repository->getSymbolSources(); $v_languages = $repository->getSymbolLanguages(); if ($v_languages) { $v_languages = implode(', ', $v_languages); } $errors = array(); if ($request->isFormPost()) { $v_sources = $request->getArr('sources'); $v_languages = $request->getStrList('languages'); $v_languages = array_map('phutil_utf8_strtolower', $v_languages); if (!$errors) { $xactions = array(); $template = id(new PhabricatorRepositoryTransaction()); $type_sources = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES; $type_lang = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE; $xactions[] = id(clone $template) ->setTransactionType($type_sources) ->setNewValue($v_sources); $xactions[] = id(clone $template) ->setTransactionType($type_lang) ->setNewValue($v_languages); try { id(new PhabricatorRepositoryEditor()) ->setContinueOnNoEffect(true) ->setContentSourceFromRequest($request) - ->setActor($user) + ->setActor($viewer) ->applyTransactions($repository, $xactions); return id(new AphrontRedirectResponse())->setURI($edit_uri); } catch (Exception $ex) { $errors[] = $ex->getMessage(); } } } $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Edit Symbols')); $title = pht('Edit %s', $repository->getName()); $form = id(new AphrontFormView()) - ->setUser($user) + ->setUser($viewer) ->appendRemarkupInstructions($this->getInstructions()) ->appendChild( id(new AphrontFormTextControl()) ->setName('languages') ->setLabel(pht('Indexed Languages')) ->setCaption(pht( 'File extensions, separate with commas, for example: php, py. '. 'Leave blank for "any".')) ->setValue($v_languages)) ->appendControl( id(new AphrontFormTokenizerControl()) ->setName('sources') ->setLabel(pht('Uses Symbols From')) ->setDatasource(new DiffusionRepositoryDatasource()) ->setValue($v_sources)) ->appendChild( id(new AphrontFormSubmitControl()) ->setValue(pht('Save')) ->addCancelButton($edit_uri)); $object_box = id(new PHUIObjectBoxView()) ->setHeaderText($title) ->setForm($form) ->setFormErrors($errors); - return $this->buildApplicationPage( - array( - $crumbs, - $object_box, - ), - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($object_box); } private function getInstructions() { return pht(<<loadDiffusionContext(); + $response = $this->loadDiffusionContextForEdit(); if ($response) { return $response; } $viewer = $this->getViewer(); $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); - $repository = id(new PhabricatorRepositoryQuery()) - ->setViewer($viewer) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->withIDs(array($repository->getID())) - ->executeOne(); - if (!$repository) { - return new Aphront404Response(); - } - $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/'); if (!$repository->canPerformAutomation()) { return $this->newDialog() ->setTitle(pht('Automation Not Configured')) ->appendParagraph( pht( 'You can not run a configuration test for this repository '. 'because you have not configured repository automation yet. '. 'Configure it first, then test the configuration.')) ->addCancelButton($edit_uri); } if ($request->isFormPost()) { $op = new DrydockTestRepositoryOperation(); $operation = DrydockRepositoryOperation::initializeNewOperation($op) ->setAuthorPHID($viewer->getPHID()) ->setObjectPHID($repository->getPHID()) ->setRepositoryPHID($repository->getPHID()) ->setRepositoryTarget('none:') ->save(); $operation->scheduleUpdate(); $operation_id = $operation->getID(); $operation_uri = "/drydock/operation/{$operation_id}/"; return id(new AphrontRedirectResponse()) ->setURI($operation_uri); } return $this->newDialog() ->setTitle(pht('Test Automation Configuration')) ->appendParagraph( pht( 'This configuration test will build a working copy of the '. 'repository and perform some basic validation. If it works, '. 'your configuration is substantially correct.')) ->appendParagraph( pht( 'The test will not perform any writes against the repository, so '. 'write operations may still fail even if the test passes. This '. 'test covers building and reading working copies, but not writing '. 'to them.')) ->appendParagraph( pht( 'If you run into write failures despite passing this test, '. 'it suggests that your setup is nearly correct but authentication '. 'is probably not fully configured.')) ->addCancelButton($edit_uri) ->addSubmitButton(pht('Start Test')); } } diff --git a/src/applications/diffusion/request/DiffusionRequest.php b/src/applications/diffusion/request/DiffusionRequest.php index 08b7c876c2..72c911f5bb 100644 --- a/src/applications/diffusion/request/DiffusionRequest.php +++ b/src/applications/diffusion/request/DiffusionRequest.php @@ -1,720 +1,733 @@ initializeFromDictionary($data); return $object; } /** * Create a new request from an Aphront request dictionary. This is an * internal method that you generally should not call directly; instead, * call @{method:newFromDictionary}. * * @param map Map of Aphront request data. * @return DiffusionRequest New request object. * @task new */ final public static function newFromAphrontRequestDictionary( array $data, AphrontRequest $request) { $identifier = phutil_unescape_uri_path_component(idx($data, 'callsign')); $object = self::newFromIdentifier($identifier, $request->getUser()); $use_branches = $object->supportsBranches(); if (isset($data['dblob'])) { $parsed = self::parseRequestBlob(idx($data, 'dblob'), $use_branches); } else { $parsed = array( 'commit' => idx($data, 'commit'), 'path' => idx($data, 'path'), 'line' => idx($data, 'line'), 'branch' => idx($data, 'branch'), ); } $object->setUser($request->getUser()); $object->initializeFromDictionary($parsed); $object->lint = $request->getStr('lint'); return $object; } /** * Internal. * * @task new */ final private function __construct() { // } /** * Internal. Use @{method:newFromDictionary}, not this method. * * @param string Repository identifier. * @param PhabricatorUser Viewing user. * @return DiffusionRequest New request object. * @task new */ final private static function newFromIdentifier( $identifier, - PhabricatorUser $viewer) { + PhabricatorUser $viewer, + $need_edit = false) { - $repository = id(new PhabricatorRepositoryQuery()) + $query = id(new PhabricatorRepositoryQuery()) ->setViewer($viewer) - ->withIdentifiers(array($identifier)) - ->executeOne(); + ->withIdentifiers(array($identifier)); + + if ($need_edit) { + $query->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )); + } + + $repository = $query->executeOne(); if (!$repository) { return null; } return self::newFromRepository($repository); } /** * Internal. Use @{method:newFromDictionary}, not this method. * * @param PhabricatorRepository Repository object. * @return DiffusionRequest New request object. * @task new */ final private static function newFromRepository( PhabricatorRepository $repository) { $map = array( PhabricatorRepositoryType::REPOSITORY_TYPE_GIT => 'DiffusionGitRequest', PhabricatorRepositoryType::REPOSITORY_TYPE_SVN => 'DiffusionSvnRequest', PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL => 'DiffusionMercurialRequest', ); $class = idx($map, $repository->getVersionControlSystem()); if (!$class) { throw new Exception(pht('Unknown version control system!')); } $object = new $class(); $object->repository = $repository; return $object; } /** * Internal. Use @{method:newFromDictionary}, not this method. * * @param map Map of parsed data. * @return void * @task new */ final private function initializeFromDictionary(array $data) { $blob = idx($data, 'blob'); if (strlen($blob)) { $blob = self::parseRequestBlob($blob, $this->supportsBranches()); $data = $blob + $data; } $this->path = idx($data, 'path'); $this->line = idx($data, 'line'); $this->initFromConduit = idx($data, 'initFromConduit', true); $this->symbolicCommit = idx($data, 'commit'); if ($this->supportsBranches()) { $this->branch = idx($data, 'branch'); } if (!$this->getUser()) { $user = idx($data, 'user'); if (!$user) { throw new Exception( pht( 'You must provide a %s in the dictionary!', 'PhabricatorUser')); } $this->setUser($user); } $this->didInitialize(); } final public function setUser(PhabricatorUser $user) { $this->user = $user; return $this; } final public function getUser() { return $this->user; } public function getRepository() { return $this->repository; } public function getCallsign() { return $this->getRepository()->getCallsign(); } public function setPath($path) { $this->path = $path; return $this; } public function getPath() { return $this->path; } public function getLine() { return $this->line; } public function getCommit() { // TODO: Probably remove all of this. if ($this->getSymbolicCommit() !== null) { return $this->getSymbolicCommit(); } return $this->getStableCommit(); } /** * Get the symbolic commit associated with this request. * * A symbolic commit may be a commit hash, an abbreviated commit hash, a * branch name, a tag name, or an expression like "HEAD^^^". The symbolic * commit may also be absent. * * This method always returns the symbol present in the original request, * in unmodified form. * * See also @{method:getStableCommit}. * * @return string|null Symbolic commit, if one was present in the request. */ public function getSymbolicCommit() { return $this->symbolicCommit; } /** * Modify the request to move the symbolic commit elsewhere. * * @param string New symbolic commit. * @return this */ public function updateSymbolicCommit($symbol) { $this->symbolicCommit = $symbol; $this->symbolicType = null; $this->stableCommit = null; return $this; } /** * Get the ref type (`commit` or `tag`) of the location associated with this * request. * * If a symbolic commit is present in the request, this method identifies * the type of the symbol. Otherwise, it identifies the type of symbol of * the location the request is implicitly associated with. This will probably * always be `commit`. * * @return string Symbolic commit type (`commit` or `tag`). */ public function getSymbolicType() { if ($this->symbolicType === null) { // As a side effect, this resolves the symbolic type. $this->getStableCommit(); } return $this->symbolicType; } /** * Retrieve the stable, permanent commit name identifying the repository * location associated with this request. * * This returns a non-symbolic identifier for the current commit: in Git and * Mercurial, a 40-character SHA1; in SVN, a revision number. * * See also @{method:getSymbolicCommit}. * * @return string Stable commit name, like a git hash or SVN revision. Not * a symbolic commit reference. */ public function getStableCommit() { if (!$this->stableCommit) { if ($this->isStableCommit($this->symbolicCommit)) { $this->stableCommit = $this->symbolicCommit; $this->symbolicType = 'commit'; } else { $this->queryStableCommit(); } } return $this->stableCommit; } public function getBranch() { return $this->branch; } public function getLint() { return $this->lint; } protected function getArcanistBranch() { return $this->getBranch(); } public function loadBranch() { // TODO: Get rid of this and do real Queries on real objects. if ($this->branchObject === false) { $this->branchObject = PhabricatorRepositoryBranch::loadBranch( $this->getRepository()->getID(), $this->getArcanistBranch()); } return $this->branchObject; } public function loadCoverage() { // TODO: This should also die. $branch = $this->loadBranch(); if (!$branch) { return; } $path = $this->getPath(); $path_map = id(new DiffusionPathIDQuery(array($path)))->loadPathIDs(); $coverage_row = queryfx_one( id(new PhabricatorRepository())->establishConnection('r'), 'SELECT * FROM %T WHERE branchID = %d AND pathID = %d ORDER BY commitID DESC LIMIT 1', 'repository_coverage', $branch->getID(), $path_map[$path]); if (!$coverage_row) { return null; } return idx($coverage_row, 'coverage'); } public function loadCommit() { if (empty($this->repositoryCommit)) { $repository = $this->getRepository(); $commit = id(new DiffusionCommitQuery()) ->setViewer($this->getUser()) ->withRepository($repository) ->withIdentifiers(array($this->getStableCommit())) ->executeOne(); if ($commit) { $commit->attachRepository($repository); } $this->repositoryCommit = $commit; } return $this->repositoryCommit; } public function loadCommitData() { if (empty($this->repositoryCommitData)) { $commit = $this->loadCommit(); $data = id(new PhabricatorRepositoryCommitData())->loadOneWhere( 'commitID = %d', $commit->getID()); if (!$data) { $data = new PhabricatorRepositoryCommitData(); $data->setCommitMessage( pht('(This commit has not been fully parsed yet.)')); } $this->repositoryCommitData = $data; } return $this->repositoryCommitData; } /* -( Managing Diffusion URIs )-------------------------------------------- */ public function generateURI(array $params) { if (empty($params['stable'])) { $default_commit = $this->getSymbolicCommit(); } else { $default_commit = $this->getStableCommit(); } $defaults = array( 'path' => $this->getPath(), 'branch' => $this->getBranch(), 'commit' => $default_commit, 'lint' => idx($params, 'lint', $this->getLint()), ); foreach ($defaults as $key => $val) { if (!isset($params[$key])) { // Overwrite NULL. $params[$key] = $val; } } return $this->getRepository()->generateURI($params); } /** * Internal. Public only for unit tests. * * Parse the request URI into components. * * @param string URI blob. * @param bool True if this VCS supports branches. * @return map Parsed URI. * * @task uri */ public static function parseRequestBlob($blob, $supports_branches) { $result = array( 'branch' => null, 'path' => null, 'commit' => null, 'line' => null, ); $matches = null; if ($supports_branches) { // Consume the front part of the URI, up to the first "/". This is the // path-component encoded branch name. if (preg_match('@^([^/]+)/@', $blob, $matches)) { $result['branch'] = phutil_unescape_uri_path_component($matches[1]); $blob = substr($blob, strlen($matches[1]) + 1); } } // Consume the back part of the URI, up to the first "$". Use a negative // lookbehind to prevent matching '$$'. We double the '$' symbol when // encoding so that files with names like "money/$100" will survive. $pattern = '@(?:(?:^|[^$])(?:[$][$])*)[$]([\d-,]+)$@'; if (preg_match($pattern, $blob, $matches)) { $result['line'] = $matches[1]; $blob = substr($blob, 0, -(strlen($matches[1]) + 1)); } // We've consumed the line number if it exists, so unescape "$" in the // rest of the string. $blob = str_replace('$$', '$', $blob); // Consume the commit name, stopping on ';;'. We allow any character to // appear in commits names, as they can sometimes be symbolic names (like // tag names or refs). if (preg_match('@(?:(?:^|[^;])(?:;;)*);([^;].*)$@', $blob, $matches)) { $result['commit'] = $matches[1]; $blob = substr($blob, 0, -(strlen($matches[1]) + 1)); } // We've consumed the commit if it exists, so unescape ";" in the rest // of the string. $blob = str_replace(';;', ';', $blob); if (strlen($blob)) { $result['path'] = $blob; } $parts = explode('/', $result['path']); foreach ($parts as $part) { // Prevent any hyjinx since we're ultimately shipping this to the // filesystem under a lot of workflows. if ($part == '..') { throw new Exception(pht('Invalid path URI.')); } } return $result; } /** * Check that the working copy of the repository is present and readable. * * @param string Path to the working copy. */ protected function validateWorkingCopy($path) { if (!is_readable(dirname($path))) { $this->raisePermissionException(); } if (!Filesystem::pathExists($path)) { $this->raiseCloneException(); } } protected function raisePermissionException() { $host = php_uname('n'); throw new DiffusionSetupException( pht( 'The clone of this repository ("%s") on the local machine ("%s") '. 'could not be read. Ensure that the repository is in a '. 'location where the web server has read permissions.', $this->getRepository()->getDisplayName(), $host)); } protected function raiseCloneException() { $host = php_uname('n'); throw new DiffusionSetupException( pht( 'The working copy for this repository ("%s") has not been cloned yet '. 'on this machine ("%s"). Make sure you havestarted the Phabricator '. 'daemons. If this problem persists for longer than a clone should '. 'take, check the daemon logs (in the Daemon Console) to see if there '. 'were errors cloning the repository. Consult the "Diffusion User '. 'Guide" in the documentation for help setting up repositories.', $this->getRepository()->getDisplayName(), $host)); } private function queryStableCommit() { $types = array(); if ($this->symbolicCommit) { $ref = $this->symbolicCommit; } else { if ($this->supportsBranches()) { $ref = $this->getBranch(); $types = array( PhabricatorRepositoryRefCursor::TYPE_BRANCH, ); } else { $ref = 'HEAD'; } } $results = $this->resolveRefs(array($ref), $types); $matches = idx($results, $ref, array()); if (!$matches) { $message = pht( 'Ref "%s" does not exist in this repository.', $ref); throw id(new DiffusionRefNotFoundException($message)) ->setRef($ref); } if (count($matches) > 1) { $match = $this->chooseBestRefMatch($ref, $matches); } else { $match = head($matches); } $this->stableCommit = $match['identifier']; $this->symbolicType = $match['type']; } public function getRefAlternatives() { // Make sure we've resolved the reference into a stable commit first. try { $this->getStableCommit(); } catch (DiffusionRefNotFoundException $ex) { // If we have a bad reference, just return the empty set of // alternatives. } return $this->refAlternatives; } private function chooseBestRefMatch($ref, array $results) { // First, filter out less-desirable matches. $candidates = array(); foreach ($results as $result) { // Exclude closed heads. if ($result['type'] == 'branch') { if (idx($result, 'closed')) { continue; } } $candidates[] = $result; } // If we filtered everything, undo the filtering. if (!$candidates) { $candidates = $results; } // TODO: Do a better job of selecting the best match? $match = head($candidates); // After choosing the best alternative, save all the alternatives so the // UI can show them to the user. if (count($candidates) > 1) { $this->refAlternatives = $candidates; } return $match; } private function resolveRefs(array $refs, array $types) { // First, try to resolve refs from fast cache sources. $cached_query = id(new DiffusionCachedResolveRefsQuery()) ->setRepository($this->getRepository()) ->withRefs($refs); if ($types) { $cached_query->withTypes($types); } $cached_results = $cached_query->execute(); // Throw away all the refs we resolved. Hopefully, we'll throw away // everything here. foreach ($refs as $key => $ref) { if (isset($cached_results[$ref])) { unset($refs[$key]); } } // If we couldn't pull everything out of the cache, execute the underlying // VCS operation. if ($refs) { $vcs_results = DiffusionQuery::callConduitWithDiffusionRequest( $this->getUser(), $this, 'diffusion.resolverefs', array( 'types' => $types, 'refs' => $refs, )); } else { $vcs_results = array(); } return $vcs_results + $cached_results; } public function setIsClusterRequest($is_cluster_request) { $this->isClusterRequest = $is_cluster_request; return $this; } public function getIsClusterRequest() { return $this->isClusterRequest; } }