Page MenuHomePhabricator

D7391.id16701.diff
No OneTemporary

D7391.id16701.diff

Index: resources/sql/patches/20131024.diffusionhost.sql
===================================================================
--- /dev/null
+++ resources/sql/patches/20131024.diffusionhost.sql
@@ -0,0 +1,5 @@
+ALTER TABLE {$NAMESPACE}_repository.repository
+ADD COLUMN pushPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin;
+
+ALTER TABLE {$NAMESPACE}_repository.repository
+ADD COLUMN hostingPath VARCHAR(255) NULL DEFAULT NULL COLLATE utf8_bin;
Index: resources/sql/patches/20131024.vcspassword.sql
===================================================================
--- /dev/null
+++ resources/sql/patches/20131024.vcspassword.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_user.user
+ADD COLUMN vcsPassword VARCHAR(10) NULL DEFAULT NULL COLLATE utf8_bin;
Index: src/__phutil_library_map__.php
===================================================================
--- src/__phutil_library_map__.php
+++ src/__phutil_library_map__.php
@@ -18,6 +18,7 @@
'AphrontAjaxResponse' => 'aphront/response/AphrontAjaxResponse.php',
'AphrontApplicationConfiguration' => 'aphront/configuration/AphrontApplicationConfiguration.php',
'AphrontBarView' => 'view/widget/bars/AphrontBarView.php',
+ 'AphrontBasicAuthResponse' => 'aphront/response/AphrontBasicAuthResponse.php',
'AphrontCSRFException' => 'aphront/exception/AphrontCSRFException.php',
'AphrontCalendarEventView' => 'applications/calendar/view/AphrontCalendarEventView.php',
'AphrontCalendarMonthView' => 'applications/calendar/view/AphrontCalendarMonthView.php',
@@ -51,6 +52,7 @@
'AphrontFormToggleButtonsControl' => 'view/form/control/AphrontFormToggleButtonsControl.php',
'AphrontFormTokenizerControl' => 'view/form/control/AphrontFormTokenizerControl.php',
'AphrontFormView' => 'view/form/AphrontFormView.php',
+ 'AphrontGitResponse' => 'aphront/response/AphrontGitResponse.php',
'AphrontGlyphBarView' => 'view/widget/bars/AphrontGlyphBarView.php',
'AphrontHTMLResponse' => 'aphront/response/AphrontHTMLResponse.php',
'AphrontHTTPSink' => 'aphront/sink/AphrontHTTPSink.php',
@@ -475,6 +477,7 @@
'DiffusionGitFileContentQuery' => 'applications/diffusion/query/filecontent/DiffusionGitFileContentQuery.php',
'DiffusionGitRawDiffQuery' => 'applications/diffusion/query/rawdiff/DiffusionGitRawDiffQuery.php',
'DiffusionGitRequest' => 'applications/diffusion/request/DiffusionGitRequest.php',
+ 'DiffusionGitServeController' => 'applications/diffusion/controller/DiffusionGitServeController.php',
'DiffusionGitStableCommitNameQuery' => 'applications/diffusion/query/stablecommitname/DiffusionGitStableCommitNameQuery.php',
'DiffusionHistoryController' => 'applications/diffusion/controller/DiffusionHistoryController.php',
'DiffusionHistoryTableView' => 'applications/diffusion/view/DiffusionHistoryTableView.php',
@@ -508,6 +511,7 @@
'DiffusionRepositoryEditBasicController' => 'applications/diffusion/controller/DiffusionRepositoryEditBasicController.php',
'DiffusionRepositoryEditController' => 'applications/diffusion/controller/DiffusionRepositoryEditController.php',
'DiffusionRepositoryEditEncodingController' => 'applications/diffusion/controller/DiffusionRepositoryEditEncodingController.php',
+ 'DiffusionRepositoryEditHostingController' => 'applications/diffusion/controller/DiffusionRepositoryEditHostingController.php',
'DiffusionRepositoryEditPolicyController' => 'applications/diffusion/controller/DiffusionRepositoryEditPolicyController.php',
'DiffusionRepositoryListController' => 'applications/diffusion/controller/DiffusionRepositoryListController.php',
'DiffusionRepositoryPath' => 'applications/diffusion/data/DiffusionRepositoryPath.php',
@@ -619,6 +623,7 @@
'FileCreateMailReceiver' => 'applications/files/mail/FileCreateMailReceiver.php',
'FileMailReceiver' => 'applications/files/mail/FileMailReceiver.php',
'FileReplyHandler' => 'applications/files/mail/FileReplyHandler.php',
+ 'GitHTTPServer' => 'infrastructure/git/GitHTTPServer.php',
'HarbormasterBuild' => 'applications/harbormaster/storage/build/HarbormasterBuild.php',
'HarbormasterBuildArtifact' => 'applications/harbormaster/storage/build/HarbormasterBuildArtifact.php',
'HarbormasterBuildItem' => 'applications/harbormaster/storage/build/HarbormasterBuildItem.php',
@@ -1528,6 +1533,7 @@
'PhabricatorPolicyCapability' => 'applications/policy/capability/PhabricatorPolicyCapability.php',
'PhabricatorPolicyCapabilityCanEdit' => 'applications/policy/capability/PhabricatorPolicyCapabilityCanEdit.php',
'PhabricatorPolicyCapabilityCanJoin' => 'applications/policy/capability/PhabricatorPolicyCapabilityCanJoin.php',
+ 'PhabricatorPolicyCapabilityCanPush' => 'applications/policy/capability/PhabricatorPolicyCapabilityCanPush.php',
'PhabricatorPolicyCapabilityCanView' => 'applications/policy/capability/PhabricatorPolicyCapabilityCanView.php',
'PhabricatorPolicyConfigOptions' => 'applications/policy/config/PhabricatorPolicyConfigOptions.php',
'PhabricatorPolicyConstants' => 'applications/policy/constants/PhabricatorPolicyConstants.php',
@@ -1699,6 +1705,7 @@
'PhabricatorSettingsPanelPassword' => 'applications/settings/panel/PhabricatorSettingsPanelPassword.php',
'PhabricatorSettingsPanelSSHKeys' => 'applications/settings/panel/PhabricatorSettingsPanelSSHKeys.php',
'PhabricatorSettingsPanelSearchPreferences' => 'applications/settings/panel/PhabricatorSettingsPanelSearchPreferences.php',
+ 'PhabricatorSettingsPanelVCSPassword' => 'applications/settings/panel/PhabricatorSettingsPanelVCSPassword.php',
'PhabricatorSetupCheck' => 'applications/config/check/PhabricatorSetupCheck.php',
'PhabricatorSetupCheckAPC' => 'applications/config/check/PhabricatorSetupCheckAPC.php',
'PhabricatorSetupCheckAuth' => 'applications/config/check/PhabricatorSetupCheckAuth.php',
@@ -2201,6 +2208,7 @@
'AphrontAbstractAttachedFileView' => 'AphrontView',
'AphrontAjaxResponse' => 'AphrontResponse',
'AphrontBarView' => 'AphrontView',
+ 'AphrontBasicAuthResponse' => 'AphrontResponse',
'AphrontCSRFException' => 'AphrontException',
'AphrontCalendarEventView' => 'AphrontView',
'AphrontCalendarMonthView' => 'AphrontView',
@@ -2234,6 +2242,7 @@
'AphrontFormToggleButtonsControl' => 'AphrontFormControl',
'AphrontFormTokenizerControl' => 'AphrontFormControl',
'AphrontFormView' => 'AphrontView',
+ 'AphrontGitResponse' => 'AphrontResponse',
'AphrontGlyphBarView' => 'AphrontBarView',
'AphrontHTMLResponse' => 'AphrontResponse',
'AphrontHTTPSinkTestCase' => 'PhabricatorTestCase',
@@ -2646,6 +2655,7 @@
'DiffusionGitFileContentQuery' => 'DiffusionFileContentQuery',
'DiffusionGitRawDiffQuery' => 'DiffusionRawDiffQuery',
'DiffusionGitRequest' => 'DiffusionRequest',
+ 'DiffusionGitServeController' => 'DiffusionController',
'DiffusionGitStableCommitNameQuery' => 'DiffusionStableCommitNameQuery',
'DiffusionHistoryController' => 'DiffusionController',
'DiffusionHistoryTableView' => 'DiffusionView',
@@ -2673,6 +2683,7 @@
'DiffusionRepositoryEditBasicController' => 'DiffusionController',
'DiffusionRepositoryEditController' => 'DiffusionController',
'DiffusionRepositoryEditEncodingController' => 'DiffusionController',
+ 'DiffusionRepositoryEditHostingController' => 'DiffusionController',
'DiffusionRepositoryEditPolicyController' => 'DiffusionController',
'DiffusionRepositoryListController' =>
array(
@@ -3817,6 +3828,7 @@
'PhabricatorPolicyCapability' => 'Phobject',
'PhabricatorPolicyCapabilityCanEdit' => 'PhabricatorPolicyCapability',
'PhabricatorPolicyCapabilityCanJoin' => 'PhabricatorPolicyCapability',
+ 'PhabricatorPolicyCapabilityCanPush' => 'PhabricatorPolicyCapability',
'PhabricatorPolicyCapabilityCanView' => 'PhabricatorPolicyCapability',
'PhabricatorPolicyConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorPolicyController' => 'PhabricatorController',
@@ -3997,6 +4009,7 @@
'PhabricatorSettingsPanelPassword' => 'PhabricatorSettingsPanel',
'PhabricatorSettingsPanelSSHKeys' => 'PhabricatorSettingsPanel',
'PhabricatorSettingsPanelSearchPreferences' => 'PhabricatorSettingsPanel',
+ 'PhabricatorSettingsPanelVCSPassword' => 'PhabricatorSettingsPanel',
'PhabricatorSetupCheckAPC' => 'PhabricatorSetupCheck',
'PhabricatorSetupCheckAuth' => 'PhabricatorSetupCheck',
'PhabricatorSetupCheckBaseURI' => 'PhabricatorSetupCheck',
Index: src/aphront/response/AphrontBasicAuthResponse.php
===================================================================
--- /dev/null
+++ src/aphront/response/AphrontBasicAuthResponse.php
@@ -0,0 +1,23 @@
+<?php
+
+final class AphrontBasicAuthResponse extends AphrontResponse {
+
+ public function buildResponseString() {
+ return "";
+ }
+
+ public function getHeaders() {
+ return array(
+ array('WWW-Authenticate', 'Basic realm="Phabricator Git Repository"'));
+ }
+
+ public function getCacheHeaders() {
+ return array();
+ }
+
+ public function getHTTPResponseCode() {
+ return 401;
+ }
+
+}
+
Index: src/aphront/response/AphrontGitResponse.php
===================================================================
--- /dev/null
+++ src/aphront/response/AphrontGitResponse.php
@@ -0,0 +1,47 @@
+<?php
+
+final class AphrontGitResponse extends AphrontResponse {
+
+ private $httpCode;
+ private $headers;
+ private $response;
+
+ public function setGitData($data) {
+ // We have to parse lines up until the HTTP double newline. First
+ // split on \r\n\r\n to get the headers and content as separate entries.
+ $headeridx = strpos($data, "\r\n\r\n");
+ $this->response = substr($data, $headeridx + 4);
+
+ // Now parse the headers, the CGI command can potentially output "Status:"
+ // to change the HTTP status code.
+ $lines = explode("\r\n", substr($data, 0, $headeridx));
+
+ $this->headers = array();
+ if (substr($lines[0], 0, 7) === "Status:") {
+ $this->httpCode = substr($lines[0], 8, 3);
+ } else {
+ $this->httpCode = 200;
+ }
+ for ($i = 1; $i < count($lines); $i++) {
+ $this->headers[] = explode(": ", $lines[$i]);
+ }
+ }
+
+ public function buildResponseString() {
+ return $this->response;
+ }
+
+ public function getHeaders() {
+ return $this->headers;
+ }
+
+ public function getCacheHeaders() {
+ return array();
+ }
+
+ public function getHTTPResponseCode() {
+ return $this->httpCode;
+ }
+
+}
+
Index: src/applications/diffusion/application/PhabricatorApplicationDiffusion.php
===================================================================
--- src/applications/diffusion/application/PhabricatorApplicationDiffusion.php
+++ src/applications/diffusion/application/PhabricatorApplicationDiffusion.php
@@ -44,6 +44,7 @@
'(?:query/(?P<queryKey>[^/]+)/)?'
=> 'DiffusionRepositoryListController',
'create/' => 'DiffusionRepositoryCreateController',
+ 'git/(?P<callsign>[A-Z]+)(?<url>.*)' => 'DiffusionGitServeController',
'(?P<callsign>[A-Z]+)/' => array(
'' => 'DiffusionRepositoryController',
@@ -69,6 +70,7 @@
'encoding/' => 'DiffusionRepositoryEditEncodingController',
'activate/' => 'DiffusionRepositoryEditActivateController',
'policy/' => 'DiffusionRepositoryEditPolicyController',
+ 'hosting/' => 'DiffusionRepositoryEditHostingController',
),
),
'inline/' => array(
Index: src/applications/diffusion/controller/DiffusionGitServeController.php
===================================================================
--- /dev/null
+++ src/applications/diffusion/controller/DiffusionGitServeController.php
@@ -0,0 +1,128 @@
+<?php
+
+final class DiffusionGitServeController extends DiffusionController {
+
+ private $callsign;
+ private $url;
+
+ public function willProcessRequest(array $data) {
+ $this->callsign = idx($data, 'callsign');
+ $this->url = idx($data, 'url');
+ }
+
+ public function shouldRequireLogin() {
+ return false;
+ }
+
+ public function shouldRequireAdmin() {
+ return false;
+ }
+
+ public function shouldRequireEnabledUser() {
+ return false;
+ }
+
+ public function shouldAllowPublic() {
+ return true;
+ }
+
+ private function isWriteRequest() {
+ return isset($_GET['service']) && $_GET['service'] == 'git-receive-pack';
+ }
+
+ public function processRequest() {
+ // Retrieve the associated hosted repository.
+ $repository = id(new PhabricatorRepository)
+ ->loadOneWhere("callsign = %s", $this->callsign);
+ if ($repository === null) {
+ return new Aphront404Response();
+ }
+ if (!$repository->isHosted()) {
+ return new Aphront403Response();
+ }
+ $path = $repository->getLocalPath();
+
+ // Check the hosting type.
+ $mode = $repository->getServeOverHTTP();
+ $serving = null;
+ if (!$this->isWriteRequest()) {
+ // Either READONLY or READWRITE allows us to serve.
+ if ($mode === PhabricatorRepository::SERVE_READONLY) {
+ $serving = true;
+ }
+ if ($mode === PhabricatorRepository::SERVE_READWRITE) {
+ $serving = true;
+ }
+ } else {
+ if ($mode === PhabricatorRepository::SERVE_READWRITE) {
+ $serving = true;
+ }
+ }
+ if (!$serving) {
+ return new Aphront403Response();
+ }
+
+ // Start with not authorized state.
+ $permitted = false;
+
+ // If this is a read-only operation, and the view policy of the
+ // repository is PhabricatorPolicies::POLICY_PUBLIC, then we don't
+ // need to prompt for authorization.
+ if (!$this->isWriteRequest() &&
+ $repository->getViewPolicy() == PhabricatorPolicies::POLICY_PUBLIC) {
+ $permitted = true;
+ }
+
+ // If we're not permitted at this point, then we do need basic HTTP
+ // authorization to be completed by the user.
+ if (!$permitted) {
+ if (!isset($_SERVER['PHP_AUTH_USER']) ||
+ !isset($_SERVER['PHP_AUTH_PW'])) {
+ return new AphrontBasicAuthResponse();
+ }
+
+ // Validate who the user is based on Git settings. We get provide users
+ // with a fake password they can send over basic HTTP; that way, even
+ // people who login with other providers such as Google.
+ $user = id(new PhabricatorUser())
+ ->loadOneWhere(
+ "userName = %s AND vcsPassword = %s",
+ $_SERVER['PHP_AUTH_USER'],
+ $_SERVER['PHP_AUTH_PW']);
+ if ($user === null) {
+ return new AphrontBasicAuthResponse();
+ }
+
+ // Check policies. viewPolicy allows GET, editPolicy allows POST / PUT.
+ if (!$this->isWriteRequest()) {
+ $permitted = PhabricatorPolicyFilter::hasCapability(
+ $user,
+ $repository,
+ PhabricatorPolicyCapability::CAN_VIEW);
+ }
+ if ($this->isWriteRequest()) {
+ $permitted = PhabricatorPolicyFilter::hasCapability(
+ $user,
+ $repository,
+ PhabricatorPolicyCapability::CAN_PUSH);
+ }
+ }
+
+ // If not permitted, throw 403 Forbidden.
+ if (!$permitted) {
+ return new Aphront403Response();
+ }
+
+ // TODO: Switch type of server based on
+ // $repository->getVersionControlSystem()!
+ $server = new GitHTTPServer();
+ $server->setProjectRoot($path);
+ $data = $server->acceptRequest($this->url);
+
+ $response = new AphrontGitResponse();
+ $response->setGitData($data);
+ return $response;
+ }
+
+}
+
Index: src/applications/diffusion/controller/DiffusionRepositoryEditController.php
===================================================================
--- src/applications/diffusion/controller/DiffusionRepositoryEditController.php
+++ src/applications/diffusion/controller/DiffusionRepositoryEditController.php
@@ -37,6 +37,10 @@
$encoding_properties =
$this->buildEncodingProperties($repository, $encoding_actions);
+ $hosting_actions = $this->buildHostingActions($repository);
+ $hosting_properties =
+ $this->buildHostingProperties($repository, $hosting_actions);
+
$xactions = id(new PhabricatorRepositoryTransactionQuery())
->setViewer($user)
->withObjectPHIDs(array($repository->getPHID()))
@@ -63,7 +67,8 @@
->setHeader($header)
->addPropertyList($basic_properties)
->addPropertyList($policy_properties)
- ->addPropertyList($encoding_properties);
+ ->addPropertyList($encoding_properties)
+ ->addPropertyList($hosting_properties);
return $this->buildApplicationPage(
array(
@@ -245,8 +250,105 @@
pht('Editable By'),
$descriptions[PhabricatorPolicyCapability::CAN_EDIT]);
+ if ($repository->isHosted()) {
+ $view->addProperty(
+ pht('Pushable By'),
+ $descriptions[PhabricatorPolicyCapability::CAN_PUSH]);
+ }
+
+ return $view;
+ }
+
+ private function buildHostingActions(PhabricatorRepository $repository) {
+ $user = $this->getRequest()->getUser();
+
+ $view = id(new PhabricatorActionListView())
+ ->setObjectURI($this->getRequest()->getRequestURI())
+ ->setUser($user);
+
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $user,
+ $repository,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ $edit = id(new PhabricatorActionView())
+ ->setIcon('edit')
+ ->setName(pht('Edit Hosting'))
+ ->setHref(
+ $this->getRepositoryControllerURI($repository, 'edit/hosting/'))
+ ->setWorkflow(!$can_edit)
+ ->setDisabled(!$can_edit);
+ $view->addAction($edit);
+
+ return $view;
+ }
+
+ private function buildHostingProperties(
+ PhabricatorRepository $repository,
+ PhabricatorActionListView $actions) {
+
+ $user = $this->getRequest()->getUser();
+
+ $view = id(new PHUIPropertyListView())
+ ->setUser($user)
+ ->setActionList($actions)
+ ->addSectionHeader(pht('Hosting'));
+
+ $hosted = $repository->getDetail('hosting-enabled');
+ if ($hosted === null) {
+ $hosted = phutil_tag('em', array(), pht('Use Default (No)'));
+ } else if ($hosted) {
+ $hosted = pht('Yes');
+ } else {
+ $hosted = pht('No');
+ }
+
+ $http_mode = $repository->getDetail('serve-over-http');
+ if ($http_mode === null) {
+ $http_mode = phutil_tag('em', array(), pht('Use Default (No)'));
+ } else {
+ switch ($http_mode) {
+ case PhabricatorRepository::SERVE_OFF:
+ $http_mode = pht('No');
+ break;
+ case PhabricatorRepository::SERVE_READONLY:
+ $http_mode = pht('Read-Only');
+ break;
+ case PhabricatorRepository::SERVE_READWRITE:
+ $http_mode = pht('Read-Write');
+ break;
+ default:
+ $http_mode = pht('Unknown');
+ break;
+ }
+ }
+
+ $ssh_mode = $repository->getDetail('serve-over-ssh');
+ if ($ssh_mode === null) {
+ $ssh_mode = phutil_tag('em', array(), pht('Use Default (No)'));
+ } else {
+ switch ($ssh_mode) {
+ case PhabricatorRepository::SERVE_OFF:
+ $ssh_mode = pht('No');
+ break;
+ case PhabricatorRepository::SERVE_READONLY:
+ $ssh_mode = pht('Read-Only');
+ break;
+ case PhabricatorRepository::SERVE_READWRITE:
+ $ssh_mode = pht('Read-Write');
+ break;
+ default:
+ $ssh_mode = pht('Unknown');
+ break;
+ }
+ }
+
+ $view->addProperty(pht('Hosted'), $hosted);
+ $view->addProperty(pht('Served over HTTP'), $http_mode);
+ $view->addProperty(pht('Served over SSH'), $ssh_mode);
return $view;
}
+
}
Index: src/applications/diffusion/controller/DiffusionRepositoryEditHostingController.php
===================================================================
--- /dev/null
+++ src/applications/diffusion/controller/DiffusionRepositoryEditHostingController.php
@@ -0,0 +1,327 @@
+<?php
+
+final class DiffusionRepositoryEditHostingController
+ extends DiffusionController {
+
+ private $repository;
+ private $edit_uri;
+
+ public function processRequest() {
+ $request = $this->getRequest();
+ $user = $request->getUser();
+ $drequest = $this->diffusionRequest;
+ $this->repository = $drequest->getRepository();
+
+ $this->repository = id(new PhabricatorRepositoryQuery())
+ ->setViewer($user)
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
+ ->withIDs(array($this->repository->getID()))
+ ->executeOne();
+
+ if (!$this->repository) {
+ return new Aphront404Response();
+ }
+
+ $this->edit_uri =
+ $this->getRepositoryControllerURI($this->repository, 'edit/');
+
+ if ($request->getInt('step') === 2) {
+ return $this->handleProtocols();
+ } else {
+ return $this->handleHosting();
+ }
+ }
+
+ private function getNextStep() {
+ return '/diffusion/'.
+ $this->repository->getCallsign().
+ '/edit/hosting/?step=2';
+ }
+
+ public function handleHosting() {
+ $request = $this->getRequest();
+ $user = $request->getUser();
+
+ $v_hosting = $this->repository->isHosted();
+ $e_hosting = null;
+ $errors = array();
+
+ if ($request->isFormPost()) {
+ $v_hosting = $request->getBool('hosting');
+
+ if (!$errors) {
+ $xactions = array();
+ $template = id(new PhabricatorRepositoryTransaction());
+
+ $type_hosting = PhabricatorRepositoryTransaction::TYPE_HOSTING;
+
+ $xactions[] = id(clone $template)
+ ->setTransactionType($type_hosting)
+ ->setNewValue($v_hosting);
+
+ try {
+ id(new PhabricatorRepositoryEditor())
+ ->setContinueOnNoEffect(true)
+ ->setContentSourceFromRequest($request)
+ ->setActor($user)
+ ->applyTransactions($this->repository, $xactions);
+
+ return id(new AphrontRedirectResponse())->setURI(
+ $this->getNextStep());
+ } catch (Exception $ex) {
+ $errors[] = $ex->getMessage();
+ }
+ }
+ }
+
+ $crumbs = $this->buildCrumbs();
+ $crumbs->addCrumb(
+ id(new PhabricatorCrumbView())
+ ->setName(pht('Edit Hosting')));
+
+ $title = pht('Edit %s', $this->repository->getName());
+
+ $error_view = null;
+ if ($errors) {
+ $error_view = id(new AphrontErrorView())
+ ->setTitle(pht('Form Errors'))
+ ->setErrors($errors);
+ }
+
+ $hosted_control =
+ id(new AphrontFormRadioButtonControl())
+ ->setName('hosting')
+ ->setLabel(pht('Hosting'))
+ ->addButton(
+ true,
+ pht('Yes'),
+ pht(
+ 'Phabricator will consider it\'s copy of the repository to be '.
+ 'authoritive. The tracking URI will be forced to point to '.
+ 'Phabricator\'s copy of the source code.'))
+ ->addButton(
+ false,
+ pht('No'),
+ pht(
+ 'Phabricator will source it\'s repository information from an '.
+ 'external repository defined by the tracking URI. Operations '.
+ 'that require making read-write changes will not be available '.
+ 'in the UI, and users will not be able to write to the '.
+ 'repository.'))
+ ->setValue($v_hosting)
+ ->setError($e_hosting);
+
+ $form = id(new AphrontFormView())
+ ->setUser($user)
+ ->appendRemarkupInstructions($this->getHostingInstructions())
+ ->appendChild($hosted_control)
+ ->appendChild(
+ id(new AphrontFormSubmitControl())
+ ->setValue(pht('Save and Continue'))
+ ->addCancelButton($this->edit_uri));
+
+ $object_box = id(new PHUIObjectBoxView())
+ ->setHeaderText($title)
+ ->setForm($form)
+ ->setFormError($error_view);
+
+ return $this->buildApplicationPage(
+ array(
+ $crumbs,
+ $object_box,
+ ),
+ array(
+ 'title' => $title,
+ 'device' => true,
+ ));
+ }
+
+ private function getHostingInstructions() {
+ return pht(<<<EOT
+A hosted repository is one where Phabricator is considered the own the master
+copy. You can read and write to hosted repositories subject to the options
+below, but you can't configure a separate tracking URL (Phabricator always
+tracks it's own copy of a hosted repository).
+EOT
+ );
+ }
+
+ public function handleProtocols() {
+ $request = $this->getRequest();
+ $user = $request->getUser();
+
+ $v_http_mode = $this->repository->getServeOverHTTP();
+ $e_http_mode = null;
+ $v_ssh_mode = $this->repository->getServeOverSSH();
+ $e_ssh_mode = null;
+ $errors = array();
+
+ if ($request->isFormPost()) {
+ $v_http_mode = $request->getStr('http');
+ $v_ssh_mode = PhabricatorRepository::SERVE_OFF;
+
+ if (!$errors) {
+ $xactions = array();
+ $template = id(new PhabricatorRepositoryTransaction());
+
+ $type_http = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP;
+ $type_ssh = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH;
+
+ $xactions[] = id(clone $template)
+ ->setTransactionType($type_http)
+ ->setNewValue($v_http_mode);
+
+ $xactions[] = id(clone $template)
+ ->setTransactionType($type_ssh)
+ ->setNewValue($v_ssh_mode);
+
+ try {
+ id(new PhabricatorRepositoryEditor())
+ ->setContinueOnNoEffect(true)
+ ->setContentSourceFromRequest($request)
+ ->setActor($user)
+ ->applyTransactions($this->repository, $xactions);
+
+ return id(new AphrontRedirectResponse())->setURI($this->edit_uri);
+ } catch (Exception $ex) {
+ $errors[] = $ex->getMessage();
+ }
+ }
+ }
+
+ $crumbs = $this->buildCrumbs();
+ $crumbs->addCrumb(
+ id(new PhabricatorCrumbView())
+ ->setName(pht('Edit Protocols')));
+
+ $title = pht('Edit %s', $this->repository->getName());
+
+ $error_view = null;
+ if ($errors) {
+ $error_view = id(new AphrontErrorView())
+ ->setTitle(pht('Form Errors'))
+ ->setErrors($errors);
+ }
+
+ $http_control =
+ id(new AphrontFormRadioButtonControl())
+ ->setName('http')
+ ->setLabel(pht('HTTP'))
+ ->addButton(
+ PhabricatorRepository::SERVE_OFF,
+ pht('Off'),
+ pht(
+ 'Phabricator will not serve this repository over HTTP.'))
+ ->addButton(
+ PhabricatorRepository::SERVE_READONLY,
+ pht('Read-Only'),
+ pht(
+ 'Phabricator will serve a read-only copy of this repository at '.
+ '%s. Users with view access will be able to %s this '.
+ 'repository with %s.',
+ phutil_tag('tt', array(), $this->repository->getHTTPUrl()),
+ $this->getCloneNoun(),
+ phutil_tag('tt', array(), $this->getCloneHTTPCommand())));
+ if ($this->repository->isHosted()) {
+ $http_control
+ ->addButton(
+ PhabricatorRepository::SERVE_READWRITE,
+ pht('Read-Write'),
+ pht(
+ 'Phabricator will serve a read-write copy of this repository at '.
+ '%s. Users with push access will be able to send '.
+ 'changes to this repository with %s.',
+ phutil_tag('tt', array(), $this->repository->getHTTPUrl()),
+ phutil_tag('tt', array(), $this->getPushHTTPCommand())));
+ } else {
+ $http_control
+ ->addButton(
+ PhabricatorRepository::SERVE_READWRITE,
+ pht('Read-Write'),
+ pht(
+ 'This repository is not hosted, so Phabricator can not serve '.
+ 'read-write access.'),
+ 'disabled',
+ $disabled = true);
+ }
+ $http_control
+ ->setValue($v_http_mode)
+ ->setError($e_http_mode);
+
+ $form = id(new AphrontFormView())
+ ->setUser($user)
+ ->appendRemarkupInstructions($this->getProtocolInstructions())
+ ->appendChild($http_control)
+ ->appendChild(
+ id(new AphrontFormSubmitControl())
+ ->setValue(pht('Save Changes'))
+ ->addCancelButton($this->edit_uri));
+
+ $object_box = id(new PHUIObjectBoxView())
+ ->setHeaderText($title)
+ ->setForm($form)
+ ->setFormError($error_view);
+
+ return $this->buildApplicationPage(
+ array(
+ $crumbs,
+ $object_box,
+ ),
+ array(
+ 'title' => $title,
+ 'device' => true,
+ ));
+ }
+
+ private function getCloneNoun() {
+ switch ($this->repository->getVersionControlSystem()) {
+ case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
+ return "checkout";
+ case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
+ case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
+ return "clone";
+ default:
+ throw new Exception("Unrecognized version control system.");
+ }
+ }
+
+ private function getCloneHTTPCommand() {
+ switch ($this->repository->getVersionControlSystem()) {
+ case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
+ return pht("svn checkout %s", $this->repository->getHTTPUrl());
+ case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
+ return pht("hg clone %s", $this->repository->getHTTPUrl());
+ case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
+ return pht("git clone %s", $this->repository->getHTTPUrl());
+ default:
+ throw new Exception("Unrecognized version control system.");
+ }
+ }
+
+ private function getPushHTTPCommand() {
+ switch ($this->repository->getVersionControlSystem()) {
+ case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
+ return pht("svn commit %s", $this->repository->getHTTPUrl());
+ case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
+ return pht("hg push %s", $this->repository->getHTTPUrl());
+ case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
+ return pht("git push %s", $this->repository->getHTTPUrl());
+ default:
+ throw new Exception("Unrecognized version control system.");
+ }
+ }
+
+ private function getProtocolInstructions() {
+ return pht(<<<EOT
+Below you can configure what protocols Phabricator will make this repository
+available on. Read-write access is only available if Phabricator is
+hosting the repository.
+EOT
+ );
+ }
+
+}
Index: src/applications/diffusion/controller/DiffusionRepositoryEditPolicyController.php
===================================================================
--- src/applications/diffusion/controller/DiffusionRepositoryEditPolicyController.php
+++ src/applications/diffusion/controller/DiffusionRepositoryEditPolicyController.php
@@ -27,16 +27,21 @@
$v_view = $repository->getViewPolicy();
$v_edit = $repository->getEditPolicy();
+ $v_push = $repository->getPushPolicy();
if ($request->isFormPost()) {
$v_view = $request->getStr('viewPolicy');
$v_edit = $request->getStr('editPolicy');
+ if ($repository->isHosted()) {
+ $v_push = $request->getStr('pushPolicy');
+ }
$xactions = array();
$template = id(new PhabricatorRepositoryTransaction());
$type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
$type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;
+ $type_push = PhabricatorTransactions::TYPE_PUSH_POLICY;
$xactions[] = id(clone $template)
->setTransactionType($type_view)
@@ -46,6 +51,12 @@
->setTransactionType($type_edit)
->setNewValue($v_edit);
+ if ($repository->isHosted()) {
+ $xactions[] = id(clone $template)
+ ->setTransactionType($type_push)
+ ->setNewValue($v_push);
+ }
+
id(new PhabricatorRepositoryEditor())
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request)
@@ -84,7 +95,18 @@
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
->setPolicyObject($repository)
->setPolicies($policies)
- ->setName('editPolicy'))
+ ->setName('editPolicy'));
+ if ($repository->isHosted()) {
+ $form
+ ->appendChild(
+ id(new AphrontFormPolicyControl())
+ ->setUser($viewer)
+ ->setCapability(PhabricatorPolicyCapability::CAN_PUSH)
+ ->setPolicyObject($repository)
+ ->setPolicies($policies)
+ ->setName('pushPolicy'));
+ }
+ $form
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Save Policies'))
Index: src/applications/people/storage/PhabricatorUser.php
===================================================================
--- src/applications/people/storage/PhabricatorUser.php
+++ src/applications/people/storage/PhabricatorUser.php
@@ -25,6 +25,7 @@
protected $consoleTab = '';
protected $conduitCertificate;
+ protected $vcsPassword;
protected $isSystemAgent = 0;
protected $isAdmin = 0;
@@ -111,6 +112,9 @@
if (!$this->getConduitCertificate()) {
$this->setConduitCertificate($this->generateConduitCertificate());
}
+ if (!$this->getVCSPassword()) {
+ $this->setVCSPassword($this->generateVCSPassword());
+ }
$result = parent::save();
if ($this->profile) {
@@ -129,6 +133,10 @@
return Filesystem::readRandomCharacters(255);
}
+ private function generateVCSPassword() {
+ return Filesystem::readRandomCharacters(10);
+ }
+
public function comparePassword(PhutilOpaqueEnvelope $envelope) {
if (!strlen($envelope->openEnvelope())) {
return false;
Index: src/applications/policy/capability/PhabricatorPolicyCapability.php
===================================================================
--- src/applications/policy/capability/PhabricatorPolicyCapability.php
+++ src/applications/policy/capability/PhabricatorPolicyCapability.php
@@ -5,6 +5,7 @@
const CAN_VIEW = 'view';
const CAN_EDIT = 'edit';
const CAN_JOIN = 'join';
+ const CAN_PUSH = 'push';
/**
* Get the unique key identifying this capability. This key must be globally
Index: src/applications/policy/capability/PhabricatorPolicyCapabilityCanPush.php
===================================================================
--- /dev/null
+++ src/applications/policy/capability/PhabricatorPolicyCapabilityCanPush.php
@@ -0,0 +1,18 @@
+<?php
+
+final class PhabricatorPolicyCapabilityCanPush
+ extends PhabricatorPolicyCapability {
+
+ public function getCapabilityKey() {
+ return self::CAN_PUSH;
+ }
+
+ public function getCapabilityName() {
+ return pht('Can Push');
+ }
+
+ public function describeCapabilityRejection() {
+ return pht('You do not have permission to push to this object.');
+ }
+
+}
Index: src/applications/repository/editor/PhabricatorRepositoryEditor.php
===================================================================
--- src/applications/repository/editor/PhabricatorRepositoryEditor.php
+++ src/applications/repository/editor/PhabricatorRepositoryEditor.php
@@ -10,8 +10,12 @@
$types[] = PhabricatorRepositoryTransaction::TYPE_NAME;
$types[] = PhabricatorRepositoryTransaction::TYPE_DESCRIPTION;
$types[] = PhabricatorRepositoryTransaction::TYPE_ENCODING;
+ $types[] = PhabricatorRepositoryTransaction::TYPE_HOSTING;
+ $types[] = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP;
+ $types[] = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
+ $types[] = PhabricatorTransactions::TYPE_PUSH_POLICY;
return $types;
}
@@ -29,6 +33,12 @@
return $object->getDetail('description');
case PhabricatorRepositoryTransaction::TYPE_ENCODING:
return $object->getDetail('encoding');
+ case PhabricatorRepositoryTransaction::TYPE_HOSTING:
+ return $object->isHosted();
+ case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP:
+ return $object->getServeOverHTTP();
+ case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH:
+ return $object->getServeOverSSH();
}
}
@@ -41,6 +51,9 @@
case PhabricatorRepositoryTransaction::TYPE_NAME:
case PhabricatorRepositoryTransaction::TYPE_DESCRIPTION:
case PhabricatorRepositoryTransaction::TYPE_ENCODING:
+ case PhabricatorRepositoryTransaction::TYPE_HOSTING:
+ case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP:
+ case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH:
return $xaction->getNewValue();
}
}
@@ -59,6 +72,15 @@
case PhabricatorRepositoryTransaction::TYPE_DESCRIPTION:
$object->setDetail('description', $xaction->getNewValue());
break;
+ case PhabricatorRepositoryTransaction::TYPE_HOSTING:
+ $object->setHosted($xaction->getNewValue());
+ break;
+ case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP:
+ $object->setServeOverHTTP($xaction->getNewValue());
+ break;
+ case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH:
+ $object->setServeOverSSH($xaction->getNewValue());
+ break;
case PhabricatorRepositoryTransaction::TYPE_ENCODING:
// Make sure the encoding is valid by converting to UTF-8. This tests
// that the user has mbstring installed, and also that they didn't type
Index: src/applications/repository/storage/PhabricatorRepository.php
===================================================================
--- src/applications/repository/storage/PhabricatorRepository.php
+++ src/applications/repository/storage/PhabricatorRepository.php
@@ -25,14 +25,20 @@
const TABLE_BADCOMMIT = 'repository_badcommit';
const TABLE_LINTMESSAGE = 'repository_lintmessage';
+ const SERVE_OFF = 'off';
+ const SERVE_READONLY = 'readonly';
+ const SERVE_READWRITE = 'readwrite';
+
protected $name;
protected $callsign;
protected $uuid;
protected $viewPolicy = PhabricatorPolicies::POLICY_USER;
protected $editPolicy = PhabricatorPolicies::POLICY_ADMIN;
+ protected $pushPolicy = PhabricatorPolicies::POLICY_USER;
protected $versionControlSystem;
protected $details = array();
+ protected $hostingPath;
private $sshKeyfile;
@@ -356,6 +362,44 @@
return $this->getDetail('tracking-enabled', false);
}
+ public function isHosted() {
+ return $this->getDetail('hosting-enabled', false);
+ }
+
+ public function setHosted($enabled) {
+ return $this->setDetail('hosting-enabled', $enabled);
+ }
+
+ public function getServeOverHTTP() {
+ return $this->getDetail('serve-over-http', self::SERVE_OFF);
+ }
+
+ public function setServeOverHTTP($mode) {
+ return $this->setDetail('serve-over-http', $mode);
+ }
+
+ public function getServeOverSSH() {
+ return $this->getDetail('serve-over-ssh', self::SERVE_OFF);
+ }
+
+ public function setServeOverSSH($mode) {
+ return $this->setDetail('serve-over-ssh', $mode);
+ }
+
+ public function getHTTPUrl() {
+ $baseURI = trim(PhabricatorEnv::getEnvConfig('phabricator.base-uri'), '/');
+ switch ($this->getVersionControlSystem()) {
+ case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
+ return $baseURI."/diffusion/svn/".$this->getCallsign();
+ case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
+ return $baseURI."/diffusion/hg/".$this->getCallsign();
+ case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
+ return $baseURI."/diffusion/git/".$this->getCallsign();
+ default:
+ throw new Exception("Unrecognized version control system.");
+ }
+ }
+
public function getDefaultBranch() {
$default = $this->getDetail('default-branch');
if (strlen($default)) {
@@ -471,6 +515,10 @@
* @task uri
*/
public function getPublicRemoteURI() {
+ if ($this->isHosted()) {
+ return $this->getHostingURI();
+ }
+
$uri = $this->getRemoteURIObject();
// Make sure we don't leak anything if this repo is using HTTP Basic Auth
@@ -684,6 +732,7 @@
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
+ PhabricatorPolicyCapability::CAN_PUSH,
);
}
@@ -693,6 +742,8 @@
return $this->getViewPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
return $this->getEditPolicy();
+ case PhabricatorPolicyCapability::CAN_PUSH:
+ return $this->getPushPolicy();
}
}
Index: src/applications/repository/storage/PhabricatorRepositoryTransaction.php
===================================================================
--- src/applications/repository/storage/PhabricatorRepositoryTransaction.php
+++ src/applications/repository/storage/PhabricatorRepositoryTransaction.php
@@ -3,10 +3,13 @@
final class PhabricatorRepositoryTransaction
extends PhabricatorApplicationTransaction {
- const TYPE_ACTIVATE = 'repo:activate';
- const TYPE_NAME = 'repo:name';
- const TYPE_DESCRIPTION = 'repo:description';
- const TYPE_ENCODING = 'repo:encoding';
+ const TYPE_ACTIVATE = 'repo:activate';
+ const TYPE_NAME = 'repo:name';
+ const TYPE_DESCRIPTION = 'repo:description';
+ const TYPE_ENCODING = 'repo:encoding';
+ const TYPE_HOSTING = 'repo:hosting';
+ const TYPE_PROTOCOL_HTTP = 'repo:http';
+ const TYPE_PROTOCOL_SSH = 'repo:ssh';
public function getApplicationName() {
return 'repository';
@@ -47,6 +50,18 @@
return pht(
'%s updated the description of this repository.',
$this->renderHandleLink($author_phid));
+ case self::TYPE_HOSTING:
+ return pht(
+ '%s updated the hosting configuration of this repository.',
+ $this->renderHandleLink($author_phid));
+ case self::TYPE_PROTOCOL_HTTP:
+ return pht(
+ '%s updated the HTTP protocol configuration of this repository.',
+ $this->renderHandleLink($author_phid));
+ case self::TYPE_PROTOCOL_SSH:
+ return pht(
+ '%s updated the SSH protocol configuration of this repository.',
+ $this->renderHandleLink($author_phid));
case self::TYPE_ENCODING:
if (strlen($old) && !strlen($new)) {
return pht(
Index: src/applications/settings/panel/PhabricatorSettingsPanelVCSPassword.php
===================================================================
--- /dev/null
+++ src/applications/settings/panel/PhabricatorSettingsPanelVCSPassword.php
@@ -0,0 +1,102 @@
+<?php
+
+final class PhabricatorSettingsPanelVCSPassword
+ extends PhabricatorSettingsPanel {
+
+ public function getPanelKey() {
+ return 'vcspassword';
+ }
+
+ public function getPanelName() {
+ return pht('Version Control');
+ }
+
+ public function getPanelGroup() {
+ return pht('Authentication');
+ }
+
+ public function processRequest(AphrontRequest $request) {
+ $user = $request->getUser();
+
+ if ($request->isFormPost()) {
+ if (!$request->isDialogFormPost()) {
+ $dialog = new AphrontDialogView();
+ $dialog->setUser($user);
+ $dialog->setTitle(pht('Really regenerate password?'));
+ $dialog->setSubmitURI($this->getPanelURI());
+ $dialog->addSubmitButton(pht('Regenerate'));
+ $dialog->addCancelbutton($this->getPanelURI());
+ $dialog->appendChild(phutil_tag('p', array(), pht(
+ 'Really destroy the old password? Any existing '.
+ 'version control configurations will need to be '.
+ 'updated.')));
+
+ return id(new AphrontDialogResponse())
+ ->setDialog($dialog);
+ }
+
+ // This implicitly regenerates the version control password.
+ $user->setVCSPassword(null);
+ $user->save();
+ return id(new AphrontRedirectResponse())
+ ->setURI($this->getPanelURI('?regenerated=true'));
+ }
+
+ if ($request->getStr('regenerated')) {
+ $notice = new AphrontErrorView();
+ $notice->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
+ $notice->setTitle(pht('Version Control Password Regenerated'));
+ $notice->appendChild(phutil_tag(
+ 'p',
+ array(),
+ pht('Your old password has been destroyed and you have been issued '.
+ 'a new password. Update your version control client if needed.')));
+ $notice = $notice->render();
+ } else {
+ $notice = null;
+ }
+
+ $cert_form = new AphrontFormView();
+ $cert_form
+ ->setUser($user)
+ ->appendChild(hsprintf(
+ '<p class="aphront-form-instructions">%s</p>',
+ pht(
+ 'This password is used to contribute to Phabricator '.
+ 'hosted repositories. Instead of using your normal password '.
+ 'when connecting to a host repository, provide the password '.
+ 'below.')))
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setLabel(pht('Password'))
+ ->setValue($user->getVCSPassword()));
+
+ $cert_form = id(new PHUIObjectBoxView())
+ ->setHeaderText(pht('Version Control Password'))
+ ->setForm($cert_form);
+
+ $regen_instruction = pht('You can regenerate this password, which '.
+ 'will invalidate the old password and issue a new one.');
+
+ $regen_form = new AphrontFormView();
+ $regen_form
+ ->setUser($user)
+ ->setAction($this->getPanelURI())
+ ->setWorkflow(true)
+ ->appendChild(hsprintf(
+ '<p class="aphront-form-instructions">%s</p>', $regen_instruction))
+ ->appendChild(
+ id(new AphrontFormSubmitControl())
+ ->setValue(pht('Regenerate Password')));
+
+ $regen_form = id(new PHUIObjectBoxView())
+ ->setHeaderText(pht('Regenerate Password'))
+ ->setForm($regen_form);
+
+ return array(
+ $notice,
+ $cert_form,
+ $regen_form,
+ );
+ }
+}
Index: src/applications/transactions/constants/PhabricatorTransactions.php
===================================================================
--- src/applications/transactions/constants/PhabricatorTransactions.php
+++ src/applications/transactions/constants/PhabricatorTransactions.php
@@ -7,6 +7,7 @@
const TYPE_VIEW_POLICY = 'core:view-policy';
const TYPE_EDIT_POLICY = 'core:edit-policy';
const TYPE_JOIN_POLICY = 'core:join-policy';
+ const TYPE_PUSH_POLICY = 'repo:push-policy';
const TYPE_EDGE = 'core:edge';
const TYPE_CUSTOMFIELD = 'core:customfield';
Index: src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
===================================================================
--- src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
+++ src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
@@ -139,6 +139,8 @@
return $object->getViewPolicy();
case PhabricatorTransactions::TYPE_EDIT_POLICY:
return $object->getEditPolicy();
+ case PhabricatorTransactions::TYPE_PUSH_POLICY:
+ return $object->getPushPolicy();
case PhabricatorTransactions::TYPE_EDGE:
$edge_type = $xaction->getMetadataValue('edge:type');
if (!$edge_type) {
@@ -175,6 +177,7 @@
return $this->getPHIDTransactionNewValue($xaction);
case PhabricatorTransactions::TYPE_VIEW_POLICY:
case PhabricatorTransactions::TYPE_EDIT_POLICY:
+ case PhabricatorTransactions::TYPE_PUSH_POLICY:
return $xaction->getNewValue();
case PhabricatorTransactions::TYPE_EDGE:
return $this->getEdgeTransactionNewValue($xaction);
@@ -235,6 +238,9 @@
case PhabricatorTransactions::TYPE_EDIT_POLICY:
$object->setEditPolicy($xaction->getNewValue());
break;
+ case PhabricatorTransactions::TYPE_PUSH_POLICY:
+ $object->setPushPolicy($xaction->getNewValue());
+ break;
case PhabricatorTransactions::TYPE_CUSTOMFIELD:
$field = $this->getCustomFieldForTransaction($object, $xaction);
return $field->applyApplicationTransactionInternalEffects($xaction);
Index: src/applications/transactions/storage/PhabricatorApplicationTransaction.php
===================================================================
--- src/applications/transactions/storage/PhabricatorApplicationTransaction.php
+++ src/applications/transactions/storage/PhabricatorApplicationTransaction.php
@@ -149,6 +149,7 @@
case PhabricatorTransactions::TYPE_EDIT_POLICY:
case PhabricatorTransactions::TYPE_VIEW_POLICY:
case PhabricatorTransactions::TYPE_JOIN_POLICY:
+ case PhabricatorTransactions::TYPE_PUSH_POLICY:
if (!PhabricatorPolicyQuery::isGlobalPolicy($old)) {
$phids[] = array($old);
}
@@ -228,6 +229,7 @@
case PhabricatorTransactions::TYPE_VIEW_POLICY:
case PhabricatorTransactions::TYPE_EDIT_POLICY:
case PhabricatorTransactions::TYPE_JOIN_POLICY:
+ case PhabricatorTransactions::TYPE_PUSH_POLICY:
return 'lock';
case PhabricatorTransactions::TYPE_EDGE:
return 'link';
@@ -245,6 +247,7 @@
case PhabricatorTransactions::TYPE_VIEW_POLICY:
case PhabricatorTransactions::TYPE_EDIT_POLICY:
case PhabricatorTransactions::TYPE_JOIN_POLICY:
+ case PhabricatorTransactions::TYPE_PUSH_POLICY:
if ($this->getOldValue() === null) {
return true;
} else {
@@ -277,6 +280,10 @@
return pht(
'This %s already has that join policy.',
$this->getApplicationObjectTypeName());
+ case PhabricatorTransactions::TYPE_PUSH_POLICY:
+ return pht(
+ 'This %s already has that push policy.',
+ $this->getApplicationObjectTypeName());
case PhabricatorTransactions::TYPE_SUBSCRIBERS:
return pht(
'All users are already subscribed to this %s.',
@@ -320,6 +327,13 @@
$this->getApplicationObjectTypeName(),
$this->renderPolicyName($old),
$this->renderPolicyName($new));
+ case PhabricatorTransactions::TYPE_PUSH_POLICY:
+ return pht(
+ '%s changed the push policy of this %s from "%s" to "%s".',
+ $this->renderHandleLink($author_phid),
+ $this->getApplicationObjectTypeName(),
+ $this->renderPolicyName($old),
+ $this->renderPolicyName($new));
case PhabricatorTransactions::TYPE_SUBSCRIBERS:
$add = array_diff($new, $old);
$rem = array_diff($old, $new);
@@ -439,6 +453,11 @@
'%s changed the join policy for %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
+ case PhabricatorTransactions::TYPE_PUSH_POLICY:
+ return pht(
+ '%s changed the push policy for %s.',
+ $this->renderHandleLink($author_phid),
+ $this->renderHandleLink($object_phid));
case PhabricatorTransactions::TYPE_SUBSCRIBERS:
return pht(
'%s updated subscribers of %s.',
@@ -507,6 +526,7 @@
case PhabricatorTransactions::TYPE_VIEW_POLICY:
case PhabricatorTransactions::TYPE_EDIT_POLICY:
case PhabricatorTransactions::TYPE_JOIN_POLICY:
+ case PhabricatorTransactions::TYPE_PUSH_POLICY:
return pht('Changed Policy');
case PhabricatorTransactions::TYPE_SUBSCRIBERS:
return pht('Changed Subscribers');
Index: src/infrastructure/git/GitHTTPServer.php
===================================================================
--- /dev/null
+++ src/infrastructure/git/GitHTTPServer.php
@@ -0,0 +1,53 @@
+<?php
+
+/**
+ * @phutil-external-symbol class PhabricatorStartup
+ */
+final class GitHTTPServer {
+
+ private $path;
+ private $projectRoot;
+
+ public function __construct($path = null) {
+ $this->path = $path;
+ if ($this->path === null) {
+ $this->path = "/usr/lib/git/git-http-backend";
+ }
+ }
+
+ public function setProjectRoot($root) {
+ $this->projectRoot = $root;
+ }
+
+ /**
+ * Accepts an Aphront request and returns the
+ * HTTP result from git-http-backend.
+ */
+ public function acceptRequest($url) {
+ // Set up environment and pipes.
+ $env = array(
+ 'REQUEST_METHOD' => $_SERVER['REQUEST_METHOD'],
+ 'QUERY_STRING' => $_SERVER['QUERY_STRING'],
+ 'CONTENT_TYPE' => $_SERVER['CONTENT_TYPE'],
+ 'REMOTE_USER' => "",
+ 'REMOTE_ADDR' => $_SERVER['REMOTE_ADDR'],
+ 'GIT_PROJECT_ROOT' => $this->projectRoot,
+ 'GIT_HTTP_EXPORT_ALL' => '1',
+ 'PATH_INFO' => $url);
+ $pwd = getcwd();
+
+ // Execute git-http-backend with ExecFuture.
+ list($err, $stdout) = id(new ExecFuture('%s', $this->path))
+ ->setCWD($pwd)
+ ->setEnv($env, true)
+ ->write(PhabricatorStartup::getRawInput())
+ ->resolve();
+
+ // Returns the output of git-http-backend, which
+ // is the direct HTTP response we should return (including
+ // HTTP headers).
+ return $stdout;
+ }
+
+}
+
Index: src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
===================================================================
--- src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
+++ src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
@@ -1704,6 +1704,14 @@
'type' => 'sql',
'name' => $this->getPatchPath('20131020.harbormaster.sql'),
),
+ '20131024.diffusionhost.sql' => array(
+ 'type' => 'sql',
+ 'name' => $this->getPatchPath('20131024.diffusionhost.sql'),
+ ),
+ '20131024.vcspassword.sql' => array(
+ 'type' => 'sql',
+ 'name' => $this->getPatchPath('20131024.vcspassword.sql'),
+ ),
);
}
}
Index: src/view/form/control/AphrontFormPolicyControl.php
===================================================================
--- src/view/form/control/AphrontFormPolicyControl.php
+++ src/view/form/control/AphrontFormPolicyControl.php
@@ -24,6 +24,7 @@
PhabricatorPolicyCapability::CAN_VIEW => pht('Visible To'),
PhabricatorPolicyCapability::CAN_EDIT => pht('Editable By'),
PhabricatorPolicyCapability::CAN_JOIN => pht('Joinable By'),
+ PhabricatorPolicyCapability::CAN_PUSH => pht('Pushable By'),
);
$this->setLabel(idx($labels, $this->capability, pht('Unknown Policy')));

File Metadata

Mime Type
text/plain
Expires
Tue, Nov 19, 5:23 AM (19 h, 47 m)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/w5/fq/6j6fjgcnxtuyb2lc
Default Alt Text
D7391.id16701.diff (53 KB)

Event Timeline