Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14387524
D7391.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
53 KB
Referenced Files
None
Subscribers
None
D7391.diff
View Options
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
Details
Attached
Mime Type
text/plain
Expires
Sun, Dec 22, 2:49 PM (19 h, 48 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6918627
Default Alt Text
D7391.diff (53 KB)
Attached To
Mode
D7391: Implemented hosted Git repositories in Phabricator over Smart HTTP.
Attached
Detach File
Event Timeline
Log In to Comment