1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-02-13 15:28:35 +01:00

(stable) Promote 2016 Week 20

This commit is contained in:
epriestley 2016-05-13 15:51:17 -07:00
commit 90c06115e5
157 changed files with 5396 additions and 5956 deletions

View file

@ -7,12 +7,12 @@
*/
return array(
'names' => array(
'core.pkg.css' => 'b729f9f5',
'core.pkg.css' => 'b7b8d101',
'core.pkg.js' => '6972d365',
'darkconsole.pkg.js' => 'e7393ebb',
'differential.pkg.css' => '7ba78475',
'differential.pkg.js' => 'd0cd0df6',
'diffusion.pkg.css' => 'dc8e0cc2',
'diffusion.pkg.css' => '91c5d3a6',
'diffusion.pkg.js' => '3a9a8bfa',
'maniphest.pkg.css' => '4845691a',
'maniphest.pkg.js' => '949a7498',
@ -52,7 +52,7 @@ return array(
'rsrc/css/application/conpherence/update.css' => 'faf6be09',
'rsrc/css/application/conpherence/widget-pane.css' => '775eaaba',
'rsrc/css/application/contentsource/content-source-view.css' => '4b8b05d4',
'rsrc/css/application/countdown/timer.css' => '96696f21',
'rsrc/css/application/countdown/timer.css' => '16c52f5c',
'rsrc/css/application/daemon/bulk-job.css' => 'df9c1d4a',
'rsrc/css/application/dashboard/dashboard.css' => 'bc6f2127',
'rsrc/css/application/diff/inline-comment-summary.css' => '51efda3a',
@ -64,7 +64,7 @@ return array(
'rsrc/css/application/differential/revision-history.css' => '0e8eb855',
'rsrc/css/application/differential/revision-list.css' => 'f3c47d33',
'rsrc/css/application/differential/table-of-contents.css' => 'ae4b7a55',
'rsrc/css/application/diffusion/diffusion-icons.css' => '3311444d',
'rsrc/css/application/diffusion/diffusion-icons.css' => 'd678600a',
'rsrc/css/application/diffusion/diffusion-readme.css' => '297373eb',
'rsrc/css/application/diffusion/diffusion-source.css' => '68b30fd3',
'rsrc/css/application/feed/feed.css' => 'ecd4ec57',
@ -104,8 +104,8 @@ return array(
'rsrc/css/application/tokens/tokens.css' => '3d0f239e',
'rsrc/css/application/uiexample/example.css' => '528b19de',
'rsrc/css/core/core.css' => 'd0801452',
'rsrc/css/core/remarkup.css' => '6aae5360',
'rsrc/css/core/syntax.css' => '9fd11da8',
'rsrc/css/core/remarkup.css' => '787105d6',
'rsrc/css/core/syntax.css' => '5101175d',
'rsrc/css/core/z-index.css' => '5b6fcf3f',
'rsrc/css/diviner/diviner-shared.css' => 'aa3656aa',
'rsrc/css/font/font-aleo.css' => '8bdb2835',
@ -113,7 +113,7 @@ return array(
'rsrc/css/font/font-lato.css' => 'c7ccd872',
'rsrc/css/font/phui-font-icon-base.css' => '6449bce8',
'rsrc/css/layout/phabricator-filetree-view.css' => 'fccf9f82',
'rsrc/css/layout/phabricator-side-menu-view.css' => '3a3d9f41',
'rsrc/css/layout/phabricator-side-menu-view.css' => 'dd849797',
'rsrc/css/layout/phabricator-source-code-view.css' => 'cbeef983',
'rsrc/css/phui/calendar/phui-calendar-day.css' => 'd1cf6f93',
'rsrc/css/phui/calendar/phui-calendar-list.css' => 'c1c7f338',
@ -123,15 +123,15 @@ return array(
'rsrc/css/phui/phui-action-panel.css' => '91c7b835',
'rsrc/css/phui/phui-badge.css' => '3baef8db',
'rsrc/css/phui/phui-big-info-view.css' => 'bd903741',
'rsrc/css/phui/phui-box.css' => 'd909ea3d',
'rsrc/css/phui/phui-box.css' => '5c8387cf',
'rsrc/css/phui/phui-button.css' => 'a64a8de6',
'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
'rsrc/css/phui/phui-crumbs-view.css' => '1a1265d4',
'rsrc/css/phui/phui-curtain-view.css' => '7148ae25',
'rsrc/css/phui/phui-document-pro.css' => '73e45fd2',
'rsrc/css/phui/phui-document-pro.css' => '8419560b',
'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf',
'rsrc/css/phui/phui-document.css' => '9c71d2bf',
'rsrc/css/phui/phui-feed-story.css' => 'd8440402',
'rsrc/css/phui/phui-document.css' => '715aedfb',
'rsrc/css/phui/phui-feed-story.css' => 'aa49845d',
'rsrc/css/phui/phui-fontkit.css' => '9cda225e',
'rsrc/css/phui/phui-form-view.css' => '6a51768e',
'rsrc/css/phui/phui-form.css' => 'aac1d51d',
@ -153,7 +153,7 @@ return array(
'rsrc/css/phui/phui-remarkup-preview.css' => '1a8f2591',
'rsrc/css/phui/phui-segment-bar-view.css' => '46342871',
'rsrc/css/phui/phui-spacing.css' => '042804d6',
'rsrc/css/phui/phui-status.css' => '37309046',
'rsrc/css/phui/phui-status.css' => 'd5263e49',
'rsrc/css/phui/phui-tag-view.css' => '6bbd83e2',
'rsrc/css/phui/phui-timeline-view.css' => '6e342216',
'rsrc/css/phui/phui-two-column-view.css' => 'b9538af1',
@ -164,6 +164,7 @@ return array(
'rsrc/css/sprite-login.css' => '60e8560e',
'rsrc/css/sprite-menu.css' => '9dd65b92',
'rsrc/css/sprite-tokens.css' => '4f399012',
'rsrc/css/syntax/syntax-default.css' => '9923583c',
'rsrc/externals/d3/d3.min.js' => 'a11a5ff2',
'rsrc/externals/font/aleo/aleo-bold.eot' => 'd3d3bed7',
'rsrc/externals/font/aleo/aleo-bold.svg' => '45899c8e',
@ -503,6 +504,7 @@ return array(
'rsrc/js/core/behavior-reveal-content.js' => '60821bc7',
'rsrc/js/core/behavior-scrollbar.js' => '834a1173',
'rsrc/js/core/behavior-search-typeahead.js' => '06c32383',
'rsrc/js/core/behavior-select-content.js' => 'bf5374ef',
'rsrc/js/core/behavior-select-on-click.js' => '4e3e79a6',
'rsrc/js/core/behavior-time-typeahead.js' => '522431f7',
'rsrc/js/core/behavior-toggle-class.js' => '92b9ec77',
@ -556,7 +558,7 @@ return array(
'differential-revision-history-css' => '0e8eb855',
'differential-revision-list-css' => 'f3c47d33',
'differential-table-of-contents-css' => 'ae4b7a55',
'diffusion-icons-css' => '3311444d',
'diffusion-icons-css' => 'd678600a',
'diffusion-readme-css' => '297373eb',
'diffusion-source-css' => '68b30fd3',
'diviner-shared-css' => 'aa3656aa',
@ -684,6 +686,7 @@ return array(
'javelin-behavior-repository-crossreference' => 'e5339c43',
'javelin-behavior-scrollbar' => '834a1173',
'javelin-behavior-search-reorder-queries' => 'e9581f08',
'javelin-behavior-select-content' => 'bf5374ef',
'javelin-behavior-select-on-click' => '4e3e79a6',
'javelin-behavior-slowvote-embed' => '887ad43f',
'javelin-behavior-stripe-payment-form' => '3f5d6dbf',
@ -758,7 +761,7 @@ return array(
'phabricator-chatlog-css' => 'd295b020',
'phabricator-content-source-view-css' => '4b8b05d4',
'phabricator-core-css' => 'd0801452',
'phabricator-countdown-css' => '96696f21',
'phabricator-countdown-css' => '16c52f5c',
'phabricator-dashboard-css' => 'bc6f2127',
'phabricator-drag-and-drop-file-upload' => '81f182b5',
'phabricator-draggable-list' => '5a13c79f',
@ -777,10 +780,10 @@ return array(
'phabricator-object-selector-css' => '85ee8ce6',
'phabricator-phtize' => 'd254d646',
'phabricator-prefab' => 'e67df814',
'phabricator-remarkup-css' => '6aae5360',
'phabricator-remarkup-css' => '787105d6',
'phabricator-search-results-css' => '7dea472c',
'phabricator-shaped-request' => '7cbe244b',
'phabricator-side-menu-view-css' => '3a3d9f41',
'phabricator-side-menu-view-css' => 'dd849797',
'phabricator-slowvote-css' => 'a94b7230',
'phabricator-source-code-view-css' => 'cbeef983',
'phabricator-standard-page-view' => 'e709f6d0',
@ -811,7 +814,7 @@ return array(
'phui-action-panel-css' => '91c7b835',
'phui-badge-view-css' => '3baef8db',
'phui-big-info-view-css' => 'bd903741',
'phui-box-css' => 'd909ea3d',
'phui-box-css' => '5c8387cf',
'phui-button-css' => 'a64a8de6',
'phui-calendar-css' => 'ccabe893',
'phui-calendar-day-css' => 'd1cf6f93',
@ -821,9 +824,9 @@ return array(
'phui-crumbs-view-css' => '1a1265d4',
'phui-curtain-view-css' => '7148ae25',
'phui-document-summary-view-css' => '9ca48bdf',
'phui-document-view-css' => '9c71d2bf',
'phui-document-view-pro-css' => '73e45fd2',
'phui-feed-story-css' => 'd8440402',
'phui-document-view-css' => '715aedfb',
'phui-document-view-pro-css' => '8419560b',
'phui-feed-story-css' => 'aa49845d',
'phui-font-icon-base-css' => '6449bce8',
'phui-fontkit-css' => '9cda225e',
'phui-form-css' => 'aac1d51d',
@ -848,7 +851,7 @@ return array(
'phui-remarkup-preview-css' => '1a8f2591',
'phui-segment-bar-view-css' => '46342871',
'phui-spacing-css' => '042804d6',
'phui-status-list-view-css' => '37309046',
'phui-status-list-view-css' => 'd5263e49',
'phui-tag-view-css' => '6bbd83e2',
'phui-theme-css' => '027ba77e',
'phui-timeline-view-css' => '6e342216',
@ -877,7 +880,8 @@ return array(
'sprite-login-css' => '60e8560e',
'sprite-menu-css' => '9dd65b92',
'sprite-tokens-css' => '4f399012',
'syntax-highlighting-css' => '9fd11da8',
'syntax-default-css' => '9923583c',
'syntax-highlighting-css' => '5101175d',
'tokens-css' => '3d0f239e',
'typeahead-browse-css' => 'd8581d2c',
'unhandled-exception-css' => '4c96257a',
@ -1239,6 +1243,9 @@ return array(
'javelin-typeahead-source',
'javelin-util',
),
'5101175d' => array(
'syntax-default-css',
),
'519705ea' => array(
'javelin-install',
'javelin-dom',
@ -1832,6 +1839,11 @@ return array(
'javelin-util',
'javelin-request',
),
'bf5374ef' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'bff6884b' => array(
'javelin-install',
'javelin-dom',
@ -2192,6 +2204,7 @@ return array(
'aphront-list-filter-view-css',
'phabricator-remarkup-css',
'syntax-highlighting-css',
'syntax-default-css',
'phui-pager-css',
'aphront-tooltip-css',
'phabricator-flag-css',

View file

@ -97,6 +97,7 @@ return array(
'phabricator-remarkup-css',
'syntax-highlighting-css',
'syntax-default-css',
'phui-pager-css',
'aphront-tooltip-css',
'phabricator-flag-css',

View file

@ -1,7 +1,4 @@
<?php
$table = new PhabricatorRepository();
foreach (new LiskMigrationIterator($table) as $repo) {
$repo->updateURIIndex();
}
// A later patch ("20160510.repo.01.uriindex.php") performs an identical
// regeneration of the index, so we no longer need to do it here.

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_repository.repository
ADD localPath VARCHAR(128) COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_repository.repository
ADD UNIQUE KEY `key_local` (localPath);

View file

@ -0,0 +1,57 @@
<?php
$table = new PhabricatorRepository();
$conn_w = $table->establishConnection('w');
$default_path = PhabricatorEnv::getEnvConfig('repository.default-local-path');
$default_path = rtrim($default_path, '/');
foreach (new LiskMigrationIterator($table) as $repository) {
$local_path = $repository->getLocalPath();
if (strlen($local_path)) {
// Repository already has a modern, unique local path.
continue;
}
$local_path = $repository->getDetail('local-path');
if (!strlen($local_path)) {
// Repository does not have a local path using the older format.
continue;
}
$random = Filesystem::readRandomCharacters(8);
// Try the configured path first, then a default path, then a path with some
// random noise.
$paths = array(
$local_path,
$default_path.'/'.$repository->getID().'/',
$default_path.'/'.$repository->getID().'-'.$random.'/',
);
foreach ($paths as $path) {
// Set, then get the path to normalize it.
$repository->setLocalPath($path);
$path = $repository->getLocalPath();
try {
queryfx(
$conn_w,
'UPDATE %T SET localPath = %s WHERE id = %d',
$table->getTableName(),
$path,
$repository->getID());
echo tsprintf(
"%s\n",
pht(
'Assigned repository "%s" to local path "%s".',
$repository->getDisplayName(),
$path));
break;
} catch (AphrontDuplicateKeyQueryException $ex) {
// Ignore, try the next one.
}
}
}

View file

@ -0,0 +1,38 @@
<?php
$table = new PhabricatorRepository();
$conn_w = $table->establishConnection('w');
$mirrors = queryfx_all(
$conn_w,
'SELECT * FROM %T',
'repository_mirror');
foreach ($mirrors as $mirror) {
$repository_phid = $mirror['repositoryPHID'];
$uri = $mirror['remoteURI'];
$already_exists = id(new PhabricatorRepositoryURI())->loadOneWhere(
'repositoryPHID = %s AND uri = %s',
$repository_phid,
$uri);
if ($already_exists) {
// Decline to migrate stuff that looks like it was already migrated.
continue;
}
$new_uri = PhabricatorRepositoryURI::initializeNewURI()
->setIOType(PhabricatorRepositoryURI::IO_MIRROR)
->setRepositoryPHID($repository_phid)
->setURI($uri)
->setCredentialPHID($mirror['credentialPHID'])
->setDateCreated($mirror['dateCreated'])
->setDateModified($mirror['dateModified'])
->save();
echo tsprintf(
"%s\n",
pht(
'Migrated mirror "%s".',
$uri));
}

View file

@ -0,0 +1,82 @@
<?php
$table = new PhabricatorRepository();
$conn_w = $table->establishConnection('w');
foreach (new LiskMigrationIterator($table) as $repository) {
$uris = array();
$serve_http = $repository->getDetail('serve-over-http');
$http_io = PhabricatorRepositoryURI::IO_DEFAULT;
$disable_http = false;
switch ($serve_http) {
case 'readwrite':
break;
case 'readonly':
$http_io = PhabricatorRepositoryURI::IO_READ;
break;
case 'off':
default:
$disable_http = true;
break;
}
$serve_ssh = $repository->getDetail('serve-over-ssh');
$ssh_io = PhabricatorRepositoryURI::IO_DEFAULT;
$disable_ssh = false;
switch ($serve_ssh) {
case 'readwrite':
break;
case 'readonly':
$ssh_io = PhabricatorRepositoryURI::IO_READ;
break;
case 'off':
default:
$disable_ssh = true;
break;
}
$uris = $repository->newBuiltinURIs();
foreach ($uris as $uri) {
$builtin_protocol = $uri->getBuiltinProtocol();
if ($builtin_protocol == PhabricatorRepositoryURI::BUILTIN_PROTOCOL_SSH) {
$uri->setIsDisabled((int)$disable_ssh);
$uri->setIoType($ssh_io);
} else {
$uri->setIsDisabled((int)$disable_http);
$uri->setIoType($http_io);
}
}
if (!$repository->isHosted()) {
$remote_uri = $repository->getDetail('remote-uri');
if (strlen($remote_uri)) {
$uris[] = PhabricatorRepositoryURI::initializeNewURI()
->setRepositoryPHID($repository->getPHID())
->attachRepository($repository)
->setURI($remote_uri)
->setCredentialPHID($repository->getCredentialPHID())
->setIOType(PhabricatorRepositoryURI::IO_OBSERVE);
}
}
foreach ($uris as $uri) {
$already_exists = id(new PhabricatorRepositoryURI())->loadOneWhere(
'repositoryPHID = %s AND uri = %s LIMIT 1',
$repository->getPHID(),
$uri->getURI());
if ($already_exists) {
continue;
}
$uri->save();
echo tsprintf(
"%s\n",
pht(
'Migrated URI "%s" for repository "%s".',
$uri->getURI(),
$repository->getDisplayName()));
}
}

View file

@ -0,0 +1,10 @@
<?php
$repos = id(new PhabricatorRepositoryQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->needURIs(true)
->execute();
foreach ($repos as $repo) {
$repo->updateURIIndex();
}

View file

@ -226,6 +226,7 @@ phutil_register_library_map(array(
'CelerityHighContrastPostprocessor' => 'applications/celerity/postprocessor/CelerityHighContrastPostprocessor.php',
'CelerityLargeFontPostprocessor' => 'applications/celerity/postprocessor/CelerityLargeFontPostprocessor.php',
'CelerityManagementMapWorkflow' => 'applications/celerity/management/CelerityManagementMapWorkflow.php',
'CelerityManagementSyntaxWorkflow' => 'applications/celerity/management/CelerityManagementSyntaxWorkflow.php',
'CelerityManagementWorkflow' => 'applications/celerity/management/CelerityManagementWorkflow.php',
'CelerityPhabricatorResourceController' => 'applications/celerity/controller/CelerityPhabricatorResourceController.php',
'CelerityPhabricatorResources' => 'applications/celerity/resources/CelerityPhabricatorResources.php',
@ -361,6 +362,7 @@ phutil_register_library_map(array(
'DifferentialBlameRevisionField' => 'applications/differential/customfield/DifferentialBlameRevisionField.php',
'DifferentialBlockHeraldAction' => 'applications/differential/herald/DifferentialBlockHeraldAction.php',
'DifferentialBranchField' => 'applications/differential/customfield/DifferentialBranchField.php',
'DifferentialChangeDetailMailView' => 'applications/differential/mail/DifferentialChangeDetailMailView.php',
'DifferentialChangeHeraldFieldGroup' => 'applications/differential/herald/DifferentialChangeHeraldFieldGroup.php',
'DifferentialChangeType' => 'applications/differential/constants/DifferentialChangeType.php',
'DifferentialChangesSinceLastUpdateField' => 'applications/differential/customfield/DifferentialChangesSinceLastUpdateField.php',
@ -369,6 +371,7 @@ phutil_register_library_map(array(
'DifferentialChangesetFileTreeSideNavBuilder' => 'applications/differential/view/DifferentialChangesetFileTreeSideNavBuilder.php',
'DifferentialChangesetHTMLRenderer' => 'applications/differential/render/DifferentialChangesetHTMLRenderer.php',
'DifferentialChangesetListView' => 'applications/differential/view/DifferentialChangesetListView.php',
'DifferentialChangesetOneUpMailRenderer' => 'applications/differential/render/DifferentialChangesetOneUpMailRenderer.php',
'DifferentialChangesetOneUpRenderer' => 'applications/differential/render/DifferentialChangesetOneUpRenderer.php',
'DifferentialChangesetOneUpTestRenderer' => 'applications/differential/render/DifferentialChangesetOneUpTestRenderer.php',
'DifferentialChangesetParser' => 'applications/differential/parser/DifferentialChangesetParser.php',
@ -458,6 +461,7 @@ phutil_register_library_map(array(
'DifferentialHunkTestCase' => 'applications/differential/storage/__tests__/DifferentialHunkTestCase.php',
'DifferentialInlineComment' => 'applications/differential/storage/DifferentialInlineComment.php',
'DifferentialInlineCommentEditController' => 'applications/differential/controller/DifferentialInlineCommentEditController.php',
'DifferentialInlineCommentMailView' => 'applications/differential/mail/DifferentialInlineCommentMailView.php',
'DifferentialInlineCommentPreviewController' => 'applications/differential/controller/DifferentialInlineCommentPreviewController.php',
'DifferentialInlineCommentQuery' => 'applications/differential/query/DifferentialInlineCommentQuery.php',
'DifferentialJIRAIssuesField' => 'applications/differential/customfield/DifferentialJIRAIssuesField.php',
@ -468,6 +472,7 @@ phutil_register_library_map(array(
'DifferentialLintField' => 'applications/differential/customfield/DifferentialLintField.php',
'DifferentialLintStatus' => 'applications/differential/constants/DifferentialLintStatus.php',
'DifferentialLocalCommitsView' => 'applications/differential/view/DifferentialLocalCommitsView.php',
'DifferentialMailView' => 'applications/differential/mail/DifferentialMailView.php',
'DifferentialManiphestTasksField' => 'applications/differential/customfield/DifferentialManiphestTasksField.php',
'DifferentialModernHunk' => 'applications/differential/storage/DifferentialModernHunk.php',
'DifferentialNextStepField' => 'applications/differential/customfield/DifferentialNextStepField.php',
@ -570,6 +575,7 @@ phutil_register_library_map(array(
'DiffusionCachedResolveRefsQuery' => 'applications/diffusion/query/DiffusionCachedResolveRefsQuery.php',
'DiffusionChangeController' => 'applications/diffusion/controller/DiffusionChangeController.php',
'DiffusionChangeHeraldFieldGroup' => 'applications/diffusion/herald/DiffusionChangeHeraldFieldGroup.php',
'DiffusionCloneURIView' => 'applications/diffusion/view/DiffusionCloneURIView.php',
'DiffusionCommandEngine' => 'applications/diffusion/protocol/DiffusionCommandEngine.php',
'DiffusionCommandEngineTestCase' => 'applications/diffusion/protocol/__tests__/DiffusionCommandEngineTestCase.php',
'DiffusionCommitAffectedFilesHeraldField' => 'applications/diffusion/herald/DiffusionCommitAffectedFilesHeraldField.php',
@ -616,6 +622,7 @@ phutil_register_library_map(array(
'DiffusionController' => 'applications/diffusion/controller/DiffusionController.php',
'DiffusionCreateCommentConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionCreateCommentConduitAPIMethod.php',
'DiffusionCreateRepositoriesCapability' => 'applications/diffusion/capability/DiffusionCreateRepositoriesCapability.php',
'DiffusionDaemonLockException' => 'applications/diffusion/exception/DiffusionDaemonLockException.php',
'DiffusionDefaultEditCapability' => 'applications/diffusion/capability/DiffusionDefaultEditCapability.php',
'DiffusionDefaultPushCapability' => 'applications/diffusion/capability/DiffusionDefaultPushCapability.php',
'DiffusionDefaultViewCapability' => 'applications/diffusion/capability/DiffusionDefaultViewCapability.php',
@ -659,6 +666,7 @@ phutil_register_library_map(array(
'DiffusionLintController' => 'applications/diffusion/controller/DiffusionLintController.php',
'DiffusionLintCountQuery' => 'applications/diffusion/query/DiffusionLintCountQuery.php',
'DiffusionLintSaveRunner' => 'applications/diffusion/DiffusionLintSaveRunner.php',
'DiffusionLocalRepositoryFilter' => 'applications/diffusion/data/DiffusionLocalRepositoryFilter.php',
'DiffusionLookSoonConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionLookSoonConduitAPIMethod.php',
'DiffusionLowLevelCommitFieldsQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelCommitFieldsQuery.php',
'DiffusionLowLevelCommitQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelCommitQuery.php',
@ -682,8 +690,6 @@ phutil_register_library_map(array(
'DiffusionMercurialWireProtocolTests' => 'applications/diffusion/protocol/__tests__/DiffusionMercurialWireProtocolTests.php',
'DiffusionMercurialWireSSHTestCase' => 'applications/diffusion/ssh/__tests__/DiffusionMercurialWireSSHTestCase.php',
'DiffusionMergedCommitsQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionMergedCommitsQueryConduitAPIMethod.php',
'DiffusionMirrorDeleteController' => 'applications/diffusion/controller/DiffusionMirrorDeleteController.php',
'DiffusionMirrorEditController' => 'applications/diffusion/controller/DiffusionMirrorEditController.php',
'DiffusionPathChange' => 'applications/diffusion/data/DiffusionPathChange.php',
'DiffusionPathChangeQuery' => 'applications/diffusion/query/pathchange/DiffusionPathChangeQuery.php',
'DiffusionPathCompleteController' => 'applications/diffusion/controller/DiffusionPathCompleteController.php',
@ -751,32 +757,21 @@ phutil_register_library_map(array(
'DiffusionRepositoryClusterEngine' => 'applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php',
'DiffusionRepositoryClusterEngineLogInterface' => 'applications/diffusion/protocol/DiffusionRepositoryClusterEngineLogInterface.php',
'DiffusionRepositoryController' => 'applications/diffusion/controller/DiffusionRepositoryController.php',
'DiffusionRepositoryCreateController' => 'applications/diffusion/controller/DiffusionRepositoryCreateController.php',
'DiffusionRepositoryDatasource' => 'applications/diffusion/typeahead/DiffusionRepositoryDatasource.php',
'DiffusionRepositoryDefaultController' => 'applications/diffusion/controller/DiffusionRepositoryDefaultController.php',
'DiffusionRepositoryEditActionsController' => 'applications/diffusion/controller/DiffusionRepositoryEditActionsController.php',
'DiffusionRepositoryDocumentationManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryDocumentationManagementPanel.php',
'DiffusionRepositoryEditActivateController' => 'applications/diffusion/controller/DiffusionRepositoryEditActivateController.php',
'DiffusionRepositoryEditAutomationController' => 'applications/diffusion/controller/DiffusionRepositoryEditAutomationController.php',
'DiffusionRepositoryEditBasicController' => 'applications/diffusion/controller/DiffusionRepositoryEditBasicController.php',
'DiffusionRepositoryEditBranchesController' => 'applications/diffusion/controller/DiffusionRepositoryEditBranchesController.php',
'DiffusionRepositoryEditConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionRepositoryEditConduitAPIMethod.php',
'DiffusionRepositoryEditController' => 'applications/diffusion/controller/DiffusionRepositoryEditController.php',
'DiffusionRepositoryEditDangerousController' => 'applications/diffusion/controller/DiffusionRepositoryEditDangerousController.php',
'DiffusionRepositoryEditDeleteController' => 'applications/diffusion/controller/DiffusionRepositoryEditDeleteController.php',
'DiffusionRepositoryEditEncodingController' => 'applications/diffusion/controller/DiffusionRepositoryEditEncodingController.php',
'DiffusionRepositoryEditEngine' => 'applications/diffusion/editor/DiffusionRepositoryEditEngine.php',
'DiffusionRepositoryEditHostingController' => 'applications/diffusion/controller/DiffusionRepositoryEditHostingController.php',
'DiffusionRepositoryEditMainController' => 'applications/diffusion/controller/DiffusionRepositoryEditMainController.php',
'DiffusionRepositoryEditStagingController' => 'applications/diffusion/controller/DiffusionRepositoryEditStagingController.php',
'DiffusionRepositoryEditStorageController' => 'applications/diffusion/controller/DiffusionRepositoryEditStorageController.php',
'DiffusionRepositoryEditSubversionController' => 'applications/diffusion/controller/DiffusionRepositoryEditSubversionController.php',
'DiffusionRepositoryEditUpdateController' => 'applications/diffusion/controller/DiffusionRepositoryEditUpdateController.php',
'DiffusionRepositoryEditproController' => 'applications/diffusion/controller/DiffusionRepositoryEditproController.php',
'DiffusionRepositoryHistoryManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryHistoryManagementPanel.php',
'DiffusionRepositoryListController' => 'applications/diffusion/controller/DiffusionRepositoryListController.php',
'DiffusionRepositoryManageController' => 'applications/diffusion/controller/DiffusionRepositoryManageController.php',
'DiffusionRepositoryManagePanelsController' => 'applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php',
'DiffusionRepositoryManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryManagementPanel.php',
'DiffusionRepositoryNewController' => 'applications/diffusion/controller/DiffusionRepositoryNewController.php',
'DiffusionRepositoryPath' => 'applications/diffusion/data/DiffusionRepositoryPath.php',
'DiffusionRepositoryPoliciesManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryPoliciesManagementPanel.php',
'DiffusionRepositoryRef' => 'applications/diffusion/data/DiffusionRepositoryRef.php',
@ -785,10 +780,11 @@ phutil_register_library_map(array(
'DiffusionRepositoryStagingManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryStagingManagementPanel.php',
'DiffusionRepositoryStatusManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryStatusManagementPanel.php',
'DiffusionRepositoryStorageManagementPanel' => 'applications/diffusion/management/DiffusionRepositoryStorageManagementPanel.php',
'DiffusionRepositorySymbolsController' => 'applications/diffusion/controller/DiffusionRepositorySymbolsController.php',
'DiffusionRepositorySubversionManagementPanel' => 'applications/diffusion/management/DiffusionRepositorySubversionManagementPanel.php',
'DiffusionRepositorySymbolsManagementPanel' => 'applications/diffusion/management/DiffusionRepositorySymbolsManagementPanel.php',
'DiffusionRepositoryTag' => 'applications/diffusion/data/DiffusionRepositoryTag.php',
'DiffusionRepositoryTestAutomationController' => 'applications/diffusion/controller/DiffusionRepositoryTestAutomationController.php',
'DiffusionRepositoryURICredentialController' => 'applications/diffusion/controller/DiffusionRepositoryURICredentialController.php',
'DiffusionRepositoryURIDisableController' => 'applications/diffusion/controller/DiffusionRepositoryURIDisableController.php',
'DiffusionRepositoryURIEditController' => 'applications/diffusion/controller/DiffusionRepositoryURIEditController.php',
'DiffusionRepositoryURIViewController' => 'applications/diffusion/controller/DiffusionRepositoryURIViewController.php',
@ -2286,6 +2282,7 @@ phutil_register_library_map(array(
'PhabricatorDateTimeSettingsPanel' => 'applications/settings/panel/PhabricatorDateTimeSettingsPanel.php',
'PhabricatorDebugController' => 'applications/system/controller/PhabricatorDebugController.php',
'PhabricatorDefaultRequestExceptionHandler' => 'aphront/handler/PhabricatorDefaultRequestExceptionHandler.php',
'PhabricatorDefaultSyntaxStyle' => 'infrastructure/syntax/PhabricatorDefaultSyntaxStyle.php',
'PhabricatorDesktopNotificationsSettingsPanel' => 'applications/settings/panel/PhabricatorDesktopNotificationsSettingsPanel.php',
'PhabricatorDestructibleInterface' => 'applications/system/interface/PhabricatorDestructibleInterface.php',
'PhabricatorDestructionEngine' => 'applications/system/engine/PhabricatorDestructionEngine.php',
@ -2357,6 +2354,7 @@ phutil_register_library_map(array(
'PhabricatorEditEngineSelectCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineSelectCommentAction.php',
'PhabricatorEditEngineTokenizerCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineTokenizerCommentAction.php',
'PhabricatorEditField' => 'applications/transactions/editfield/PhabricatorEditField.php',
'PhabricatorEditPage' => 'applications/transactions/editengine/PhabricatorEditPage.php',
'PhabricatorEditType' => 'applications/transactions/edittype/PhabricatorEditType.php',
'PhabricatorEditor' => 'infrastructure/PhabricatorEditor.php',
'PhabricatorElasticFulltextStorageEngine' => 'applications/search/fulltextstorage/PhabricatorElasticFulltextStorageEngine.php',
@ -3188,7 +3186,6 @@ phutil_register_library_map(array(
'PhabricatorRepositoryManagementCacheWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementCacheWorkflow.php',
'PhabricatorRepositoryManagementClusterizeWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementClusterizeWorkflow.php',
'PhabricatorRepositoryManagementDiscoverWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementDiscoverWorkflow.php',
'PhabricatorRepositoryManagementEditWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementEditWorkflow.php',
'PhabricatorRepositoryManagementImportingWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementImportingWorkflow.php',
'PhabricatorRepositoryManagementListPathsWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementListPathsWorkflow.php',
'PhabricatorRepositoryManagementListWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementListWorkflow.php',
@ -3207,8 +3204,6 @@ phutil_register_library_map(array(
'PhabricatorRepositoryMercurialCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/PhabricatorRepositoryMercurialCommitMessageParserWorker.php',
'PhabricatorRepositoryMirror' => 'applications/repository/storage/PhabricatorRepositoryMirror.php',
'PhabricatorRepositoryMirrorEngine' => 'applications/repository/engine/PhabricatorRepositoryMirrorEngine.php',
'PhabricatorRepositoryMirrorPHIDType' => 'applications/repository/phid/PhabricatorRepositoryMirrorPHIDType.php',
'PhabricatorRepositoryMirrorQuery' => 'applications/repository/query/PhabricatorRepositoryMirrorQuery.php',
'PhabricatorRepositoryParsedChange' => 'applications/repository/data/PhabricatorRepositoryParsedChange.php',
'PhabricatorRepositoryPullEngine' => 'applications/repository/engine/PhabricatorRepositoryPullEngine.php',
'PhabricatorRepositoryPullEvent' => 'applications/repository/storage/PhabricatorRepositoryPullEvent.php',
@ -3469,6 +3464,7 @@ phutil_register_library_map(array(
'PhabricatorSupportApplication' => 'applications/support/application/PhabricatorSupportApplication.php',
'PhabricatorSyntaxHighlighter' => 'infrastructure/markup/PhabricatorSyntaxHighlighter.php',
'PhabricatorSyntaxHighlightingConfigOptions' => 'applications/config/option/PhabricatorSyntaxHighlightingConfigOptions.php',
'PhabricatorSyntaxStyle' => 'infrastructure/syntax/PhabricatorSyntaxStyle.php',
'PhabricatorSystemAction' => 'applications/system/action/PhabricatorSystemAction.php',
'PhabricatorSystemActionEngine' => 'applications/system/engine/PhabricatorSystemActionEngine.php',
'PhabricatorSystemActionGarbageCollector' => 'applications/system/garbagecollector/PhabricatorSystemActionGarbageCollector.php',
@ -4103,7 +4099,6 @@ phutil_register_library_map(array(
'ReleephWorkRecordPickStatusConduitAPIMethod' => 'applications/releeph/conduit/work/ReleephWorkRecordPickStatusConduitAPIMethod.php',
'RemarkupProcessConduitAPIMethod' => 'applications/remarkup/conduit/RemarkupProcessConduitAPIMethod.php',
'RepositoryConduitAPIMethod' => 'applications/repository/conduit/RepositoryConduitAPIMethod.php',
'RepositoryCreateConduitAPIMethod' => 'applications/repository/conduit/RepositoryCreateConduitAPIMethod.php',
'RepositoryQueryConduitAPIMethod' => 'applications/repository/conduit/RepositoryQueryConduitAPIMethod.php',
'ShellLogView' => 'applications/harbormaster/view/ShellLogView.php',
'SlowvoteConduitAPIMethod' => 'applications/slowvote/conduit/SlowvoteConduitAPIMethod.php',
@ -4413,6 +4408,7 @@ phutil_register_library_map(array(
'CelerityHighContrastPostprocessor' => 'CelerityPostprocessor',
'CelerityLargeFontPostprocessor' => 'CelerityPostprocessor',
'CelerityManagementMapWorkflow' => 'CelerityManagementWorkflow',
'CelerityManagementSyntaxWorkflow' => 'CelerityManagementWorkflow',
'CelerityManagementWorkflow' => 'PhabricatorManagementWorkflow',
'CelerityPhabricatorResourceController' => 'CelerityResourceController',
'CelerityPhabricatorResources' => 'CelerityResourcesOnDisk',
@ -4446,7 +4442,7 @@ phutil_register_library_map(array(
'ConduitCallTestCase' => 'PhabricatorTestCase',
'ConduitColumnsParameterType' => 'ConduitParameterType',
'ConduitConnectConduitAPIMethod' => 'ConduitAPIMethod',
'ConduitEpochParameterType' => 'ConduitListParameterType',
'ConduitEpochParameterType' => 'ConduitParameterType',
'ConduitException' => 'Exception',
'ConduitGetCapabilitiesConduitAPIMethod' => 'ConduitAPIMethod',
'ConduitGetCertificateConduitAPIMethod' => 'ConduitAPIMethod',
@ -4470,7 +4466,7 @@ phutil_register_library_map(array(
'ConduitTokenGarbageCollector' => 'PhabricatorGarbageCollector',
'ConduitUserListParameterType' => 'ConduitListParameterType',
'ConduitUserParameterType' => 'ConduitParameterType',
'ConduitWildParameterType' => 'ConduitListParameterType',
'ConduitWildParameterType' => 'ConduitParameterType',
'ConpherenceColumnViewController' => 'ConpherenceController',
'ConpherenceConduitAPIMethod' => 'ConduitAPIMethod',
'ConpherenceConfigOptions' => 'PhabricatorApplicationConfigOptions',
@ -4559,6 +4555,7 @@ phutil_register_library_map(array(
'DifferentialBlameRevisionField' => 'DifferentialStoredCustomField',
'DifferentialBlockHeraldAction' => 'HeraldAction',
'DifferentialBranchField' => 'DifferentialCustomField',
'DifferentialChangeDetailMailView' => 'DifferentialMailView',
'DifferentialChangeHeraldFieldGroup' => 'HeraldFieldGroup',
'DifferentialChangeType' => 'Phobject',
'DifferentialChangesSinceLastUpdateField' => 'DifferentialCustomField',
@ -4570,6 +4567,7 @@ phutil_register_library_map(array(
'DifferentialChangesetFileTreeSideNavBuilder' => 'Phobject',
'DifferentialChangesetHTMLRenderer' => 'DifferentialChangesetRenderer',
'DifferentialChangesetListView' => 'AphrontView',
'DifferentialChangesetOneUpMailRenderer' => 'DifferentialChangesetRenderer',
'DifferentialChangesetOneUpRenderer' => 'DifferentialChangesetHTMLRenderer',
'DifferentialChangesetOneUpTestRenderer' => 'DifferentialChangesetTestRenderer',
'DifferentialChangesetParser' => 'Phobject',
@ -4672,6 +4670,7 @@ phutil_register_library_map(array(
'PhabricatorInlineCommentInterface',
),
'DifferentialInlineCommentEditController' => 'PhabricatorInlineCommentController',
'DifferentialInlineCommentMailView' => 'DifferentialMailView',
'DifferentialInlineCommentPreviewController' => 'PhabricatorInlineCommentPreviewController',
'DifferentialInlineCommentQuery' => 'PhabricatorOffsetPagedQuery',
'DifferentialJIRAIssuesField' => 'DifferentialStoredCustomField',
@ -4682,6 +4681,7 @@ phutil_register_library_map(array(
'DifferentialLintField' => 'DifferentialHarbormasterField',
'DifferentialLintStatus' => 'Phobject',
'DifferentialLocalCommitsView' => 'AphrontView',
'DifferentialMailView' => 'Phobject',
'DifferentialManiphestTasksField' => 'DifferentialCoreCustomField',
'DifferentialModernHunk' => 'DifferentialHunk',
'DifferentialNextStepField' => 'DifferentialCustomField',
@ -4799,6 +4799,7 @@ phutil_register_library_map(array(
'DiffusionCachedResolveRefsQuery' => 'DiffusionLowLevelQuery',
'DiffusionChangeController' => 'DiffusionController',
'DiffusionChangeHeraldFieldGroup' => 'HeraldFieldGroup',
'DiffusionCloneURIView' => 'AphrontView',
'DiffusionCommandEngine' => 'Phobject',
'DiffusionCommandEngineTestCase' => 'PhabricatorTestCase',
'DiffusionCommitAffectedFilesHeraldField' => 'DiffusionCommitHeraldField',
@ -4845,6 +4846,7 @@ phutil_register_library_map(array(
'DiffusionController' => 'PhabricatorController',
'DiffusionCreateCommentConduitAPIMethod' => 'DiffusionConduitAPIMethod',
'DiffusionCreateRepositoriesCapability' => 'PhabricatorPolicyCapability',
'DiffusionDaemonLockException' => 'Exception',
'DiffusionDefaultEditCapability' => 'PhabricatorPolicyCapability',
'DiffusionDefaultPushCapability' => 'PhabricatorPolicyCapability',
'DiffusionDefaultViewCapability' => 'PhabricatorPolicyCapability',
@ -4891,6 +4893,7 @@ phutil_register_library_map(array(
'DiffusionLintController' => 'DiffusionController',
'DiffusionLintCountQuery' => 'PhabricatorQuery',
'DiffusionLintSaveRunner' => 'Phobject',
'DiffusionLocalRepositoryFilter' => 'Phobject',
'DiffusionLookSoonConduitAPIMethod' => 'DiffusionConduitAPIMethod',
'DiffusionLowLevelCommitFieldsQuery' => 'DiffusionLowLevelQuery',
'DiffusionLowLevelCommitQuery' => 'DiffusionLowLevelQuery',
@ -4914,8 +4917,6 @@ phutil_register_library_map(array(
'DiffusionMercurialWireProtocolTests' => 'PhabricatorTestCase',
'DiffusionMercurialWireSSHTestCase' => 'PhabricatorTestCase',
'DiffusionMergedCommitsQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
'DiffusionMirrorDeleteController' => 'DiffusionController',
'DiffusionMirrorEditController' => 'DiffusionController',
'DiffusionPathChange' => 'Phobject',
'DiffusionPathChangeQuery' => 'Phobject',
'DiffusionPathCompleteController' => 'DiffusionController',
@ -4982,32 +4983,21 @@ phutil_register_library_map(array(
'DiffusionRepositoryByIDRemarkupRule' => 'PhabricatorObjectRemarkupRule',
'DiffusionRepositoryClusterEngine' => 'Phobject',
'DiffusionRepositoryController' => 'DiffusionController',
'DiffusionRepositoryCreateController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryDatasource' => 'PhabricatorTypeaheadDatasource',
'DiffusionRepositoryDefaultController' => 'DiffusionController',
'DiffusionRepositoryEditActionsController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryEditActivateController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryEditAutomationController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryEditBasicController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryEditBranchesController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryDocumentationManagementPanel' => 'DiffusionRepositoryManagementPanel',
'DiffusionRepositoryEditActivateController' => 'DiffusionRepositoryManageController',
'DiffusionRepositoryEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
'DiffusionRepositoryEditController' => 'DiffusionController',
'DiffusionRepositoryEditDangerousController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryEditDeleteController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryEditEncodingController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryEditController' => 'DiffusionRepositoryManageController',
'DiffusionRepositoryEditDangerousController' => 'DiffusionRepositoryManageController',
'DiffusionRepositoryEditDeleteController' => 'DiffusionRepositoryManageController',
'DiffusionRepositoryEditEngine' => 'PhabricatorEditEngine',
'DiffusionRepositoryEditHostingController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryEditMainController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryEditStagingController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryEditStorageController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryEditSubversionController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryEditUpdateController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryEditproController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryEditUpdateController' => 'DiffusionRepositoryManageController',
'DiffusionRepositoryHistoryManagementPanel' => 'DiffusionRepositoryManagementPanel',
'DiffusionRepositoryListController' => 'DiffusionController',
'DiffusionRepositoryManageController' => 'DiffusionController',
'DiffusionRepositoryManagePanelsController' => 'DiffusionRepositoryManageController',
'DiffusionRepositoryManagementPanel' => 'Phobject',
'DiffusionRepositoryNewController' => 'DiffusionController',
'DiffusionRepositoryPath' => 'Phobject',
'DiffusionRepositoryPoliciesManagementPanel' => 'DiffusionRepositoryManagementPanel',
'DiffusionRepositoryRef' => 'Phobject',
@ -5016,10 +5006,11 @@ phutil_register_library_map(array(
'DiffusionRepositoryStagingManagementPanel' => 'DiffusionRepositoryManagementPanel',
'DiffusionRepositoryStatusManagementPanel' => 'DiffusionRepositoryManagementPanel',
'DiffusionRepositoryStorageManagementPanel' => 'DiffusionRepositoryManagementPanel',
'DiffusionRepositorySymbolsController' => 'DiffusionRepositoryEditController',
'DiffusionRepositorySubversionManagementPanel' => 'DiffusionRepositoryManagementPanel',
'DiffusionRepositorySymbolsManagementPanel' => 'DiffusionRepositoryManagementPanel',
'DiffusionRepositoryTag' => 'Phobject',
'DiffusionRepositoryTestAutomationController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryTestAutomationController' => 'DiffusionRepositoryManageController',
'DiffusionRepositoryURICredentialController' => 'DiffusionController',
'DiffusionRepositoryURIDisableController' => 'DiffusionController',
'DiffusionRepositoryURIEditController' => 'DiffusionController',
'DiffusionRepositoryURIViewController' => 'DiffusionController',
@ -6793,6 +6784,7 @@ phutil_register_library_map(array(
'PhabricatorDateTimeSettingsPanel' => 'PhabricatorSettingsPanel',
'PhabricatorDebugController' => 'PhabricatorController',
'PhabricatorDefaultRequestExceptionHandler' => 'PhabricatorRequestExceptionHandler',
'PhabricatorDefaultSyntaxStyle' => 'PhabricatorSyntaxStyle',
'PhabricatorDesktopNotificationsSettingsPanel' => 'PhabricatorSettingsPanel',
'PhabricatorDestructionEngine' => 'Phobject',
'PhabricatorDestructionEngineExtension' => 'Phobject',
@ -6870,6 +6862,7 @@ phutil_register_library_map(array(
'PhabricatorEditEngineSelectCommentAction' => 'PhabricatorEditEngineCommentAction',
'PhabricatorEditEngineTokenizerCommentAction' => 'PhabricatorEditEngineCommentAction',
'PhabricatorEditField' => 'Phobject',
'PhabricatorEditPage' => 'Phobject',
'PhabricatorEditType' => 'Phobject',
'PhabricatorEditor' => 'Phobject',
'PhabricatorElasticFulltextStorageEngine' => 'PhabricatorFulltextStorageEngine',
@ -7864,7 +7857,6 @@ phutil_register_library_map(array(
'PhabricatorRepositoryManagementCacheWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
'PhabricatorRepositoryManagementClusterizeWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
'PhabricatorRepositoryManagementDiscoverWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
'PhabricatorRepositoryManagementEditWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
'PhabricatorRepositoryManagementImportingWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
'PhabricatorRepositoryManagementListPathsWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
'PhabricatorRepositoryManagementListWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
@ -7881,13 +7873,8 @@ phutil_register_library_map(array(
'PhabricatorRepositoryManagementWorkflow' => 'PhabricatorManagementWorkflow',
'PhabricatorRepositoryMercurialCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
'PhabricatorRepositoryMercurialCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
'PhabricatorRepositoryMirror' => array(
'PhabricatorRepositoryDAO',
'PhabricatorPolicyInterface',
),
'PhabricatorRepositoryMirror' => 'PhabricatorRepositoryDAO',
'PhabricatorRepositoryMirrorEngine' => 'PhabricatorRepositoryEngine',
'PhabricatorRepositoryMirrorPHIDType' => 'PhabricatorPHIDType',
'PhabricatorRepositoryMirrorQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorRepositoryParsedChange' => 'Phobject',
'PhabricatorRepositoryPullEngine' => 'PhabricatorRepositoryEngine',
'PhabricatorRepositoryPullEvent' => array(
@ -8184,6 +8171,7 @@ phutil_register_library_map(array(
'PhabricatorSupportApplication' => 'PhabricatorApplication',
'PhabricatorSyntaxHighlighter' => 'Phobject',
'PhabricatorSyntaxHighlightingConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorSyntaxStyle' => 'Phobject',
'PhabricatorSystemAction' => 'Phobject',
'PhabricatorSystemActionEngine' => 'Phobject',
'PhabricatorSystemActionGarbageCollector' => 'PhabricatorGarbageCollector',
@ -8984,7 +8972,6 @@ phutil_register_library_map(array(
'ReleephWorkRecordPickStatusConduitAPIMethod' => 'ReleephConduitAPIMethod',
'RemarkupProcessConduitAPIMethod' => 'ConduitAPIMethod',
'RepositoryConduitAPIMethod' => 'ConduitAPIMethod',
'RepositoryCreateConduitAPIMethod' => 'RepositoryConduitAPIMethod',
'RepositoryQueryConduitAPIMethod' => 'RepositoryConduitAPIMethod',
'ShellLogView' => 'AphrontView',
'SlowvoteConduitAPIMethod' => 'ConduitAPIMethod',

View file

@ -484,7 +484,7 @@ final class AphrontRequest extends Phobject {
pht(
'This Phabricator install is configured as "%s", but you are '.
'using the domain name "%s" to access a page which is trying to '.
'set a cookie. Acccess Phabricator on the configured primary '.
'set a cookie. Access Phabricator on the configured primary '.
'domain or a configured alternate domain. Phabricator will not '.
'set cookies on other domains for security reasons.',
$configured_as,

View file

@ -10,6 +10,14 @@ final class AlmanacKeys extends Phobject {
}
public static function getDeviceID() {
// While running unit tests, ignore any configured device identity.
try {
PhabricatorTestCase::assertExecutingUnitTests();
return null;
} catch (Exception $ex) {
// Continue normally.
}
$device_id_path = self::getKeyPath('device.id');
if (Filesystem::pathExists($device_id_path)) {

View file

@ -624,11 +624,11 @@ abstract class PhabricatorApplication
'(?P<id>[0-9]\d*)/)?'.
'(?:'.
'(?:'.
'(?P<editAction>parameters|nodefault|nocreate|nomanage|comment)'.
'(?P<editAction>parameters|nodefault|nocreate|nomanage|comment)/'.
'|'.
'(?:form/(?P<formKey>[^/]+))'.
'(?:form/(?P<formKey>[^/]+)/)?(?:page/(?P<pageKey>[^/]+)/)?'.
')'.
'/)?';
')?';
}
protected function getQueryRoutePattern($base = null) {

View file

@ -0,0 +1,67 @@
<?php
final class CelerityManagementSyntaxWorkflow
extends CelerityManagementWorkflow {
protected function didConstruct() {
$this
->setName('syntax')
->setExamples('**syntax** [options]')
->setSynopsis(pht('Rebuild syntax highlighting CSS.'))
->setArguments(
array());
}
public function execute(PhutilArgumentParser $args) {
$styles = PhabricatorSyntaxStyle::getAllStyles();
$root = dirname(phutil_get_library_root('phabricator'));
$root = $root.'/webroot/rsrc/css/syntax/';
foreach ($styles as $key => $style) {
$content = $this->generateCSS($style);
$path = $root.'/syntax-'.$key.'.css';
Filesystem::writeFile($path, $content);
echo tsprintf(
"%s\n",
pht(
'Rebuilt "%s" syntax CSS.',
basename($path)));
}
return 0;
}
private function generateCSS(PhabricatorSyntaxStyle $style) {
$key = $style->getSyntaxStyleKey();
$provides = "syntax-{$key}-css";
$generated = 'generated';
$header =
"/**\n".
" * @provides {$provides}\n".
" * @{$generated}\n".
" */\n\n";
$groups = array();
$map = $style->getStyleMap();
ksort($map);
foreach ($map as $key => $value) {
$groups[$value][] = $key;
}
$rules = array();
foreach ($groups as $body => $classes) {
$parts = array();
foreach ($classes as $class) {
$parts[] = ".remarkup-code .{$class}";
}
$rules[] = implode(",\n", $parts)." {\n {$body}\n}";
}
$rules = implode("\n\n", $rules);
return $header.$rules."\n";
}
}

View file

@ -1,7 +1,7 @@
<?php
final class ConduitEpochParameterType
extends ConduitListParameterType {
extends ConduitParameterType {
protected function getParameterValue(array $request, $key) {
$value = parent::getParameterValue($request, $key);

View file

@ -1,7 +1,7 @@
<?php
final class ConduitWildParameterType
extends ConduitListParameterType {
extends ConduitParameterType {
protected function getParameterTypeName() {
return 'wild';

View file

@ -310,6 +310,10 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck {
'notification.enabled' => $aphlict_reason,
'notification.client-uri' => $aphlict_reason,
'notification.server-uri' => $aphlict_reason,
'metamta.differential.unified-comment-context' => pht(
'Inline comments are now always rendered with a limited amount '.
'of context.'),
);
return $ancient_config;

View file

@ -287,21 +287,6 @@ final class PhabricatorDifferentialConfigOptions
'unified')
->setDescription(
pht("Format for inlined or attached patches: 'git' or 'unified'.")),
$this->newOption(
'metamta.differential.unified-comment-context',
'bool',
false)
->setBoolOptions(
array(
pht('Show context'),
pht('Do not show context'),
))
->setSummary(pht('Show diff context around inline comments in email.'))
->setDescription(
pht(
'Normally, inline comments in emails are shown with a file and '.
'line but without any diff context. Enabling this option adds '.
'diff context and the comment thread.')),
);
}

View file

@ -1212,9 +1212,7 @@ final class DifferentialTransactionEditor
}
if ($inlines) {
$body->addTextSection(
pht('INLINE COMMENTS'),
$this->renderInlineCommentsForMail($object, $inlines));
$this->appendInlineCommentsForMail($object, $inlines, $body);
}
$changed_uri = $this->getChangedPriorToCommitURI();
@ -1253,21 +1251,18 @@ final class DifferentialTransactionEditor
$config_attach = PhabricatorEnv::getEnvConfig($config_key_attach);
if ($config_inline || $config_attach) {
$patch_section = $this->renderPatchForMail($diff);
$lines = count(phutil_split_lines($patch_section->getPlaintext()));
$patch = $this->buildPatchForMail($diff);
$lines = substr_count($patch, "\n");
if ($config_inline && ($lines <= $config_inline)) {
$body->addTextSection(
pht('CHANGE DETAILS'),
$patch_section);
$this->appendChangeDetailsForMail($object, $diff, $patch, $body);
}
if ($config_attach) {
$name = pht('D%s.%s.patch', $object->getID(), $diff->getID());
$mime_type = 'text/x-patch; charset=utf-8';
$body->addAttachment(
new PhabricatorMetaMTAAttachment(
$patch_section->getPlaintext(), $name, $mime_type));
new PhabricatorMetaMTAAttachment($patch, $name, $mime_type));
}
}
}
@ -1374,138 +1369,64 @@ final class DifferentialTransactionEditor
return $result;
}
protected function indentForMail(array $lines) {
$indented = array();
foreach ($lines as $line) {
$indented[] = '> '.$line;
}
return $indented;
}
protected function nestCommentHistory(
DifferentialTransactionComment $comment, array $comments_by_line_number,
array $users_by_phid) {
$nested = array();
$previous_comments = $comments_by_line_number[$comment->getChangesetID()]
[$comment->getLineNumber()];
foreach ($previous_comments as $previous_comment) {
if ($previous_comment->getID() >= $comment->getID()) {
break;
}
$nested = $this->indentForMail(
array_merge(
$nested,
explode("\n", $previous_comment->getContent())));
$user = idx($users_by_phid, $previous_comment->getAuthorPHID(), null);
if ($user) {
array_unshift($nested, pht('%s wrote:', $user->getUserName()));
}
}
$nested = array_merge($nested, explode("\n", $comment->getContent()));
return implode("\n", $nested);
}
private function renderInlineCommentsForMail(
private function appendInlineCommentsForMail(
PhabricatorLiskDAO $object,
array $inlines) {
array $inlines,
PhabricatorMetaMTAMailBody $body) {
$context_key = 'metamta.differential.unified-comment-context';
$show_context = PhabricatorEnv::getEnvConfig($context_key);
$changeset_ids = array();
$line_numbers_by_changeset = array();
foreach ($inlines as $inline) {
$id = $inline->getComment()->getChangesetID();
$changeset_ids[$id] = $id;
$line_numbers_by_changeset[$id][] =
$inline->getComment()->getLineNumber();
}
$changesets = id(new DifferentialChangesetQuery())
$section = id(new DifferentialInlineCommentMailView())
->setViewer($this->getActor())
->withIDs($changeset_ids)
->needHunks(true)
->execute();
->setInlines($inlines)
->buildMailSection();
$inline_groups = DifferentialTransactionComment::sortAndGroupInlines(
$inlines,
$changesets);
$header = pht('INLINE COMMENTS');
if ($show_context) {
$hunk_parser = new DifferentialHunkParser();
$table = new DifferentialTransactionComment();
$conn_r = $table->establishConnection('r');
$queries = array();
foreach ($line_numbers_by_changeset as $id => $line_numbers) {
$queries[] = qsprintf(
$conn_r,
'(changesetID = %d AND lineNumber IN (%Ld))',
$id, $line_numbers);
$section_text = "\n".$section->getPlaintext();
$style = array(
'margin: 6px 0 12px 0;',
);
$section_html = phutil_tag(
'div',
array(
'style' => implode(' ', $style),
),
$section->getHTML());
$body->addPlaintextSection($header, $section_text, false);
$body->addHTMLSection($header, $section_html);
}
$all_comments = id(new DifferentialTransactionComment())->loadAllWhere(
'transactionPHID IS NOT NULL AND (%Q)', implode(' OR ', $queries));
$comments_by_line_number = array();
foreach ($all_comments as $comment) {
$comments_by_line_number
[$comment->getChangesetID()]
[$comment->getLineNumber()]
[$comment->getID()] = $comment;
}
$author_phids = mpull($all_comments, 'getAuthorPHID');
$authors = id(new PhabricatorPeopleQuery())
private function appendChangeDetailsForMail(
PhabricatorLiskDAO $object,
DifferentialDiff $diff,
$patch,
PhabricatorMetaMTAMailBody $body) {
$section = id(new DifferentialChangeDetailMailView())
->setViewer($this->getActor())
->withPHIDs($author_phids)
->execute();
$authors_by_phid = mpull($authors, null, 'getPHID');
}
->setDiff($diff)
->setPatch($patch)
->buildMailSection();
$section = new PhabricatorMetaMTAMailSection();
foreach ($inline_groups as $changeset_id => $group) {
$changeset = idx($changesets, $changeset_id);
if (!$changeset) {
continue;
}
$header = pht('CHANGE DETAILS');
foreach ($group as $inline) {
$comment = $inline->getComment();
$file = $changeset->getFilename();
$start = $comment->getLineNumber();
$len = $comment->getLineLength();
if ($len) {
$range = $start.'-'.($start + $len);
} else {
$range = $start;
}
$section_text = "\n".$section->getPlaintext();
$inline_content = $comment->getContent();
$style = array(
'margin: 6px 0 12px 0;',
);
if (!$show_context) {
$section->addFragment("{$file}:{$range} {$inline_content}");
} else {
$patch = $hunk_parser->makeContextDiff(
$changeset->getHunks(),
$comment->getIsNewFile(),
$comment->getLineNumber(),
$comment->getLineLength(),
1);
$nested_comments = $this->nestCommentHistory(
$inline->getComment(), $comments_by_line_number, $authors_by_phid);
$section_html = phutil_tag(
'div',
array(
'style' => implode(' ', $style),
),
$section->getHTML());
$section
->addFragment('================')
->addFragment(pht('Comment at: %s:%s', $file, $range))
->addPlaintextFragment($patch)
->addHTMLFragment($this->renderPatchHTMLForMail($patch))
->addFragment('----------------')
->addFragment($nested_comments)
->addFragment(null);
}
}
}
return $section;
$body->addPlaintextSection($header, $section_text, false);
$body->addHTMLSection($header, $section_html);
}
private function loadDiff($phid, $need_changesets = false) {
@ -1766,20 +1687,14 @@ final class DifferentialTransactionEditor
array('style' => 'font-family: monospace;'), $patch);
}
private function renderPatchForMail(DifferentialDiff $diff) {
private function buildPatchForMail(DifferentialDiff $diff) {
$format = PhabricatorEnv::getEnvConfig('metamta.differential.patch-format');
$patch = id(new DifferentialRawDiffRenderer())
return id(new DifferentialRawDiffRenderer())
->setViewer($this->getActor())
->setFormat($format)
->setChangesets($diff->getChangesets())
->buildPatch();
$section = new PhabricatorMetaMTAMailSection();
$section->addHTMLFragment($this->renderPatchHTMLForMail($patch));
$section->addPlaintextFragment($patch);
return $section;
}
protected function willPublish(PhabricatorLiskDAO $object, array $xactions) {

View file

@ -0,0 +1,77 @@
<?php
final class DifferentialChangeDetailMailView
extends DifferentialMailView {
private $viewer;
private $diff;
private $patch;
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
public function setDiff(DifferentialDiff $diff) {
$this->diff = $diff;
return $this;
}
public function getDiff() {
return $this->diff;
}
public function setPatch($patch) {
$this->patch = $patch;
return $this;
}
public function getPatch() {
return $this->patch;
}
public function buildMailSection() {
$viewer = $this->getViewer();
$diff = $this->getDiff();
$engine = new PhabricatorMarkupEngine();
$out = array();
foreach ($diff->getChangesets() as $changeset) {
$parser = id(new DifferentialChangesetParser())
->setUser($viewer)
->setChangeset($changeset)
->setLinesOfContext(2)
->setMarkupEngine($engine);
$parser->setRenderer(new DifferentialChangesetOneUpMailRenderer());
$block = $parser->render();
$filename = $changeset->getFilename();
$filename = $this->renderHeaderBold($filename);
$header = $this->renderHeaderBlock($filename);
$out[] = $this->renderContentBox(
array(
$header,
$this->renderCodeBlock($block),
));
}
$out = phutil_implode_html(phutil_tag('br'), $out);
$patch_html = $out;
$patch_text = $this->getPatch();
return id(new PhabricatorMetaMTAMailSection())
->addPlaintextFragment($patch_text)
->addHTMLFragment($patch_html);
}
}

View file

@ -0,0 +1,477 @@
<?php
final class DifferentialInlineCommentMailView
extends DifferentialMailView {
private $viewer;
private $inlines;
private $changesets;
private $authors;
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
public function setInlines($inlines) {
$this->inlines = $inlines;
return $this;
}
public function getInlines() {
return $this->inlines;
}
public function buildMailSection() {
$inlines = $this->getInlines();
$comments = mpull($inlines, 'getComment');
$comments = mpull($comments, null, 'getPHID');
$parents = $this->loadParents($comments);
$all_comments = $comments + $parents;
$this->changesets = $this->loadChangesets($all_comments);
$this->authors = $this->loadAuthors($all_comments);
$groups = $this->groupInlines($inlines);
$hunk_parser = new DifferentialHunkParser();
$spacer_text = null;
$spacer_html = phutil_tag('br');
$section = new PhabricatorMetaMTAMailSection();
$last_group_key = last_key($groups);
foreach ($groups as $changeset_id => $group) {
$changeset = $this->getChangeset($changeset_id);
if (!$changeset) {
continue;
}
$is_last_group = ($changeset_id == $last_group_key);
$last_inline_key = last_key($group);
foreach ($group as $inline_key => $inline) {
$comment = $inline->getComment();
$parent_phid = $comment->getReplyToCommentPHID();
$is_last_inline = ($inline_key == $last_inline_key);
$context_text = null;
$context_html = null;
if ($parent_phid) {
$parent = idx($parents, $parent_phid);
if ($parent) {
$context_text = $this->renderInline($parent, false, true);
$context_html = $this->renderInline($parent, true, true);
}
} else {
$patch_text = $this->getPatch($hunk_parser, $comment, false);
$context_text = $this->renderPatch($comment, $patch_text, false);
$patch_html = $this->getPatch($hunk_parser, $comment, true);
$context_html = $this->renderPatch($comment, $patch_html, true);
}
$render_text = $this->renderInline($comment, false, false);
$render_html = $this->renderInline($comment, true, false);
$section->addPlaintextFragment($context_text);
$section->addPlaintextFragment($spacer_text);
$section->addPlaintextFragment($render_text);
$html_fragment = $this->renderContentBox(
array(
$context_html,
$render_html,
));
$section->addHTMLFragment($html_fragment);
if (!$is_last_group || !$is_last_inline) {
$section->addPlaintextFragment($spacer_text);
$section->addHTMLFragment($spacer_html);
}
}
}
return $section;
}
private function loadChangesets(array $comments) {
if (!$comments) {
return array();
}
$ids = array();
foreach ($comments as $comment) {
$ids[] = $comment->getChangesetID();
}
$changesets = id(new DifferentialChangesetQuery())
->setViewer($this->getViewer())
->withIDs($ids)
->needHunks(true)
->execute();
return mpull($changesets, null, 'getID');
}
private function loadParents(array $comments) {
$viewer = $this->getViewer();
$phids = array();
foreach ($comments as $comment) {
$parent_phid = $comment->getReplyToCommentPHID();
if (!$parent_phid) {
continue;
}
$phids[] = $parent_phid;
}
if (!$phids) {
return array();
}
$parents = id(new DifferentialDiffInlineCommentQuery())
->setViewer($viewer)
->withPHIDs($phids)
->execute();
return mpull($parents, null, 'getPHID');
}
private function loadAuthors(array $comments) {
$viewer = $this->getViewer();
$phids = array();
foreach ($comments as $comment) {
$author_phid = $comment->getAuthorPHID();
if (!$author_phid) {
continue;
}
$phids[] = $author_phid;
}
if (!$phids) {
return array();
}
return $viewer->loadHandles($phids);
}
private function groupInlines(array $inlines) {
return DifferentialTransactionComment::sortAndGroupInlines(
$inlines,
$this->changesets);
}
private function renderInline(
DifferentialTransactionComment $comment,
$is_html,
$is_quote) {
$changeset = $this->getChangeset($comment->getChangesetID());
if (!$changeset) {
return null;
}
$content = $comment->getContent();
$content = $this->renderRemarkupContent($content, $is_html);
if ($is_quote) {
$header = $this->renderHeader($comment, $is_html, true);
} else {
$header = null;
}
if ($is_html) {
$style = array(
'margin: 8px 0;',
'padding: 0 12px;',
);
if ($is_quote) {
$style[] = 'color: #74777D;';
}
$content = phutil_tag(
'div',
array(
'style' => implode(' ', $style),
),
$content);
}
$parts = array(
$header,
"\n",
$content,
);
if (!$is_html) {
$parts = implode('', $parts);
$parts = trim($parts);
}
if ($is_quote) {
if ($is_html) {
$parts = $this->quoteHTML($parts);
} else {
$parts = $this->quoteText($parts);
}
}
return $parts;
}
private function renderRemarkupContent($content, $is_html) {
$viewer = $this->getViewer();
$production_uri = PhabricatorEnv::getProductionURI('/');
if ($is_html) {
$mode = PhutilRemarkupEngine::MODE_HTML_MAIL;
} else {
$mode = PhutilRemarkupEngine::MODE_TEXT;
}
$attributes = array(
'style' => 'padding: 0; margin: 8px;',
);
$engine = PhabricatorMarkupEngine::newMarkupEngine(array())
->setConfig('viewer', $viewer)
->setConfig('uri.base', $production_uri)
->setConfig('default.p.attributes', $attributes)
->setMode($mode);
try {
return $engine->markupText($content);
} catch (Exception $ex) {
return $content;
}
}
private function getChangeset($id) {
return idx($this->changesets, $id);
}
private function getAuthor($phid) {
if (isset($this->authors[$phid])) {
return $this->authors[$phid];
}
return null;
}
private function quoteText($block) {
$block = phutil_split_lines($block);
foreach ($block as $key => $line) {
$block[$key] = '> '.$line;
}
return implode('', $block);
}
private function quoteHTML($block) {
$styles = array(
'padding: 0;',
'background: #F7F7F7;',
'border-color: #e3e4e8;',
'border-style: solid;',
'border-width: 0 0 1px 0;',
'margin: 0;',
);
$styles = implode(' ', $styles);
return phutil_tag(
'div',
array(
'style' => $styles,
),
$block);
}
private function getPatch(
DifferentialHunkParser $parser,
DifferentialTransactionComment $comment,
$is_html) {
$changeset = $this->getChangeset($comment->getChangesetID());
$is_new = $comment->getIsNewFile();
$start = $comment->getLineNumber();
$length = $comment->getLineLength();
// By default, show one line of context around the target inline.
$context = 1;
// If the inline is at least 3 lines long, don't show any extra context.
if ($length >= 2) {
$context = 0;
}
// If the inline is more than 7 lines long, only show the first 7 lines.
if ($length >= 6) {
$length = 6;
}
if (!$is_html) {
$hunks = $changeset->getHunks();
$patch = $parser->makeContextDiff(
$hunks,
$is_new,
$start,
$length,
$context);
$patch = phutil_split_lines($patch);
// Remove the "@@ -x,y +u,v @@" line.
array_shift($patch);
return implode('', $patch);
}
$viewer = $this->getViewer();
$engine = new PhabricatorMarkupEngine();
if ($is_new) {
$offset_mode = 'new';
} else {
$offset_mode = 'old';
}
$parser = id(new DifferentialChangesetParser())
->setUser($viewer)
->setChangeset($changeset)
->setOffsetMode($offset_mode)
->setMarkupEngine($engine);
$parser->setRenderer(new DifferentialChangesetOneUpMailRenderer());
return $parser->render(
$start - $context,
$length + 1 + (2 * $context),
array());
}
private function renderPatch(
DifferentialTransactionComment $comment,
$patch,
$is_html) {
if ($is_html) {
$patch = $this->renderCodeBlock($patch);
}
$header = $this->renderHeader($comment, $is_html, false);
$patch = array(
$header,
"\n",
$patch,
);
if (!$is_html) {
$patch = implode('', $patch);
$patch = $this->quoteText($patch);
} else {
$patch = $this->quoteHTML($patch);
}
return $patch;
}
private function renderHeader(
DifferentialTransactionComment $comment,
$is_html,
$with_author) {
$changeset = $this->getChangeset($comment->getChangesetID());
$path = $changeset->getFilename();
// Only show the filename.
$path = basename($path);
$start = $comment->getLineNumber();
$length = $comment->getLineLength();
if ($length) {
$range = pht('%s-%s', $start, $start + $length);
} else {
$range = $start;
}
$header = "{$path}:{$range}";
if ($is_html) {
$header = $this->renderHeaderBold($header);
}
if ($with_author) {
$author = $this->getAuthor($comment->getAuthorPHID());
} else {
$author = null;
}
if ($author) {
$byline = $author->getName();
if ($is_html) {
$byline = $this->renderHeaderBold($byline);
}
$header = pht('%s wrote in %s', $byline, $header);
}
if ($is_html) {
$link_href = $this->getInlineURI($comment);
if ($link_href) {
$link_style = array(
'float: right;',
'text-decoration: none;',
);
$link = phutil_tag(
'a',
array(
'style' => implode(' ', $link_style),
'href' => $link_href,
),
pht('View Inline'));
} else {
$link = null;
}
$header = $this->renderHeaderBlock(array($link, $header));
}
return $header;
}
private function getInlineURI(DifferentialTransactionComment $comment) {
$changeset = $this->getChangeset($comment->getChangesetID());
if (!$changeset) {
return null;
}
$diff = $changeset->getDiff();
if (!$diff) {
return null;
}
$revision = $diff->getRevision();
if (!$revision) {
return null;
}
$link_href = '/'.$revision->getMonogram().'#inline-'.$comment->getID();
$link_href = PhabricatorEnv::getProductionURI($link_href);
return $link_href;
}
}

View file

@ -0,0 +1,62 @@
<?php
abstract class DifferentialMailView
extends Phobject {
protected function renderCodeBlock($block) {
$style = array(
'font: 11px/15px "Menlo", "Consolas", "Monaco", monospace;',
'white-space: pre-wrap;',
'clear: both;',
'padding: 4px 0;',
'margin: 0;',
);
return phutil_tag(
'div',
array(
'style' => implode(' ', $style),
),
$block);
}
protected function renderHeaderBlock($block) {
$style = array(
'color: #74777d;',
'background: #eff2f4;',
'padding: 6px 8px;',
'overflow: hidden;',
);
return phutil_tag(
'div',
array(
'style' => implode(' ', $style),
),
$block);
}
protected function renderHeaderBold($content) {
return phutil_tag(
'span',
array(
'style' => 'color: #4b4d51; font-weight: bold;',
),
$content);
}
protected function renderContentBox($content) {
$style = array(
'border: 1px solid #C7CCD9;',
'border-radius: 3px;',
);
return phutil_tag(
'div',
array(
'style' => implode(' ', $style),
),
$content);
}
}

View file

@ -50,10 +50,12 @@ final class DifferentialChangesetParser extends Phobject {
private $showEditAndReplyLinks = true;
private $canMarkDone;
private $objectOwnerPHID;
private $offsetMode;
private $rangeStart;
private $rangeEnd;
private $mask;
private $linesOfContext = 8;
private $highlightEngine;
@ -138,6 +140,15 @@ final class DifferentialChangesetParser extends Phobject {
return $this->objectOwnerPHID;
}
public function setOffsetMode($offset_mode) {
$this->offsetMode = $offset_mode;
return $this;
}
public function getOffsetMode() {
return $this->offsetMode;
}
public static function getDefaultRendererForViewer(PhabricatorUser $viewer) {
$prefs = $viewer->loadPreferences();
$pref_unified = PhabricatorUserPreferences::PREFERENCE_DIFF_UNIFIED;
@ -185,8 +196,6 @@ final class DifferentialChangesetParser extends Phobject {
const ATTR_WHITELINES = 'attr:white';
const ATTR_MOVEAWAY = 'attr:moveaway';
const LINES_CONTEXT = 8;
const WHITESPACE_SHOW_ALL = 'show-all';
const WHITESPACE_IGNORE_TRAILING = 'ignore-trailing';
const WHITESPACE_IGNORE_MOST = 'ignore-most';
@ -217,6 +226,16 @@ final class DifferentialChangesetParser extends Phobject {
return $this;
}
public function setLinesOfContext($lines_of_context) {
$this->linesOfContext = $lines_of_context;
return $this;
}
public function getLinesOfContext() {
return $this->linesOfContext;
}
/**
* Configure which Changeset comments added to the right side of the visible
* diff will be attached to. The ID must be the ID of a real Differential
@ -714,8 +733,10 @@ final class DifferentialChangesetParser extends Phobject {
self::ATTR_MOVEAWAY => $moveaway,
));
$lines_context = $this->getLinesOfContext();
$hunk_parser->generateIntraLineDiffs();
$hunk_parser->generateVisibileLinesMask();
$hunk_parser->generateVisibileLinesMask($lines_context);
$this->setOldLines($hunk_parser->getOldLines());
$this->setNewLines($hunk_parser->getNewLines());
@ -829,6 +850,22 @@ final class DifferentialChangesetParser extends Phobject {
}
$this->tryCacheStuff();
// If we're rendering in an offset mode, treat the range numbers as line
// numbers instead of rendering offsets.
$offset_mode = $this->getOffsetMode();
if ($offset_mode) {
if ($offset_mode == 'new') {
$offset_map = $this->new;
} else {
$offset_map = $this->old;
}
$range_end = $this->getOffset($offset_map, $range_start + $range_len);
$range_start = $this->getOffset($offset_map, $range_start);
$range_len = ($range_end - $range_start);
}
$render_pch = $this->shouldRenderPropertyChangeHeader($this->changeset);
$rows = max(
@ -933,6 +970,7 @@ final class DifferentialChangesetParser extends Phobject {
$old_mask = array();
$new_mask = array();
$feedback_mask = array();
$lines_context = $this->getLinesOfContext();
if ($this->comments) {
// If there are any comments which appear in sections of the file which
@ -975,10 +1013,10 @@ final class DifferentialChangesetParser extends Phobject {
}
$start = max($comment->getLineNumber() - self::LINES_CONTEXT, 0);
$start = max($comment->getLineNumber() - $lines_context, 0);
$end = $comment->getLineNumber() +
$comment->getLineLength() +
self::LINES_CONTEXT;
$lines_context;
for ($ii = $start; $ii <= $end; $ii++) {
if ($new_side) {
$new_mask[$ii] = true;
@ -1163,6 +1201,8 @@ final class DifferentialChangesetParser extends Phobject {
$range_start,
$range_len) {
$lines_context = $this->getLinesOfContext();
// Calculate gaps and mask first
$gaps = array();
$gap_start = 0;
@ -1173,7 +1213,7 @@ final class DifferentialChangesetParser extends Phobject {
if (isset($base_mask[$ii])) {
if ($in_gap) {
$gap_length = $ii - $gap_start;
if ($gap_length <= self::LINES_CONTEXT) {
if ($gap_length <= $lines_context) {
for ($jj = $gap_start; $jj <= $gap_start + $gap_length; $jj++) {
$base_mask[$jj] = true;
}
@ -1632,5 +1672,21 @@ final class DifferentialChangesetParser extends Phobject {
return $results;
}
private function getOffset(array $map, $line) {
if (!$map) {
return null;
}
$line = (int)$line;
foreach ($map as $key => $spec) {
if ($spec && isset($spec['line'])) {
if ((int)$spec['line'] >= $line) {
return $key;
}
}
}
return $key;
}
}

View file

@ -353,8 +353,7 @@ final class DifferentialHunkParser extends Phobject {
return $this;
}
public function generateVisibileLinesMask() {
$lines_context = DifferentialChangesetParser::LINES_CONTEXT;
public function generateVisibileLinesMask($lines_context) {
$old = $this->getOldLines();
$new = $this->getNewLines();
$max_length = max(count($old), count($new));

View file

@ -0,0 +1,160 @@
<?php
final class DifferentialChangesetOneUpMailRenderer
extends DifferentialChangesetRenderer {
public function isOneUpRenderer() {
return true;
}
protected function getRendererTableClass() {
return 'diff-1up-mail';
}
public function getRendererKey() {
return '1up-mail';
}
protected function renderChangeTypeHeader($force) {
return null;
}
protected function renderUndershieldHeader() {
return null;
}
public function renderShield($message, $force = 'default') {
return null;
}
protected function renderPropertyChangeHeader() {
return null;
}
public function renderFileChange(
$old_file = null,
$new_file = null,
$id = 0,
$vs = 0) {
return null;
}
public function renderTextChange(
$range_start,
$range_len,
$rows) {
$primitives = $this->buildPrimitives($range_start, $range_len);
return $this->renderPrimitives($primitives, $rows);
}
protected function renderPrimitives(array $primitives, $rows) {
$out = array();
$context_style = array(
'background: #F7F7F7;',
'color: #74777D;',
'border-style: dashed;',
'border-color: #C7CCD9;',
'border-width: 1px 0;',
);
$context_style = implode(' ', $context_style);
foreach ($primitives as $k => $p) {
$type = $p['type'];
switch ($type) {
case 'old':
case 'new':
case 'old-file':
case 'new-file':
$is_old = ($type == 'old' || $type == 'old-file');
if ($is_old) {
if ($p['htype']) {
$style = 'background: #ffd0d0;';
} else {
$style = null;
}
} else {
if ($p['htype']) {
$style = 'background: #d0ffd0;';
} else {
$style = null;
}
}
$out[] = array(
'style' => $style,
'render' => $p['render'],
'text' => (string)$p['render'],
);
break;
case 'context':
// NOTE: These are being included with no text so they get stripped
// in the header and footer.
$out[] = array(
'style' => $context_style,
'render' => '...',
'text' => '',
);
break;
default:
break;
}
}
// Remove all leading and trailing empty lines, since these just look kind
// of weird in mail.
foreach ($out as $key => $line) {
if (!strlen(trim($line['text']))) {
unset($out[$key]);
} else {
break;
}
}
$keys = array_reverse(array_keys($out));
foreach ($keys as $key) {
$line = $out[$key];
if (!strlen(trim($line['text']))) {
unset($out[$key]);
} else {
break;
}
}
// If the user has commented on an empty line in the middle of a bunch of
// other empty lines, emit an explicit marker instead of just rendering
// nothing.
if (!$out) {
$out[] = array(
'style' => 'color: #888888;',
'render' => pht('(Empty.)'),
);
}
$render = array();
foreach ($out as $line) {
$style = $line['style'];
$style = "padding: 0 8px; margin: 0 4px; {$style}";
$render[] = phutil_tag(
'div',
array(
'style' => $style,
),
$line['render']);
}
$style_map = id(new PhabricatorDefaultSyntaxStyle())
->getRemarkupStyleMap();
$styled_body = id(new PhutilPygmentizeParser())
->setMap($style_map)
->parse((string)hsprintf('%s', $render));
return phutil_safe_html($styled_body);
}
}

View file

@ -58,15 +58,15 @@ final class DifferentialReviewersView extends AphrontView {
} else {
$item->setIcon(
PHUIStatusItemView::ICON_ACCEPT,
'dark',
'bluegrey',
pht('Accepted Prior Diff'));
}
break;
case DifferentialReviewerStatus::STATUS_ACCEPTED_OLDER:
$item->setIcon(
PHUIStatusItemView::ICON_ACCEPT,
'dark',
'fa-check-circle-o',
'bluegrey',
pht('Accepted Prior Diff'));
break;
@ -78,28 +78,29 @@ final class DifferentialReviewersView extends AphrontView {
pht('Requested Changes'));
} else {
$item->setIcon(
PHUIStatusItemView::ICON_REJECT,
'dark',
'fa-times-circle-o',
'bluegrey',
pht('Requested Changes to Prior Diff'));
}
break;
case DifferentialReviewerStatus::STATUS_REJECTED_OLDER:
$item->setIcon(
PHUIStatusItemView::ICON_REJECT,
'dark',
'fa-times-circle-o',
'bluegrey',
pht('Rejected Prior Diff'));
break;
case DifferentialReviewerStatus::STATUS_COMMENTED:
if ($is_current) {
$item->setIcon(
PHUIStatusItemView::ICON_INFO,
'fa-question-circle',
'blue',
pht('Commented'));
} else {
$item->setIcon(
'info-dark',
'fa-question-circle-o',
'bluegrey',
pht('Commented Previously'));
}
break;

View file

@ -57,11 +57,8 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication {
'/diffusion/' => array(
$this->getQueryRoutePattern()
=> 'DiffusionRepositoryListController',
$this->getEditRoutePattern('editpro/') =>
'DiffusionRepositoryEditproController',
'new/' => 'DiffusionRepositoryNewController',
'(?P<edit>create)/' => 'DiffusionRepositoryCreateController',
'(?P<edit>import)/' => 'DiffusionRepositoryCreateController',
$this->getEditRoutePattern('edit/') =>
'DiffusionRepositoryEditController',
'pushlog/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'DiffusionPushLogListController',
'view/(?P<id>\d+)/' => 'DiffusionPushEventViewController',
@ -90,40 +87,24 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication {
'commit/(?P<commit>[a-z0-9]+)/edit/'
=> 'DiffusionCommitEditController',
'manage/(?:(?P<panel>[^/]+)/)?'
=> 'DiffusionRepositoryManageController',
=> 'DiffusionRepositoryManagePanelsController',
'uri/' => array(
'view/(?P<id>[0-9]\d*)/' => 'DiffusionRepositoryURIViewController',
'disable/(?P<id>[0-9]\d*)/'
=> 'DiffusionRepositoryURIDisableController',
$this->getEditRoutePattern('edit/')
=> 'DiffusionRepositoryURIEditController',
'credential/(?P<id>[0-9]\d*)/(?P<action>edit|remove)/'
=> 'DiffusionRepositoryURICredentialController',
),
'edit/' => array(
'' => 'DiffusionRepositoryEditMainController',
'basic/' => 'DiffusionRepositoryEditBasicController',
'encoding/' => 'DiffusionRepositoryEditEncodingController',
'activate/' => 'DiffusionRepositoryEditActivateController',
'dangerous/' => 'DiffusionRepositoryEditDangerousController',
'branches/' => 'DiffusionRepositoryEditBranchesController',
'subversion/' => 'DiffusionRepositoryEditSubversionController',
'actions/' => 'DiffusionRepositoryEditActionsController',
'(?P<edit>remote)/' => 'DiffusionRepositoryCreateController',
'(?P<edit>policy)/' => 'DiffusionRepositoryCreateController',
'storage/' => 'DiffusionRepositoryEditStorageController',
'delete/' => 'DiffusionRepositoryEditDeleteController',
'hosting/' => 'DiffusionRepositoryEditHostingController',
'(?P<serve>serve)/' => 'DiffusionRepositoryEditHostingController',
'update/' => 'DiffusionRepositoryEditUpdateController',
'symbol/' => 'DiffusionRepositorySymbolsController',
'staging/' => 'DiffusionRepositoryEditStagingController',
'automation/' => 'DiffusionRepositoryEditAutomationController',
'testautomation/' => 'DiffusionRepositoryTestAutomationController',
),
'pathtree/(?P<dblob>.*)' => 'DiffusionPathTreeController',
'mirror/' => array(
'edit/(?:(?P<id>\d+)/)?' => 'DiffusionMirrorEditController',
'delete/(?P<id>\d+)/' => 'DiffusionMirrorDeleteController',
),
),
// NOTE: This must come after the rule above; it just gives us a

View file

@ -1,45 +0,0 @@
<?php
final class DiffusionMirrorDeleteController
extends DiffusionController {
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContext();
if ($response) {
return $response;
}
$viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$mirror = id(new PhabricatorRepositoryMirrorQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('id')))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$mirror) {
return new Aphront404Response();
}
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/#mirrors');
if ($request->isFormPost()) {
$mirror->delete();
return id(new AphrontReloadResponse())->setURI($edit_uri);
}
return $this->newDialog()
->setTitle(pht('Really delete mirror?'))
->appendChild(
pht('Phabricator will stop pushing updates to this mirror.'))
->addSubmitButton(pht('Delete Mirror'))
->addCancelButton($edit_uri);
}
}

View file

@ -1,130 +0,0 @@
<?php
final class DiffusionMirrorEditController
extends DiffusionController {
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContext();
if ($response) {
return $response;
}
$viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
PhabricatorPolicyFilter::requireCapability(
$viewer,
$repository,
PhabricatorPolicyCapability::CAN_EDIT);
if ($request->getURIData('id')) {
$mirror = id(new PhabricatorRepositoryMirrorQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('id')))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$mirror) {
return new Aphront404Response();
}
$is_new = false;
} else {
$mirror = PhabricatorRepositoryMirror::initializeNewMirror($viewer)
->setRepositoryPHID($repository->getPHID())
->attachRepository($repository);
$is_new = true;
}
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/#mirrors');
$v_remote = $mirror->getRemoteURI();
$e_remote = true;
$v_credentials = $mirror->getCredentialPHID();
$e_credentials = null;
$credentials = id(new PassphraseCredentialQuery())
->setViewer($viewer)
->withIsDestroyed(false)
->execute();
$errors = array();
if ($request->isFormPost()) {
$v_remote = $request->getStr('remoteURI');
if (strlen($v_remote)) {
try {
PhabricatorRepository::assertValidRemoteURI($v_remote);
$e_remote = null;
} catch (Exception $ex) {
$e_remote = pht('Invalid');
$errors[] = $ex->getMessage();
}
} else {
$e_remote = pht('Required');
$errors[] = pht('You must provide a remote URI.');
}
$v_credentials = $request->getStr('credential');
if ($v_credentials) {
$phids = mpull($credentials, null, 'getPHID');
if (empty($phids[$v_credentials])) {
$e_credentials = pht('Invalid');
$errors[] = pht(
'You do not have permission to use those credentials.');
}
}
if (!$errors) {
$mirror
->setRemoteURI($v_remote)
->setCredentialPHID($v_credentials)
->save();
return id(new AphrontReloadResponse())->setURI($edit_uri);
}
}
$form_errors = null;
if ($errors) {
$form_errors = id(new PHUIInfoView())
->setErrors($errors);
}
if ($is_new) {
$title = pht('Create Mirror');
$submit = pht('Create Mirror');
} else {
$title = pht('Edit Mirror');
$submit = pht('Save Changes');
}
$form = id(new PHUIFormLayoutView())
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Remote URI'))
->setName('remoteURI')
->setValue($v_remote)
->setError($e_remote))
->appendChild(
id(new PassphraseCredentialControl())
->setLabel(pht('Credentials'))
->setName('credential')
->setAllowNull(true)
->setValue($v_credentials)
->setError($e_credentials)
->setOptions($credentials));
return $this->newDialog()
->setTitle($title)
->setWidth(AphrontDialogView::WIDTH_FORM)
->appendChild($form_errors)
->appendChild($form)
->addSubmitButton($submit)
->addCancelButton($edit_uri);
}
}

View file

@ -248,21 +248,14 @@ final class DiffusionRepositoryController extends DiffusionController {
private function buildCurtain(PhabricatorRepository $repository) {
$viewer = $this->getViewer();
$edit_uri = $repository->getPathURI('edit/');
$edit_uri = $repository->getPathURI('manage/');
$curtain = $this->newCurtainView($repository);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$repository,
PhabricatorPolicyCapability::CAN_EDIT);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Repository'))
->setIcon('fa-pencil')
->setHref($edit_uri)
->setWorkflow(!$can_edit)
->setDisabled(!$can_edit));
->setName(pht('Manage Repository'))
->setIcon('fa-cogs')
->setHref($edit_uri));
if ($repository->isHosted()) {
$push_uri = $this->getApplicationURI(
@ -301,56 +294,27 @@ final class DiffusionRepositoryController extends DiffusionController {
$view = id(new PHUIPropertyListView())
->setUser($viewer);
if ($repository->isHosted()) {
$ssh_uri = $repository->getSSHCloneURIObject();
if ($ssh_uri) {
$clone_uri = $this->renderCloneCommand(
$repository,
$ssh_uri,
$repository->getServeOverSSH(),
'/settings/panel/ssh/');
$display_never = PhabricatorRepositoryURI::DISPLAY_NEVER;
$view->addProperty(
$repository->isSVN()
? pht('Checkout (SSH)')
: pht('Clone (SSH)'),
$clone_uri);
$uris = $repository->getURIs();
foreach ($uris as $uri) {
if ($uri->getIsDisabled()) {
continue;
}
$http_uri = $repository->getHTTPCloneURIObject();
if ($http_uri) {
$clone_uri = $this->renderCloneCommand(
$repository,
$http_uri,
$repository->getServeOverHTTP(),
PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth')
? '/settings/panel/vcspassword/'
: null);
$view->addProperty(
$repository->isSVN()
? pht('Checkout (HTTP)')
: pht('Clone (HTTP)'),
$clone_uri);
if ($uri->getEffectiveDisplayType() == $display_never) {
continue;
}
if ($repository->isSVN()) {
$label = pht('Checkout');
} else {
switch ($repository->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$view->addProperty(
pht('Clone'),
$this->renderCloneCommand(
$repository,
$repository->getPublicCloneURI()));
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$view->addProperty(
pht('Checkout'),
$this->renderCloneCommand(
$repository,
$repository->getPublicCloneURI()));
break;
$label = pht('Clone');
}
$view->addProperty(
$label,
$this->renderCloneURI($repository, $uri));
}
$box = id(new PHUIObjectBoxView())
@ -701,80 +665,27 @@ final class DiffusionRepositoryController extends DiffusionController {
);
}
private function renderCloneCommand(
private function renderCloneURI(
PhabricatorRepository $repository,
$uri,
$serve_mode = null,
$manage_uri = null) {
PhabricatorRepositoryURI $uri) {
require_celerity_resource('diffusion-icons-css');
Javelin::initBehavior('select-on-click');
switch ($repository->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$command = csprintf(
'git clone %R',
$uri);
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$command = csprintf(
'hg clone %R',
$uri);
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
if ($repository->isHosted()) {
$command = csprintf(
if ($repository->isSVN()) {
$display = csprintf(
'svn checkout %R %R',
$uri,
(string)$uri->getDisplayURI(),
$repository->getCloneName());
} else {
$command = csprintf(
'svn checkout %R',
$uri);
}
break;
$display = csprintf('%R', (string)$uri->getDisplayURI());
}
$input = javelin_tag(
'input',
array(
'type' => 'text',
'value' => (string)$command,
'class' => 'diffusion-clone-uri',
'sigil' => 'select-on-click',
'readonly' => 'true',
));
$display = (string)$display;
$viewer = $this->getViewer();
$extras = array();
if ($serve_mode) {
if ($serve_mode === PhabricatorRepository::SERVE_READONLY) {
$extras[] = pht('(Read Only)');
}
}
if ($manage_uri) {
if ($this->getRequest()->getUser()->isLoggedIn()) {
$extras[] = phutil_tag(
'a',
array(
'href' => $manage_uri,
),
pht('Manage Credentials'));
}
}
if ($extras) {
$extras = phutil_implode_html(' ', $extras);
$extras = phutil_tag(
'div',
array(
'class' => 'diffusion-clone-extras',
),
$extras);
}
return array($input, $extras);
return id(new DiffusionCloneURIView())
->setViewer($viewer)
->setRepository($repository)
->setRepositoryURI($uri)
->setDisplayURI($display);
}
}

View file

@ -1,848 +0,0 @@
<?php
final class DiffusionRepositoryCreateController
extends DiffusionRepositoryEditController {
private $edit;
private $repository;
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$this->edit = $request->getURIData('edit');
// NOTE: We can end up here via either "Create Repository", or via
// "Import Repository", or via "Edit Remote", or via "Edit Policies". In
// the latter two cases, we show only a few of the pages.
$repository = null;
$service = null;
switch ($this->edit) {
case 'remote':
case 'policy':
$response = $this->loadDiffusionContextForEdit();
if ($response) {
return $response;
}
$repository = $this->getDiffusionRequest()->getRepository();
// Make sure we have CAN_EDIT.
PhabricatorPolicyFilter::requireCapability(
$viewer,
$repository,
PhabricatorPolicyCapability::CAN_EDIT);
$this->setRepository($repository);
$cancel_uri = $this->getRepositoryControllerURI($repository, 'edit/');
break;
case 'import':
case 'create':
$this->requireApplicationCapability(
DiffusionCreateRepositoriesCapability::CAPABILITY);
// Pick a random open service to allocate this repository on, if any
// exist. If there are no services, we aren't in cluster mode and
// will allocate locally. If there are services but none permit
// allocations, we fail.
$services = id(new AlmanacServiceQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withServiceTypes(
array(
AlmanacClusterRepositoryServiceType::SERVICETYPE,
))
->needProperties(true)
->execute();
if ($services) {
// Filter out services which do not permit new allocations.
foreach ($services as $key => $possible_service) {
if ($possible_service->getAlmanacPropertyValue('closed')) {
unset($services[$key]);
}
}
if (!$services) {
throw new Exception(
pht(
'This install is configured in cluster mode, but all '.
'available repository cluster services are closed to new '.
'allocations. At least one service must be open to allow '.
'new allocations to take place.'));
}
shuffle($services);
$service = head($services);
}
$cancel_uri = $this->getApplicationURI('new/');
break;
default:
throw new Exception(pht('Invalid edit operation!'));
}
$form = id(new PHUIPagedFormView())
->setUser($viewer)
->setCancelURI($cancel_uri);
switch ($this->edit) {
case 'remote':
$title = pht('Edit Remote');
$form
->addPage('remote-uri', $this->buildRemoteURIPage())
->addPage('auth', $this->buildAuthPage());
break;
case 'policy':
$title = pht('Edit Policies');
$form
->addPage('policy', $this->buildPolicyPage());
break;
case 'create':
$title = pht('Create Repository');
$form
->addPage('vcs', $this->buildVCSPage())
->addPage('name', $this->buildNamePage())
->addPage('policy', $this->buildPolicyPage())
->addPage('done', $this->buildDonePage());
break;
case 'import':
$title = pht('Import Repository');
$form
->addPage('vcs', $this->buildVCSPage())
->addPage('name', $this->buildNamePage())
->addPage('remote-uri', $this->buildRemoteURIPage())
->addPage('auth', $this->buildAuthPage())
->addPage('policy', $this->buildPolicyPage())
->addPage('done', $this->buildDonePage());
break;
}
if ($request->isFormPost()) {
$form->readFromRequest($request);
if ($form->isComplete()) {
$is_create = ($this->edit === 'import' || $this->edit === 'create');
$is_auth = ($this->edit == 'import' || $this->edit == 'remote');
$is_policy = ($this->edit != 'remote');
$is_init = ($this->edit == 'create');
if ($is_create) {
$repository = PhabricatorRepository::initializeNewRepository(
$viewer);
}
$template = id(new PhabricatorRepositoryTransaction());
$type_name = PhabricatorRepositoryTransaction::TYPE_NAME;
$type_vcs = PhabricatorRepositoryTransaction::TYPE_VCS;
$type_activate = PhabricatorRepositoryTransaction::TYPE_ACTIVATE;
$type_remote_uri = PhabricatorRepositoryTransaction::TYPE_REMOTE_URI;
$type_hosting = PhabricatorRepositoryTransaction::TYPE_HOSTING;
$type_http = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP;
$type_ssh = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH;
$type_credential = PhabricatorRepositoryTransaction::TYPE_CREDENTIAL;
$type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
$type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;
$type_space = PhabricatorTransactions::TYPE_SPACE;
$type_push = PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY;
$type_service = PhabricatorRepositoryTransaction::TYPE_SERVICE;
$xactions = array();
// If we're creating a new repository, set all this core stuff.
if ($is_create) {
$xactions[] = id(clone $template)
->setTransactionType($type_name)
->setNewValue(
$form->getPage('name')->getControl('name')->getValue());
$xactions[] = id(clone $template)
->setTransactionType($type_vcs)
->setNewValue(
$form->getPage('vcs')->getControl('vcs')->getValue());
$activate = $form->getPage('done')
->getControl('activate')->getValue();
if ($activate == 'start') {
$initial_status = PhabricatorRepository::STATUS_ACTIVE;
} else {
$initial_status = PhabricatorRepository::STATUS_INACTIVE;
}
$xactions[] = id(clone $template)
->setTransactionType($type_activate)
->setNewValue($initial_status);
if ($service) {
$xactions[] = id(clone $template)
->setTransactionType($type_service)
->setNewValue($service->getPHID());
}
}
if ($is_init) {
$xactions[] = id(clone $template)
->setTransactionType($type_hosting)
->setNewValue(true);
$vcs = $form->getPage('vcs')->getControl('vcs')->getValue();
if ($vcs != PhabricatorRepositoryType::REPOSITORY_TYPE_SVN) {
if (PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth')) {
$v_http_mode = PhabricatorRepository::SERVE_READWRITE;
} else {
$v_http_mode = PhabricatorRepository::SERVE_OFF;
}
$xactions[] = id(clone $template)
->setTransactionType($type_http)
->setNewValue($v_http_mode);
}
if (PhabricatorEnv::getEnvConfig('diffusion.ssh-user')) {
$v_ssh_mode = PhabricatorRepository::SERVE_READWRITE;
} else {
$v_ssh_mode = PhabricatorRepository::SERVE_OFF;
}
$xactions[] = id(clone $template)
->setTransactionType($type_ssh)
->setNewValue($v_ssh_mode);
}
if ($is_auth) {
$xactions[] = id(clone $template)
->setTransactionType($type_remote_uri)
->setNewValue(
$form->getPage('remote-uri')->getControl('remoteURI')
->getValue());
$xactions[] = id(clone $template)
->setTransactionType($type_credential)
->setNewValue(
$form->getPage('auth')->getControl('credential')->getValue());
}
if ($is_policy) {
$policy_page = $form->getPage('policy');
$xactions[] = id(clone $template)
->setTransactionType($type_view)
->setNewValue($policy_page->getControl('viewPolicy')->getValue());
$xactions[] = id(clone $template)
->setTransactionType($type_edit)
->setNewValue($policy_page->getControl('editPolicy')->getValue());
if ($is_init || $repository->isHosted()) {
$xactions[] = id(clone $template)
->setTransactionType($type_push)
->setNewValue($policy_page->getControl('pushPolicy')->getValue());
}
$xactions[] = id(clone $template)
->setTransactionType($type_space)
->setNewValue(
$policy_page->getControl('viewPolicy')->getSpacePHID());
}
id(new PhabricatorRepositoryEditor())
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request)
->setActor($viewer)
->applyTransactions($repository, $xactions);
$repo_uri = $this->getRepositoryControllerURI($repository, 'edit/');
return id(new AphrontRedirectResponse())->setURI($repo_uri);
}
} else {
$dict = array();
if ($repository) {
$dict = array(
'remoteURI' => $repository->getRemoteURI(),
'credential' => $repository->getCredentialPHID(),
'viewPolicy' => $repository->getViewPolicy(),
'editPolicy' => $repository->getEditPolicy(),
'pushPolicy' => $repository->getPushPolicy(),
'spacePHID' => $repository->getSpacePHID(),
);
}
$form->readFromObject($dict);
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($title);
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon('fa-pencil');
$form_box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setForm($form);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
$form,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
/* -( Page: VCS Type )----------------------------------------------------- */
private function buildVCSPage() {
$is_import = ($this->edit == 'import');
if ($is_import) {
$git_str = pht(
'Import a Git repository (for example, a repository hosted '.
'on GitHub).');
$hg_str = pht(
'Import a Mercurial repository (for example, a repository '.
'hosted on Bitbucket).');
$svn_str = pht('Import a Subversion repository.');
} else {
$git_str = pht('Create a new, empty Git repository.');
$hg_str = pht('Create a new, empty Mercurial repository.');
$svn_str = pht('Create a new, empty Subversion repository.');
}
$control = id(new AphrontFormRadioButtonControl())
->setName('vcs')
->setLabel(pht('Type'))
->addButton(
PhabricatorRepositoryType::REPOSITORY_TYPE_GIT,
pht('Git'),
$git_str)
->addButton(
PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL,
pht('Mercurial'),
$hg_str)
->addButton(
PhabricatorRepositoryType::REPOSITORY_TYPE_SVN,
pht('Subversion'),
$svn_str);
return id(new PHUIFormPageView())
->setPageName(pht('Repository Type'))
->setUser($this->getRequest()->getUser())
->setValidateFormPageCallback(array($this, 'validateVCSPage'))
->addControl($control);
}
public function validateVCSPage(PHUIFormPageView $page) {
$valid = array(
PhabricatorRepositoryType::REPOSITORY_TYPE_GIT => true,
PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL => true,
PhabricatorRepositoryType::REPOSITORY_TYPE_SVN => true,
);
$c_vcs = $page->getControl('vcs');
$v_vcs = $c_vcs->getValue();
if (!$v_vcs) {
$c_vcs->setError(pht('Required'));
$page->addPageError(
pht('You must select a version control system.'));
} else if (empty($valid[$v_vcs])) {
$c_vcs->setError(pht('Invalid'));
$page->addPageError(
pht('You must select a valid version control system.'));
}
return $c_vcs->isValid();
}
/* -( Page: Name )--------------------------------------------------------- */
private function buildNamePage() {
return id(new PHUIFormPageView())
->setUser($this->getRequest()->getUser())
->setPageName(pht('Repository Name and Location'))
->setValidateFormPageCallback(array($this, 'validateNamePage'))
->addRemarkupInstructions(
pht(
'**Choose a human-readable name for this repository**, like '.
'"CompanyName Mobile App" or "CompanyName Backend Server". You '.
'can change this later.'))
->addControl(
id(new AphrontFormTextControl())
->setName('name')
->setLabel(pht('Name')));
}
public function validateNamePage(PHUIFormPageView $page) {
$c_name = $page->getControl('name');
$v_name = $c_name->getValue();
if (!strlen($v_name)) {
$c_name->setError(pht('Required'));
$page->addPageError(
pht('You must choose a name for this repository.'));
}
return $c_name->isValid();
}
/* -( Page: Remote URI )--------------------------------------------------- */
private function buildRemoteURIPage() {
return id(new PHUIFormPageView())
->setUser($this->getRequest()->getUser())
->setPageName(pht('Repository Remote URI'))
->setValidateFormPageCallback(array($this, 'validateRemoteURIPage'))
->setAdjustFormPageCallback(array($this, 'adjustRemoteURIPage'))
->addControl(
id(new AphrontFormTextControl())
->setName('remoteURI'));
}
public function adjustRemoteURIPage(PHUIFormPageView $page) {
$form = $page->getForm();
$is_git = false;
$is_svn = false;
$is_mercurial = false;
if ($this->getRepository()) {
$vcs = $this->getRepository()->getVersionControlSystem();
} else {
$vcs = $form->getPage('vcs')->getControl('vcs')->getValue();
}
switch ($vcs) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$is_git = true;
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$is_svn = true;
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$is_mercurial = true;
break;
default:
throw new Exception(pht('Unsupported VCS!'));
}
$has_local = ($is_git || $is_mercurial);
if ($is_git) {
$uri_label = pht('Remote URI');
$instructions = pht(
'Enter the URI to clone this Git repository from. It should usually '.
'look like one of these examples:'.
"\n\n".
"| Example Git Remote URIs |\n".
"| ----------------------- |\n".
"| `git@github.com:example/example.git` |\n".
"| `ssh://user@host.com/git/example.git` |\n".
"| `https://example.com/repository.git` |\n");
} else if ($is_mercurial) {
$uri_label = pht('Remote URI');
$instructions = pht(
'Enter the URI to clone this Mercurial repository from. It should '.
'usually look like one of these examples:'.
"\n\n".
"| Example Mercurial Remote URIs |\n".
"| ----------------------- |\n".
"| `ssh://hg@bitbucket.org/example/repository` |\n".
"| `https://bitbucket.org/example/repository` |\n");
} else if ($is_svn) {
$uri_label = pht('Repository Root');
$instructions = pht(
'Enter the **Repository Root** for this Subversion repository. '.
'You can figure this out by running `svn info` in a working copy '.
'and looking at the value in the `Repository Root` field. It '.
'should be a URI and will usually look like these:'.
"\n\n".
"| Example Subversion Repository Root URIs |\n".
"| ------------------------------ |\n".
"| `http://svn.example.org/svnroot/` |\n".
"| `svn+ssh://svn.example.com/svnroot/` |\n".
"| `svn://svn.example.net/svnroot/` |\n".
"\n\n".
"You **MUST** specify the root of the repository, not a ".
"subdirectory. (If you want to import only part of a Subversion ".
"repository, use the //Import Only// option at the end of this ".
"workflow.)");
} else {
throw new Exception(pht('Unsupported VCS!'));
}
$page->addRemarkupInstructions($instructions, 'remoteURI');
$page->getControl('remoteURI')->setLabel($uri_label);
}
public function validateRemoteURIPage(PHUIFormPageView $page) {
$c_remote = $page->getControl('remoteURI');
$v_remote = $c_remote->getValue();
if (!strlen($v_remote)) {
$c_remote->setError(pht('Required'));
$page->addPageError(
pht('You must specify a URI.'));
} else {
try {
PhabricatorRepository::assertValidRemoteURI($v_remote);
} catch (Exception $ex) {
$c_remote->setError(pht('Invalid'));
$page->addPageError($ex->getMessage());
}
}
return $c_remote->isValid();
}
/* -( Page: Authentication )----------------------------------------------- */
public function buildAuthPage() {
return id(new PHUIFormPageView())
->setPageName(pht('Authentication'))
->setUser($this->getRequest()->getUser())
->setAdjustFormPageCallback(array($this, 'adjustAuthPage'))
->addControl(
id(new PassphraseCredentialControl())
->setName('credential'));
}
public function adjustAuthPage($page) {
$form = $page->getForm();
if ($this->getRepository()) {
$vcs = $this->getRepository()->getVersionControlSystem();
} else {
$vcs = $form->getPage('vcs')->getControl('vcs')->getValue();
}
$remote_uri = $form->getPage('remote-uri')
->getControl('remoteURI')
->getValue();
$proto = PhabricatorRepository::getRemoteURIProtocol($remote_uri);
$remote_user = $this->getRemoteURIUser($remote_uri);
$c_credential = $page->getControl('credential');
$c_credential->setDefaultUsername($remote_user);
if ($this->isSSHProtocol($proto)) {
$c_credential->setLabel(pht('SSH Key'));
$c_credential->setCredentialType(
PassphraseSSHPrivateKeyTextCredentialType::CREDENTIAL_TYPE);
$provides_type = PassphraseSSHPrivateKeyCredentialType::PROVIDES_TYPE;
$page->addRemarkupInstructions(
pht(
'Choose or add the SSH credentials to use to connect to the '.
'repository hosted at:'.
"\n\n".
" lang=text\n".
" %s",
$remote_uri),
'credential');
} else if ($this->isUsernamePasswordProtocol($proto)) {
$c_credential->setLabel(pht('Password'));
$c_credential->setAllowNull(true);
$c_credential->setCredentialType(
PassphrasePasswordCredentialType::CREDENTIAL_TYPE);
$provides_type = PassphrasePasswordCredentialType::PROVIDES_TYPE;
$page->addRemarkupInstructions(
pht(
'Choose the username and password used to connect to the '.
'repository hosted at:'.
"\n\n".
" lang=text\n".
" %s".
"\n\n".
"If this repository does not require a username or password, ".
"you can continue to the next step.",
$remote_uri),
'credential');
} else {
throw new Exception(pht('Unknown URI protocol!'));
}
if ($provides_type) {
$viewer = $this->getRequest()->getUser();
$options = id(new PassphraseCredentialQuery())
->setViewer($viewer)
->withIsDestroyed(false)
->withProvidesTypes(array($provides_type))
->execute();
$c_credential->setOptions($options);
}
}
public function validateAuthPage(PHUIFormPageView $page) {
$form = $page->getForm();
$remote_uri = $form->getPage('remote')->getControl('remoteURI')->getValue();
$proto = $this->getRemoteURIProtocol($remote_uri);
$c_credential = $page->getControl('credential');
$v_credential = $c_credential->getValue();
// NOTE: We're using the omnipotent user here because the viewer might be
// editing a repository they're allowed to edit which uses a credential they
// are not allowed to see. This is fine, as long as they don't change it.
$credential = id(new PassphraseCredentialQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs(array($v_credential))
->executeOne();
if ($this->isSSHProtocol($proto)) {
if (!$credential) {
$c_credential->setError(pht('Required'));
$page->addPageError(
pht('You must choose an SSH credential to connect over SSH.'));
}
$ssh_type = PassphraseSSHPrivateKeyCredentialType::PROVIDES_TYPE;
if ($credential->getProvidesType() !== $ssh_type) {
$c_credential->setError(pht('Invalid'));
$page->addPageError(
pht(
'You must choose an SSH credential, not some other type '.
'of credential.'));
}
} else if ($this->isUsernamePasswordProtocol($proto)) {
if ($credential) {
$password_type = PassphrasePasswordCredentialType::PROVIDES_TYPE;
if ($credential->getProvidesType() !== $password_type) {
$c_credential->setError(pht('Invalid'));
$page->addPageError(
pht(
'You must choose a username/password credential, not some other '.
'type of credential.'));
}
}
return $c_credential->isValid();
} else {
return true;
}
}
/* -( Page: Policy )------------------------------------------------------- */
private function buildPolicyPage() {
$viewer = $this->getRequest()->getUser();
if ($this->getRepository()) {
$repository = $this->getRepository();
} else {
$repository = PhabricatorRepository::initializeNewRepository($viewer);
}
$policies = id(new PhabricatorPolicyQuery())
->setViewer($viewer)
->setObject($repository)
->execute();
$view_policy = id(new AphrontFormPolicyControl())
->setUser($viewer)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
->setPolicyObject($repository)
->setPolicies($policies)
->setName('viewPolicy');
$edit_policy = id(new AphrontFormPolicyControl())
->setUser($viewer)
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
->setPolicyObject($repository)
->setPolicies($policies)
->setName('editPolicy');
$push_policy = id(new AphrontFormPolicyControl())
->setUser($viewer)
->setCapability(DiffusionPushCapability::CAPABILITY)
->setPolicyObject($repository)
->setPolicies($policies)
->setName('pushPolicy');
return id(new PHUIFormPageView())
->setPageName(pht('Policies'))
->setValidateFormPageCallback(array($this, 'validatePolicyPage'))
->setAdjustFormPageCallback(array($this, 'adjustPolicyPage'))
->setUser($viewer)
->addRemarkupInstructions(
pht('Select access policies for this repository.'))
->addControl($view_policy)
->addControl($edit_policy)
->addControl($push_policy);
}
public function adjustPolicyPage(PHUIFormPageView $page) {
if ($this->getRepository()) {
$repository = $this->getRepository();
$show_push = $repository->isHosted();
} else {
$show_push = ($this->edit == 'create');
}
if (!$show_push) {
$c_push = $page->getControl('pushPolicy');
$c_push->setHidden(true);
}
}
public function validatePolicyPage(PHUIFormPageView $page) {
$form = $page->getForm();
$viewer = $this->getRequest()->getUser();
$c_view = $page->getControl('viewPolicy');
$c_edit = $page->getControl('editPolicy');
$c_push = $page->getControl('pushPolicy');
$v_view = $c_view->getValue();
$v_edit = $c_edit->getValue();
$v_push = $c_push->getValue();
if ($this->getRepository()) {
$repository = $this->getRepository();
} else {
$repository = PhabricatorRepository::initializeNewRepository($viewer);
}
$proxy = clone $repository;
$proxy->setViewPolicy($v_view);
$proxy->setEditPolicy($v_edit);
$can_view = PhabricatorPolicyFilter::hasCapability(
$viewer,
$proxy,
PhabricatorPolicyCapability::CAN_VIEW);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$proxy,
PhabricatorPolicyCapability::CAN_EDIT);
if (!$can_view) {
$c_view->setError(pht('Invalid'));
$page->addPageError(
pht(
'You can not use the selected policy, because you would be unable '.
'to see the repository.'));
}
if (!$can_edit) {
$c_edit->setError(pht('Invalid'));
$page->addPageError(
pht(
'You can not use the selected edit policy, because you would be '.
'unable to edit the repository.'));
}
return $c_view->isValid() &&
$c_edit->isValid();
}
/* -( Page: Done )--------------------------------------------------------- */
private function buildDonePage() {
$is_create = ($this->edit == 'create');
if ($is_create) {
$now_label = pht('Create Repository Now');
$now_caption = pht(
'Create the repository right away. This will create the repository '.
'using default settings.');
$wait_label = pht('Configure More Options First');
$wait_caption = pht(
'Configure more options before creating the repository. '.
'This will let you fine-tune settings. You can create the repository '.
'whenever you are ready.');
} else {
$now_label = pht('Start Import Now');
$now_caption = pht(
'Start importing the repository right away. This will import '.
'the entire repository using default settings.');
$wait_label = pht('Configure More Options First');
$wait_caption = pht(
'Configure more options before beginning the repository '.
'import. This will let you fine-tune settings. You can '.
'start the import whenever you are ready.');
}
return id(new PHUIFormPageView())
->setPageName(pht('Repository Ready!'))
->setValidateFormPageCallback(array($this, 'validateDonePage'))
->setUser($this->getRequest()->getUser())
->addControl(
id(new AphrontFormRadioButtonControl())
->setName('activate')
->setLabel(pht('Start Now'))
->addButton(
'start',
$now_label,
$now_caption)
->addButton(
'wait',
$wait_label,
$wait_caption));
}
public function validateDonePage(PHUIFormPageView $page) {
$c_activate = $page->getControl('activate');
$v_activate = $c_activate->getValue();
if ($v_activate != 'start' && $v_activate != 'wait') {
$c_activate->setError(pht('Required'));
$page->addPageError(
pht('Make a choice about repository activation.'));
}
return $c_activate->isValid();
}
/* -( Internal )----------------------------------------------------------- */
private function getRemoteURIUser($raw_uri) {
$uri = new PhutilURI($raw_uri);
if ($uri->getUser()) {
return $uri->getUser();
}
$git_uri = new PhutilGitURI($raw_uri);
if (strlen($git_uri->getDomain()) && strlen($git_uri->getPath())) {
return $git_uri->getUser();
}
return null;
}
private function isSSHProtocol($proto) {
return ($proto == 'git' || $proto == 'ssh' || $proto == 'svn+ssh');
}
private function isUsernamePasswordProtocol($proto) {
return ($proto == 'http' || $proto == 'https' || $proto == 'svn');
}
private function setRepository(PhabricatorRepository $repository) {
$this->repository = $repository;
return $this;
}
private function getRepository() {
return $this->repository;
}
}

View file

@ -1,121 +0,0 @@
<?php
final class DiffusionRepositoryEditActionsController
extends DiffusionRepositoryEditController {
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContextForEdit();
if ($response) {
return $response;
}
$viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
// NOTE: We're inverting these here, because the storage is silly.
$v_notify = !$repository->getHumanReadableDetail('herald-disabled');
$v_autoclose = !$repository->getHumanReadableDetail('disable-autoclose');
if ($request->isFormPost()) {
$v_notify = $request->getBool('notify');
$v_autoclose = $request->getBool('autoclose');
$xactions = array();
$template = id(new PhabricatorRepositoryTransaction());
$type_notify = PhabricatorRepositoryTransaction::TYPE_NOTIFY;
$type_autoclose = PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE;
$xactions[] = id(clone $template)
->setTransactionType($type_notify)
->setNewValue($v_notify);
$xactions[] = id(clone $template)
->setTransactionType($type_autoclose)
->setNewValue($v_autoclose);
id(new PhabricatorRepositoryEditor())
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request)
->setActor($viewer)
->applyTransactions($repository, $xactions);
return id(new AphrontRedirectResponse())->setURI($edit_uri);
}
$content = array();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Edit Actions'));
$title = pht('Edit Actions (%s)', $repository->getName());
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon('fa-pencil');
$policies = id(new PhabricatorPolicyQuery())
->setViewer($viewer)
->setObject($repository)
->execute();
$form = id(new AphrontFormView())
->setUser($viewer)
->appendRemarkupInstructions(
pht(
"Normally, Phabricator publishes notifications when it discovers ".
"new commits. You can disable publishing for this repository by ".
"turning off **Notify/Publish**. This will disable notifications, ".
"feed, and Herald (including audits and build plans) for this ".
"repository.\n\n".
"When Phabricator discovers a new commit, it can automatically ".
"close associated revisions and tasks. If you don't want ".
"Phabricator to close objects when it discovers new commits in ".
"this repository, you can disable **Autoclose**."))
->appendChild(
id(new AphrontFormSelectControl())
->setName('notify')
->setLabel(pht('Notify/Publish'))
->setValue((int)$v_notify)
->setOptions(
array(
1 => pht('Enable Notifications, Feed and Herald'),
0 => pht('Disable Notifications, Feed and Herald'),
)))
->appendChild(
id(new AphrontFormSelectControl())
->setName('autoclose')
->setLabel(pht('Autoclose'))
->setValue((int)$v_autoclose)
->setOptions(
array(
1 => pht('Enable Autoclose'),
0 => pht('Disable Autoclose'),
)))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Save Actions'))
->addCancelButton($edit_uri));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Actions'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setForm($form);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
$form_box,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
}

View file

@ -1,7 +1,7 @@
<?php
final class DiffusionRepositoryEditActivateController
extends DiffusionRepositoryEditController {
extends DiffusionRepositoryManageController {
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContextForEdit();
@ -13,7 +13,9 @@ final class DiffusionRepositoryEditActivateController
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
$panel_uri = id(new DiffusionRepositoryBasicsManagementPanel())
->setRepository($repository)
->getPanelURI();
if ($request->isFormPost()) {
if (!$repository->isTracked()) {
@ -33,24 +35,44 @@ final class DiffusionRepositoryEditActivateController
->setActor($viewer)
->applyTransactions($repository, array($xaction));
return id(new AphrontReloadResponse())->setURI($edit_uri);
return id(new AphrontReloadResponse())->setURI($panel_uri);
}
if ($repository->isTracked()) {
return $this->newDialog()
->setTitle(pht('Deactivate Repository?'))
->appendChild(
pht('Deactivate this repository?'))
->addSubmitButton(pht('Deactivate Repository'))
->addCancelButton($edit_uri);
$title = pht('Deactivate Repository');
$body = pht(
'If you deactivate this repository, it will no longer be updated. '.
'Observation and mirroring will cease, and pushing and pulling will '.
'be disabled. You can reactivate the repository later.');
$submit = pht('Deactivate Repository');
} else {
return $this->newDialog()
->setTitle(pht('Activate Repository?'))
->appendChild(
pht('Activate this repository?'))
->addSubmitButton(pht('Activate Repository'))
->addCancelButton($edit_uri);
$title = pht('Activate Repository');
$is_new = $repository->isNewlyInitialized();
if ($is_new) {
if ($repository->isHosted()) {
$body = pht(
'This repository will become a new hosted repository. '.
'It will begin serving read and write traffic.');
} else {
$body = pht(
'This repository will observe an existing remote repository. '.
'It will begin fetching changes from the remote.');
}
} else {
$body = pht(
'This repository will resume updates, observation, mirroring, '.
'and serving any configured read and write traffic.');
}
$submit = pht('Activate Repository');
}
return $this->newDialog()
->setTitle($title)
->appendChild($body)
->addSubmitButton($submit)
->addCancelButton($panel_uri);
}
}

View file

@ -1,93 +0,0 @@
<?php
final class DiffusionRepositoryEditAutomationController
extends DiffusionRepositoryEditController {
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContextForEdit();
if ($response) {
return $response;
}
$viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
if (!$repository->supportsAutomation()) {
return new Aphront404Response();
}
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
$v_blueprints = $repository->getHumanReadableDetail(
'automation.blueprintPHIDs');
if ($request->isFormPost()) {
$v_blueprints = $request->getArr('blueprintPHIDs');
$xactions = array();
$template = id(new PhabricatorRepositoryTransaction());
$type_blueprints =
PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS;
$xactions[] = id(clone $template)
->setTransactionType($type_blueprints)
->setNewValue($v_blueprints);
id(new PhabricatorRepositoryEditor())
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request)
->setActor($viewer)
->applyTransactions($repository, $xactions);
return id(new AphrontRedirectResponse())->setURI($edit_uri);
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Edit Automation'));
$title = pht('Edit %s', $repository->getName());
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon('fa-pencil');
$form = id(new AphrontFormView())
->setUser($viewer)
->appendRemarkupInstructions(
pht(
"Configure **Repository Automation** to allow Phabricator to ".
"write to this repository.".
"\n\n".
"IMPORTANT: This feature is new, experimental, and not supported. ".
"Use it at your own risk."))
->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Use Blueprints'))
->setName('blueprintPHIDs')
->setValue($v_blueprints)
->setDatasource(new DrydockBlueprintDatasource()))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Save'))
->addCancelButton($edit_uri));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Automation'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setForm($form);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
$form_box,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
}

View file

@ -1,193 +0,0 @@
<?php
final class DiffusionRepositoryEditBasicController
extends DiffusionRepositoryEditController {
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContextForEdit();
if ($response) {
return $response;
}
$viewer = $request->getUser();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
$v_name = $repository->getName();
$v_desc = $repository->getDetail('description');
$v_slug = $repository->getRepositorySlug();
$v_callsign = $repository->getCallsign();
$v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs(
$repository->getPHID(),
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
$e_name = true;
$e_slug = null;
$e_callsign = null;
$errors = array();
$validation_exception = null;
if ($request->isFormPost()) {
$v_name = $request->getStr('name');
$v_desc = $request->getStr('description');
$v_projects = $request->getArr('projectPHIDs');
$v_slug = $request->getStr('slug');
$v_callsign = $request->getStr('callsign');
if (!strlen($v_name)) {
$e_name = pht('Required');
$errors[] = pht('Repository name is required.');
} else {
$e_name = null;
}
if (!$errors) {
$xactions = array();
$template = id(new PhabricatorRepositoryTransaction());
$type_name = PhabricatorRepositoryTransaction::TYPE_NAME;
$type_desc = PhabricatorRepositoryTransaction::TYPE_DESCRIPTION;
$type_edge = PhabricatorTransactions::TYPE_EDGE;
$type_slug = PhabricatorRepositoryTransaction::TYPE_SLUG;
$type_callsign = PhabricatorRepositoryTransaction::TYPE_CALLSIGN;
$xactions[] = id(clone $template)
->setTransactionType($type_name)
->setNewValue($v_name);
$xactions[] = id(clone $template)
->setTransactionType($type_desc)
->setNewValue($v_desc);
$xactions[] = id(clone $template)
->setTransactionType($type_slug)
->setNewValue($v_slug);
$xactions[] = id(clone $template)
->setTransactionType($type_callsign)
->setNewValue($v_callsign);
$xactions[] = id(clone $template)
->setTransactionType($type_edge)
->setMetadataValue(
'edge:type',
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST)
->setNewValue(
array(
'=' => array_fuse($v_projects),
));
$editor = id(new PhabricatorRepositoryEditor())
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request)
->setActor($viewer);
try {
$editor->applyTransactions($repository, $xactions);
// The preferred edit URI may have changed if the callsign or slug
// were adjusted, so grab a fresh copy.
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
return id(new AphrontRedirectResponse())->setURI($edit_uri);
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex;
$e_slug = $ex->getShortMessage($type_slug);
$e_callsign = $ex->getShortMessage($type_callsign);
}
}
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Edit Basics'));
$title = pht('Edit %s', $repository->getName());
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon('fa-pencil');
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new AphrontFormTextControl())
->setName('name')
->setLabel(pht('Name'))
->setValue($v_name)
->setError($e_name))
->appendChild(
id(new AphrontFormTextControl())
->setName('slug')
->setLabel(pht('Short Name'))
->setValue($v_slug)
->setError($e_slug))
->appendChild(
id(new AphrontFormTextControl())
->setName('callsign')
->setLabel(pht('Callsign'))
->setValue($v_callsign)
->setError($e_callsign))
->appendChild(
id(new PhabricatorRemarkupControl())
->setUser($viewer)
->setName('description')
->setLabel(pht('Description'))
->setValue($v_desc))
->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorProjectDatasource())
->setName('projectPHIDs')
->setLabel(pht('Projects'))
->setValue($v_projects))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Save'))
->addCancelButton($edit_uri))
->appendChild(id(new PHUIFormDividerControl()))
->appendRemarkupInstructions($this->getReadmeInstructions());
$form_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Basic Information'))
->setValidationException($validation_exception)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setForm($form)
->setFormErrors($errors);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
$form_box,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function getReadmeInstructions() {
return pht(<<<EOTEXT
You can also create a `%s` file at the repository root (or in any
subdirectory) to provide information about the repository. These formats are
supported:
| File Name | Rendered As... |
|-----------|-----------------|
| `%s` | Plain Text |
| `%s` | Plain Text |
| `%s` | Remarkup |
| `%s` | Remarkup |
| `%s` | \xC2\xA1Fiesta! |
EOTEXT
,
'README',
'README',
'README.txt',
'README.remarkup',
'README.md',
'README.rainbow');
}
}

View file

@ -1,248 +0,0 @@
<?php
final class DiffusionRepositoryEditBranchesController
extends DiffusionRepositoryEditController {
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContextForEdit();
if ($response) {
return $response;
}
$viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$is_git = false;
$is_hg = false;
switch ($repository->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$is_git = true;
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$is_hg = true;
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
throw new Exception(
pht('Subversion does not support branches!'));
default:
throw new Exception(
pht('Repository has unknown version control system!'));
}
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
$v_default = $repository->getHumanReadableDetail('default-branch');
$v_track = $repository->getDetail(
'branch-filter',
array());
$v_track = array_keys($v_track);
$v_autoclose = $repository->getDetail(
'close-commits-filter',
array());
$v_autoclose = array_keys($v_autoclose);
$e_track = null;
$e_autoclose = null;
$validation_exception = null;
if ($request->isFormPost()) {
$v_default = $request->getStr('default');
$v_track = $this->processBranches($request->getStr('track'));
if (!$is_hg) {
$v_autoclose = $this->processBranches($request->getStr('autoclose'));
}
$xactions = array();
$template = id(new PhabricatorRepositoryTransaction());
$type_default = PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH;
$type_track = PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY;
$type_autoclose = PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY;
$xactions[] = id(clone $template)
->setTransactionType($type_default)
->setNewValue($v_default);
$xactions[] = id(clone $template)
->setTransactionType($type_track)
->setNewValue($v_track);
if (!$is_hg) {
$xactions[] = id(clone $template)
->setTransactionType($type_autoclose)
->setNewValue($v_autoclose);
}
$editor = id(new PhabricatorRepositoryEditor())
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request)
->setActor($viewer);
try {
$editor->applyTransactions($repository, $xactions);
return id(new AphrontRedirectResponse())->setURI($edit_uri);
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex;
$e_track = $validation_exception->getShortMessage($type_track);
$e_autoclose = $validation_exception->getShortMessage($type_autoclose);
}
}
$content = array();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Edit Branches'));
$title = pht('Edit Branches (%s)', $repository->getName());
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon('fa-pencil');
$policies = id(new PhabricatorPolicyQuery())
->setViewer($viewer)
->setObject($repository)
->execute();
$rows = array();
$rows[] = array(
array(
'master',
),
pht('Select only master.'),
);
$rows[] = array(
array(
'master',
'develop',
'release',
),
pht('Select %s, %s, and %s.', 'master', 'develop', 'release'),
);
$rows[] = array(
array(
'master',
'regexp(/^release-/)',
),
pht('Select master, and all branches which start with "%s".', 'release-'),
);
$rows[] = array(
array(
'regexp(/^(?!temp-)/)',
),
pht('Select all branches which do not start with "%s".', 'temp-'),
);
foreach ($rows as $k => $row) {
$rows[$k][0] = phutil_tag(
'pre',
array(),
implode("\n", $row[0]));
}
$example_table = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('Example'),
pht('Effect'),
))
->setColumnClasses(
array(
'',
'wide',
));
$v_track = implode("\n", $v_track);
$v_autoclose = implode("\n", $v_autoclose);
$form = id(new AphrontFormView())
->setUser($viewer)
->appendRemarkupInstructions(
pht('You can choose a **Default Branch** for viewing this repository.'))
->appendChild(
id(new AphrontFormTextControl())
->setName('default')
->setLabel(pht('Default Branch'))
->setValue($v_default))
->appendRemarkupInstructions(
pht(
'If you want to import only some branches into Diffusion, you can '.
'list them in **Track Only**. Other branches will be ignored. If '.
'you do not specify any branches, all branches are tracked.'));
if (!$is_hg) {
$form->appendRemarkupInstructions(
pht(
'If you have **Autoclose** enabled for this repository, Phabricator '.
'can close tasks and revisions when corresponding commits are '.
'pushed to the repository. If you want to autoclose objects only '.
'when commits appear on specific branches, you can list those '.
'branches in **Autoclose Only**. By default, all tracked branches '.
'will autoclose objects.'));
}
$form
->appendRemarkupInstructions(
pht(
'When specifying branches, you should enter one branch name per '.
'line. You can use regular expressions to match branches by '.
'wrapping an expression in `%s`. For example:',
'regexp(...)'))
->appendChild(
id(new AphrontFormMarkupControl())
->setValue($example_table))
->appendChild(
id(new AphrontFormTextAreaControl())
->setName('track')
->setLabel(pht('Track Only'))
->setError($e_track)
->setValue($v_track));
if (!$is_hg) {
$form->appendChild(
id(new AphrontFormTextAreaControl())
->setName('autoclose')
->setLabel(pht('Autoclose Only'))
->setError($e_autoclose)
->setValue($v_autoclose));
}
$form->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Save Branches'))
->addCancelButton($edit_uri));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Branches'))
->setValidationException($validation_exception)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setForm($form);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
$form_box,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function processBranches($string) {
$lines = phutil_split_lines($string, $retain_endings = false);
foreach ($lines as $key => $line) {
$lines[$key] = trim($line);
if (!strlen($lines[$key])) {
unset($lines[$key]);
}
}
return array_values($lines);
}
}

View file

@ -1,28 +1,69 @@
<?php
abstract class DiffusionRepositoryEditController
extends DiffusionController {
final class DiffusionRepositoryEditController
extends DiffusionRepositoryManageController {
protected function buildApplicationCrumbs($is_main = false) {
$crumbs = parent::buildApplicationCrumbs();
public function handleRequest(AphrontRequest $request) {
$engine = id(new DiffusionRepositoryEditEngine())
->setController($this);
if ($this->hasDiffusionRequest()) {
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$repo_uri = $repository->getURI();
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
$id = $request->getURIData('id');
if (!$id) {
$this->requireApplicationCapability(
DiffusionCreateRepositoriesCapability::CAPABILITY);
$crumbs->addTextCrumb($repository->getDisplayname(), $repo_uri);
if ($is_main) {
$crumbs->addTextCrumb(pht('Edit Repository'));
} else {
$crumbs->addTextCrumb(pht('Edit'), $edit_uri);
$vcs = $request->getStr('vcs');
$vcs_types = PhabricatorRepositoryType::getRepositoryTypeMap();
if (empty($vcs_types[$vcs])) {
return $this->buildVCSTypeResponse();
}
$engine
->addContextParameter('vcs', $vcs)
->setVersionControlSystem($vcs);
}
return $engine->buildResponse();
}
private function buildVCSTypeResponse() {
$vcs_types = PhabricatorRepositoryType::getRepositoryTypeMap();
$request = $this->getRequest();
$viewer = $this->getViewer();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Create Repository'));
$crumbs->setBorder(true);
return $crumbs;
$title = pht('Choose Repository Type');
$header = id(new PHUIHeaderView())
->setHeader(pht('Create Repository'))
->setHeaderIcon('fa-plus-square');
$layout = id(new AphrontMultiColumnView())
->setFluidLayout(true);
$create_uri = $request->getRequestURI();
foreach ($vcs_types as $vcs_key => $vcs_type) {
$action = id(new PHUIActionPanelView())
->setIcon(idx($vcs_type, 'icon'))
->setHeader(idx($vcs_type, 'create.header'))
->setHref($create_uri->alter('vcs', $vcs_key))
->setSubheader(idx($vcs_type, 'create.subheader'));
$layout->addColumn($action);
}
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter($layout);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
}

View file

@ -1,7 +1,7 @@
<?php
final class DiffusionRepositoryEditDangerousController
extends DiffusionRepositoryEditController {
extends DiffusionRepositoryManageController {
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContextForEdit();
@ -13,11 +13,31 @@ final class DiffusionRepositoryEditDangerousController
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
if (!$repository->canAllowDangerousChanges()) {
return new Aphront400Response();
}
$panel_uri = id(new DiffusionRepositoryBasicsManagementPanel())
->setRepository($repository)
->getPanelURI();
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
if (!$repository->canAllowDangerousChanges()) {
if ($repository->isSVN()) {
return $this->newDialog()
->setTitle(pht('Not in Danger'))
->appendParagraph(
pht(
'It is not possible for users to push any dangerous changes '.
'to a Subversion repository. Pushes to a Subversion repository '.
'can always be reverted and never destroy data.'))
->addCancelButton($panel_uri);
} else {
return $this->newDialog()
->setTitle(pht('Unprotectable Repository'))
->appendParagraph(
pht(
'This repository can not be protected from dangerous changes '.
'because Phabricator does not control what users are allowed '.
'to push to it.'))
->addCancelButton($panel_uri);
}
}
if ($request->isFormPost()) {
$xaction = id(new PhabricatorRepositoryTransaction())
@ -30,33 +50,33 @@ final class DiffusionRepositoryEditDangerousController
->setActor($viewer)
->applyTransactions($repository, array($xaction));
return id(new AphrontReloadResponse())->setURI($edit_uri);
return id(new AphrontReloadResponse())->setURI($panel_uri);
}
$force = phutil_tag('tt', array(), '--force');
if ($repository->shouldAllowDangerousChanges()) {
return $this->newDialog()
->setTitle(pht('Prevent Dangerous changes?'))
->appendChild(
pht(
$title = pht('Prevent Dangerous Changes');
$body = pht(
'It will no longer be possible to delete branches from this '.
'repository, or %s push to this repository.',
$force))
->addSubmitButton(pht('Prevent Dangerous Changes'))
->addCancelButton($edit_uri);
$force);
$submit = pht('Prevent Dangerous Changes');
} else {
return $this->newDialog()
->setTitle(pht('Allow Dangerous Changes?'))
->appendChild(
pht(
$title = pht('Allow Dangerous Changes');
$body = pht(
'If you allow dangerous changes, it will be possible to delete '.
'branches and %s push this repository. These operations can '.
'alter a repository in a way that is difficult to recover from.',
$force))
->addSubmitButton(pht('Allow Dangerous Changes'))
->addCancelButton($edit_uri);
$force);
$submit = pht('Allow Dangerous Changes');
}
return $this->newDialog()
->setTitle($title)
->appendParagraph($body)
->addSubmitButton($submit)
->addCancelButton($panel_uri);
}
}

View file

@ -1,7 +1,7 @@
<?php
final class DiffusionRepositoryEditDeleteController
extends DiffusionRepositoryEditController {
extends DiffusionRepositoryManageController {
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContextForEdit();
@ -13,7 +13,9 @@ final class DiffusionRepositoryEditDeleteController
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
$panel_uri = id(new DiffusionRepositoryBasicsManagementPanel())
->setRepository($repository)
->getPanelURI();
$dialog = new AphrontDialogView();
$text_1 = pht(
@ -40,7 +42,7 @@ final class DiffusionRepositoryEditDeleteController
return $this->newDialog()
->setTitle(pht('Really want to delete the repository?'))
->appendChild($body)
->addCancelButton($edit_uri, pht('Okay'));
->addCancelButton($panel_uri, pht('Okay'));
}
}

View file

@ -1,107 +0,0 @@
<?php
final class DiffusionRepositoryEditEncodingController
extends DiffusionRepositoryEditController {
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContextForEdit();
if ($response) {
return $response;
}
$user = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
$v_encoding = $repository->getDetail('encoding');
$e_encoding = null;
$errors = array();
if ($request->isFormPost()) {
$v_encoding = $request->getStr('encoding');
if (!$errors) {
$xactions = array();
$template = id(new PhabricatorRepositoryTransaction());
$type_encoding = PhabricatorRepositoryTransaction::TYPE_ENCODING;
$xactions[] = id(clone $template)
->setTransactionType($type_encoding)
->setNewValue($v_encoding);
try {
id(new PhabricatorRepositoryEditor())
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request)
->setActor($user)
->applyTransactions($repository, $xactions);
return id(new AphrontRedirectResponse())->setURI($edit_uri);
} catch (Exception $ex) {
$errors[] = $ex->getMessage();
}
}
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Edit Encoding'));
$title = pht('Edit %s', $repository->getName());
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon('fa-pencil');
$form = id(new AphrontFormView())
->setUser($user)
->appendRemarkupInstructions($this->getEncodingInstructions())
->appendChild(
id(new AphrontFormTextControl())
->setName('encoding')
->setLabel(pht('Text Encoding'))
->setValue($v_encoding)
->setError($e_encoding))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Save Encoding'))
->addCancelButton($edit_uri));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Encoding'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setForm($form)
->setFormErrors($errors);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
$form_box,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function getEncodingInstructions() {
return pht(<<<EOT
If source code in this repository uses a character encoding other than UTF-8
(for example, `ISO-8859-1`), specify it here.
**Normally, you can leave this field blank.** If your source code is written in
ASCII or UTF-8, everything will work correctly.
Source files will be translated from the specified encoding to UTF-8 when they
are read from the repository, before they are displayed in Diffusion.
See [[%s | UTF-8 and Character Encoding]] for more information on how
Phabricator handles text encodings.
EOT
,
PhabricatorEnv::getDoclink('User Guide: UTF-8 and Character Encoding'));
}
}

View file

@ -1,288 +0,0 @@
<?php
final class DiffusionRepositoryEditHostingController
extends DiffusionRepositoryEditController {
private $serve;
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContextForEdit();
if ($response) {
return $response;
}
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$this->serve = $request->getURIData('serve');
if (!$this->serve) {
return $this->handleHosting($repository);
} else {
return $this->handleProtocols($repository);
}
}
public function handleHosting(PhabricatorRepository $repository) {
$request = $this->getRequest();
$user = $request->getUser();
$v_hosting = $repository->isHosted();
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
$next_uri = $this->getRepositoryControllerURI($repository, 'edit/serve/');
if ($request->isFormPost()) {
$v_hosting = $request->getBool('hosting');
$xactions = array();
$template = id(new PhabricatorRepositoryTransaction());
$type_hosting = PhabricatorRepositoryTransaction::TYPE_HOSTING;
$xactions[] = id(clone $template)
->setTransactionType($type_hosting)
->setNewValue($v_hosting);
id(new PhabricatorRepositoryEditor())
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request)
->setActor($user)
->applyTransactions($repository, $xactions);
return id(new AphrontRedirectResponse())->setURI($next_uri);
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Edit Hosting'));
$title = pht('Edit Hosting (%s)', $repository->getName());
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon('fa-pencil');
$hosted_control = id(new AphrontFormRadioButtonControl())
->setName('hosting')
->setLabel(pht('Hosting'))
->addButton(
true,
pht('Host Repository on Phabricator'),
pht(
'Phabricator will host this repository. Users will be able to '.
'push commits to Phabricator. Phabricator will not pull '.
'changes from elsewhere.'))
->addButton(
false,
pht('Host Repository Elsewhere'),
pht(
'Phabricator will pull updates to this repository from a master '.
'repository elsewhere (for example, on GitHub or Bitbucket). '.
'Users will not be able to push commits to this repository.'))
->setValue($v_hosting);
$doc_href = PhabricatorEnv::getDoclink(
'Diffusion User Guide: Repository Hosting');
$form = id(new AphrontFormView())
->setUser($user)
->appendRemarkupInstructions(
pht(
'Phabricator can host repositories, or it can track repositories '.
'hosted elsewhere (like on GitHub or Bitbucket). For information '.
'on configuring hosting, see [[ %s | Diffusion User Guide: '.
'Repository Hosting]]',
$doc_href))
->appendChild($hosted_control)
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Save and Continue'))
->addCancelButton($edit_uri));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Hosting'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setForm($form);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
$form_box,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
public function handleProtocols(PhabricatorRepository $repository) {
$request = $this->getRequest();
$user = $request->getUser();
$type = $repository->getVersionControlSystem();
$is_svn = ($type == PhabricatorRepositoryType::REPOSITORY_TYPE_SVN);
$v_http_mode = $repository->getDetail(
'serve-over-http',
PhabricatorRepository::SERVE_OFF);
$v_ssh_mode = $repository->getDetail(
'serve-over-ssh',
PhabricatorRepository::SERVE_OFF);
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
$prev_uri = $this->getRepositoryControllerURI($repository, 'edit/hosting/');
if ($request->isFormPost()) {
$v_http_mode = $request->getStr('http');
$v_ssh_mode = $request->getStr('ssh');
$xactions = array();
$template = id(new PhabricatorRepositoryTransaction());
$type_http = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP;
$type_ssh = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH;
if (!$is_svn) {
$xactions[] = id(clone $template)
->setTransactionType($type_http)
->setNewValue($v_http_mode);
}
$xactions[] = id(clone $template)
->setTransactionType($type_ssh)
->setNewValue($v_ssh_mode);
id(new PhabricatorRepositoryEditor())
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request)
->setActor($user)
->applyTransactions($repository, $xactions);
return id(new AphrontRedirectResponse())->setURI($edit_uri);
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Edit Protocols'));
$title = pht('Edit Protocols (%s)', $repository->getName());
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon('fa-pencil');
$rw_message = pht(
'Phabricator will serve a read-write copy of this repository.');
if (!$repository->isHosted()) {
$rw_message = array(
$rw_message,
phutil_tag('br'),
phutil_tag('br'),
pht(
'%s: This repository is hosted elsewhere, so Phabricator can not '.
'perform writes. This mode will act like "Read Only" for '.
'repositories hosted elsewhere.',
phutil_tag('strong', array(), pht('WARNING'))),
);
}
$ssh_control =
id(new AphrontFormRadioButtonControl())
->setName('ssh')
->setLabel(pht('SSH'))
->setValue($v_ssh_mode)
->addButton(
PhabricatorRepository::SERVE_OFF,
PhabricatorRepository::getProtocolAvailabilityName(
PhabricatorRepository::SERVE_OFF),
pht('Phabricator will not serve this repository over SSH.'))
->addButton(
PhabricatorRepository::SERVE_READONLY,
PhabricatorRepository::getProtocolAvailabilityName(
PhabricatorRepository::SERVE_READONLY),
pht(
'Phabricator will serve a read-only copy of this repository '.
'over SSH.'))
->addButton(
PhabricatorRepository::SERVE_READWRITE,
PhabricatorRepository::getProtocolAvailabilityName(
PhabricatorRepository::SERVE_READWRITE),
$rw_message);
$http_control =
id(new AphrontFormRadioButtonControl())
->setName('http')
->setLabel(pht('HTTP'))
->setValue($v_http_mode)
->addButton(
PhabricatorRepository::SERVE_OFF,
PhabricatorRepository::getProtocolAvailabilityName(
PhabricatorRepository::SERVE_OFF),
pht('Phabricator will not serve this repository over HTTP.'))
->addButton(
PhabricatorRepository::SERVE_READONLY,
PhabricatorRepository::getProtocolAvailabilityName(
PhabricatorRepository::SERVE_READONLY),
pht(
'Phabricator will serve a read-only copy of this repository '.
'over HTTP.'))
->addButton(
PhabricatorRepository::SERVE_READWRITE,
PhabricatorRepository::getProtocolAvailabilityName(
PhabricatorRepository::SERVE_READWRITE),
$rw_message);
if ($is_svn) {
$http_control = id(new AphrontFormMarkupControl())
->setLabel(pht('HTTP'))
->setValue(
phutil_tag(
'em',
array(),
pht(
'Phabricator does not currently support HTTP access to '.
'Subversion repositories.')));
}
$form = id(new AphrontFormView())
->setUser($user)
->appendRemarkupInstructions(
pht(
'Phabricator can serve repositories over various protocols. You can '.
'configure server protocols here.'))
->appendChild($ssh_control);
if (!PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth')) {
$form->appendRemarkupInstructions(
pht(
'NOTE: The configuration setting [[ %s | %s ]] is currently '.
'disabled. You must enable it to activate authenticated access '.
'to repositories over HTTP.',
'/config/edit/diffusion.allow-http-auth/',
'diffusion.allow-http-auth'));
}
$form
->appendChild($http_control)
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Save Changes'))
->addCancelButton($prev_uri, pht('Back')));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Protocols'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setForm($form);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
$form_box,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
}

View file

@ -1,91 +0,0 @@
<?php
final class DiffusionRepositoryEditStagingController
extends DiffusionRepositoryEditController {
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContextForEdit();
if ($response) {
return $response;
}
$viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
if (!$repository->supportsStaging()) {
return new Aphront404Response();
}
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
$v_area = $repository->getHumanReadableDetail('staging-uri');
if ($request->isFormPost()) {
$v_area = $request->getStr('area');
$xactions = array();
$template = id(new PhabricatorRepositoryTransaction());
$type_encoding = PhabricatorRepositoryTransaction::TYPE_STAGING_URI;
$xactions[] = id(clone $template)
->setTransactionType($type_encoding)
->setNewValue($v_area);
id(new PhabricatorRepositoryEditor())
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request)
->setActor($viewer)
->applyTransactions($repository, $xactions);
return id(new AphrontRedirectResponse())->setURI($edit_uri);
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Edit Staging'));
$title = pht('Edit Staging (%s)', $repository->getName());
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon('fa-pencil');
$form = id(new AphrontFormView())
->setUser($viewer)
->appendRemarkupInstructions(
pht(
"To make it easier to run integration tests and builds on code ".
"under review, you can configure a **Staging Area**. When `arc` ".
"creates a diff, it will push a copy of the changes to the ".
"configured staging area with a corresponding tag.".
"\n\n".
"IMPORTANT: This feature is new, experimental, and not supported. ".
"Use it at your own risk."))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Staging Area URI'))
->setName('area')
->setValue($v_area))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Save'))
->addCancelButton($edit_uri));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Staging'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setForm($form);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
$form_box,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
}

View file

@ -1,80 +0,0 @@
<?php
final class DiffusionRepositoryEditStorageController
extends DiffusionRepositoryEditController {
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContextForEdit();
if ($response) {
return $response;
}
$viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
$v_local = $repository->getHumanReadableDetail('local-path');
$errors = array();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Edit Storage'));
$title = pht('Edit %s', $repository->getName());
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon('fa-pencil');
$service_phid = $repository->getAlmanacServicePHID();
if ($service_phid) {
$handles = $this->loadViewerHandles(array($service_phid));
$v_service = $handles[$service_phid]->renderLink();
} else {
$v_service = phutil_tag(
'em',
array(),
pht('Local'));
}
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Storage Service'))
->setValue($v_service))
->appendChild(
id(new AphrontFormMarkupControl())
->setName('local')
->setLabel(pht('Storage Path'))
->setValue($v_local))
->appendRemarkupInstructions(
pht(
"You can not adjust the local path for this repository from the ".
"web interface. To edit it, run this command:\n\n %s",
sprintf(
'phabricator/ $ ./bin/repository edit %s --as %s --local-path ...',
$repository->getMonogram(),
$viewer->getUsername())))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($edit_uri, pht('Done')));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Storage'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setForm($form);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
$form_box,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
}

View file

@ -1,118 +0,0 @@
<?php
final class DiffusionRepositoryEditSubversionController
extends DiffusionRepositoryEditController {
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContextForEdit();
if ($response) {
return $response;
}
$viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
switch ($repository->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
throw new Exception(
pht('Git and Mercurial do not support editing SVN properties!'));
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
break;
default:
throw new Exception(
pht('Repository has unknown version control system!'));
}
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
$v_subpath = $repository->getHumanReadableDetail('svn-subpath');
$v_uuid = $repository->getUUID();
if ($request->isFormPost()) {
$v_subpath = $request->getStr('subpath');
$v_uuid = $request->getStr('uuid');
$xactions = array();
$template = id(new PhabricatorRepositoryTransaction());
$type_subpath = PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH;
$type_uuid = PhabricatorRepositoryTransaction::TYPE_UUID;
$xactions[] = id(clone $template)
->setTransactionType($type_subpath)
->setNewValue($v_subpath);
$xactions[] = id(clone $template)
->setTransactionType($type_uuid)
->setNewValue($v_uuid);
id(new PhabricatorRepositoryEditor())
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request)
->setActor($viewer)
->applyTransactions($repository, $xactions);
return id(new AphrontRedirectResponse())->setURI($edit_uri);
}
$content = array();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Edit Subversion Info'));
$title = pht('Edit Subversion Info (%s)', $repository->getName());
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon('fa-pencil');
$policies = id(new PhabricatorPolicyQuery())
->setViewer($viewer)
->setObject($repository)
->execute();
$form = id(new AphrontFormView())
->setUser($viewer)
->appendRemarkupInstructions(
pht(
"You can set the **Repository UUID**, which will help Phabriactor ".
"provide better context in some cases. You can find the UUID of a ".
"repository by running `%s`.\n\n".
"If you want to import only part of a repository, like `trunk/`, ".
"you can set a path in **Import Only**. Phabricator will ignore ".
"commits which do not affect this path.",
'svn info'))
->appendChild(
id(new AphrontFormTextControl())
->setName('uuid')
->setLabel(pht('Repository UUID'))
->setValue($v_uuid))
->appendChild(
id(new AphrontFormTextControl())
->setName('subpath')
->setLabel(pht('Import Only'))
->setValue($v_subpath))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Save Subversion Info'))
->addCancelButton($edit_uri));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Subversion'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setForm($form);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
$form_box,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
}

View file

@ -1,7 +1,7 @@
<?php
final class DiffusionRepositoryEditUpdateController
extends DiffusionRepositoryEditController {
extends DiffusionRepositoryManageController {
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContextForEdit();
@ -13,7 +13,9 @@ final class DiffusionRepositoryEditUpdateController
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
$panel_uri = id(new DiffusionRepositoryStatusManagementPanel())
->setRepository($repository)
->getPanelURI();
if ($request->isFormPost()) {
$params = array(
@ -26,7 +28,7 @@ final class DiffusionRepositoryEditUpdateController
->setUser($viewer)
->execute();
return id(new AphrontRedirectResponse())->setURI($edit_uri);
return id(new AphrontRedirectResponse())->setURI($panel_uri);
}
$doc_name = 'Diffusion User Guide: Repository Updates';
@ -58,7 +60,7 @@ final class DiffusionRepositoryEditUpdateController
'To learn more about how Phabricator updates repositories, '.
'read %s in the documentation.',
$doc_link))
->addCancelButton($edit_uri)
->addCancelButton($panel_uri)
->addSubmitButton(pht('Schedule Update'));
}

View file

@ -1,69 +0,0 @@
<?php
final class DiffusionRepositoryEditproController
extends DiffusionRepositoryEditController {
public function handleRequest(AphrontRequest $request) {
$engine = id(new DiffusionRepositoryEditEngine())
->setController($this);
$id = $request->getURIData('id');
if (!$id) {
$this->requireApplicationCapability(
DiffusionCreateRepositoriesCapability::CAPABILITY);
$vcs = $request->getStr('vcs');
$vcs_types = PhabricatorRepositoryType::getRepositoryTypeMap();
if (empty($vcs_types[$vcs])) {
return $this->buildVCSTypeResponse();
}
$engine
->addContextParameter('vcs', $vcs)
->setVersionControlSystem($vcs);
}
return $engine->buildResponse();
}
private function buildVCSTypeResponse() {
$vcs_types = PhabricatorRepositoryType::getRepositoryTypeMap();
$request = $this->getRequest();
$viewer = $this->getViewer();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Create Repository'));
$crumbs->setBorder(true);
$title = pht('Choose Repository Type');
$header = id(new PHUIHeaderView())
->setHeader(pht('Create Repository'))
->setHeaderIcon('fa-plus-square');
$layout = id(new AphrontMultiColumnView())
->setFluidLayout(true);
$create_uri = $request->getRequestURI();
foreach ($vcs_types as $vcs_key => $vcs_type) {
$action = id(new PHUIActionPanelView())
->setIcon(idx($vcs_type, 'icon'))
->setHeader(idx($vcs_type, 'create.header'))
->setHref($create_uri->alter('vcs', $vcs_key))
->setSubheader(idx($vcs_type, 'create.subheader'));
$layout->addColumn($action);
}
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter($layout);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
}

View file

@ -15,16 +15,9 @@ final class DiffusionRepositoryListController extends DiffusionController {
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$can_create = $this->hasApplicationCapability(
DiffusionCreateRepositoriesCapability::CAPABILITY);
$crumbs->addAction(
id(new PHUIListItemView())
->setName(pht('New Repository'))
->setHref($this->getApplicationURI('new/'))
->setDisabled(!$can_create)
->setWorkflow(!$can_create)
->setIcon('fa-plus-square'));
id(new DiffusionRepositoryEditEngine())
->setViewer($this->getViewer())
->addActionToCrumbs($crumbs);
return $crumbs;
}

View file

@ -1,129 +1,25 @@
<?php
final class DiffusionRepositoryManageController
abstract class DiffusionRepositoryManageController
extends DiffusionController {
private $navigation;
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
public function buildApplicationMenu() {
// TODO: This is messy for now; the mobile menu should be set automatically
// when the body content is a two-column view with navigation.
if ($this->navigation) {
return $this->navigation->getMenu();
}
return parent::buildApplicationMenu();
}
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContext();
if ($response) {
return $response;
}
$viewer = $this->getViewer();
if ($this->hasDiffusionRequest()) {
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$panels = DiffusionRepositoryManagementPanel::getAllPanels();
foreach ($panels as $panel) {
$panel
->setViewer($viewer)
->setRepository($repository)
->setController($this);
}
$selected = $request->getURIData('panel');
if (!strlen($selected)) {
$selected = head_key($panels);
}
if (empty($panels[$selected])) {
return new Aphront404Response();
}
$nav = $this->renderSideNav($repository, $panels, $selected);
$this->navigation = $nav;
$panel = $panels[$selected];
$content = $panel->buildManagementPanelContent();
$title = array(
$panel->getManagementPanelLabel(),
$repository->getDisplayName(),
);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(
$repository->getDisplayName(),
$repository->getURI());
$crumbs->addTextCrumb(
pht('Manage'),
$repository->getPathURI('manage/'));
$crumbs->addTextCrumb($panel->getManagementPanelLabel());
$header_text = pht(
'%s: %s',
$repository->getDisplayName(),
$panel->getManagementPanelLabel());
$header = id(new PHUIHeaderView())
->setHeader($header_text)
->setHeaderIcon('fa-pencil');
if ($repository->isTracked()) {
$header->setStatus('fa-check', 'bluegrey', pht('Active'));
} else {
$header->setStatus('fa-ban', 'dark', pht('Inactive'));
}
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setNavigation($nav)
->setMainColumn($content);
$curtain = $panel->buildManagementPanelCurtain();
if ($curtain) {
$view->setCurtain($curtain);
return $crumbs;
}
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function renderSideNav(
PhabricatorRepository $repository,
array $panels,
$selected) {
$base_uri = $repository->getPathURI('manage/');
$base_uri = new PhutilURI($base_uri);
$nav = id(new AphrontSideNavFilterView())
->setBaseURI($base_uri);
foreach ($panels as $panel) {
$nav->addFilter(
$panel->getManagementPanelKey(),
$panel->getManagementPanelLabel());
}
$nav->selectFilter($selected);
return $nav;
}
public function newTimeline(PhabricatorRepository $repository) {
$timeline = $this->buildTransactionTimeline(
$repository,
new PhabricatorRepositoryTransactionQuery());
$timeline->setShouldTerminate(true);
return $timeline;
}
}

View file

@ -0,0 +1,146 @@
<?php
final class DiffusionRepositoryManagePanelsController
extends DiffusionRepositoryManageController {
private $navigation;
public function buildApplicationMenu() {
// TODO: This is messy for now; the mobile menu should be set automatically
// when the body content is a two-column view with navigation.
if ($this->navigation) {
return $this->navigation->getMenu();
}
return parent::buildApplicationMenu();
}
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContext();
if ($response) {
return $response;
}
$viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$panels = DiffusionRepositoryManagementPanel::getAllPanels();
foreach ($panels as $key => $panel) {
$panel
->setViewer($viewer)
->setRepository($repository)
->setController($this);
if (!$panel->shouldEnableForRepository($repository)) {
unset($panels[$key]);
continue;
}
}
$selected = $request->getURIData('panel');
if (!strlen($selected)) {
$selected = head_key($panels);
}
if (empty($panels[$selected])) {
return new Aphront404Response();
}
$nav = $this->renderSideNav($repository, $panels, $selected);
$this->navigation = $nav;
$panel = $panels[$selected];
$content = $panel->buildManagementPanelContent();
$title = array(
$panel->getManagementPanelLabel(),
$repository->getDisplayName(),
);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($panel->getManagementPanelLabel());
$crumbs->setBorder(true);
$header_text = pht(
'%s: %s',
$repository->getDisplayName(),
$panel->getManagementPanelLabel());
$header = id(new PHUIHeaderView())
->setHeader($header_text)
->setHeaderIcon('fa-pencil');
if ($repository->isTracked()) {
$header->setStatus('fa-check', 'bluegrey', pht('Active'));
} else {
$header->setStatus('fa-ban', 'dark', pht('Inactive'));
}
$header->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setText(pht('View Repository'))
->setHref($repository->getURI())
->setIcon('fa-code'));
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setNavigation($nav)
->setMainColumn($content);
$curtain = $panel->buildManagementPanelCurtain();
if ($curtain) {
$view->setCurtain($curtain);
}
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function renderSideNav(
PhabricatorRepository $repository,
array $panels,
$selected) {
$base_uri = $repository->getPathURI('manage/');
$base_uri = new PhutilURI($base_uri);
$nav = id(new AphrontSideNavFilterView())
->setBaseURI($base_uri);
foreach ($panels as $panel) {
$key = $panel->getManagementPanelKey();
$label = $panel->getManagementPanelLabel();
$icon = $panel->getManagementPanelIcon();
$href = $panel->getPanelNavigationURI();
$item = id(new PHUIListItemView())
->setKey($key)
->setName($label)
->setType(PHUIListItemView::TYPE_LINK)
->setHref($href)
->setIcon($icon);
$nav->addMenuItem($item);
}
$nav->selectFilter($selected);
return $nav;
}
public function newTimeline(PhabricatorRepository $repository) {
$timeline = $this->buildTransactionTimeline(
$repository,
new PhabricatorRepositoryTransactionQuery());
$timeline->setShouldTerminate(true);
return $timeline;
}
}

View file

@ -1,79 +0,0 @@
<?php
final class DiffusionRepositoryNewController extends DiffusionController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$this->requireApplicationCapability(
DiffusionCreateRepositoriesCapability::CAPABILITY);
if ($request->isFormPost()) {
if ($request->getStr('type')) {
switch ($request->getStr('type')) {
case 'create':
$uri = $this->getApplicationURI('create/');
break;
case 'import':
default:
$uri = $this->getApplicationURI('import/');
break;
}
return id(new AphrontRedirectResponse())->setURI($uri);
}
}
$doc_href = PhabricatorEnv::getDoclink(
'Diffusion User Guide: Repository Hosting');
$doc_link = phutil_tag(
'a',
array(
'href' => $doc_href,
'target' => '_blank',
),
pht('Diffusion User Guide: Repository Hosting'));
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new AphrontFormRadioButtonControl())
->setName('type')
->addButton(
'create',
pht('Create a New Hosted Repository'),
array(
pht(
'Create a new, empty repository which Phabricator will host. '.
'For instructions on configuring repository hosting, see %s.',
$doc_link),
))
->addButton(
'import',
pht('Import an Existing External Repository'),
pht(
"Import a repository hosted somewhere else, like GitHub, ".
"Bitbucket, or your organization's existing servers. ".
"Phabricator will read changes from the repository but will ".
"not host or manage it. The authoritative master version of ".
"the repository will stay where it is now.")))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Continue'))
->addCancelButton($this->getApplicationURI()));
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('New Repository'));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Create or Import Repository'))
->setForm($form);
return $this->newPage()
->setTitle(pht('New Repository'))
->setCrumbs($crumbs)
->appendChild($form_box);
}
}

View file

@ -1,120 +0,0 @@
<?php
final class DiffusionRepositorySymbolsController
extends DiffusionRepositoryEditController {
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContextForEdit();
if ($response) {
return $response;
}
$viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
$v_sources = $repository->getSymbolSources();
$v_languages = $repository->getSymbolLanguages();
if ($v_languages) {
$v_languages = implode(', ', $v_languages);
}
$errors = array();
if ($request->isFormPost()) {
$v_sources = $request->getArr('sources');
$v_languages = $request->getStrList('languages');
$v_languages = array_map('phutil_utf8_strtolower', $v_languages);
if (!$errors) {
$xactions = array();
$template = id(new PhabricatorRepositoryTransaction());
$type_sources = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES;
$type_lang = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE;
$xactions[] = id(clone $template)
->setTransactionType($type_sources)
->setNewValue($v_sources);
$xactions[] = id(clone $template)
->setTransactionType($type_lang)
->setNewValue($v_languages);
try {
id(new PhabricatorRepositoryEditor())
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request)
->setActor($viewer)
->applyTransactions($repository, $xactions);
return id(new AphrontRedirectResponse())->setURI($edit_uri);
} catch (Exception $ex) {
$errors[] = $ex->getMessage();
}
}
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Edit Symbols'));
$title = pht('Edit Symbols (%s)', $repository->getName());
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon('fa-pencil');
$form = id(new AphrontFormView())
->setUser($viewer)
->appendRemarkupInstructions($this->getInstructions())
->appendChild(
id(new AphrontFormTextControl())
->setName('languages')
->setLabel(pht('Indexed Languages'))
->setCaption(pht(
'File extensions, separate with commas, for example: php, py. '.
'Leave blank for "any".'))
->setValue($v_languages))
->appendControl(
id(new AphrontFormTokenizerControl())
->setName('sources')
->setLabel(pht('Uses Symbols From'))
->setDatasource(new DiffusionRepositoryDatasource())
->setValue($v_sources))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Save'))
->addCancelButton($edit_uri));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Symbols'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setForm($form)
->setFormErrors($errors);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
$form_box,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function getInstructions() {
return pht(<<<EOT
Configure Symbols for this repository.
See [[%s | Symbol Indexes]] for more information on using symbols.
EOT
,
PhabricatorEnv::getDoclink(
'Diffusion User Guide: Symbol Indexes'));
}
}

View file

@ -1,7 +1,7 @@
<?php
final class DiffusionRepositoryTestAutomationController
extends DiffusionRepositoryEditController {
extends DiffusionRepositoryManageController {
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContextForEdit();
@ -13,7 +13,9 @@ final class DiffusionRepositoryTestAutomationController
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
$panel_uri = id(new DiffusionRepositoryAutomationManagementPanel())
->setRepository($repository)
->getPanelURI();
if (!$repository->canPerformAutomation()) {
return $this->newDialog()
@ -23,7 +25,7 @@ final class DiffusionRepositoryTestAutomationController
'You can not run a configuration test for this repository '.
'because you have not configured repository automation yet. '.
'Configure it first, then test the configuration.'))
->addCancelButton($edit_uri);
->addCancelButton($panel_uri);
}
if ($request->isFormPost()) {
@ -63,7 +65,7 @@ final class DiffusionRepositoryTestAutomationController
'If you run into write failures despite passing this test, '.
'it suggests that your setup is nearly correct but authentication '.
'is probably not fully configured.'))
->addCancelButton($edit_uri)
->addCancelButton($panel_uri)
->addSubmitButton(pht('Start Test'));
}

View file

@ -0,0 +1,159 @@
<?php
final class DiffusionRepositoryURICredentialController
extends DiffusionController {
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContextForEdit();
if ($response) {
return $response;
}
$viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$id = $request->getURIData('id');
$uri = id(new PhabricatorRepositoryURIQuery())
->setViewer($viewer)
->withIDs(array($id))
->withRepositories(array($repository))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$uri) {
return new Aphront404Response();
}
$is_builtin = $uri->isBuiltin();
$has_credential = (bool)$uri->getCredentialPHID();
$view_uri = $uri->getViewURI();
$is_remove = ($request->getURIData('action') == 'remove');
if ($is_builtin) {
return $this->newDialog()
->setTitle(pht('Builtin URIs Do Not Use Credentials'))
->appendParagraph(
pht(
'You can not set a credential for builtin URIs which Phabricator '.
'hosts and serves. Phabricator does not fetch from these URIs or '.
'push to these URIs, and does not need credentials to '.
'authenticate any activity against them.'))
->addCancelButton($view_uri);
}
if ($request->isFormPost()) {
$xactions = array();
if ($is_remove) {
$new_phid = null;
} else {
$new_phid = $request->getStr('credentialPHID');
}
$type_credential = PhabricatorRepositoryURITransaction::TYPE_CREDENTIAL;
$xactions[] = id(new PhabricatorRepositoryURITransaction())
->setTransactionType($type_credential)
->setNewValue($new_phid);
$editor = id(new DiffusionURIEditor())
->setActor($viewer)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true)
->setContentSourceFromRequest($request)
->applyTransactions($uri, $xactions);
return id(new AphrontRedirectResponse())->setURI($view_uri);
}
$command_engine = $uri->newCommandEngine();
$is_supported = $command_engine->isCredentialSupported();
$body = null;
$form = null;
$width = AphrontDialogView::WIDTH_DEFAULT;
if ($is_remove) {
if ($has_credential) {
$title = pht('Remove Credential');
$body = pht(
'This credential will no longer be used to authenticate activity '.
'against this URI.');
$button = pht('Remove Credential');
} else {
$title = pht('No Credential');
$body = pht(
'This URI does not have an associated credential.');
$button = null;
}
} else if (!$is_supported) {
$title = pht('Unauthenticated Protocol');
$body = pht(
'The protocol for this URI ("%s") does not use authentication, so '.
'you can not provide a credential.',
$command_engine->getDisplayProtocol());
$button = null;
} else {
$effective_uri = $uri->getEffectiveURI();
$label = $command_engine->getPassphraseCredentialLabel();
$credential_type = $command_engine->getPassphraseDefaultCredentialType();
$provides_type = $command_engine->getPassphraseProvidesCredentialType();
$options = id(new PassphraseCredentialQuery())
->setViewer($viewer)
->withIsDestroyed(false)
->withProvidesTypes(array($provides_type))
->execute();
$control = id(new PassphraseCredentialControl())
->setName('credentialPHID')
->setLabel($label)
->setValue($uri->getCredentialPHID())
->setCredentialType($credential_type)
->setOptions($options);
$default_user = $effective_uri->getUser();
if (strlen($default_user)) {
$control->setDefaultUsername($default_user);
}
$form = id(new AphrontFormView())
->setViewer($viewer)
->appendControl($control);
if ($has_credential) {
$title = pht('Update Credential');
$button = pht('Update Credential');
} else {
$title = pht('Set Credential');
$button = pht('Set Credential');
}
$width = AphrontDialogView::WIDTH_FORM;
}
$dialog = $this->newDialog()
->setWidth($width)
->setTitle($title)
->addCancelButton($view_uri);
if ($body) {
$dialog->appendParagraph($body);
}
if ($form) {
$dialog->appendForm($form);
}
if ($button) {
$dialog->addSubmitButton($button);
}
return $dialog;
}
}

View file

@ -31,6 +31,16 @@ final class DiffusionRepositoryURIDisableController
$is_disabled = $uri->getIsDisabled();
$view_uri = $uri->getViewURI();
if ($uri->isBuiltin()) {
return $this->newDialog()
->setTitle(pht('Builtin URI'))
->appendParagraph(
pht(
'You can not manually disable builtin URIs. To hide a builtin '.
'URI, configure its "Display" behavior instead.'))
->addCancelButton($view_uri);
}
if ($request->isFormPost()) {
$xactions = array();

View file

@ -82,6 +82,7 @@ final class DiffusionRepositoryURIViewController
private function buildCurtain(PhabricatorRepositoryURI $uri) {
$viewer = $this->getViewer();
$repository = $uri->getRepository();
$id = $uri->getID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
@ -89,7 +90,6 @@ final class DiffusionRepositoryURIViewController
$uri,
PhabricatorPolicyCapability::CAN_EDIT);
$curtain = $this->newCurtainView($uri);
$edit_uri = $uri->getEditURI();
@ -102,6 +102,43 @@ final class DiffusionRepositoryURIViewController
->setWorkflow(!$can_edit)
->setDisabled(!$can_edit));
$credential_uri = $repository->getPathURI("uri/credential/{$id}/edit/");
$remove_uri = $repository->getPathURI("uri/credential/{$id}/remove/");
$has_credential = (bool)$uri->getCredentialPHID();
if ($uri->isBuiltin()) {
$can_credential = false;
} else if (!$uri->newCommandEngine()->isCredentialSupported()) {
$can_credential = false;
} else {
$can_credential = true;
}
$can_update = ($can_edit && $can_credential);
$can_remove = ($can_edit && $has_credential);
if ($has_credential) {
$credential_name = pht('Update Credential');
} else {
$credential_name = pht('Set Credential');
}
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-key')
->setName($credential_name)
->setHref($credential_uri)
->setWorkflow(true)
->setDisabled(!$can_edit));
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-times')
->setName(pht('Remove Credential'))
->setHref($remove_uri)
->setWorkflow(true)
->setDisabled(!$can_remove));
if ($uri->getIsDisabled()) {
$disable_name = pht('Enable URI');
$disable_icon = 'fa-check';
@ -110,7 +147,9 @@ final class DiffusionRepositoryURIViewController
$disable_icon = 'fa-ban';
}
$disable_uri = $uri->getRepository()->getPathURI("uri/disable/{$id}/");
$can_disable = ($can_edit && !$uri->isBuiltin());
$disable_uri = $repository->getPathURI("uri/disable/{$id}/");
$curtain->addAction(
id(new PhabricatorActionView())
@ -118,7 +157,7 @@ final class DiffusionRepositoryURIViewController
->setName($disable_name)
->setHref($disable_uri)
->setWorkflow(true)
->setDisabled(!$can_edit));
->setDisabled(!$can_disable));
return $curtain;
}
@ -130,7 +169,84 @@ final class DiffusionRepositoryURIViewController
->setUser($viewer);
$properties->addProperty(pht('URI'), $uri->getDisplayURI());
$properties->addProperty(pht('Credential'), 'TODO');
$credential_phid = $uri->getCredentialPHID();
$command_engine = $uri->newCommandEngine();
$is_optional = $command_engine->isCredentialOptional();
$is_supported = $command_engine->isCredentialSupported();
$is_builtin = $uri->isBuiltin();
if ($is_builtin) {
$credential_icon = 'fa-circle-o';
$credential_color = 'grey';
$credential_label = pht('Builtin');
$credential_note = pht('Builtin URIs do not use credentials.');
} else if (!$is_supported) {
$credential_icon = 'fa-circle-o';
$credential_color = 'grey';
$credential_label = pht('Not Supported');
$credential_note = pht('This protocol does not support authentication.');
} else if (!$credential_phid) {
if ($is_optional) {
$credential_icon = 'fa-circle-o';
$credential_color = 'green';
$credential_label = pht('No Credential');
$credential_note = pht('Configured for anonymous access.');
} else {
$credential_icon = 'fa-times';
$credential_color = 'red';
$credential_label = pht('Required');
$credential_note = pht('Credential required but not configured.');
}
} else {
// Don't raise a policy exception if we can't see the credential.
$credentials = id(new PassphraseCredentialQuery())
->setViewer($viewer)
->withPHIDs(array($credential_phid))
->execute();
$credential = head($credentials);
if (!$credential) {
$handles = $viewer->loadHandles(array($credential_phid));
$handle = $handles[$credential_phid];
if ($handle->getPolicyFiltered()) {
$credential_icon = 'fa-lock';
$credential_color = 'grey';
$credential_label = pht('Restricted');
$credential_note = pht(
'You do not have permission to view the configured '.
'credential.');
} else {
$credential_icon = 'fa-times';
$credential_color = 'red';
$credential_label = pht('Invalid');
$credential_note = pht('Configured credential is invalid.');
}
} else {
$provides = $credential->getProvidesType();
$needs = $command_engine->getPassphraseProvidesCredentialType();
if ($provides != $needs) {
$credential_icon = 'fa-times';
$credential_color = 'red';
$credential_label = pht('Wrong Type');
} else {
$credential_icon = 'fa-check';
$credential_color = 'green';
$credential_label = $command_engine->getPassphraseCredentialLabel();
}
$credential_note = $viewer->renderHandle($credential_phid);
}
}
$credential_item = id(new PHUIStatusItemView())
->setIcon($credential_icon, $credential_color)
->setTarget(phutil_tag('strong', array(), $credential_label))
->setNote($credential_note);
$credential_view = id(new PHUIStatusListView())
->addItem($credential_item);
$properties->addProperty(pht('Credential'), $credential_view);
$io_type = $uri->getEffectiveIOType();

View file

@ -219,6 +219,7 @@ final class DiffusionServeController extends DiffusionController {
$repository = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->withIdentifiers(array($identifier))
->needURIs(true)
->executeOne();
if (!$repository) {
return new PhabricatorVCSResponse(
@ -266,22 +267,32 @@ final class DiffusionServeController extends DiffusionController {
// token from SSH. If they're using HTTP username + password auth, they
// have to obey the normal HTTP rules.
} else {
switch ($repository->getServeOverHTTP()) {
case PhabricatorRepository::SERVE_READONLY:
// For now, we don't distinguish between HTTP and HTTPS-originated
// requests that are proxied within the cluster, so the user can connect
// with HTTPS but we may be on HTTP by the time we reach this part of
// the code. Allow things to move forward as long as either protocol
// can be served.
$proto_https = PhabricatorRepositoryURI::BUILTIN_PROTOCOL_HTTPS;
$proto_http = PhabricatorRepositoryURI::BUILTIN_PROTOCOL_HTTP;
$can_read =
$repository->canServeProtocol($proto_https, false) ||
$repository->canServeProtocol($proto_http, false);
if (!$can_read) {
return new PhabricatorVCSResponse(
403,
pht('This repository is not available over HTTP.'));
}
if ($is_push) {
$can_write =
$repository->canServeProtocol($proto_https, true) ||
$repository->canServeProtocol($proto_http, true);
if (!$can_write) {
return new PhabricatorVCSResponse(
403,
pht('This repository is read-only over HTTP.'));
}
break;
case PhabricatorRepository::SERVE_READWRITE:
// We'll check for push capability below.
break;
case PhabricatorRepository::SERVE_OFF:
default:
return new PhabricatorVCSResponse(
403,
pht('This repository is not available over HTTP.'));
}
}

View file

@ -0,0 +1,184 @@
<?php
/**
* Filter a list of repositories, removing repositories not local to the
* current device.
*/
final class DiffusionLocalRepositoryFilter extends Phobject {
private $viewer;
private $device;
private $repositories;
private $rejectionReasons;
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
public function setDevice(AlmanacDevice $device = null) {
$this->device = $device;
return $this;
}
public function getDevice() {
return $this->device;
}
public function setRepositories(array $repositories) {
$this->repositories = $repositories;
return $this;
}
public function getRepositories() {
return $this->repositories;
}
public function setRejectionReasons($rejection_reasons) {
$this->rejectionReasons = $rejection_reasons;
return $this;
}
public function getRejectionReasons() {
return $this->rejectionReasons;
}
public function execute() {
$repositories = $this->getRepositories();
$device = $this->getDevice();
$viewer = $this->getViewer();
$reasons = array();
$service_phids = array();
foreach ($repositories as $key => $repository) {
$service_phid = $repository->getAlmanacServicePHID();
// If the repository is bound to a service but this host is not a
// recognized device, or vice versa, don't pull the repository unless
// we're sure it's safe because the repository has no local working copy
// or the working copy already exists on disk.
$is_cluster_repo = (bool)$service_phid;
$is_cluster_device = (bool)$device;
if ($is_cluster_repo != $is_cluster_device) {
$has_working_copy = $repository->hasLocalWorkingCopy();
if ($is_cluster_device) {
if (!$has_working_copy) {
$reasons[$key] = pht(
'Repository "%s" is not a cluster repository, but the current '.
'host is a cluster device ("%s") and updating this repository '.
'would create a new local working copy. This is dangerous, so '.
'the repository will not be updated on this host.',
$repository->getDisplayName(),
$device->getName());
unset($repositories[$key]);
continue;
}
} else {
$reasons[$key] = pht(
'Repository "%s" is a cluster repository, but the current host '.
'is not a cluster device (it has no device ID), so the '.
'repository will not be updated on this host.',
$repository->getDisplayName());
unset($repositories[$key]);
continue;
}
}
if ($service_phid) {
$service_phids[] = $service_phid;
}
}
if (!$device) {
$this->rejectionReasons = $reasons;
return $repositories;
}
$device_phid = $device->getPHID();
if ($service_phids) {
// We could include `withDevicePHIDs()` here to pull a smaller result
// set, but we can provide more helpful diagnostic messages below if
// we fetch a little more data.
$services = id(new AlmanacServiceQuery())
->setViewer($viewer)
->withPHIDs($service_phids)
->withServiceTypes(
array(
AlmanacClusterRepositoryServiceType::SERVICETYPE,
))
->needBindings(true)
->execute();
$services = mpull($services, null, 'getPHID');
} else {
$services = array();
}
foreach ($repositories as $key => $repository) {
$service_phid = $repository->getAlmanacServicePHID();
if (!$service_phid) {
continue;
}
$service = idx($services, $service_phid);
if (!$service) {
$reasons[$key] = pht(
'Repository "%s" is on cluster service "%s", but that service '.
'could not be loaded, so the repository will not be updated on '.
'this host.',
$repository->getDisplayName(),
$service_phid);
unset($repositories[$key]);
continue;
}
$bindings = $service->getBindings();
$bindings = mgroup($bindings, 'getDevicePHID');
$bindings = idx($bindings, $device_phid);
if (!$bindings) {
$reasons[$key] = pht(
'Repository "%s" is on cluster service "%s", but that service is '.
'not bound to this device ("%s"), so the repository will not be '.
'updated on this host.',
$repository->getDisplayName(),
$service->getName(),
$device->getName());
unset($repositories[$key]);
continue;
}
$all_disabled = true;
foreach ($bindings as $binding) {
if (!$binding->getIsDisabled()) {
$all_disabled = false;
break;
}
}
if ($all_disabled) {
$reasons[$key] = pht(
'Repository "%s" is on cluster service "%s", but the binding '.
'between that service and this device ("%s") is disabled, so it '.
'can not be updated on this host.',
$repository->getDisplayName(),
$service->getName(),
$device->getName());
unset($repositories[$key]);
continue;
}
// We have a valid service that is actively bound to the current host
// device, so we're good to go.
}
$this->rejectionReasons = $reasons;
return $repositories;
}
}

View file

@ -40,11 +40,51 @@ final class DiffusionRepositoryEditEngine
$viewer = $this->getViewer();
$repository = PhabricatorRepository::initializeNewRepository($viewer);
$repository->setDetail('newly-initialized', true);
$vcs = $this->getVersionControlSystem();
if ($vcs) {
$repository->setVersionControlSystem($vcs);
}
// Pick a random open service to allocate this repository on, if any exist.
// If there are no services, we aren't in cluster mode and will allocate
// locally. If there are services but none permit allocations, we fail.
// Eventually we can make this more flexible, but this rule is a reasonable
// starting point as we begin to deploy cluster services.
$services = id(new AlmanacServiceQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withServiceTypes(
array(
AlmanacClusterRepositoryServiceType::SERVICETYPE,
))
->needProperties(true)
->execute();
if ($services) {
// Filter out services which do not permit new allocations.
foreach ($services as $key => $possible_service) {
if ($possible_service->getAlmanacPropertyValue('closed')) {
unset($services[$key]);
}
}
if (!$services) {
throw new Exception(
pht(
'This install is configured in cluster mode, but all available '.
'repository cluster services are closed to new allocations. '.
'At least one service must be open to allow new allocations to '.
'take place.'));
}
shuffle($services);
$service = head($services);
$repository->setAlmanacServicePHID($service->getPHID());
}
return $repository;
}
@ -85,6 +125,77 @@ final class DiffusionRepositoryEditEngine
DiffusionCreateRepositoriesCapability::CAPABILITY);
}
protected function newPages($object) {
$panels = DiffusionRepositoryManagementPanel::getAllPanels();
$pages = array();
$uris = array();
foreach ($panels as $panel_key => $panel) {
$panel->setRepository($object);
$uris[$panel_key] = $panel->getPanelURI();
$page = $panel->newEditEnginePage();
if (!$page) {
continue;
}
$pages[] = $page;
}
$basics_key = DiffusionRepositoryBasicsManagementPanel::PANELKEY;
$basics_uri = $uris[$basics_key];
$more_pages = array(
id(new PhabricatorEditPage())
->setKey('encoding')
->setLabel(pht('Text Encoding'))
->setViewURI($basics_uri)
->setFieldKeys(
array(
'encoding',
)),
id(new PhabricatorEditPage())
->setKey('extensions')
->setLabel(pht('Extensions'))
->setIsDefault(true),
);
foreach ($more_pages as $page) {
$pages[] = $page;
}
return $pages;
}
protected function willConfigureFields($object, array $fields) {
// Change the default field order so related fields are adjacent.
$after = array(
'policy.edit' => array('policy.push'),
);
$result = array();
foreach ($fields as $key => $value) {
$result[$key] = $value;
if (!isset($after[$key])) {
continue;
}
foreach ($after[$key] as $next_key) {
if (!isset($fields[$next_key])) {
continue;
}
unset($result[$next_key]);
$result[$next_key] = $fields[$next_key];
unset($fields[$next_key]);
}
}
return $result;
}
protected function buildCustomEditFields($object) {
$viewer = $this->getViewer();
@ -99,6 +210,27 @@ final class DiffusionRepositoryEditEngine
$autoclose_value = $object->getDetail('close-commits-filter', array());
$autoclose_value = array_keys($autoclose_value);
$automation_instructions = pht(
"Configure **Repository Automation** to allow Phabricator to ".
"write to this repository.".
"\n\n".
"IMPORTANT: This feature is new, experimental, and not supported. ".
"Use it at your own risk.");
$staging_instructions = pht(
"To make it easier to run integration tests and builds on code ".
"under review, you can configure a **Staging Area**. When `arc` ".
"creates a diff, it will push a copy of the changes to the ".
"configured staging area with a corresponding tag.".
"\n\n".
"IMPORTANT: This feature is new, experimental, and not supported. ".
"Use it at your own risk.");
$subpath_instructions = pht(
'If you want to import only part of a repository, like `trunk/`, '.
'you can set a path in **Import Only**. Phabricator will ignore '.
'commits which do not affect this path.');
return array(
id(new PhabricatorSelectEditField())
->setKey('vcs')
@ -211,6 +343,17 @@ final class DiffusionRepositoryEditEngine
->setConduitDescription(pht('Set the autoclose branches.'))
->setConduitTypeDescription(pht('New default tracked branchs.'))
->setValue($autoclose_value),
id(new PhabricatorTextEditField())
->setKey('importOnly')
->setLabel(pht('Import Only'))
->setTransactionType(
PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH)
->setIsCopyable(true)
->setDescription(pht('Subpath to selectively import.'))
->setConduitDescription(pht('Set the subpath to import.'))
->setConduitTypeDescription(pht('New subpath to import.'))
->setValue($object->getDetail('svn-subpath'))
->setControlInstructions($subpath_instructions),
id(new PhabricatorTextEditField())
->setKey('stagingAreaURI')
->setLabel(pht('Staging Area URI'))
@ -220,7 +363,8 @@ final class DiffusionRepositoryEditEngine
->setDescription(pht('Staging area URI.'))
->setConduitDescription(pht('Set the staging area URI.'))
->setConduitTypeDescription(pht('New staging area URI.'))
->setValue($object->getStagingURI()),
->setValue($object->getStagingURI())
->setControlInstructions($staging_instructions),
id(new PhabricatorDatasourceEditField())
->setKey('automationBlueprintPHIDs')
->setLabel(pht('Use Blueprints'))
@ -231,7 +375,8 @@ final class DiffusionRepositoryEditEngine
->setDescription(pht('Automation blueprints.'))
->setConduitDescription(pht('Change automation blueprints.'))
->setConduitTypeDescription(pht('New blueprint PHIDs.'))
->setValue($object->getAutomationBlueprintPHIDs()),
->setValue($object->getAutomationBlueprintPHIDs())
->setControlInstructions($automation_instructions),
id(new PhabricatorStringListEditField())
->setKey('symbolLanguages')
->setLabel(pht('Languages'))

View file

@ -83,6 +83,67 @@ final class DiffusionURIEditEngine
protected function buildCustomEditFields($object) {
$viewer = $this->getViewer();
$uri_instructions = null;
if ($object->isBuiltin()) {
$is_builtin = true;
$uri_value = (string)$object->getDisplayURI();
switch ($object->getBuiltinProtocol()) {
case PhabricatorRepositoryURI::BUILTIN_PROTOCOL_SSH:
$uri_instructions = pht(
" - Configure [[ %s | %s ]] to change the SSH username.\n".
" - Configure [[ %s | %s ]] to change the SSH host.\n".
" - Configure [[ %s | %s ]] to change the SSH port.",
'/config/edit/diffusion.ssh-user/',
'diffusion.ssh-user',
'/config/edit/diffusion.ssh-host/',
'diffusion.ssh-host',
'/config/edit/diffusion.ssh-port/',
'diffusion.ssh-port');
break;
}
} else {
$is_builtin = false;
$uri_value = $object->getURI();
if ($object->getRepositoryPHID()) {
$repository = $object->getRepository();
if ($repository->isGit()) {
$uri_instructions = pht(
"Provide the URI of a Git repository. It should usually look ".
"like one of these examples:\n".
"\n".
"| Example Git URIs\n".
"| -----------------------\n".
"| `git@github.com:example/example.git`\n".
"| `ssh://user@host.com/git/example.git`\n".
"| `https://example.com/repository.git`");
} else if ($repository->isHg()) {
$uri_instructions = pht(
"Provide the URI of a Mercurial repository. It should usually ".
"look like one of these examples:\n".
"\n".
"| Example Mercurial URIs\n".
"|-----------------------\n".
"| `ssh://hg@bitbucket.org/example/repository`\n".
"| `https://bitbucket.org/example/repository`");
} else if ($repository->isSVN()) {
$uri_instructions = pht(
"Provide the **Repository Root** of a Subversion repository. ".
"You can identify this by running `svn info` in a working ".
"copy. It should usually look like one of these examples:\n".
"\n".
"| Example Subversion URIs\n".
"|-----------------------\n".
"| `http://svn.example.org/svnroot/`\n".
"| `svn+ssh://svn.example.com/svnroot/`\n".
"| `svn://svn.example.net/svnroot/`\n\n".
"You **MUST** specify the root of the repository, not a ".
"subdirectory.");
}
}
}
return array(
id(new PhabricatorHandlesEditField())
->setKey('repository')
@ -104,12 +165,14 @@ final class DiffusionURIEditEngine
id(new PhabricatorTextEditField())
->setKey('uri')
->setLabel(pht('URI'))
->setIsRequired(true)
->setTransactionType(PhabricatorRepositoryURITransaction::TYPE_URI)
->setDescription(pht('The repository URI.'))
->setConduitDescription(pht('Change the repository URI.'))
->setConduitTypeDescription(pht('New repository URI.'))
->setValue($object->getURI()),
->setIsRequired(!$is_builtin)
->setIsLocked($is_builtin)
->setValue($uri_value)
->setControlInstructions($uri_instructions),
id(new PhabricatorSelectEditField())
->setKey('io')
->setLabel(pht('I/O Type'))

View file

@ -3,6 +3,9 @@
final class DiffusionURIEditor
extends PhabricatorApplicationTransactionEditor {
private $repository;
private $repositoryPHID;
public function getEditorApplicationClass() {
return 'PhabricatorDiffusionApplication';
}
@ -70,7 +73,42 @@ final class DiffusionURIEditor
switch ($xaction->getTransactionType()) {
case PhabricatorRepositoryURITransaction::TYPE_URI:
if (!$this->getIsNewObject()) {
$old_uri = $object->getEffectiveURI();
} else {
$old_uri = null;
}
$object->setURI($xaction->getNewValue());
// If we've changed the domain or protocol of the URI, remove the
// current credential. This improves behavior in several cases:
// If a user switches between protocols with different credential
// types, like HTTP and SSH, the old credential won't be valid anyway.
// It's cleaner to remove it than leave a bad credential in place.
// If a user switches hosts, the old credential is probably not
// correct (and potentially confusing/misleading). Removing it forces
// users to double check that they have the correct credentials.
// If an attacker can't see a symmetric credential like a username and
// password, they could still potentially capture it by changing the
// host for a URI that uses it to `evil.com`, a server they control,
// then observing the requests. Removing the credential prevents this
// kind of escalation.
// Since port and path changes are less likely to fall among these
// cases, they don't trigger a credential wipe.
$new_uri = $object->getEffectiveURI();
if ($old_uri) {
$new_proto = ($old_uri->getProtocol() != $new_uri->getProtocol());
$new_domain = ($old_uri->getDomain() != $new_uri->getDomain());
if ($new_proto || $new_domain) {
$object->setCredentialPHID(null);
}
}
break;
case PhabricatorRepositoryURITransaction::TYPE_IO:
$object->setIOType($xaction->getNewValue());
@ -80,6 +118,7 @@ final class DiffusionURIEditor
break;
case PhabricatorRepositoryURITransaction::TYPE_REPOSITORY:
$object->setRepositoryPHID($xaction->getNewValue());
$object->attachRepository($this->repository);
break;
case PhabricatorRepositoryURITransaction::TYPE_CREDENTIAL:
$object->setCredentialPHID($xaction->getNewValue());
@ -116,6 +155,9 @@ final class DiffusionURIEditor
switch ($type) {
case PhabricatorRepositoryURITransaction::TYPE_REPOSITORY:
// Save this, since we need it to validate TYPE_IO transactions.
$this->repositoryPHID = $object->getRepositoryPHID();
$missing = $this->validateIsEmptyTextField(
$object->getRepositoryPHID(),
$xactions);
@ -173,6 +215,9 @@ final class DiffusionURIEditor
$xaction);
continue;
}
$this->repository = $repository;
$this->repositoryPHID = $repository_phid;
}
break;
case PhabricatorRepositoryURITransaction::TYPE_CREDENTIAL:
@ -184,6 +229,11 @@ final class DiffusionURIEditor
continue;
}
// Anyone who can edit a URI can remove the credential.
if ($credential_phid === null) {
continue;
}
$credential = id(new PassphraseCredentialQuery())
->setViewer($viewer)
->withPHIDs(array($credential_phid))
@ -275,7 +325,7 @@ final class DiffusionURIEditor
if ($no_observers || $no_readwrite) {
$repository = id(new PhabricatorRepositoryQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs(array($object->getRepositoryPHID()))
->withPHIDs(array($this->repositoryPHID))
->needURIs(true)
->executeOne();
$uris = $repository->getURIs();
@ -365,9 +415,69 @@ final class DiffusionURIEditor
}
}
break;
case PhabricatorRepositoryURITransaction::TYPE_DISABLE:
$old = $object->getIsDisabled();
foreach ($xactions as $xaction) {
$new = $xaction->getNewValue();
if ($old == $new) {
continue;
}
if (!$object->isBuiltin()) {
continue;
}
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
pht('You can not manually disable builtin URIs.'));
}
break;
}
return $errors;
}
protected function applyFinalEffects(
PhabricatorLiskDAO $object,
array $xactions) {
// Synchronize the repository state based on the presence of an "Observe"
// URI.
$repository = $object->getRepository();
$uris = id(new PhabricatorRepositoryURIQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withRepositories(array($repository))
->execute();
$observe_uri = null;
foreach ($uris as $uri) {
if ($uri->getIoType() != PhabricatorRepositoryURI::IO_OBSERVE) {
continue;
}
$observe_uri = $uri;
break;
}
if ($observe_uri) {
$repository
->setHosted(false)
->setDetail('remote-uri', (string)$observe_uri->getEffectiveURI())
->setCredentialPHID($observe_uri->getCredentialPHID());
} else {
$repository
->setHosted(true)
->setDetail('remote-uri', null)
->setCredentialPHID(null);
}
$repository->save();
return $xactions;
}
}

View file

@ -172,14 +172,7 @@ final class DiffusionCommitHookEngine extends Phobject {
if ($this->isInitialImport($all_updates)) {
$repository = $this->getRepository();
$repository->openTransaction();
$repository->beginReadLocking();
$repository = $repository->reload();
$repository->setDetail('importing', true);
$repository->save();
$repository->endReadLocking();
$repository->saveTransaction();
$repository->markImporting();
}
if ($this->emailPHIDs) {
@ -1244,7 +1237,7 @@ final class DiffusionCommitHookEngine extends Phobject {
$commit_count++;
}
if ($commit_count <= 7) {
if ($commit_count <= PhabricatorRepository::IMPORT_THRESHOLD) {
// If this pushes a very small number of commits, assume it's an
// initial commit or stack of a few initial commits.
return false;

View file

@ -16,6 +16,18 @@ final class DiffusionRepositoryURIsIndexEngineExtension
public function indexObject(
PhabricatorIndexEngine $engine,
$object) {
// Reload the repository to pick up URIs, which we need in order to update
// the URI index.
$object = id(new PhabricatorRepositoryQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs(array($object->getPHID()))
->needURIs(true)
->executeOne();
if (!$object) {
return;
}
$object->updateURIIndex();
}

View file

@ -0,0 +1,3 @@
<?php
final class DiffusionDaemonLockException extends Exception {}

View file

@ -13,6 +13,30 @@ final class DiffusionRepositoryActionsManagementPanel
return 1100;
}
public function getManagementPanelIcon() {
$repository = $this->getRepository();
$has_any =
$repository->getDetail('herald-disabled') ||
$repository->getDetail('disable-autoclose');
// NOTE: Any value here really means something is disabled, so try to
// hint that a little bit with the icon.
if ($has_any) {
return 'fa-comment-o';
} else {
return 'fa-commenting grey';
}
}
protected function getEditEngineFieldKeys() {
return array(
'publish',
'autoclose',
);
}
protected function buildManagementPanelActions() {
$repository = $this->getRepository();
$viewer = $this->getViewer();
@ -22,7 +46,7 @@ final class DiffusionRepositoryActionsManagementPanel
$repository,
PhabricatorPolicyCapability::CAN_EDIT);
$actions_uri = $repository->getPathURI('edit/actions/');
$actions_uri = $this->getEditPageURI();
return array(
id(new PhabricatorActionView())

View file

@ -13,6 +13,39 @@ final class DiffusionRepositoryAutomationManagementPanel
return 800;
}
public function shouldEnableForRepository(
PhabricatorRepository $repository) {
return $repository->isGit();
}
protected function getEditEngineFieldKeys() {
return array(
'automationBlueprintPHIDs',
);
}
public function getManagementPanelIcon() {
$repository = $this->getRepository();
if (!$repository->canPerformAutomation()) {
return 'fa-truck grey';
}
$blueprint_phids = $repository->getAutomationBlueprintPHIDs();
if (!$blueprint_phids) {
return 'fa-truck grey';
}
$is_authorized = DrydockAuthorizationQuery::isFullyAuthorized(
$repository->getPHID(),
$blueprint_phids);
if (!$is_authorized) {
return 'fa-exclamation-triangle yellow';
}
return 'fa-truck';
}
protected function buildManagementPanelActions() {
$repository = $this->getRepository();
$viewer = $this->getViewer();
@ -24,7 +57,7 @@ final class DiffusionRepositoryAutomationManagementPanel
$can_test = $can_edit && $repository->canPerformAutomation();
$automation_uri = $repository->getPathURI('edit/automation/');
$automation_uri = $this->getEditPageURI();
$test_uri = $repository->getPathURI('edit/testautomation/');
return array(

View file

@ -13,6 +13,26 @@ final class DiffusionRepositoryBasicsManagementPanel
return 100;
}
public function getManagementPanelIcon() {
$repository = $this->getRepository();
if (!$repository->isTracked()) {
return 'fa-ban indigo';
} else {
return 'fa-code';
}
}
protected function getEditEngineFieldKeys() {
return array(
'name',
'callsign',
'shortName',
'description',
'projectPHIDs',
);
}
protected function buildManagementPanelActions() {
$repository = $this->getRepository();
$viewer = $this->getViewer();
@ -22,10 +42,10 @@ final class DiffusionRepositoryBasicsManagementPanel
$repository,
PhabricatorPolicyCapability::CAN_EDIT);
$edit_uri = $repository->getPathURI('manage/');
$edit_uri = $this->getEditPageURI();
$activate_uri = $repository->getPathURI('edit/activate/');
$delete_uri = $repository->getPathURI('edit/delete/');
$encoding_uri = $repository->getPathURI('edit/encoding/');
$encoding_uri = $this->getEditPageURI('encoding');
$dangerous_uri = $repository->getPathURI('edit/dangerous/');
if ($repository->isTracked()) {
@ -84,7 +104,38 @@ final class DiffusionRepositoryBasicsManagementPanel
public function buildManagementPanelContent() {
$result = array();
$result[] = $this->newBox(pht('Repository Basics'), $this->buildBasics());
$basics = $this->newBox(pht('Repository Basics'), $this->buildBasics());
$repository = $this->getRepository();
$is_new = $repository->isNewlyInitialized();
if ($is_new) {
$messages = array();
$messages[] = pht(
'This newly created repository is not active yet. Configure policies, '.
'options, and URIs. When ready, %s the repository.',
phutil_tag('strong', array(), pht('Activate')));
if ($repository->isHosted()) {
$messages[] = pht(
'If activated now, this repository will become a new hosted '.
'repository. To observe an existing repository instead, configure '.
'it in the %s panel.',
phutil_tag('strong', array(), pht('URIs')));
} else {
$messages[] = pht(
'If activated now, this repository will observe an existing remote '.
'repository and begin importing changes.');
}
$info_view = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
->setErrors($messages);
$basics->setInfoView($info_view);
}
$result[] = $basics;
$description = $this->buildDescription();
if ($description) {

View file

@ -13,6 +13,34 @@ final class DiffusionRepositoryBranchesManagementPanel
return 1000;
}
public function shouldEnableForRepository(
PhabricatorRepository $repository) {
return ($repository->isGit() || $repository->isHg());
}
public function getManagementPanelIcon() {
$repository = $this->getRepository();
$has_any =
$repository->getDetail('default-branch') ||
$repository->getDetail('branch-filter') ||
$repository->getDetail('close-commits-filter');
if ($has_any) {
return 'fa-code-fork';
} else {
return 'fa-code-fork grey';
}
}
protected function getEditEngineFieldKeys() {
return array(
'defaultBranch',
'trackOnly',
'autocloseOnly',
);
}
protected function buildManagementPanelActions() {
$repository = $this->getRepository();
$viewer = $this->getViewer();
@ -22,7 +50,7 @@ final class DiffusionRepositoryBranchesManagementPanel
$repository,
PhabricatorPolicyCapability::CAN_EDIT);
$branches_uri = $repository->getPathURI('edit/branches/');
$branches_uri = $this->getEditPageURI();
return array(
id(new PhabricatorActionView())

View file

@ -0,0 +1,29 @@
<?php
final class DiffusionRepositoryDocumentationManagementPanel
extends DiffusionRepositoryManagementPanel {
const PANELKEY = 'documentation';
public function getManagementPanelLabel() {
return pht('Documentation');
}
public function getManagementPanelOrder() {
return 3000;
}
public function getManagementPanelIcon() {
return 'fa-book';
}
public function buildManagementPanelContent() {
return null;
}
public function getPanelNavigationURI() {
return PhabricatorEnv::getDoclink(
'Diffusion User Guide: Managing Repositories');
}
}

View file

@ -13,6 +13,10 @@ final class DiffusionRepositoryHistoryManagementPanel
return 2000;
}
public function getManagementPanelIcon() {
return 'fa-list-ul';
}
public function buildManagementPanelContent() {
return $this->newTimeline();
}

View file

@ -38,10 +38,19 @@ abstract class DiffusionRepositoryManagementPanel
abstract public function getManagementPanelOrder();
abstract public function buildManagementPanelContent();
public function getManagementPanelIcon() {
return 'fa-pencil';
}
protected function buildManagementPanelActions() {
return array();
}
public function shouldEnableForRepository(
PhabricatorRepository $repository) {
return true;
}
final protected function newActions() {
$actions = $this->buildManagementPanelActions();
if (!$actions) {
@ -98,4 +107,45 @@ abstract class DiffusionRepositoryManagementPanel
return $this->controller->newTimeline($this->getRepository());
}
final public function getPanelURI() {
$repository = $this->getRepository();
$key = $this->getManagementPanelKey();
return $repository->getPathURI("manage/{$key}/");
}
final public function newEditEnginePage() {
$field_keys = $this->getEditEngineFieldKeys();
if (!$field_keys) {
return null;
}
$key = $this->getManagementPanelKey();
$label = $this->getManagementPanelLabel();
$panel_uri = $this->getPanelURI();
return id(new PhabricatorEditPage())
->setKey($key)
->setLabel($label)
->setViewURI($panel_uri)
->setFieldKeys($field_keys);
}
protected function getEditEngineFieldKeys() {
return array();
}
protected function getEditPageURI($page = null) {
if ($page === null) {
$page = $this->getManagementPanelKey();
}
$repository = $this->getRepository();
$id = $repository->getID();
return "/diffusion/edit/{$id}/page/{$page}/";
}
public function getPanelNavigationURI() {
return $this->getPanelURI();
}
}

View file

@ -13,6 +13,47 @@ final class DiffusionRepositoryPoliciesManagementPanel
return 300;
}
public function getManagementPanelIcon() {
$viewer = $this->getViewer();
$repository = $this->getRepository();
$can_view = PhabricatorPolicyCapability::CAN_VIEW;
$can_edit = PhabricatorPolicyCapability::CAN_EDIT;
$can_push = DiffusionPushCapability::CAPABILITY;
$actual_values = array(
'spacePHID' => $repository->getSpacePHID(),
'view' => $repository->getPolicy($can_view),
'edit' => $repository->getPolicy($can_edit),
'push' => $repository->getPolicy($can_push),
);
$default = PhabricatorRepository::initializeNewRepository(
$viewer);
$default_values = array(
'spacePHID' => $default->getSpacePHID(),
'view' => $default->getPolicy($can_view),
'edit' => $default->getPolicy($can_edit),
'push' => $default->getPolicy($can_push),
);
if ($actual_values === $default_values) {
return 'fa-lock grey';
} else {
return 'fa-lock';
}
}
protected function getEditEngineFieldKeys() {
return array(
'policy.view',
'policy.edit',
'spacePHID',
'policy.push',
);
}
protected function buildManagementPanelActions() {
$repository = $this->getRepository();
$viewer = $this->getViewer();
@ -22,7 +63,7 @@ final class DiffusionRepositoryPoliciesManagementPanel
$repository,
PhabricatorPolicyCapability::CAN_EDIT);
$edit_uri = $repository->getPathURI('manage/');
$edit_uri = $this->getEditPageURI();
return array(
id(new PhabricatorActionView())

View file

@ -13,6 +13,30 @@ final class DiffusionRepositoryStagingManagementPanel
return 700;
}
public function shouldEnableForRepository(
PhabricatorRepository $repository) {
return $repository->isGit();
}
public function getManagementPanelIcon() {
$repository = $this->getRepository();
$staging_uri = $repository->getStagingURI();
if ($staging_uri) {
return 'fa-upload';
} else {
return 'fa-upload grey';
}
}
protected function getEditEngineFieldKeys() {
return array(
'stagingAreaURI',
);
}
protected function buildManagementPanelActions() {
$repository = $this->getRepository();
$viewer = $this->getViewer();
@ -22,7 +46,7 @@ final class DiffusionRepositoryStagingManagementPanel
$repository,
PhabricatorPolicyCapability::CAN_EDIT);
$staging_uri = $repository->getPathURI('edit/staging/');
$staging_uri = $this->getEditPageURI();
return array(
id(new PhabricatorActionView())

View file

@ -13,6 +13,21 @@ final class DiffusionRepositoryStatusManagementPanel
return 200;
}
public function getManagementPanelIcon() {
$repository = $this->getRepository();
// TODO: We could try to show a warning icon in more cases, but just
// raise in the most serious cases for now.
$messages = $this->loadStatusMessages($repository);
$raw_error = $this->buildRepositoryRawError($repository, $messages);
if ($raw_error) {
return 'fa-exclamation-triangle red';
}
return 'fa-check grey';
}
protected function buildManagementPanelActions() {
$repository = $this->getRepository();
$viewer = $this->getViewer();
@ -46,9 +61,7 @@ final class DiffusionRepositoryStatusManagementPanel
pht('Update Frequency'),
$this->buildRepositoryUpdateInterval($repository));
$messages = id(new PhabricatorRepositoryStatusMessage())
->loadAllWhere('repositoryID = %d', $repository->getID());
$messages = mpull($messages, null, 'getStatusType');
$messages = $this->loadStatusMessages($repository);
$status = $this->buildRepositoryStatus($repository, $messages);
$raw_error = $this->buildRepositoryRawError($repository, $messages);
@ -122,7 +135,12 @@ final class DiffusionRepositoryStatusManagementPanel
}
if ($repository->isHosted()) {
if ($repository->getServeOverHTTP() != PhabricatorRepository::SERVE_OFF) {
$proto_https = PhabricatorRepositoryURI::BUILTIN_PROTOCOL_HTTPS;
$proto_http = PhabricatorRepositoryURI::BUILTIN_PROTOCOL_HTTP;
$can_http = $repository->canServeProtocol($proto_http, false) ||
$repository->canServeProtocol($proto_https, false);
if ($can_http) {
switch ($repository->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$binaries[] = 'git-http-backend';
@ -138,7 +156,12 @@ final class DiffusionRepositoryStatusManagementPanel
break;
}
}
if ($repository->getServeOverSSH() != PhabricatorRepository::SERVE_OFF) {
$proto_ssh = PhabricatorRepositoryURI::BUILTIN_PROTOCOL_SSH;
$can_ssh = $repository->canServeProtocol($proto_ssh, false);
if ($can_ssh) {
switch ($repository->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$binaries[] = 'git-receive-pack';
@ -469,5 +492,22 @@ final class DiffusionRepositoryStatusManagementPanel
return $raw_message;
}
private function loadStatusMessages(PhabricatorRepository $repository) {
$messages = id(new PhabricatorRepositoryStatusMessage())
->loadAllWhere('repositoryID = %d', $repository->getID());
$messages = mpull($messages, null, 'getStatusType');
return $messages;
}
private function getEnvConfigLink() {
$config_href = '/config/edit/environment.append-paths/';
return phutil_tag(
'a',
array(
'href' => $config_href,
),
'environment.append-paths');
}
}

View file

@ -13,6 +13,18 @@ final class DiffusionRepositoryStorageManagementPanel
return 600;
}
public function getManagementPanelIcon() {
$repository = $this->getRepository();
if ($repository->getAlmanacServicePHID()) {
return 'fa-sitemap';
} else if ($repository->isHosted()) {
return 'fa-folder';
} else {
return 'fa-download';
}
}
public function buildManagementPanelContent() {
return array(
$this->buildStorageStatusPanel(),
@ -28,7 +40,7 @@ final class DiffusionRepositoryStorageManagementPanel
->setViewer($viewer);
if ($repository->usesLocalWorkingCopy()) {
$storage_path = $repository->getHumanReadableDetail('local-path');
$storage_path = $repository->getLocalPath();
} else {
$storage_path = phutil_tag('em', array(), pht('No Local Working Copy'));
}

View file

@ -0,0 +1,77 @@
<?php
final class DiffusionRepositorySubversionManagementPanel
extends DiffusionRepositoryManagementPanel {
const PANELKEY = 'subversion';
public function getManagementPanelLabel() {
return pht('Subversion');
}
public function getManagementPanelOrder() {
return 1000;
}
public function shouldEnableForRepository(
PhabricatorRepository $repository) {
return $repository->isSVN();
}
public function getManagementPanelIcon() {
$repository = $this->getRepository();
$has_any = (bool)$repository->getDetail('svn-subpath');
if ($has_any) {
return 'fa-database';
} else {
return 'fa-database grey';
}
}
protected function getEditEngineFieldKeys() {
return array(
'importOnly',
);
}
protected function buildManagementPanelActions() {
$repository = $this->getRepository();
$viewer = $this->getViewer();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$repository,
PhabricatorPolicyCapability::CAN_EDIT);
$subversion_uri = $this->getEditPageURI();
return array(
id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Properties'))
->setHref($subversion_uri)
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit),
);
}
public function buildManagementPanelContent() {
$repository = $this->getRepository();
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setViewer($viewer)
->setActionList($this->newActions());
$default_branch = nonempty(
$repository->getHumanReadableDetail('svn-subpath'),
phutil_tag('em', array(), pht('Import Entire Repository')));
$view->addProperty(pht('Import Only'), $default_branch);
return $this->newBox(pht('Subversion'), $view);
}
}

View file

@ -13,6 +13,27 @@ final class DiffusionRepositorySymbolsManagementPanel
return 900;
}
public function getManagementPanelIcon() {
$repository = $this->getRepository();
$has_any =
$repository->getSymbolLanguages() ||
$repository->getSymbolSources();
if ($has_any) {
return 'fa-link';
} else {
return 'fa-link grey';
}
}
protected function getEditEngineFieldKeys() {
return array(
'symbolLanguages',
'symbolRepositoryPHIDs',
);
}
protected function buildManagementPanelActions() {
$repository = $this->getRepository();
$viewer = $this->getViewer();
@ -22,7 +43,7 @@ final class DiffusionRepositorySymbolsManagementPanel
$repository,
PhabricatorPolicyCapability::CAN_EDIT);
$symbols_uri = $repository->getPathURI('edit/symbols/');
$symbols_uri = $this->getEditPageURI();
return array(
id(new PhabricatorActionView())

View file

@ -6,11 +6,15 @@ final class DiffusionRepositoryURIsManagementPanel
const PANELKEY = 'uris';
public function getManagementPanelLabel() {
return pht('Clone / Fetch / Mirror');
return pht('URIs');
}
public function getManagementPanelIcon() {
return 'fa-cogs';
}
public function getManagementPanelOrder() {
return 300;
return 400;
}
public function buildManagementPanelContent() {
@ -109,8 +113,44 @@ final class DiffusionRepositoryURIsManagementPanel
->setTag('a')
->setText(pht('Documentation')));
$is_new = $repository->isNewlyInitialized();
$messages = array();
if ($repository->isHosted()) {
if ($is_new) {
$host_message = pht('Phabricator will host this repository.');
} else {
$host_message = pht('Phabricator is hosting this repository.');
}
$messages[] = array(
id(new PHUIIconView())->setIcon('fa-folder'),
' ',
$host_message,
);
} else {
if ($is_new) {
$observe_message = pht(
'Phabricator will observe a remote repository.');
} else {
$observe_message = pht(
'This repository is hosted remotely. Phabricator is observing it.');
}
$messages[] = array(
id(new PHUIIconView())->setIcon('fa-download'),
' ',
$observe_message,
);
}
$info_view = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
->setErrors($messages);
return id(new PHUIObjectBoxView())
->setHeader($header)
->setInfoView($info_view)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($table);
}

View file

@ -57,6 +57,10 @@ abstract class DiffusionCommandEngine extends Phobject {
return $this->protocol;
}
public function getDisplayProtocol() {
return $this->getProtocol().'://';
}
public function setCredentialPHID($credential_phid) {
$this->credentialPHID = $credential_phid;
return $this;
@ -197,34 +201,82 @@ abstract class DiffusionCommandEngine extends Phobject {
return $env;
}
protected function isSSHProtocol() {
public function isSSHProtocol() {
return ($this->getProtocol() == 'ssh');
}
protected function isSVNProtocol() {
public function isSVNProtocol() {
return ($this->getProtocol() == 'svn');
}
protected function isSVNSSHProtocol() {
public function isSVNSSHProtocol() {
return ($this->getProtocol() == 'svn+ssh');
}
protected function isHTTPProtocol() {
public function isHTTPProtocol() {
return ($this->getProtocol() == 'http');
}
protected function isHTTPSProtocol() {
public function isHTTPSProtocol() {
return ($this->getProtocol() == 'https');
}
protected function isAnyHTTPProtocol() {
public function isAnyHTTPProtocol() {
return ($this->isHTTPProtocol() || $this->isHTTPSProtocol());
}
protected function isAnySSHProtocol() {
public function isAnySSHProtocol() {
return ($this->isSSHProtocol() || $this->isSVNSSHProtocol());
}
public function isCredentialSupported() {
return ($this->getPassphraseProvidesCredentialType() !== null);
}
public function isCredentialOptional() {
if ($this->isAnySSHProtocol()) {
return false;
}
return true;
}
public function getPassphraseCredentialLabel() {
if ($this->isAnySSHProtocol()) {
return pht('SSH Key');
}
if ($this->isAnyHTTPProtocol() || $this->isSVNProtocol()) {
return pht('Password');
}
return null;
}
public function getPassphraseDefaultCredentialType() {
if ($this->isAnySSHProtocol()) {
return PassphraseSSHPrivateKeyTextCredentialType::CREDENTIAL_TYPE;
}
if ($this->isAnyHTTPProtocol() || $this->isSVNProtocol()) {
return PassphrasePasswordCredentialType::CREDENTIAL_TYPE;
}
return null;
}
public function getPassphraseProvidesCredentialType() {
if ($this->isAnySSHProtocol()) {
return PassphraseSSHPrivateKeyCredentialType::PROVIDES_TYPE;
}
if ($this->isAnyHTTPProtocol() || $this->isSVNProtocol()) {
return PassphrasePasswordCredentialType::PROVIDES_TYPE;
}
return null;
}
protected function getSSHWrapper() {
$root = dirname(phutil_get_library_root('phabricator'));
return $root.'/bin/ssh-connect';

View file

@ -24,8 +24,7 @@ final class DiffusionGitCommandEngine
// really silly, but seems like the least damaging approach to
// mitigating the issue.
$root = dirname(phutil_get_library_root('phabricator'));
$env['HOME'] = $root.'/support/empty/';
$env['HOME'] = PhabricatorEnv::getEmptyCWD();
if ($this->isAnySSHProtocol()) {
$env['GIT_SSH'] = $this->getSSHWrapper();

View file

@ -31,6 +31,11 @@ final class DiffusionLowLevelResolveRefsQuery
return array();
}
$repository = $this->getRepository();
if (!$repository->hasLocalWorkingCopy()) {
return array();
}
switch ($this->getRepository()->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$result = $this->resolveGitRefs();

View file

@ -15,7 +15,7 @@ final class DiffusionGitReceivePackSSHWorkflow extends DiffusionGitSSHWorkflow {
protected function executeRepositoryOperations() {
$repository = $this->getRepository();
$viewer = $this->getViewer();
$viewer = $this->getUser();
$device = AlmanacKeys::getLiveDevice();
// This is a write, and must have write access.

View file

@ -185,20 +185,15 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
$repository = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->withIdentifiers(array($identifier))
->needURIs(true)
->executeOne();
if (!$repository) {
throw new Exception(
pht('No repository "%s" exists!', $identifier));
}
switch ($repository->getServeOverSSH()) {
case PhabricatorRepository::SERVE_READONLY:
case PhabricatorRepository::SERVE_READWRITE:
// If we have read or read/write access, proceed for now. We will
// check write access when the user actually issues a write command.
break;
case PhabricatorRepository::SERVE_OFF:
default:
$protocol = PhabricatorRepositoryURI::BUILTIN_PROTOCOL_SSH;
if (!$repository->canServeProtocol($protocol, false)) {
throw new Exception(
pht(
'This repository ("%s") is not available over SSH.',
@ -224,8 +219,17 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
'user account.'));
}
switch ($repository->getServeOverSSH()) {
case PhabricatorRepository::SERVE_READONLY:
$protocol = PhabricatorRepositoryURI::BUILTIN_PROTOCOL_SSH;
if ($repository->canServeProtocol($protocol, true)) {
$can_push = PhabricatorPolicyFilter::hasCapability(
$viewer,
$repository,
DiffusionPushCapability::CAPABILITY);
if (!$can_push) {
throw new Exception(
pht('You do not have permission to push to this repository.'));
}
} else {
if ($protocol_command !== null) {
throw new Exception(
pht(
@ -236,23 +240,6 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
throw new Exception(
pht('This repository is read-only over SSH.'));
}
break;
case PhabricatorRepository::SERVE_READWRITE:
$can_push = PhabricatorPolicyFilter::hasCapability(
$viewer,
$repository,
DiffusionPushCapability::CAPABILITY);
if (!$can_push) {
throw new Exception(
pht('You do not have permission to push to this repository.'));
}
break;
case PhabricatorRepository::SERVE_OFF:
default:
// This shouldn't be reachable because we don't get this far if the
// repository isn't enabled, but kick them out anyway.
throw new Exception(
pht('This repository is not available over SSH.'));
}
$this->hasWriteAccess = true;

View file

@ -148,15 +148,25 @@ final class DiffusionSubversionServeSSHWorkflow
if ($this->shouldProxy()) {
$command = $this->getProxyCommand();
$this->isProxying = true;
$cwd = null;
} else {
$command = csprintf(
'svnserve -t --tunnel-user=%s',
$this->getUser()->getUsername());
$cwd = PhabricatorEnv::getEmptyCWD();
}
$command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
$future = new ExecFuture('%C', $command);
// If we're receiving a commit, svnserve will fail to execute the commit
// hook with an unhelpful error if the CWD isn't readable by the user we
// are sudoing to. Switch to a readable, empty CWD before running
// svnserve. See T10941.
if ($cwd !== null) {
$future->setCWD($cwd);
}
$this->inProtocol = new DiffusionSubversionWireProtocol();
$this->outProtocol = new DiffusionSubversionWireProtocol();

View file

@ -0,0 +1,131 @@
<?php
final class DiffusionCloneURIView extends AphrontView {
private $repository;
private $repositoryURI;
private $displayURI;
public function setRepository(PhabricatorRepository $repository) {
$this->repository = $repository;
return $this;
}
public function getRepository() {
return $this->repository;
}
public function setRepositoryURI(PhabricatorRepositoryURI $repository_uri) {
$this->repositoryURI = $repository_uri;
return $this;
}
public function getRepositoryURI() {
return $this->repositoryURI;
}
public function setDisplayURI($display_uri) {
$this->displayURI = $display_uri;
return $this;
}
public function getDisplayURI() {
return $this->displayURI;
}
public function render() {
require_celerity_resource('diffusion-icons-css');
Javelin::initBehavior('select-content');
$uri_id = celerity_generate_unique_node_id();
$display = $this->getDisplayURI();
$input = javelin_tag(
'input',
array(
'id' => $uri_id,
'type' => 'text',
'value' => $display,
'class' => 'diffusion-clone-uri',
'readonly' => 'true',
));
$uri = $this->getRepositoryURI();
switch ($uri->getEffectiveIOType()) {
case PhabricatorRepositoryURI::IO_READ:
$io_icon = 'fa-eye';
$io_tip = pht('Read-Only');
break;
case PhabricatorRepositoryURI::IO_READWRITE:
$io_icon = 'fa-download';
$io_tip = pht('Read / Write');
break;
default:
$io_icon = 'fa-cloud';
$io_tip = pht('External');
break;
}
$io = id(new PHUIButtonView())
->setTag('a')
->setColor(PHUIButtonView::GREY)
->setIcon($io_icon)
->setHref('#')
->addSigil('select-content')
->addSigil('has-tooltip')
->setMetadata(
array(
'tip' => $io_tip,
'selectID' => $uri_id,
));
switch ($uri->getEffectiveIOType()) {
case PhabricatorRepositoryURI::IO_READ:
case PhabricatorRepositoryURI::IO_READWRITE:
switch ($uri->getBuiltinProtocol()) {
case PhabricatorRepositoryURI::BUILTIN_PROTOCOL_SSH:
$auth_uri = '/settings/panel/ssh/';
$auth_tip = pht('Manage SSH Keys');
$auth_disabled = false;
break;
default:
$auth_uri = '/settings/panel/vcspassword';
$auth_tip = pht('Manage Password');
$auth_disabled = false;
break;
}
break;
default:
$auth_disabled = true;
$auth_tip = pht('External');
$auth_uri = '#';
break;
}
$credentials = id(new PHUIButtonView())
->setTag('a')
->setColor(PHUIButtonView::GREY)
->setIcon('fa-key')
->setTooltip($auth_tip)
->setHref($auth_uri)
->setDisabled($auth_disabled);
$cells = array();
$cells[] = phutil_tag('td', array(), $input);
$cells[] = phutil_tag('th', array(), $io);
$cells[] = phutil_tag('th', array(), $credentials);
$row = phutil_tag('tr', array(), $cells);
return phutil_tag(
'table',
array(
'class' => 'diffusion-clone-uri-table',
),
$row);
}
}

View file

@ -9,6 +9,35 @@ final class DrydockAuthorizationQuery extends DrydockQuery {
private $blueprintStates;
private $objectStates;
public static function isFullyAuthorized(
$object_phid,
array $blueprint_phids) {
if (!$blueprint_phids) {
return true;
}
$authorizations = id(new self())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withObjectPHIDs(array($object_phid))
->withBlueprintPHIDs($blueprint_phids)
->execute();
$authorizations = mpull($authorizations, null, 'getBlueprintPHID');
foreach ($blueprint_phids as $phid) {
$authorization = idx($authorizations, $phid);
if (!$authorization) {
return false;
}
if (!$authorization->isAuthorized()) {
return false;
}
}
return true;
}
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;

View file

@ -93,6 +93,11 @@ final class DrydockAuthorization extends DrydockDAO
return idx($map, $state, pht('<Unknown: %s>', $state));
}
public function isAuthorized() {
$state = $this->getBlueprintAuthorizationState();
return ($state == self::BLUEPRINTAUTH_AUTHORIZED);
}
/**
* Apply external authorization effects after a user chagnes the value of a
* blueprint selector control an object.

View file

@ -109,6 +109,14 @@ final class PhabricatorEmbedFileRemarkupRule
);
$image_class = 'phabricator-remarkup-embed-image-full';
break;
// Displays "full" in normal Remarkup, "wide" in Documents
case 'wide':
$attrs += array(
'src' => $file->getBestURI(),
'width' => $file->getImageWidth(),
);
$image_class = 'phabricator-remarkup-embed-image-wide';
break;
case 'thumb':
default:
$preview_key = PhabricatorFileThumbnailTransform::TRANSFORM_PREVIEW;

View file

@ -868,23 +868,9 @@ final class ManiphestTransactionEditor
switch ($type) {
case PhabricatorTransactions::TYPE_COLUMNS:
try {
$this->buildMoveTransaction($object, $xaction);
// Implicilty add the task to any boards that we're moving it
// on, since moves on a board the task isn't part of are not
// meaningful.
$board_phids = ipull($xaction->getNewValue(), 'boardPHID');
if ($board_phids) {
$results[] = id(new ManiphestTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue(
'edge:type',
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST)
->setIgnoreOnNoEffect(true)
->setNewValue(
array(
'+' => array_fuse($board_phids),
));
$more_xactions = $this->buildMoveTransaction($object, $xaction);
foreach ($more_xactions as $more_xaction) {
$results[] = $more_xaction;
}
} catch (Exception $ex) {
$error = new PhabricatorApplicationTransactionValidationError(
@ -1098,6 +1084,89 @@ final class ManiphestTransactionEditor
$new = array_values($new);
$xaction->setNewValue($new);
$more = array();
// If we're moving the object into a column and it does not already belong
// in the column, add the appropriate board. For normal columns, this
// is the board PHID. For proxy columns, it is the proxy PHID, unless the
// object is already a member of some descendant of the proxy PHID.
// The major case where this can happen is moves via the API, but it also
// happens when a user drags a task from the "Backlog" to a milestone
// column.
if ($object_phid) {
$current_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
$object_phid,
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
$current_phids = array_fuse($current_phids);
} else {
$current_phids = array();
}
$add_boards = array();
foreach ($new as $move) {
$column_phid = $move['columnPHID'];
$board_phid = $move['boardPHID'];
$column = $columns[$column_phid];
$proxy_phid = $column->getProxyPHID();
// If this is a normal column, add the board if the object isn't already
// associated.
if (!$proxy_phid) {
if (!isset($current_phids[$board_phid])) {
$add_boards[] = $board_phid;
}
continue;
}
// If this is a proxy column but the object is already associated with
// the proxy board, we don't need to do anything.
if (isset($current_phids[$proxy_phid])) {
continue;
}
// If this a proxy column and the object is already associated with some
// descendant of the proxy board, we also don't need to do anything.
$descendants = id(new PhabricatorProjectQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withAncestorProjectPHIDs(array($proxy_phid))
->execute();
$found_descendant = false;
foreach ($descendants as $descendant) {
if (isset($current_phids[$descendant->getPHID()])) {
$found_descendant = true;
break;
}
}
if ($found_descendant) {
continue;
}
// Otherwise, we're moving the object to a proxy column which it is not
// a member of yet, so add an association to the column's proxy board.
$add_boards[] = $proxy_phid;
}
if ($add_boards) {
$more[] = id(new ManiphestTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue(
'edge:type',
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST)
->setIgnoreOnNoEffect(true)
->setNewValue(
array(
'+' => array_fuse($add_boards),
));
}
return $more;
}
private function applyBoardMove($object, array $move) {

View file

@ -111,8 +111,11 @@ final class PhabricatorMetaMTAMailBody extends Phobject {
return $this;
}
public function addPlaintextSection($header, $text) {
$this->sections[] = $header."\n".$this->indent($text);
public function addPlaintextSection($header, $text, $indent = true) {
if ($indent) {
$text = $this->indent($text);
}
$this->sections[] = $header."\n".$text;
return $this;
}

View file

@ -82,7 +82,7 @@ final class PhabricatorOwnersPathsController
}
}
$repos = mpull($repos, 'getMonogram', 'getPHID');
$repos = mpull($repos, 'getDisplayName', 'getPHID');
asort($repos);
$template = new AphrontTypeaheadTemplateView();

View file

@ -42,10 +42,50 @@ final class PassphraseCredentialControl extends AphrontFormControl {
foreach ($this->options as $option) {
$options_map[$option->getPHID()] = pht(
'%s %s',
'K'.$option->getID(),
$option->getMonogram(),
$option->getName());
}
// The user editing the form may not have permission to see the current
// credential. Populate it into the menu to allow them to save the form
// without making any changes.
$current_phid = $this->getValue();
if (strlen($current_phid) && empty($options_map[$current_phid])) {
$viewer = $this->getViewer();
$user_credential = id(new PassphraseCredentialQuery())
->setViewer($viewer)
->withPHIDs(array($current_phid))
->executeOne();
if (!$user_credential) {
// Pull the credential with the ominipotent viewer so we can look up
// the ID and tell if it's restricted or invalid.
$omnipotent_credential = id(new PassphraseCredentialQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs(array($current_phid))
->executeOne();
if ($omnipotent_credential) {
$current_name = pht(
'%s (Restricted Credential)',
$omnipotent_credential->getMonogram());
} else {
$current_name = pht(
'Invalid Credential ("%s")',
$current_phid);
}
} else {
$current_name = pht(
'%s %s',
$user_credential->getMonogram(),
$user_credential->getName());
}
$options_map = array(
$current_phid => $current_name,
) + $options_map;
}
$disabled = $this->getDisabled();
if ($this->allowNull) {
$options_map = array('' => pht('(No Credentials)')) + $options_map;

View file

@ -1011,6 +1011,39 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase {
$column->getPHID(),
);
$this->assertColumns($expect, $user, $board, $task);
// Move the task within the "Milestone" column. This should not affect
// the projects the task is tagged with. See T10912.
$task_a = $task;
$task_b = $this->newTask($user, array($backlog));
$this->moveToColumn($user, $board, $task_b, $backlog, $column);
$a_options = array(
'beforePHID' => $task_b->getPHID(),
);
$b_options = array(
'beforePHID' => $task_a->getPHID(),
);
$old_projects = $this->getTaskProjects($task);
// Move the target task to the top.
$this->moveToColumn($user, $board, $task_a, $column, $column, $a_options);
$new_projects = $this->getTaskProjects($task_a);
$this->assertEqual($old_projects, $new_projects);
// Move the other task.
$this->moveToColumn($user, $board, $task_b, $column, $column, $b_options);
$new_projects = $this->getTaskProjects($task_a);
$this->assertEqual($old_projects, $new_projects);
// Move the target task again.
$this->moveToColumn($user, $board, $task_a, $column, $column, $a_options);
$new_projects = $this->getTaskProjects($task_a);
$this->assertEqual($old_projects, $new_projects);
}
public function testColumnExtendedPolicies() {

View file

@ -96,32 +96,6 @@ final class PhabricatorProjectMoveController
}
}
$proxy = $column->getProxy();
if ($proxy) {
// We're moving the task into a subproject or milestone column, so add
// the subproject or milestone.
$add_projects = array($proxy->getPHID());
} else if ($project->getHasSubprojects() || $project->getHasMilestones()) {
// We're moving the task into the "Backlog" column on the parent project,
// so add the parent explicitly. This gets rid of any subproject or
// milestone tags.
$add_projects = array($project->getPHID());
} else {
$add_projects = array();
}
if ($add_projects) {
$project_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
$xactions[] = id(new ManiphestTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $project_type)
->setNewValue(
array(
'+' => array_fuse($add_projects),
));
}
$editor = id(new ManiphestTransactionEditor())
->setActor($viewer)
->setContinueOnMissingFields(true)

View file

@ -1,140 +0,0 @@
<?php
final class RepositoryCreateConduitAPIMethod
extends RepositoryConduitAPIMethod {
public function getAPIMethodName() {
return 'repository.create';
}
public function getMethodStatus() {
return self::METHOD_STATUS_UNSTABLE;
}
public function getMethodStatusDescription() {
return pht('Repository methods are new and subject to change.');
}
public function getMethodDescription() {
return pht('Create a new repository.');
}
protected function defineParamTypes() {
$vcs_const = $this->formatStringConstants(array('git', 'hg', 'svn'));
return array(
'name' => 'required string',
'vcs' => 'required '.$vcs_const,
'callsign' => 'required string',
'description' => 'optional string',
'encoding' => 'optional string',
'tracking' => 'optional bool',
'uri' => 'required string',
'credentialPHID' => 'optional string',
'svnSubpath' => 'optional string',
'branchFilter' => 'optional list<string>',
'closeCommitsFilter' => 'optional list<string>',
'pullFrequency' => 'optional int',
'defaultBranch' => 'optional string',
'heraldEnabled' => 'optional bool, default = true',
'autocloseEnabled' => 'optional bool, default = true',
'svnUUID' => 'optional string',
);
}
protected function defineReturnType() {
return 'nonempty dict';
}
protected function defineErrorTypes() {
return array(
'ERR-DUPLICATE' => pht('Duplicate repository callsign.'),
'ERR-BAD-CALLSIGN' => pht(
'Callsign is required and must be ALL UPPERCASE LETTERS.'),
'ERR-UNKNOWN-REPOSITORY-VCS' => pht('Unknown repository VCS type.'),
);
}
protected function execute(ConduitAPIRequest $request) {
$application = id(new PhabricatorApplicationQuery())
->setViewer($request->getUser())
->withClasses(array('PhabricatorDiffusionApplication'))
->executeOne();
PhabricatorPolicyFilter::requireCapability(
$request->getUser(),
$application,
DiffusionCreateRepositoriesCapability::CAPABILITY);
// TODO: This has some duplication with (and lacks some of the validation
// of) the web workflow; refactor things so they can share more code as this
// stabilizes. Specifically, this should move to transactions since they
// work properly now.
$repository = PhabricatorRepository::initializeNewRepository(
$request->getUser());
$repository->setName($request->getValue('name'));
$callsign = $request->getValue('callsign');
if (!preg_match('/^[A-Z]+\z/', $callsign)) {
throw new ConduitException('ERR-BAD-CALLSIGN');
}
$repository->setCallsign($callsign);
$local_path = PhabricatorEnv::getEnvConfig(
'repository.default-local-path');
$local_path = rtrim($local_path, '/');
$local_path = $local_path.'/'.$callsign.'/';
$vcs = $request->getValue('vcs');
$map = array(
'git' => PhabricatorRepositoryType::REPOSITORY_TYPE_GIT,
'hg' => PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL,
'svn' => PhabricatorRepositoryType::REPOSITORY_TYPE_SVN,
);
if (empty($map[$vcs])) {
throw new ConduitException('ERR-UNKNOWN-REPOSITORY-VCS');
}
$repository->setVersionControlSystem($map[$vcs]);
$repository->setCredentialPHID($request->getValue('credentialPHID'));
$remote_uri = $request->getValue('uri');
PhabricatorRepository::assertValidRemoteURI($remote_uri);
$details = array(
'encoding' => $request->getValue('encoding'),
'description' => $request->getValue('description'),
'tracking-enabled' => (bool)$request->getValue('tracking', true),
'remote-uri' => $remote_uri,
'local-path' => $local_path,
'branch-filter' => array_fill_keys(
$request->getValue('branchFilter', array()),
true),
'close-commits-filter' => array_fill_keys(
$request->getValue('closeCommitsFilter', array()),
true),
'pull-frequency' => $request->getValue('pullFrequency'),
'default-branch' => $request->getValue('defaultBranch'),
'herald-disabled' => !$request->getValue('heraldEnabled', true),
'svn-subpath' => $request->getValue('svnSubpath'),
'disable-autoclose' => !$request->getValue('autocloseEnabled', true),
);
foreach ($details as $key => $value) {
$repository->setDetail($key, $value);
}
try {
$repository->save();
} catch (AphrontDuplicateKeyQueryException $ex) {
throw new ConduitException('ERR-DUPLICATE');
}
return $repository->toDictionary();
}
}

View file

@ -354,117 +354,17 @@ final class PhabricatorRepositoryPullLocalDaemon
}
}
$service_phids = array();
foreach ($repositories as $key => $repository) {
$service_phid = $repository->getAlmanacServicePHID();
$viewer = $this->getViewer();
// If the repository is bound to a service but this host is not a
// recognized device, or vice versa, don't pull the repository.
$is_cluster_repo = (bool)$service_phid;
$is_cluster_device = (bool)$device;
if ($is_cluster_repo != $is_cluster_device) {
if ($is_cluster_device) {
$this->log(
pht(
'Repository "%s" is not a cluster repository, but the current '.
'host is a cluster device ("%s"), so the repository will not '.
'be updated on this host.',
$repository->getDisplayName(),
$device->getName()));
} else {
$this->log(
pht(
'Repository "%s" is a cluster repository, but the current '.
'host is not a cluster device (it has no device ID), so the '.
'repository will not be updated on this host.',
$repository->getDisplayName()));
}
unset($repositories[$key]);
continue;
}
$filter = id(new DiffusionLocalRepositoryFilter())
->setViewer($viewer)
->setDevice($device)
->setRepositories($repositories);
if ($service_phid) {
$service_phids[] = $service_phid;
}
}
$repositories = $filter->execute();
if ($device) {
$device_phid = $device->getPHID();
if ($service_phids) {
// We could include `withDevicePHIDs()` here to pull a smaller result
// set, but we can provide more helpful diagnostic messages below if
// we fetch a little more data.
$services = id(new AlmanacServiceQuery())
->setViewer($this->getViewer())
->withPHIDs($service_phids)
->withServiceTypes(
array(
AlmanacClusterRepositoryServiceType::SERVICETYPE,
))
->needBindings(true)
->execute();
$services = mpull($services, null, 'getPHID');
} else {
$services = array();
}
foreach ($repositories as $key => $repository) {
$service_phid = $repository->getAlmanacServicePHID();
$service = idx($services, $service_phid);
if (!$service) {
$this->log(
pht(
'Repository "%s" is on cluster service "%s", but that service '.
'could not be loaded, so the repository will not be updated '.
'on this host.',
$repository->getDisplayName(),
$service_phid));
unset($repositories[$key]);
continue;
}
$bindings = $service->getBindings();
$bindings = mgroup($bindings, 'getDevicePHID');
$bindings = idx($bindings, $device_phid);
if (!$bindings) {
$this->log(
pht(
'Repository "%s" is on cluster service "%s", but that service '.
'is not bound to this device ("%s"), so the repository will '.
'not be updated on this host.',
$repository->getDisplayName(),
$service->getName(),
$device->getName()));
unset($repositories[$key]);
continue;
}
$all_disabled = true;
foreach ($bindings as $binding) {
if (!$binding->getIsDisabled()) {
$all_disabled = false;
break;
}
}
if ($all_disabled) {
$this->log(
pht(
'Repository "%s" is on cluster service "%s", but the binding '.
'between that service and this device ("%s") is disabled, so '.
'the not be updated on this host.',
$repository->getDisplayName(),
$service->getName(),
$device->getName()));
unset($repositories[$key]);
continue;
}
// We have a valid service that is actively bound to the current host
// device, so we're good to go.
}
foreach ($filter->getRejectionReasons() as $reason) {
$this->log($reason);
}
// Shuffle the repositories, then re-key the array since shuffle()

View file

@ -59,6 +59,14 @@ final class PhabricatorRepositoryURINormalizer extends Phobject {
$this->uri = $uri;
}
public static function getAllURITypes() {
return array(
self::TYPE_GIT,
self::TYPE_SVN,
self::TYPE_MERCURIAL,
);
}
/* -( Normalizing URIs )--------------------------------------------------- */
@ -91,6 +99,10 @@ final class PhabricatorRepositoryURINormalizer extends Phobject {
}
}
public function getNormalizedURI() {
return $this->getNormalizedDomain().'/'.$this->getNormalizedPath();
}
/**
* @task normal
@ -113,11 +125,32 @@ final class PhabricatorRepositoryURINormalizer extends Phobject {
// example.
$matches = null;
if (preg_match('@^(diffusion/[A-Z]+)@', $path, $matches)) {
if (preg_match('@^(diffusion/(?:[A-Z]+|\d+))@', $path, $matches)) {
$path = $matches[1];
}
return $path;
}
public function getNormalizedDomain() {
$domain = null;
$uri = new PhutilURI($this->uri);
if ($uri->getProtocol()) {
$domain = $uri->getDomain();
}
if (!strlen($domain)) {
$uri = new PhutilGitURI($this->uri);
$domain = $uri->getDomain();
}
if (!strlen($domain)) {
$domain = '<void>';
}
return phutil_utf8_strtolower($domain);
}
}

View file

@ -26,18 +26,7 @@ final class PhabricatorRepositoryEditor
$types[] = PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH;
$types[] = PhabricatorRepositoryTransaction::TYPE_NOTIFY;
$types[] = PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE;
$types[] = PhabricatorRepositoryTransaction::TYPE_REMOTE_URI;
$types[] = PhabricatorRepositoryTransaction::TYPE_SSH_LOGIN;
$types[] = PhabricatorRepositoryTransaction::TYPE_SSH_KEY;
$types[] = PhabricatorRepositoryTransaction::TYPE_SSH_KEYFILE;
$types[] = PhabricatorRepositoryTransaction::TYPE_HTTP_LOGIN;
$types[] = PhabricatorRepositoryTransaction::TYPE_HTTP_PASS;
$types[] = PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH;
$types[] = PhabricatorRepositoryTransaction::TYPE_HOSTING;
$types[] = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP;
$types[] = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH;
$types[] = PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY;
$types[] = PhabricatorRepositoryTransaction::TYPE_CREDENTIAL;
$types[] = PhabricatorRepositoryTransaction::TYPE_DANGEROUS;
$types[] = PhabricatorRepositoryTransaction::TYPE_SLUG;
$types[] = PhabricatorRepositoryTransaction::TYPE_SERVICE;
@ -83,20 +72,8 @@ final class PhabricatorRepositoryEditor
return (int)!$object->getDetail('herald-disabled');
case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE:
return (int)!$object->getDetail('disable-autoclose');
case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI:
return $object->getDetail('remote-uri');
case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH:
return $object->getDetail('local-path');
case PhabricatorRepositoryTransaction::TYPE_HOSTING:
return $object->isHosted();
case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP:
return $object->getServeOverHTTP();
case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH:
return $object->getServeOverSSH();
case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY:
return $object->getPushPolicy();
case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL:
return $object->getCredentialPHID();
case PhabricatorRepositoryTransaction::TYPE_DANGEROUS:
return $object->shouldAllowDangerousChanges();
case PhabricatorRepositoryTransaction::TYPE_SLUG:
@ -130,19 +107,8 @@ final class PhabricatorRepositoryEditor
case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY:
case PhabricatorRepositoryTransaction::TYPE_UUID:
case PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH:
case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI:
case PhabricatorRepositoryTransaction::TYPE_SSH_LOGIN:
case PhabricatorRepositoryTransaction::TYPE_SSH_KEY:
case PhabricatorRepositoryTransaction::TYPE_SSH_KEYFILE:
case PhabricatorRepositoryTransaction::TYPE_HTTP_LOGIN:
case PhabricatorRepositoryTransaction::TYPE_HTTP_PASS:
case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH:
case PhabricatorRepositoryTransaction::TYPE_VCS:
case PhabricatorRepositoryTransaction::TYPE_HOSTING:
case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP:
case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH:
case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY:
case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL:
case PhabricatorRepositoryTransaction::TYPE_DANGEROUS:
case PhabricatorRepositoryTransaction::TYPE_SERVICE:
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE:
@ -172,7 +138,15 @@ final class PhabricatorRepositoryEditor
$object->setVersionControlSystem($xaction->getNewValue());
break;
case PhabricatorRepositoryTransaction::TYPE_ACTIVATE:
$object->setDetail('tracking-enabled', $xaction->getNewValue());
$active = $xaction->getNewValue();
// The first time a repository is activated, clear the "new repository"
// flag so we stop showing setup hints.
if ($active) {
$object->setDetail('newly-initialized', false);
}
$object->setDetail('tracking-enabled', $active);
break;
case PhabricatorRepositoryTransaction::TYPE_NAME:
$object->setName($xaction->getNewValue());
@ -205,22 +179,8 @@ final class PhabricatorRepositoryEditor
case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE:
$object->setDetail('disable-autoclose', (int)!$xaction->getNewValue());
break;
case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI:
$object->setDetail('remote-uri', $xaction->getNewValue());
break;
case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH:
$object->setDetail('local-path', $xaction->getNewValue());
break;
case PhabricatorRepositoryTransaction::TYPE_HOSTING:
return $object->setHosted($xaction->getNewValue());
case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP:
return $object->setServeOverHTTP($xaction->getNewValue());
case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH:
return $object->setServeOverSSH($xaction->getNewValue());
case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY:
return $object->setPushPolicy($xaction->getNewValue());
case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL:
return $object->setCredentialPHID($xaction->getNewValue());
case PhabricatorRepositoryTransaction::TYPE_DANGEROUS:
$object->setDetail('allow-dangerous-changes', $xaction->getNewValue());
return;
@ -258,27 +218,6 @@ final class PhabricatorRepositoryEditor
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL:
// Adjust the object <-> credential edge for this repository.
$old_phid = $xaction->getOldValue();
$new_phid = $xaction->getNewValue();
$editor = new PhabricatorEdgeEditor();
$edge_type = PhabricatorObjectUsesCredentialsEdgeType::EDGECONST;
$src_phid = $object->getPHID();
if ($old_phid) {
$editor->removeEdge($src_phid, $edge_type, $old_phid);
}
if ($new_phid) {
$editor->addEdge($src_phid, $edge_type, $new_phid);
}
$editor->save();
break;
case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS:
DrydockAuthorization::applyAuthorizationChanges(
$this->getActor(),
@ -304,21 +243,10 @@ final class PhabricatorRepositoryEditor
case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY:
case PhabricatorRepositoryTransaction::TYPE_UUID:
case PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH:
case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI:
case PhabricatorRepositoryTransaction::TYPE_SSH_LOGIN:
case PhabricatorRepositoryTransaction::TYPE_SSH_KEY:
case PhabricatorRepositoryTransaction::TYPE_SSH_KEYFILE:
case PhabricatorRepositoryTransaction::TYPE_HTTP_LOGIN:
case PhabricatorRepositoryTransaction::TYPE_HTTP_PASS:
case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH:
case PhabricatorRepositoryTransaction::TYPE_VCS:
case PhabricatorRepositoryTransaction::TYPE_NOTIFY:
case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE:
case PhabricatorRepositoryTransaction::TYPE_HOSTING:
case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP:
case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH:
case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY:
case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL:
case PhabricatorRepositoryTransaction::TYPE_DANGEROUS:
case PhabricatorRepositoryTransaction::TYPE_SLUG:
case PhabricatorRepositoryTransaction::TYPE_SERVICE:
@ -388,38 +316,6 @@ final class PhabricatorRepositoryEditor
}
break;
case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI:
foreach ($xactions as $xaction) {
$new_uri = $xaction->getNewValue();
try {
PhabricatorRepository::assertValidRemoteURI($new_uri);
} catch (Exception $ex) {
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
$ex->getMessage(),
$xaction);
}
}
break;
case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL:
$ok = PassphraseCredentialControl::validateTransactions(
$this->getActor(),
$xactions);
if (!$ok) {
foreach ($xactions as $xaction) {
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
pht(
'The selected credential does not exist, or you do not have '.
'permission to use it.'),
$xaction);
}
}
break;
case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS:
foreach ($xactions as $xaction) {
$old = nonempty($xaction->getOldValue(), array());
@ -706,7 +602,7 @@ final class PhabricatorRepositoryEditor
// If the repository does not have a local path yet, assign it one based
// on its ID. We can't do this earlier because we won't have an ID yet.
$local_path = $object->getDetail('local-path');
$local_path = $object->getLocalPath();
if (!strlen($local_path)) {
$local_key = 'repository.default-local-path';
@ -716,11 +612,22 @@ final class PhabricatorRepositoryEditor
$id = $object->getID();
$local_path = "{$local_root}/{$id}/";
$object->setDetail('local-path', $local_path);
$object->setLocalPath($local_path);
$object->save();
}
if ($this->getIsNewObject()) {
// The default state of repositories is to be hosted, if they are
// enabled without configuring any "Observe" URIs.
$object->setHosted(true);
$object->save();
// Create this repository's builtin URIs.
$builtin_uris = $object->newBuiltinURIs();
foreach ($builtin_uris as $uri) {
$uri->save();
}
id(new DiffusionRepositoryClusterEngine())
->setViewer($this->getActor())
->setRepository($object)

View file

@ -37,6 +37,33 @@ final class PhabricatorRepositoryDiscoveryEngine
public function discoverCommits() {
$repository = $this->getRepository();
$lock = $this->newRepositoryLock($repository, 'repo.look', false);
try {
$lock->lock();
} catch (PhutilLockException $ex) {
throw new DiffusionDaemonLockException(
pht(
'Another process is currently discovering repository "%s", '.
'skipping discovery.',
$repository->getDisplayName()));
}
try {
$result = $this->discoverCommitsWithLock();
} catch (Exception $ex) {
$lock->unlock();
throw $ex;
}
$lock->unlock();
return $result;
}
private function discoverCommitsWithLock() {
$repository = $this->getRepository();
$vcs = $repository->getVersionControlSystem();
switch ($vcs) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
@ -52,6 +79,16 @@ final class PhabricatorRepositoryDiscoveryEngine
throw new Exception(pht("Unknown VCS '%s'!", $vcs));
}
if ($this->isInitialImport($refs)) {
$this->log(
pht(
'Discovered more than %s commit(s) in an empty repository, '.
'marking repository as importing.',
new PhutilNumber(PhabricatorRepository::IMPORT_THRESHOLD)));
$repository->markImporting();
}
// Clear the working set cache.
$this->workingSet = array();
@ -579,4 +616,30 @@ final class PhabricatorRepositoryDiscoveryEngine
PhabricatorWorker::scheduleTask($class, $data);
}
private function isInitialImport(array $refs) {
$commit_count = count($refs);
if ($commit_count <= PhabricatorRepository::IMPORT_THRESHOLD) {
// If we fetched a small number of commits, assume it's an initial
// commit or a stack of a few initial commits.
return false;
}
$viewer = $this->getViewer();
$repository = $this->getRepository();
$any_commits = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withRepository($repository)
->setLimit(1)
->execute();
if ($any_commits) {
// If the repository already has commits, this isn't an import.
return false;
}
return true;
}
}

View file

@ -51,6 +51,27 @@ abstract class PhabricatorRepositoryEngine extends Phobject {
return PhabricatorUser::getOmnipotentUser();
}
protected function newRepositoryLock(
PhabricatorRepository $repository,
$lock_key,
$lock_device_only) {
$lock_parts = array();
$lock_parts[] = $lock_key;
$lock_parts[] = $repository->getID();
if ($lock_device_only) {
$device = AlmanacKeys::getLiveDevice();
if ($device) {
$lock_parts[] = $device->getID();
}
}
$lock_name = implode(':', $lock_parts);
return PhabricatorGlobalLock::newLock($lock_name);
}
/**
* Verify that the "origin" remote exists, and points at the correct URI.
*

Some files were not shown because too many files have changed in this diff Show more